diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0bcaa0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# IDE files +*.csproj.user +*.sublime-project +*.sublime-workspace +.idea/ + +# Dojo source (downloaded at develop or build time) +/widgets/dijit +/widgets/dojo +/widgets/dojox +/widgets/util + +# Built dojo +/dist/static/widgets + +# User-specific settings +/dist/user.json + +# Binary and log stuffs from /dist/node_modules +builderror.log +.bin/ +build/ + +# Don't ignore our root build directory +!/build/ \ No newline at end of file diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d8cf7d4 --- /dev/null +++ b/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a7309c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,87 @@ +# +# Copyright (c) 2013 Citrix Systems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#Product: OpenXT +#Project: Synchronizer Administration Web UI +#Copyright© Citrix 2012 + +WUI=${DESTDIR}/sync-wui +WUI_SRCS=${DESTDIR}/sync-wui-sources + +BUILT=built +SRCS=sources + +DOJO=dojo-1.8.1 + +JS_SRC=$(shell find widgets -type f -print ) +DIST_SRC=$(shell find dist -type f -print ) + + +all: built.stamp + +# to maintain compatability with the existing relative paths we run two directories down, +# we don't need this as we can run our dojo binary from anywhere. + +# create the sources directory first, so that it does not contain any output from the build +built.stamp: sources build/sync-wui.profile.js ${JS_SRC} ${DIST_SRC} + mkdir -p a/b/c + mkdir -p ${BUILT} + cp -dr dist/. ${BUILT} + rm -rf dist/static/widgets + mkdir -p dist/static/widgets + (cd a/b/c && ${DOJO} profile="../../../build/sync-wui.profile.js" --release --bin java ) + mkdir -p dist/static/widgets + mkdir -p ${BUILT}/static/widgets + cp -dr dist/static/widgets/. ${BUILT}/static/widgets + rm -rf dist/static/widgets + find ${BUILT}/static/widgets/citrix/common -name '*.js' -exec rm '{}' ';' + find ${BUILT}/static/widgets/citrix/common/themes/tundra -name '[^tundra]*.css' -exec rm '{}' ';' + find ${BUILT}/static/widgets/citrix/sync-wui -name '*.js' -exec rm '{}' ';' + find ${BUILT}/static/widgets/citrix/sync-wui/themes/tundra -name '[^tundra]*.css' -exec rm '{}' ';' + find ${BUILT} -name '*.uncompressed.js' -exec rm '{}' ';' + touch $@ + +sources: + mkdir -p ${SRCS} + cp README.md ${SRCS}/ + cp -dr dist ${SRCS}/ + cp -dr build ${SRCS}/ + cp -dr widgets ${SRCS}/ + +install: + install -m 0755 -d ${WUI} + cp -dr ${BUILT}/. ${WUI} + install -m 0755 -d ${WUI_SRCS} + cp -dr ${SRCS}/. ${WUI_SRCS} + + rm -rf ${WUI}/static/widgets/citrix/common/templates + rm -rf ${WUI}/static/widgets/citrix/sync-wui/templates + rm -rf ${WUI}/static/widgets/citrix/sync-wui/nls + rm -rf ${WUI}/static/widgets/build-report.txt + + # Avoid packaging errors by removing the demos directory in dojox + rm -rf ${WUI}/static/widgets/dojox/data/demos/ + + # Remove unused themes + +clean: + rm -f built.stamp + rm -rf dist/static/widgets + rm -rf ${BUILT} + rm -rf ${SRCS} + diff --git a/README.md b/README.md new file mode 100644 index 0000000..51da445 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +#Product: OpenXT +#Project: Synchronizer Administration Web UI +#Copyright© Citrix 2012 + +Welcome to XenClient™ XT Synchronizer Web UI + +Read the README.md in the '/dist' folder first + +# Development Prerequisites + +git repos: sync-database and sync-ui-helper (parallel to this repo) + +# Set up your environment + +Go to http://download.dojotoolkit.org/release-1.8.1/ +Unpack a source distro from above into './widgets' to get dojo, dijit and utils + +# Running + +To debug, run the 'debug.sh' file in the build folder. It will serve dojo files from './widgets' so saving the source should refresh your browser +To build, run the 'build.sh' file in the build folder. It will compile dojo into layers to be served from './dist/widgets' \ No newline at end of file diff --git a/build/build.sh b/build/build.sh new file mode 100755 index 0000000..373363d --- /dev/null +++ b/build/build.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# +# Copyright (c) 2013 Citrix Systems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#Product: OpenXT +#Project: Synchronizer Administration Web UI +#Copyright© Citrix 2012 + +set -e + +if [ "${1}" ]; then + port="${1}" + file='app' + killall node || true +fi + +( cd "../widgets/util/buildscripts/" && rm -rf "../../../dist/static/widgets" && ./build.sh profile="../../../build/sync-wui.profile.js" --release --bin java ) + +rm -rf ../dist/static/widgets/citrix/common/*.js +rm -rf ../dist/static/widgets/citrix/common/templates +find ../dist/static/widgets/citrix/common/themes/tundra -type f -name [^tundra]*.css | xargs rm + +rm -rf ../dist/static/widgets/citrix/sync-wui/*.js +rm -rf ../dist/static/widgets/citrix/sync-wui/templates +rm -rf ../dist/static/widgets/citrix/sync-wui/nls +find ../dist/static/widgets/citrix/sync-wui/themes/tundra -type f -name [^tundra]*.css | xargs rm + +rm -rf ../dist/static/widgets/acme/*.uncompressed.js + +if [ "${port}" ]; then + cd "../dist/" + node "${file}" --port "${port}" --environment "release" & + sleep 3 + chromium-browser "http://127.0.0.1:${port}" +fi diff --git a/build/debug.sh b/build/debug.sh new file mode 100755 index 0000000..8a92b49 --- /dev/null +++ b/build/debug.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# +# Copyright (c) 2013 Citrix Systems, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +#Product: OpenXT +#Project: Synchronizer Administration Web UI +#Copyright© Citrix 2012 + +set -e +port='9090' +file='app' + +killall node || true +cd "../dist/" +rm -rf "static/widgets" +node "${file}" --port "${port}" --environment "debug" & +sleep 3 +chromium-browser "http://127.0.0.1:${port}" diff --git a/build/npm-cache.js b/build/npm-cache.js new file mode 100644 index 0000000..1ac682e --- /dev/null +++ b/build/npm-cache.js @@ -0,0 +1,75 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var fs = require('fs'), + path = require('path'), + exec = require('child_process').exec, + util = require('util'); + +var packageFileName = 'package.json'; +var modulesDirName = 'node_modules'; +var cacheDirectory = process.cwd(); +var npmCacheAddMask = 'npm cache add %s@%s; echo %s'; +var sourceDirMask = '%s/%s/%s/package'; +var targetDirMask = '%s/node_modules/%s'; + +function deleteFolder(folder) { + if (fs.existsSync(folder)) { + var files = fs.readdirSync(folder); + files.forEach(function(file) { + file = folder + "/" + file; + if (fs.lstatSync(file).isDirectory()) { + deleteFolder(file); + } else { + fs.unlinkSync(file); + } + }); + fs.rmdirSync(folder); + } +} + +function downloadSource(folder) { + var packageFile = path.join(folder, packageFileName); + if (fs.existsSync(packageFile)) { + var data = fs.readFileSync(packageFile); + var package = JSON.parse(data); + + function getVersion(data) { + var version = data.match(/-([^-]+)\.tgz/); + return version[1]; + } + + var callback = function(error, stdout, stderr) { + var dependency = stdout.trim(); + var version = getVersion(stderr); + var sourceDir = util.format(sourceDirMask, cacheDirectory, dependency, version); + var targetDir = util.format(targetDirMask, folder, dependency); + var modulesDir = folder + '/' + modulesDirName; + + if (!fs.existsSync(modulesDir)) { + fs.mkdirSync(modulesDir); + } + + fs.renameSync(sourceDir, targetDir); + deleteFolder(cacheDirectory + '/' + dependency); + downloadSource(targetDir); + }; + + for (dependency in package.dependencies) { + var version = package.dependencies[dependency]; + exec(util.format(npmCacheAddMask, dependency, version, dependency), callback); + } + } +} + +if (!fs.existsSync(path.join(process.cwd(), packageFileName))) { + console.log(util.format("Unable to find file '%s'.", packageFileName)); + process.exit(); +} + +deleteFolder(path.join(process.cwd(), modulesDirName)); +process.env.npm_config_cache = cacheDirectory; +downloadSource(process.cwd()); \ No newline at end of file diff --git a/build/sync-wui.profile.js b/build/sync-wui.profile.js new file mode 100644 index 0000000..1aeaff0 --- /dev/null +++ b/build/sync-wui.profile.js @@ -0,0 +1,128 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +cwd = (new java.io.File(".")).getAbsolutePath(); + +for(var dojohome = ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){ + arg = (rhinoArgs[i++] + "").split("="); + if(arg[0] == "baseUrl"){ + dojohome = arg[1]; + break; + } +} + +if (dojohome.indexOf(".")==0) { + realBuildScriptsPath=cwd+"/"; +} else { + realBuildScriptsPath=dojohome; +} + +var profile = { + basePath: "../widgets/util/buildscripts/", + layerOptimize: "shrinksafe", + cssOptimize: "comments", + releaseDir: "../../../dist/static/widgets", + mini: true, + copyTests: false, + + packages: [ + { + name: "dojo", + location: realBuildScriptsPath + "../../dojo" + }, + { + name: "dijit", + location: realBuildScriptsPath + "../../dijit" + }, + { + name: "dojox", + location: realBuildScriptsPath + "../../dojox" + }, + { + name: "dgrid", + location: cwd + "/../../../widgets/dgrid" + }, + { + name: "put-selector", + location: cwd + "/../../../widgets/put-selector" + }, + { + name: "xstyle", + location: cwd + "/../../../widgets/xstyle" + }, + { + name: "citrix", + location: cwd + "/../../../widgets/citrix" + }, + { + name: "acme", + location: cwd + "/../../../widgets/acme" + } + ], + layers: { + "dojo": { + include: [ + "dojo/_base/url", + "dojo/cache", + "dojo/cookie", + "dojo/data/ItemFileReadStore", + "dojo/data/util/filter", + "dojo/data/util/simpleFetch", + "dojo/data/util/sorter", + "dojo/date/locale", + "dojo/date/stamp", + "dojo/DeferredList", + "dojo/dnd/autoscroll", + "dojo/dnd/Avatar", + "dojo/dnd/common", + "dojo/dnd/Container", + "dojo/dnd/Manager", + "dojo/dnd/Moveable", + "dojo/dnd/Mover", + "dojo/dnd/Selector", + "dojo/dnd/Source", + "dojo/dnd/TimedMoveable", + "dojo/html", + "dojo/NodeList-manipulate", + "dojo/NodeList-traverse", + "dojo/number", + "dojo/parser", + "dojo/require", + "dojo/selector/acme", + "dojo/Stateful", + "dojo/text", + "dojo/touch", + "dojo/uacss", + "dojo/window" + ] + }, + "citrix/sync-wui": { + copyright: "// Author: Rob Moran\n// Company: Citrix\n// Product: OpenXT\n// Project: Synchronizer Administration Web UI\n// Copyright© Citrix 2012\n", + exclude: [ + "dojo" + ], + include: [ + "citrix/sync-wui/AlertDialog", + "citrix/sync-wui/MainTabContainer", + "citrix/common/ContentPane", + "citrix/sync-wui/UnseenDeviceWidget", + "citrix/sync-wui/DeviceWidget", + "citrix/sync-wui/VMWidget", + "citrix/sync-wui/ServiceVMWidget", + "citrix/sync-wui/DiskWidget" + ] + }, + "acme": { + copyright: "// Author: Rob Moran\n// Company: Citrix\n// Product: OpenXT\n// Project: Synchronizer Administration Web UI\n// Copyright© Citrix 2012\n", + exclude: [ + "dojo" + ], + include: [ + "acme/SampleWidget" + ] + } + } + }; diff --git a/dist/README.md b/dist/README.md new file mode 100644 index 0000000..2ad24f9 --- /dev/null +++ b/dist/README.md @@ -0,0 +1,43 @@ +#Product: OpenXT +#Project: Synchronizer Administration Web UI +#Copyright© Citrix 2012 + +Welcome to XenClient™ XT Synchronizer Web UI + +# Prerequisites + +Node.js 0.8+ (http://nodejs.org) +Python 2.6+ (http://www.python.org) +NPM (should ship with node) +Citrix sync-ui-helper and sync-database + +# Building Node.js modules + +Run 'npm rebuild' in this folder to build the source in 'node_modules' + +# Installing OCI + +1 Download instantclient-basic-*.zip and instantclient-sdk-*.zip for your system from http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html + +2 Unzip the above to somewhere (e.g. '/opt/instantclient') + +3 Symlink some stuffs: + $ cd /opt/instantclient + $ sudo ln -s libocci.so.11.1 libocci.so + $ sudo ln -s libclntsh.so.11.1 libclntsh.so + +4 Install libaio: + $ sudo apt-get install libaio-dev + +5 Set environment variables (add them to your '~/.bashrc'): + LD_LIBRARY_PATH="/opt/instantclient" + +# Configuration + +Edit 'config.json' to reflect your environment, specifically the ldap and sync-ui-helper sections +Alternatively, add a 'user.json' file with a similar json structure which will overwrite specific elements in the 'config.json' file + +# Running + +Execute: + $ node app [--port --environment "debug"/"release"] \ No newline at end of file diff --git a/dist/app.js b/dist/app.js new file mode 100644 index 0000000..0ddda44 --- /dev/null +++ b/dist/app.js @@ -0,0 +1,87 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var express = require('express') + , socketio = require('socket.io') + , hbs = require('hbs') + , nconf = require('nconf') + , path = require('path') + , passport = require('passport') + , flash = require('connect-flash') + , http = require('http') + , fs = require('fs'); + +// Configuration +nconf.argv().env(); +if (fs.existsSync(path.join(__dirname, 'user.json'))) { + nconf.file('user', path.join(__dirname, 'user.json')); +} +nconf.file('global', path.join(__dirname, 'config.json')); + +// Variables +var app = express(); +var server = http.createServer(app); +var io = socketio.listen(server); +var sessionStore = new express.session.MemoryStore(); +var reloadPaths = [path.join(__dirname, 'static'), path.join(__dirname, 'views')]; + +// Configuration +app.configure(function() { + app.locals.title = "XenClient™ XT"; + app.set('views', __dirname + '/views'); + app.set('view engine', 'hbs'); + app.use(express.bodyParser()); + app.use(express.cookieParser('sync-wui')); + app.use(express.session({ store: sessionStore, cookie: { httpOnly: false } })); + app.use(flash()); + app.use(passport.initialize()); + app.use(passport.session()); + app.use(app.router); + app.use(express.static(path.join(__dirname, 'static'))); +}); + +// Development +if (nconf.get("environment") == "debug") { + app.use(express.static(path.join(__dirname, '../'))); + app.use(express.logger('dev')); + app.use(express.errorHandler()); + reloadPaths.push(path.join(__dirname, '../widgets')); + app.locals.suffix = "?" + Date.now(); +} else { + app.locals.release = true; +} +// need to put this somewhere else with a file watcher if we want to support icon addition/deletion after server is started +app.locals.defaulticons = fs.readdirSync("./static/images/vms/default"); +app.locals.defaultwallpapers = fs.readdirSync("./static/images/wallpaper/default"); +app.locals.pluginicons = fs.readdirSync("./static/images/vms/plugins"); +app.locals.pluginwallpapers = fs.readdirSync("./static/images/wallpaper/plugins"); + +// Libraries +var strategy = require('./lib/passport-ldap').Strategy; +var account = require('./controllers/account')(strategy); +var controllers = require('./controllers/index'); +var dataAdapter = require('./data_access/sync-ui-helper'); +require('./lib/socket-wrapper')(io, sessionStore, dataAdapter); +require('./lib/live-reload')(io, reloadPaths); +require('./lib/hbs-block')(hbs); + +// Routes +app.get('/', account.authenticated, controllers("home")); +app.get('/login', account.login); +app.get('/logout', account.logout); +app.post('/login', account.authenticate); + +// Start +var address = nconf.get("address"); +if(address && address != "") { + server.listen(nconf.get("port"), nconf.get("address"), function() { + console.log("node server listening on port " + nconf.get("port") + " and address " + nconf.get("address")); + }); +} else { + server.listen(nconf.get("port"), function() { + console.log("node server listening on port " + nconf.get("port")); + }); +} diff --git a/dist/config.json b/dist/config.json new file mode 100644 index 0000000..85edc26 --- /dev/null +++ b/dist/config.json @@ -0,0 +1,16 @@ +{ + "port": 80, + "address": "", + "ldap": { + "url": "ldap://:389", + "adminDn": "CN=,OU=,OU=,DC=,DC=", + "adminPassword": "", + "searchBase": "OU=,OU=,DC=,DC=", + "searchMap": "sAMAccountName" + }, + "sync-ui-helper": { + "path": "/usr/bin/sync-ui-helper", + "connection": "/@", + "package": "sync_admin" + } +} \ No newline at end of file diff --git a/dist/controllers/account.js b/dist/controllers/account.js new file mode 100644 index 0000000..194a569 --- /dev/null +++ b/dist/controllers/account.js @@ -0,0 +1,50 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var querystring = require('querystring') + , passport = require('passport') + , util = require('util'); + +module.exports = function(strategy) { + + var passportStrategy = new strategy(); + + passport.serializeUser(function(user, done) { + done(null, user); + }); + + passport.deserializeUser(function(user, done) { + done(null, user); + }); + + passport.use(passportStrategy); + + return { + login: function(req, res) { + res.render('login', { login: true, message: req.flash('error') }); + }, + + logout: function(req, res) { + req.logout(); + res.redirect('/'); + }, + + authenticate: function(req, res, next) { + var returnUrl = req.query.returnUrl || "/"; + var query = querystring.stringify({ returnUrl: returnUrl }); + passport.authenticate(passportStrategy.name, { successRedirect: returnUrl, failureRedirect: util.format('/login?%s', query), failureFlash: true })(req, res, next); + }, + + authenticated: function(req, res, next) { + if (req.isAuthenticated()) { + res.locals.user = req.user; + return next(); + } + var query = querystring.stringify({ returnUrl: req.url }); + res.redirect(util.format('/login?%s', query)); + } + } +} \ No newline at end of file diff --git a/dist/controllers/index.js b/dist/controllers/index.js new file mode 100644 index 0000000..61719df --- /dev/null +++ b/dist/controllers/index.js @@ -0,0 +1,11 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +module.exports = function(view) { + return function(req, res) { + res.render(view); + } +}; \ No newline at end of file diff --git a/dist/data_access/oracle.js b/dist/data_access/oracle.js new file mode 100644 index 0000000..b995469 --- /dev/null +++ b/dist/data_access/oracle.js @@ -0,0 +1,72 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var oracle = require('db-oracle') + , nconf = require('nconf'); + +var self = this; + +function connect(fn) { + new oracle.Database({ + hostname: nconf.get("oracle:host"), + user: nconf.get("oracle:username"), + password: nconf.get("oracle:password") + }).connect(fn); +} + +self.list_devices = function(params, fn) { + connect(function(error) { + if (error) { + return console.log("CONNECTION ERROR: " + error); + } + + this.query().select('DEVICE_UUID as "device_uuid", DEVICE_NAME as "device_name"').from('DEVICE').execute(function(error, rows) { + if (error) { + return console.log('ERROR: ' + error); + } + return fn(rows); + }); + }); +}; + +self.list_vms = function(params, fn) { + connect(function(error) { + if (error) { + return console.log("CONNECTION ERROR: " + error); + } + + this.query().select('VM_UUID as "vm_uuid", VM_NAME as "vm_name"').from('VM').execute(function(error, rows) { + if (error) { + return console.log('ERROR: ' + error); + } + return fn(rows); + }); + }); +}; + +self.get_vm = function(params, fn) { + connect(function(error) { + if (error) { + return console.log("CONNECTION ERROR: " + error); + } + + this.query().select('VM_UUID as "vm_uuid", VM_NAME as "vm_name"').from('VM').where('VM_UUID = ?', [ params.vm_uuid ]).execute(function(error, result) { + if (error) { + return console.log('ERROR: ' + error); + } + return fn(result); + }); + }); +}; + +// fn, args, fn +exports.execute = function() { + var args = [].slice.call(arguments); + var fn = args.shift(); + if (typeof(self[fn]) === "function") { + self[fn].apply(self, args); + } +} \ No newline at end of file diff --git a/dist/data_access/sync-ui-helper.js b/dist/data_access/sync-ui-helper.js new file mode 100644 index 0000000..7417ee6 --- /dev/null +++ b/dist/data_access/sync-ui-helper.js @@ -0,0 +1,106 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var nconf = require('nconf') + , spawn = require('child_process').spawn; + +var success_status = "success"; +var json_connect = { command: "connect", login: nconf.get("sync-ui-helper:connection") }; +var json_call = { command: "call", procedure: null, package: nconf.get("sync-ui-helper:package"), params: null }; +var json_disconnect = { command: "disconnect" }; +var json_quit = { command: "quit" }; + +var helper = spawn(nconf.get("sync-ui-helper:path")); +console.log("sync-ui-helper spawned with pid: " + helper.pid); + +var id = 0; +var returnFns = {}; +var data = ""; + +helper.stdout.on('data', function (chunk) { + data += chunk; + var index = data.indexOf('\n'); + while (index > -1) { + var message = data.slice(0, index); + receiveMessage(message); + data = data.slice(index + 1); + index = data.indexOf('\n'); + } +}); + +helper.stderr.on('data', function (error) { + console.log("sync-ui-helper returned error: " + error); +}); + +helper.on('exit', function (code) { + console.log("sync-ui-helper exited with code: " + code); +}); + +function sendMessage(json, cb) { + json.id = id; + helper.stdin.write(JSON.stringify(json) + "\n"); + if (cb) { + returnFns[id] = cb; + } + id ++; +} + +function getStatus(json) { + var code = 0; + var message = "An error occurred"; + var status = json.status; + + code = json.oracle_code || json.code || code; + message = json.oracle_message || json.message || message; + + console.log("sync-ui-helper returned " + status + ": [" + code + "] " + message); + + return { error: { status: status, code: code, message: message }}; +} + +function receiveMessage(message) { + var json = JSON.parse(message); + var result; + + if (json.status == success_status) { + result = json.return_val; + } else { + result = getStatus(json); + } + + if (returnFns[json.id]) { + returnFns[json.id].call(this, result, json.status); + delete returnFns[json.id]; + } +} + +process.on('exit', function () { + sendMessage(json_disconnect); + sendMessage(json_quit); +}); + +sendMessage(json_connect, function(result, status) { + if (status == success_status) { + console.log("sync-ui-helper connected to database"); + } +}); + +exports.execute = function() { + var args = [].slice.call(arguments); + var cb = null; + + json_call.procedure = args.shift(); + json_call.params = null; + + if (typeof(args[0]) === "object") { + json_call.params = args.shift(); + } + if (typeof(args[0]) === "function") { + cb = args.shift(); + } + + sendMessage(json_call, cb); +}; \ No newline at end of file diff --git a/dist/lib/hbs-block/index.js b/dist/lib/hbs-block/index.js new file mode 100644 index 0000000..3c48f22 --- /dev/null +++ b/dist/lib/hbs-block/index.js @@ -0,0 +1,27 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +module.exports = function(hbs) { + + var blocks = {}; + + hbs.registerHelper('extend', function(name, context) { + var block = blocks[name]; + if (!block) { + block = blocks[name] = []; + } + + block.push(context(this)); + }); + + hbs.registerHelper('block', function(name) { + var val = (blocks[name] || []).join('\n'); + + // clear the block + blocks[name] = []; + return val; + }); +}; \ No newline at end of file diff --git a/dist/lib/live-reload/index.js b/dist/lib/live-reload/index.js new file mode 100644 index 0000000..2579d58 --- /dev/null +++ b/dist/lib/live-reload/index.js @@ -0,0 +1,52 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var chokidar, consoleMessage, cssExtensions, lastRun, pathlib, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + +pathlib = require('path'); + +chokidar = require('chokidar'); + +lastRun = { + refresh_css: Date.now(), + refresh_site: Date.now() +}; + +cssExtensions = ['.css', '.styl', '.stylus', '.less']; + +consoleMessage = { + refresh_css: 'CSS files changed. Updating browser...', + refresh_site: 'Client files changed. Reloading browser...' +}; + +module.exports = function(io, dirs) { + var dir, onChange, watchDirs, watcher; + watcher = chokidar.watch(dirs, { + ignored: /(\/\.|~$)/ + }); + watcher.on('add', function(path) { + return onChange(path, 'added'); + }); + watcher.on('change', function(path) { + return onChange(path, 'changed'); + }); + watcher.on('unlink', function(path) { + return onChange(path, 'removed'); + }); + watcher.on('error', function(error) { + return console.log('✎', ("Error: " + error)); + }); + return onChange = function(path, event) { + var action, _ref; + action = (_ref = pathlib.extname(path), __indexOf.call(cssExtensions, _ref) >= 0) ? 'refresh_css' : 'refresh_site'; + if ((Date.now() - lastRun[action]) > 1000) { + console.log('✎', consoleMessage[action]); + io.sockets.emit(action); + return lastRun[action] = Date.now(); + } + }; +}; \ No newline at end of file diff --git a/dist/lib/passport-ldap/index.js b/dist/lib/passport-ldap/index.js new file mode 100644 index 0000000..084dc82 --- /dev/null +++ b/dist/lib/passport-ldap/index.js @@ -0,0 +1,54 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var passport = require('passport') + , nconf = require('nconf') + , util = require('util') + , ldapAuth = require('ldapauth'); + +var ldap = new ldapAuth({ + url: nconf.get("ldap:url"), + adminDn: nconf.get("ldap:adminDn"), + adminPassword: nconf.get("ldap:adminPassword"), + searchBase: nconf.get("ldap:searchBase"), + searchFilter: util.format("(%s={{username}})", nconf.get("ldap:searchMap")), + cache: true +}); + +function Strategy(options) { + options = options || {}; + + this._usernameField = options.usernameField || 'username'; + this._passwordField = options.passwordField || 'password'; + + passport.Strategy.call(this); + this.name = 'ldap'; +} + +util.inherits(Strategy, passport.Strategy); + +Strategy.prototype.authenticate = function(req) { + + var username = req.body[this._usernameField]; + var password = req.body[this._passwordField]; + + if (!username || !password) { + return this.fail(new Error('Missing Username or password')); + } + + var self = this; + + ldap.authenticate(username, password, function (err, user) { + if (err) { + console.log(err.message); + self.fail(new Error("Incorrect Username or password")); + } else { + self.success({ username: username, displayName: user.displayName }); + } + }); +}; + +exports.Strategy = Strategy; \ No newline at end of file diff --git a/dist/lib/passport-local/index.js b/dist/lib/passport-local/index.js new file mode 100644 index 0000000..6951fb0 --- /dev/null +++ b/dist/lib/passport-local/index.js @@ -0,0 +1,46 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var passport = require('passport') + , nconf = require('nconf') + , util = require('util'); + +var users = nconf.get("passport-local:users"); + +function Strategy(options) { + options = options || {}; + + this._usernameField = options.usernameField || 'username'; + this._passwordField = options.passwordField || 'password'; + + passport.Strategy.call(this); + this.name = 'local'; +} + +util.inherits(Strategy, passport.Strategy); + +Strategy.prototype.authenticate = function(req) { + + var username = req.body[this._usernameField]; + var password = req.body[this._passwordField]; + + if (!username || !password) { + return this.fail(new Error('Missing Username or password')); + } + + var self = this; + + for (var i = 0; i < users.length; i++) { + var user = users[i]; + if (user.username === username && user.password === password) { + return self.success({ username: username, displayName: user.displayname }); + } + } + + self.fail(new Error("Incorrect Username or password")); +}; + +exports.Strategy = Strategy; \ No newline at end of file diff --git a/dist/lib/socket-wrapper/index.js b/dist/lib/socket-wrapper/index.js new file mode 100644 index 0000000..224d216 --- /dev/null +++ b/dist/lib/socket-wrapper/index.js @@ -0,0 +1,46 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +var qs = require('querystring'); + +processSession = function(socket) { + if (socket.sessionId) { + return true; + } + try { + var rawCookie = socket.handshake.headers.cookie; + var cookie = qs.parse(rawCookie, '; '); + var sessionId = cookie['connect.sid'].split('.')[0]; + var unsignedSessionId = sessionId.split(':')[1].replace(/\s/g, '+'); + return socket.sessionId = unsignedSessionId; + } catch (e) { + console.log('Warning: connect.sid session cookie not detected. User may have cookies disabled or session cookie has expired'); + return false; + } +}; + +module.exports = function(io, sessionStore, dataAdapter) { + io.set('log level', 1); + io.sockets.on('connection', function (socket) { + if (processSession(socket)) { + socket.on('data_access', function () { + var args = [].slice.call(arguments); + // Check for callback + if (typeof(args[args.length - 1]) === "function") { + sessionStore.get(socket.sessionId, function (err, session) { + if (!session.passport.user) { + var cb = args.pop(); + cb({ redirect: "/login" }); + } + if (dataAdapter) { + dataAdapter.execute.apply(dataAdapter, args); + } + }); + } + }); + } + }); +} \ No newline at end of file diff --git a/dist/node_modules/chokidar/.npmignore b/dist/node_modules/chokidar/.npmignore new file mode 100644 index 0000000..6824a2f --- /dev/null +++ b/dist/node_modules/chokidar/.npmignore @@ -0,0 +1,8 @@ +.lock-wscript +.svn/ +.hg/ +.git/ +CVS/ +*~ +*.bak +.DS_Store diff --git a/dist/node_modules/chokidar/CHANGELOG.md b/dist/node_modules/chokidar/CHANGELOG.md new file mode 100644 index 0000000..2d474bb --- /dev/null +++ b/dist/node_modules/chokidar/CHANGELOG.md @@ -0,0 +1,37 @@ +# Chokidar 0.4.0 (July 26, 2012) +* Added `all` event that receives two args (event name and path) that +combines `add`, `change` and `unlink` events. +* Switched to `fs.watchFile` on node.js 0.8 on windows. +* Files are now correctly unwatched after unlink. + +# Chokidar 0.3.0 (June 24, 2012) +* `unlink` event are no longer emitted for directories, for consistency +with `add`. + +# Chokidar 0.2.6 (June 8, 2012) +* Prevented creating of duplicate 'add' events. + +# Chokidar 0.2.5 (June 8, 2012) +* Fixed a bug when new files in new directories hadn't been added. + +# Chokidar 0.2.4 (June 7, 2012) +* Fixed a bug when unlinked files emitted events after unlink. + +# Chokidar 0.2.3 (May 12, 2012) +* Fixed watching of files on windows. + +# Chokidar 0.2.2 (May 4, 2012) +* Fixed watcher signature. + +# Chokidar 0.2.1 (May 4, 2012) +* Fixed invalid API bug when using `watch()`. + +# Chokidar 0.2.0 (May 4, 2012) +* Rewritten in js. + +# Chokidar 0.1.1 (April 26, 2012) +* Changed api to `chokidar.watch()`. +* Fixed compilation on windows. + +# Chokidar 0.1.0 (April 20, 2012) +* Initial release. diff --git a/dist/node_modules/chokidar/README.md b/dist/node_modules/chokidar/README.md new file mode 100644 index 0000000..89d97bd --- /dev/null +++ b/dist/node_modules/chokidar/README.md @@ -0,0 +1,83 @@ +# Chokidar +A neat wrapper around node.js fs.watch / fs.watchFile. + +## Why? +Node.js `fs.watch`: + +* Doesn't report filenames on mac. +* Doesn't report events at all when using editors like TextMate2 on mac. +* Sometimes report events twice. +* Has only one non-useful event: `rename`. +* Has [a lot of other issues](https://github.com/joyent/node/issues/search?utf8=✓&q=fs.watch) + +Node.js `fs.watchFile`: + +* Doesn't work on windows +* Almost as shitty in event tracking. + +Chokidar resolves this problems. + +It is used in [brunch](http://brunch.io) and had proven itself in production env. + +## Getting started +Install chokidar via node.js package manager: + + npm install chokidar + +Then just require the package in your code: + +```javascript +var chokidar = require('chokidar'); + +var watcher = chokidar.watch('file or dir', {ignored: /^\./, persistent: true}); + +watcher + .on('add', function(path) {console.log('File', path, 'has been added');}) + .on('change', function(path) {console.log('File', path, 'has been changed');}) + .on('unlink', function(path) {console.log('File', path, 'has been removed');}) + .on('error', function(error) {console.error('Error happened', error);}) + +watcher.add('new-file'); +watcher.add(['new-file-2', 'new-file-3']); + +// Only needed if watching is persistent. +watcher.close(); +``` + +## API +* `chokidar.watch(paths, options)`: takes paths to be watched and options: + * `options.ignored` (regexp or function) files to be ignored. Example: + `chokidar.watch('file', {ignored: /^\./})`. + * `options.persistent` (default: `false`). indicates whether the process + should continue to run as long as files are being watched. + +`chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`: + +* `.add(file / files)`: add directories / files for tracking. +Takes an array of strings (file paths) or just one path. +* `.on(event, callback)`: listen for an FS event. +Available events: `add`, `change`, `unlink`, `error`, `all`. +* `.close()`: remove all listeners from watched files. + +## License +The MIT license. + +Copyright (c) 2012 Paul Miller (http://paulmillr.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE 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/dist/node_modules/chokidar/lib/index.js b/dist/node_modules/chokidar/lib/index.js new file mode 100644 index 0000000..a79f5bc --- /dev/null +++ b/dist/node_modules/chokidar/lib/index.js @@ -0,0 +1,237 @@ +// Generated by CoffeeScript 1.3.3 +(function() { + 'use strict'; + + var EventEmitter, FSWatcher, fs, nodeVersion, sysPath, + __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __slice = [].slice; + + EventEmitter = require('events').EventEmitter; + + fs = require('fs'); + + sysPath = require('path'); + + nodeVersion = process.versions.node.substring(0, 3); + + exports.FSWatcher = FSWatcher = (function(_super) { + + __extends(FSWatcher, _super); + + function FSWatcher(options) { + var _base, _ref, + _this = this; + this.options = options != null ? options : {}; + this.close = __bind(this.close, this); + + this.add = __bind(this.add, this); + + this._handle = __bind(this._handle, this); + + this._handleDir = __bind(this._handleDir, this); + + this._handleFile = __bind(this._handleFile, this); + + this._watch = __bind(this._watch, this); + + this._remove = __bind(this._remove, this); + + this._removeFromWatchedDir = __bind(this._removeFromWatchedDir, this); + + this._addToWatchedDir = __bind(this._addToWatchedDir, this); + + this._getWatchedDir = __bind(this._getWatchedDir, this); + + this.watched = Object.create(null); + this.watchers = []; + if ((_ref = (_base = this.options).persistent) == null) { + _base.persistent = false; + } + this._ignored = (function() { + switch (toString.call(_this.options.ignored)) { + case '[object RegExp]': + return function(string) { + return this.options.ignored.test(string); + }; + case '[object Function]': + return _this.options.ignored; + default: + return function() { + return false; + }; + } + })(); + } + + FSWatcher.prototype._getWatchedDir = function(directory) { + var dir, _base, _ref; + dir = directory.replace(/[\\\/]$/, ''); + return (_ref = (_base = this.watched)[dir]) != null ? _ref : _base[dir] = []; + }; + + FSWatcher.prototype._addToWatchedDir = function(directory, file) { + var watchedFiles; + watchedFiles = this._getWatchedDir(directory); + return watchedFiles.push(file); + }; + + FSWatcher.prototype._removeFromWatchedDir = function(directory, file) { + var watchedFiles, + _this = this; + watchedFiles = this._getWatchedDir(directory); + return watchedFiles.some(function(watchedFile, index) { + if (watchedFile === file) { + watchedFiles.splice(index, 1); + return true; + } + }); + }; + + FSWatcher.prototype._remove = function(directory, item) { + var fullPath, nestedDirectoryChildren, + _this = this; + fullPath = sysPath.join(directory, item); + nestedDirectoryChildren = this._getWatchedDir(fullPath).slice(); + this._removeFromWatchedDir(directory, item); + nestedDirectoryChildren.forEach(function(nestedItem) { + return _this._remove(fullPath, nestedItem); + }); + fs.unwatchFile(fullPath); + return this.emit('unlink', fullPath); + }; + + FSWatcher.prototype._watch = function(item, itemType, callback) { + var basename, directory, options, parent, watcher, + _this = this; + if (callback == null) { + callback = (function() {}); + } + directory = sysPath.dirname(item); + basename = sysPath.basename(item); + parent = this._getWatchedDir(directory); + options = { + persistent: this.options.persistent + }; + if (parent.indexOf(basename) >= 0) { + return; + } + this._addToWatchedDir(directory, basename); + if (process.platform === 'win32' && nodeVersion === '0.6') { + watcher = fs.watch(item, options, function(event, path) { + return callback(item); + }); + this.watchers.push(watcher); + } else { + options.interval = 100; + fs.watchFile(item, options, function(curr, prev) { + if (curr.mtime.getTime() !== prev.mtime.getTime()) { + return callback(item); + } + }); + } + if (itemType === 'file') { + return this.emit('add', item); + } + }; + + FSWatcher.prototype._handleFile = function(file) { + var _this = this; + return this._watch(file, 'file', function(file) { + return _this.emit('change', file); + }); + }; + + FSWatcher.prototype._handleDir = function(directory) { + var read, + _this = this; + read = function(directory) { + return fs.readdir(directory, function(error, current) { + var previous; + if (error != null) { + return _this.emit('error', error); + } + if (!current) { + return; + } + previous = _this._getWatchedDir(directory); + previous.filter(function(file) { + return current.indexOf(file) < 0; + }).forEach(function(file) { + return _this._remove(directory, file); + }); + return current.filter(function(file) { + return previous.indexOf(file) < 0; + }).forEach(function(file) { + return _this._handle(sysPath.join(directory, file)); + }); + }); + }; + read(directory); + return this._watch(directory, 'directory', read); + }; + + FSWatcher.prototype._handle = function(item) { + var _this = this; + if (this._ignored(item)) { + return; + } + return fs.realpath(item, function(error, path) { + if (error != null) { + return _this.emit('error', error); + } + return fs.stat(item, function(error, stats) { + if (error != null) { + return _this.emit('error', error); + } + if (stats.isFile()) { + _this._handleFile(item); + } + if (stats.isDirectory()) { + return _this._handleDir(item); + } + }); + }); + }; + + FSWatcher.prototype.emit = function() { + var args, event; + event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + FSWatcher.__super__.emit.apply(this, arguments); + if (event === 'add' || event === 'change' || event === 'unlink') { + return FSWatcher.__super__.emit.apply(this, ['all', event].concat(__slice.call(args))); + } + }; + + FSWatcher.prototype.add = function(files) { + if (!Array.isArray(files)) { + files = [files]; + } + files.forEach(this._handle); + return this; + }; + + FSWatcher.prototype.close = function() { + var _this = this; + this.watchers.forEach(function(watcher) { + return watcher.close(); + }); + Object.keys(this.watched).forEach(function(directory) { + return _this.watched[directory].forEach(function(file) { + return fs.unwatchFile(sysPath.join(directory, file)); + }); + }); + this.watched = Object.create(null); + return this; + }; + + return FSWatcher; + + })(EventEmitter); + + exports.watch = function(files, options) { + return new FSWatcher(options).add(files); + }; + +}).call(this); diff --git a/dist/node_modules/chokidar/package.json b/dist/node_modules/chokidar/package.json new file mode 100644 index 0000000..6bfbac4 --- /dev/null +++ b/dist/node_modules/chokidar/package.json @@ -0,0 +1,33 @@ +{ + "name": "chokidar", + "description": "A neat wrapper around node.js fs.watch / fs.watchFile.", + "version": "0.4.0", + "keywords": ["fs", "watch", "watchFile", "watcher", "file"], + "homepage": "https://github.com/paulmillr/chokidar", + "author": "Paul Miller (http://paulmillr.com)", + "repository": { + "type": "git", + "url": "https://github.com/paulmillr/chokidar.git" + }, + "bugs": { + "url": "http://github.com/paulmillr/chokidar/issues" + }, + "licenses": [{ + "type": "MIT", + "url": "http://github.com/paulmillr/chokidar/raw/master/README.md" + }], + "main": "./lib/index", + "engines": { + "node": "~0.6.10 || 0.7 || 0.8" + }, + "scripts": { + "prepublish": "coffee -o lib/ src/", + "postinstall": "node setup.js postinstall", + "test": "node setup.js test" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "1.3.0", + "expect.js": "0.1.2" + } +} diff --git a/dist/node_modules/chokidar/setup.js b/dist/node_modules/chokidar/setup.js new file mode 100644 index 0000000..a9e806a --- /dev/null +++ b/dist/node_modules/chokidar/setup.js @@ -0,0 +1,30 @@ +var exec = require('child_process').exec; +var sysPath = require('path'); +var fs = require('fs'); + +// Cross-platform npm postinstall & test script. + +var mode = process.argv[2]; + +var fsExists = fs.exists || sysPath.exists; + +var execute = function(pathParts, params, callback) { + if (callback == null) callback = function() {}; + var path = sysPath.join.apply(null, pathParts); + var command = 'node ' + path + ' ' + params; + console.log('Executing', command); + exec(command, function(error, stdout, stderr) { + if (error != null) return process.stderr.write(stderr.toString()); + console.log(stdout.toString()); + }); +}; + +if (mode === 'postinstall') { + fsExists(sysPath.join(__dirname, 'lib'), function(exists) { + if (exists) return; + execute(['node_modules', 'coffee-script', 'bin', 'coffee'], '-o lib/ src/'); + }); +} else if (mode === 'test') { + execute(['node_modules', 'mocha', 'bin', 'mocha'], + '--compilers coffee:coffee-script --require test/common.coffee --colors'); +} diff --git a/dist/node_modules/chokidar/src/index.coffee b/dist/node_modules/chokidar/src/index.coffee new file mode 100644 index 0000000..81717eb --- /dev/null +++ b/dist/node_modules/chokidar/src/index.coffee @@ -0,0 +1,189 @@ +'use strict' + +{EventEmitter} = require 'events' +fs = require 'fs' +sysPath = require 'path' + +nodeVersion = process.versions.node.substring(0, 3) + +# Watches files & directories for changes. +# +# Emitted events: `add`, `change`, `unlink`, `error`. +# +# Examples +# +# var watcher = new FSWatcher() +# .add(directories) +# .on('add', function(path) {console.log('File', path, 'was added');}) +# .on('change', function(path) {console.log('File', path, 'was changed');}) +# .on('unlink', function(path) {console.log('File', path, 'was removed');}) +# +exports.FSWatcher = class FSWatcher extends EventEmitter + constructor: (@options = {}) -> + @watched = Object.create(null) + @watchers = [] + @options.persistent ?= no + @_ignored = do => + switch toString.call(@options.ignored) + when '[object RegExp]' then (string) -> @options.ignored.test(string) + when '[object Function]' then @options.ignored + else -> no + + _getWatchedDir: (directory) => + dir = directory.replace(/[\\\/]$/, '') + @watched[dir] ?= [] + + _addToWatchedDir: (directory, file) => + watchedFiles = @_getWatchedDir directory + watchedFiles.push file + + _removeFromWatchedDir: (directory, file) => + watchedFiles = @_getWatchedDir directory + watchedFiles.some (watchedFile, index) => + if watchedFile is file + watchedFiles.splice(index, 1) + yes + + # Private: Handles emitting unlink events for + # files and directories, and via recursion, for + # files and directories within directories that are unlinked + # + # directory - string, directory within which the following item is located + # item - string, base path of item/directory + # + # Returns nothing. + _remove: (directory, item) => + # if what is being deleted is a directory, get that directory's paths + # for recursive deleting and cleaning of watched object + # if it is not a directory, nestedDirectoryChildren will be empty array + fullPath = sysPath.join(directory, item) + nestedDirectoryChildren = @_getWatchedDir(fullPath).slice() + + # Remove directory / file from watched list. + @_removeFromWatchedDir directory, item + + # Recursively remove children directories / files. + nestedDirectoryChildren.forEach (nestedItem) => + @_remove fullPath, nestedItem + fs.unwatchFile fullPath + @emit 'unlink', fullPath + + # Private: Watch file for changes with fs.watchFile or fs.watch. + # + # item - string, path to file or directory. + # callback - function that will be executed on fs change. + # + # Returns nothing. + _watch: (item, itemType, callback = (->)) => + directory = sysPath.dirname(item) + basename = sysPath.basename(item) + parent = @_getWatchedDir directory + options = {persistent: @options.persistent} + + # Prevent memory leaks. + return if parent.indexOf(basename) >= 0 + + @_addToWatchedDir directory, basename + if process.platform is 'win32' and nodeVersion is '0.6' + watcher = fs.watch item, options, (event, path) => + callback item + @watchers.push watcher + else + options.interval = 100 + fs.watchFile item, options, (curr, prev) => + callback item if curr.mtime.getTime() isnt prev.mtime.getTime() + @emit 'add', item if itemType is 'file' + + # Private: Emit `change` event once and watch file to emit it in the future + # once the file is changed. + # + # file - string, fs path. + # + # Returns nothing. + _handleFile: (file) => + @_watch file, 'file', (file) => + @emit 'change', file + + # Private: Read directory to add / remove files from `@watched` list + # and re-read it on change. + # + # directory - string, fs path. + # + # Returns nothing. + _handleDir: (directory) => + read = (directory) => + fs.readdir directory, (error, current) => + return @emit 'error', error if error? + return unless current + previous = @_getWatchedDir(directory) + + # Files that absent in current directory snapshot + # but present in previous emit `remove` event + # and are removed from @watched[directory]. + previous + .filter (file) => + current.indexOf(file) < 0 + .forEach (file) => + @_remove directory, file + + # Files that present in current directory snapshot + # but absent in previous are added to watch list and + # emit `add` event. + current + .filter (file) => + previous.indexOf(file) < 0 + .forEach (file) => + @_handle sysPath.join(directory, file) + + read directory + @_watch directory, 'directory', read + + # Private: Handle added file or directory. + # Delegates call to _handleFile / _handleDir after checks. + # + # item - string, path to file or directory. + # + # Returns nothing. + _handle: (item) => + # Don't handle invalid files, dotfiles etc. + return if @_ignored item + + # Get the canonicalized absolute pathname. + fs.realpath item, (error, path) => + return @emit 'error', error if error? + # Get file info, check is it file, directory or something else. + fs.stat item, (error, stats) => + return @emit 'error', error if error? + @_handleFile item if stats.isFile() + @_handleDir item if stats.isDirectory() + + emit: (event, args...) -> + super + super 'all', event, args... if event in ['add', 'change', 'unlink'] + + # Public: Adds directories / files for tracking. + # + # * files - array of strings (file paths). + # + # Examples + # + # add ['app', 'vendor'] + # + # Returns an instance of FSWatcher for chaning. + add: (files) => + files = [files] unless Array.isArray files + files.forEach @_handle + this + + # Public: Remove all listeners from watched files. + # Returns an instance of FSWatcher for chaning. + close: => + @watchers.forEach (watcher) -> watcher.close() + Object.keys(@watched).forEach (directory) => + @watched[directory].forEach (file) => + fs.unwatchFile sysPath.join(directory, file) + @watched = Object.create(null) + this + +exports.watch = (files, options) -> + new FSWatcher(options).add(files) diff --git a/dist/node_modules/connect-flash/.npmignore b/dist/node_modules/connect-flash/.npmignore new file mode 100644 index 0000000..d9ceb36 --- /dev/null +++ b/dist/node_modules/connect-flash/.npmignore @@ -0,0 +1,8 @@ +*.md +.DS_Store +.git* +Makefile +docs/ +examples/ +support/ +test/ diff --git a/dist/node_modules/connect-flash/.travis.yml b/dist/node_modules/connect-flash/.travis.yml new file mode 100644 index 0000000..2644170 --- /dev/null +++ b/dist/node_modules/connect-flash/.travis.yml @@ -0,0 +1,4 @@ +language: "node_js" +node_js: + - 0.4 + - 0.6 diff --git a/dist/node_modules/connect-flash/LICENSE b/dist/node_modules/connect-flash/LICENSE new file mode 100644 index 0000000..74524ce --- /dev/null +++ b/dist/node_modules/connect-flash/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2011 Jared Hanson + +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/dist/node_modules/connect-flash/lib/flash.js b/dist/node_modules/connect-flash/lib/flash.js new file mode 100644 index 0000000..f3470ae --- /dev/null +++ b/dist/node_modules/connect-flash/lib/flash.js @@ -0,0 +1,76 @@ +/** + * Module dependencies. + */ +var format = require('util').format; + + +/** + * Expose `flash()` function on requests. + * + * @return {Function} + * @api public + */ +module.exports = function flash(options) { + options = options || {}; + var safe = (options.unsafe === undefined) ? true : !options.unsafe; + + return function(req, res, next) { + if (req.flash && safe) { return next(); } + req.flash = _flash; + next(); + } +} + +/** + * Queue flash `msg` of the given `type`. + * + * Examples: + * + * req.flash('info', 'email sent'); + * req.flash('error', 'email delivery failed'); + * req.flash('info', 'email re-sent'); + * // => 2 + * + * req.flash('info'); + * // => ['email sent', 'email re-sent'] + * + * req.flash('info'); + * // => [] + * + * req.flash(); + * // => { error: ['email delivery failed'], info: [] } + * + * Formatting: + * + * Flash notifications also support arbitrary formatting support. + * For example you may pass variable arguments to `req.flash()` + * and use the %s specifier to be replaced by the associated argument: + * + * req.flash('info', 'email has been sent to %s.', userName); + * + * Formatting uses `util.format()`, which is available on Node 0.6+. + * + * @param {String} type + * @param {String} msg + * @return {Array|Object|Number} + * @api public + */ +function _flash(type, msg) { + if (this.session === undefined) throw Error('req.flash() requires sessions'); + var msgs = this.session.flash = this.session.flash || {}; + if (type && msg) { + // util.format is available in Node.js 0.6+ + if (arguments.length > 2 && format) { + var args = Array.prototype.slice.call(arguments, 1); + msg = format.apply(undefined, args); + } + return (msgs[type] = msgs[type] || []).push(msg); + } else if (type) { + var arr = msgs[type]; + delete msgs[type]; + return arr || []; + } else { + this.session.flash = {}; + return msgs; + } +} diff --git a/dist/node_modules/connect-flash/lib/index.js b/dist/node_modules/connect-flash/lib/index.js new file mode 100644 index 0000000..b4d5961 --- /dev/null +++ b/dist/node_modules/connect-flash/lib/index.js @@ -0,0 +1,4 @@ +/** + * Expose middleware. + */ +exports = module.exports = require('./flash'); diff --git a/dist/node_modules/connect-flash/package.json b/dist/node_modules/connect-flash/package.json new file mode 100644 index 0000000..99ea8f0 --- /dev/null +++ b/dist/node_modules/connect-flash/package.json @@ -0,0 +1,28 @@ +{ + "name": "connect-flash", + "version": "0.1.0", + "description": "Flash message middleware for Connect.", + "author": { "name": "Jared Hanson", "email": "jaredhanson@gmail.com", "url": "http://www.jaredhanson.net/" }, + "repository": { + "type": "git", + "url": "git://github.com/jaredhanson/connect-flash.git" + }, + "bugs": { + "url": "http://github.com/jaredhanson/connect-flash/issues" + }, + "main": "./lib", + "dependencies": { + }, + "devDependencies": { + "vows": "0.6.x" + }, + "scripts": { + "test": "NODE_PATH=lib node_modules/.bin/vows test/*-test.js" + }, + "engines": { "node": ">= 0.4.0" }, + "licenses": [ { + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + } ], + "keywords": ["connect", "express", "flash", "messages"] +} diff --git a/dist/node_modules/express/.npmignore b/dist/node_modules/express/.npmignore new file mode 100644 index 0000000..caf574d --- /dev/null +++ b/dist/node_modules/express/.npmignore @@ -0,0 +1,9 @@ +.git* +docs/ +examples/ +support/ +test/ +testing.js +.DS_Store +coverage.html +lib-cov diff --git a/dist/node_modules/express/.travis.yml b/dist/node_modules/express/.travis.yml new file mode 100644 index 0000000..b8e1f17 --- /dev/null +++ b/dist/node_modules/express/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.6 \ No newline at end of file diff --git a/dist/node_modules/express/History.md b/dist/node_modules/express/History.md new file mode 100644 index 0000000..f9472ba --- /dev/null +++ b/dist/node_modules/express/History.md @@ -0,0 +1,1043 @@ + +3.0.1 / 2012-11-01 +================== + + * update connect + +3.0.0 / 2012-10-23 +================== + + * add `make clean` + * add "Basic" check to req.auth + * add `req.auth` test coverage + * add cb && cb(payload) to `res.jsonp()`. Closes #1374 + * add backwards compat for `res.redirect()` status. Closes #1336 + * add support for `res.json()` to retain previously defined Content-Types. Closes #1349 + * update connect + * change `res.redirect()` to utilize a pathname-relative Location again. Closes #1382 + * remove non-primitive string support for `res.send()` + * fix view-locals example. Closes #1370 + * fix route-separation example + +3.0.0rc5 / 2012-09-18 +================== + + * update connect + * add redis search example + * add static-files example + * add "x-powered-by" setting (`app.disable('x-powered-by')`) + * add "application/octet-stream" redirect Accept test case. Closes #1317 + +3.0.0rc4 / 2012-08-30 +================== + + * add `res.jsonp()`. Closes #1307 + * add "verbose errors" option to error-pages example + * add another route example to express(1) so people are not so confused + * add redis online user activity tracking example + * update connect dep + * fix etag quoting. Closes #1310 + * fix error-pages 404 status + * fix jsonp callback char restrictions + * remove old OPTIONS default response + +3.0.0rc3 / 2012-08-13 +================== + + * update connect dep + * fix signed cookies to work with `connect.cookieParser()` ("s:" prefix was missing) [tnydwrds] + * fix `res.render()` clobbering of "locals" + +3.0.0rc2 / 2012-08-03 +================== + + * add CORS example + * update connect dep + * deprecate `.createServer()` & remove old stale examples + * fix: escape `res.redirect()` link + * fix vhost example + +3.0.0rc1 / 2012-07-24 +================== + + * add more examples to view-locals + * add scheme-relative redirects (`res.redirect("//foo.com")`) support + * update cookie dep + * update connect dep + * update send dep + * fix `express(1)` -h flag, use -H for hogan. Closes #1245 + * fix `res.sendfile()` socket error handling regression + +3.0.0beta7 / 2012-07-16 +================== + + * update connect dep for `send()` root normalization regression + +3.0.0beta6 / 2012-07-13 +================== + + * add `err.view` property for view errors. Closes #1226 + * add "jsonp callback name" setting + * add support for "/foo/:bar*" non-greedy matches + * change `res.sendfile()` to use `send()` module + * change `res.send` to use "response-send" module + * remove `app.locals.use` and `res.locals.use`, use regular middleware + +3.0.0beta5 / 2012-07-03 +================== + + * add "make check" support + * add route-map example + * add `res.json(obj, status)` support back for BC + * add "methods" dep, remove internal methods module + * update connect dep + * update auth example to utilize cores pbkdf2 + * updated tests to use "supertest" + +3.0.0beta4 / 2012-06-25 +================== + + * Added `req.auth` + * Added `req.range(size)` + * Added `res.links(obj)` + * Added `res.send(body, status)` support back for backwards compat + * Added `.default()` support to `res.format()` + * Added 2xx / 304 check to `req.fresh` + * Revert "Added + support to the router" + * Fixed `res.send()` freshness check, respect res.statusCode + +3.0.0beta3 / 2012-06-15 +================== + + * Added hogan `--hjs` to express(1) [nullfirm] + * Added another example to content-negotiation + * Added `fresh` dep + * Changed: `res.send()` always checks freshness + * Fixed: expose connects mime module. Cloases #1165 + +3.0.0beta2 / 2012-06-06 +================== + + * Added `+` support to the router + * Added `req.host` + * Changed `req.param()` to check route first + * Update connect dep + +3.0.0beta1 / 2012-06-01 +================== + + * Added `res.format()` callback to override default 406 behaviour + * Fixed `res.redirect()` 406. Closes #1154 + +3.0.0alpha5 / 2012-05-30 +================== + + * Added `req.ip` + * Added `{ signed: true }` option to `res.cookie()` + * Removed `res.signedCookie()` + * Changed: dont reverse `req.ips` + * Fixed "trust proxy" setting check for `req.ips` + +3.0.0alpha4 / 2012-05-09 +================== + + * Added: allow `[]` in jsonp callback. Closes #1128 + * Added `PORT` env var support in generated template. Closes #1118 [benatkin] + * Updated: connect 2.2.2 + +3.0.0alpha3 / 2012-05-04 +================== + + * Added public `app.routes`. Closes #887 + * Added _view-locals_ example + * Added _mvc_ example + * Added `res.locals.use()`. Closes #1120 + * Added conditional-GET support to `res.send()` + * Added: coerce `res.set()` values to strings + * Changed: moved `static()` in generated apps below router + * Changed: `res.send()` only set ETag when not previously set + * Changed connect 2.2.1 dep + * Changed: `make test` now runs unit / acceptance tests + * Fixed req/res proto inheritance + +3.0.0alpha2 / 2012-04-26 +================== + + * Added `make benchmark` back + * Added `res.send()` support for `String` objects + * Added client-side data exposing example + * Added `res.header()` and `req.header()` aliases for BC + * Added `express.createServer()` for BC + * Perf: memoize parsed urls + * Perf: connect 2.2.0 dep + * Changed: make `expressInit()` middleware self-aware + * Fixed: use app.get() for all core settings + * Fixed redis session example + * Fixed session example. Closes #1105 + * Fixed generated express dep. Closes #1078 + +3.0.0alpha1 / 2012-04-15 +================== + + * Added `app.locals.use(callback)` + * Added `app.locals` object + * Added `app.locals(obj)` + * Added `res.locals` object + * Added `res.locals(obj)` + * Added `res.format()` for content-negotiation + * Added `app.engine()` + * Added `res.cookie()` JSON cookie support + * Added "trust proxy" setting + * Added `req.subdomains` + * Added `req.protocol` + * Added `req.secure` + * Added `req.path` + * Added `req.ips` + * Added `req.fresh` + * Added `req.stale` + * Added comma-delmited / array support for `req.accepts()` + * Added debug instrumentation + * Added `res.set(obj)` + * Added `res.set(field, value)` + * Added `res.get(field)` + * Added `app.get(setting)`. Closes #842 + * Added `req.acceptsLanguage()` + * Added `req.acceptsCharset()` + * Added `req.accepted` + * Added `req.acceptedLanguages` + * Added `req.acceptedCharsets` + * Added "json replacer" setting + * Added "json spaces" setting + * Added X-Forwarded-Proto support to `res.redirect()`. Closes #92 + * Added `--less` support to express(1) + * Added `express.response` prototype + * Added `express.request` prototype + * Added `express.application` prototype + * Added `app.path()` + * Added `app.render()` + * Added `res.type()` to replace `res.contentType()` + * Changed: `res.redirect()` to add relative support + * Changed: enable "jsonp callback" by default + * Changed: renamed "case sensitive routes" to "case sensitive routing" + * Rewrite of all tests with mocha + * Removed "root" setting + * Removed `res.redirect('home')` support + * Removed `req.notify()` + * Removed `app.register()` + * Removed `app.redirect()` + * Removed `app.is()` + * Removed `app.helpers()` + * Removed `app.dynamicHelpers()` + * Fixed `res.sendfile()` with non-GET. Closes #723 + * Fixed express(1) public dir for windows. Closes #866 + +2.5.9/ 2012-04-02 +================== + + * Added support for PURGE request method [pbuyle] + * Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki] + +2.5.8 / 2012-02-08 +================== + + * Update mkdirp dep. Closes #991 + +2.5.7 / 2012-02-06 +================== + + * Fixed `app.all` duplicate DELETE requests [mscdex] + +2.5.6 / 2012-01-13 +================== + + * Updated hamljs dev dep. Closes #953 + +2.5.5 / 2012-01-08 +================== + + * Fixed: set `filename` on cached templates [matthewleon] + +2.5.4 / 2012-01-02 +================== + + * Fixed `express(1)` eol on 0.4.x. Closes #947 + +2.5.3 / 2011-12-30 +================== + + * Fixed `req.is()` when a charset is present + +2.5.2 / 2011-12-10 +================== + + * Fixed: express(1) LF -> CRLF for windows + +2.5.1 / 2011-11-17 +================== + + * Changed: updated connect to 1.8.x + * Removed sass.js support from express(1) + +2.5.0 / 2011-10-24 +================== + + * Added ./routes dir for generated app by default + * Added npm install reminder to express(1) app gen + * Added 0.5.x support + * Removed `make test-cov` since it wont work with node 0.5.x + * Fixed express(1) public dir for windows. Closes #866 + +2.4.7 / 2011-10-05 +================== + + * Added mkdirp to express(1). Closes #795 + * Added simple _json-config_ example + * Added shorthand for the parsed request's pathname via `req.path` + * Changed connect dep to 1.7.x to fix npm issue... + * Fixed `res.redirect()` __HEAD__ support. [reported by xerox] + * Fixed `req.flash()`, only escape args + * Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie] + +2.4.6 / 2011-08-22 +================== + + * Fixed multiple param callback regression. Closes #824 [reported by TroyGoode] + +2.4.5 / 2011-08-19 +================== + + * Added support for routes to handle errors. Closes #809 + * Added `app.routes.all()`. Closes #803 + * Added "basepath" setting to work in conjunction with reverse proxies etc. + * Refactored `Route` to use a single array of callbacks + * Added support for multiple callbacks for `app.param()`. Closes #801 +Closes #805 + * Changed: removed .call(self) for route callbacks + * Dependency: `qs >= 0.3.1` + * Fixed `res.redirect()` on windows due to `join()` usage. Closes #808 + +2.4.4 / 2011-08-05 +================== + + * Fixed `res.header()` intention of a set, even when `undefined` + * Fixed `*`, value no longer required + * Fixed `res.send(204)` support. Closes #771 + +2.4.3 / 2011-07-14 +================== + + * Added docs for `status` option special-case. Closes #739 + * Fixed `options.filename`, exposing the view path to template engines + +2.4.2. / 2011-07-06 +================== + + * Revert "removed jsonp stripping" for XSS + +2.4.1 / 2011-07-06 +================== + + * Added `res.json()` JSONP support. Closes #737 + * Added _extending-templates_ example. Closes #730 + * Added "strict routing" setting for trailing slashes + * Added support for multiple envs in `app.configure()` calls. Closes #735 + * Changed: `res.send()` using `res.json()` + * Changed: when cookie `path === null` don't default it + * Changed; default cookie path to "home" setting. Closes #731 + * Removed _pids/logs_ creation from express(1) + +2.4.0 / 2011-06-28 +================== + + * Added chainable `res.status(code)` + * Added `res.json()`, an explicit version of `res.send(obj)` + * Added simple web-service example + +2.3.12 / 2011-06-22 +================== + + * \#express is now on freenode! come join! + * Added `req.get(field, param)` + * Added links to Japanese documentation, thanks @hideyukisaito! + * Added; the `express(1)` generated app outputs the env + * Added `content-negotiation` example + * Dependency: connect >= 1.5.1 < 2.0.0 + * Fixed view layout bug. Closes #720 + * Fixed; ignore body on 304. Closes #701 + +2.3.11 / 2011-06-04 +================== + + * Added `npm test` + * Removed generation of dummy test file from `express(1)` + * Fixed; `express(1)` adds express as a dep + * Fixed; prune on `prepublish` + +2.3.10 / 2011-05-27 +================== + + * Added `req.route`, exposing the current route + * Added _package.json_ generation support to `express(1)` + * Fixed call to `app.param()` function for optional params. Closes #682 + +2.3.9 / 2011-05-25 +================== + + * Fixed bug-ish with `../' in `res.partial()` calls + +2.3.8 / 2011-05-24 +================== + + * Fixed `app.options()` + +2.3.7 / 2011-05-23 +================== + + * Added route `Collection`, ex: `app.get('/user/:id').remove();` + * Added support for `app.param(fn)` to define param logic + * Removed `app.param()` support for callback with return value + * Removed module.parent check from express(1) generated app. Closes #670 + * Refactored router. Closes #639 + +2.3.6 / 2011-05-20 +================== + + * Changed; using devDependencies instead of git submodules + * Fixed redis session example + * Fixed markdown example + * Fixed view caching, should not be enabled in development + +2.3.5 / 2011-05-20 +================== + + * Added export `.view` as alias for `.View` + +2.3.4 / 2011-05-08 +================== + + * Added `./examples/say` + * Fixed `res.sendfile()` bug preventing the transfer of files with spaces + +2.3.3 / 2011-05-03 +================== + + * Added "case sensitive routes" option. + * Changed; split methods supported per rfc [slaskis] + * Fixed route-specific middleware when using the same callback function several times + +2.3.2 / 2011-04-27 +================== + + * Fixed view hints + +2.3.1 / 2011-04-26 +================== + + * Added `app.match()` as `app.match.all()` + * Added `app.lookup()` as `app.lookup.all()` + * Added `app.remove()` for `app.remove.all()` + * Added `app.remove.VERB()` + * Fixed template caching collision issue. Closes #644 + * Moved router over from connect and started refactor + +2.3.0 / 2011-04-25 +================== + + * Added options support to `res.clearCookie()` + * Added `res.helpers()` as alias of `res.locals()` + * Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel * Dependency `connect >= 1.4.0` + * Changed; auto set Content-Type in res.attachement [Aaron Heckmann] + * Renamed "cache views" to "view cache". Closes #628 + * Fixed caching of views when using several apps. Closes #637 + * Fixed gotcha invoking `app.param()` callbacks once per route middleware. +Closes #638 + * Fixed partial lookup precedence. Closes #631 +Shaw] + +2.2.2 / 2011-04-12 +================== + + * Added second callback support for `res.download()` connection errors + * Fixed `filename` option passing to template engine + +2.2.1 / 2011-04-04 +================== + + * Added `layout(path)` helper to change the layout within a view. Closes #610 + * Fixed `partial()` collection object support. + Previously only anything with `.length` would work. + When `.length` is present one must still be aware of holes, + however now `{ collection: {foo: 'bar'}}` is valid, exposes + `keyInCollection` and `keysInCollection`. + + * Performance improved with better view caching + * Removed `request` and `response` locals + * Changed; errorHandler page title is now `Express` instead of `Connect` + +2.2.0 / 2011-03-30 +================== + + * Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606 + * Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606 + * Added `app.VERB(path)` as alias of `app.lookup.VERB()`. + * Dependency `connect >= 1.2.0` + +2.1.1 / 2011-03-29 +================== + + * Added; expose `err.view` object when failing to locate a view + * Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann] + * Fixed; `res.send(undefined)` responds with 204 [aheckmann] + +2.1.0 / 2011-03-24 +================== + + * Added `/_?` partial lookup support. Closes #447 + * Added `request`, `response`, and `app` local variables + * Added `settings` local variable, containing the app's settings + * Added `req.flash()` exception if `req.session` is not available + * Added `res.send(bool)` support (json response) + * Fixed stylus example for latest version + * Fixed; wrap try/catch around `res.render()` + +2.0.0 / 2011-03-17 +================== + + * Fixed up index view path alternative. + * Changed; `res.locals()` without object returns the locals + +2.0.0rc3 / 2011-03-17 +================== + + * Added `res.locals(obj)` to compliment `res.local(key, val)` + * Added `res.partial()` callback support + * Fixed recursive error reporting issue in `res.render()` + +2.0.0rc2 / 2011-03-17 +================== + + * Changed; `partial()` "locals" are now optional + * Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01] + * Fixed .filename view engine option [reported by drudge] + * Fixed blog example + * Fixed `{req,res}.app` reference when mounting [Ben Weaver] + +2.0.0rc / 2011-03-14 +================== + + * Fixed; expose `HTTPSServer` constructor + * Fixed express(1) default test charset. Closes #579 [reported by secoif] + * Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP] + +2.0.0beta3 / 2011-03-09 +================== + + * Added support for `res.contentType()` literal + The original `res.contentType('.json')`, + `res.contentType('application/json')`, and `res.contentType('json')` + will work now. + * Added `res.render()` status option support back + * Added charset option for `res.render()` + * Added `.charset` support (via connect 1.0.4) + * Added view resolution hints when in development and a lookup fails + * Added layout lookup support relative to the page view. + For example while rendering `./views/user/index.jade` if you create + `./views/user/layout.jade` it will be used in favour of the root layout. + * Fixed `res.redirect()`. RFC states absolute url [reported by unlink] + * Fixed; default `res.send()` string charset to utf8 + * Removed `Partial` constructor (not currently used) + +2.0.0beta2 / 2011-03-07 +================== + + * Added res.render() `.locals` support back to aid in migration process + * Fixed flash example + +2.0.0beta / 2011-03-03 +================== + + * Added HTTPS support + * Added `res.cookie()` maxAge support + * Added `req.header()` _Referrer_ / _Referer_ special-case, either works + * Added mount support for `res.redirect()`, now respects the mount-point + * Added `union()` util, taking place of `merge(clone())` combo + * Added stylus support to express(1) generated app + * Added secret to session middleware used in examples and generated app + * Added `res.local(name, val)` for progressive view locals + * Added default param support to `req.param(name, default)` + * Added `app.disabled()` and `app.enabled()` + * Added `app.register()` support for omitting leading ".", either works + * Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539 + * Added `app.param()` to map route params to async/sync logic + * Added; aliased `app.helpers()` as `app.locals()`. Closes #481 + * Added extname with no leading "." support to `res.contentType()` + * Added `cache views` setting, defaulting to enabled in "production" env + * Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_. + * Added `req.accepts()` support for extensions + * Changed; `res.download()` and `res.sendfile()` now utilize Connect's + static file server `connect.static.send()`. + * Changed; replaced `connect.utils.mime()` with npm _mime_ module + * Changed; allow `req.query` to be pre-defined (via middleware or other parent + * Changed view partial resolution, now relative to parent view + * Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`. + * Fixed `req.param()` bug returning Array.prototype methods. Closes #552 + * Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()` + * Fixed; using _qs_ module instead of _querystring_ + * Fixed; strip unsafe chars from jsonp callbacks + * Removed "stream threshold" setting + +1.0.8 / 2011-03-01 +================== + + * Allow `req.query` to be pre-defined (via middleware or other parent app) + * "connect": ">= 0.5.0 < 1.0.0". Closes #547 + * Removed the long deprecated __EXPRESS_ENV__ support + +1.0.7 / 2011-02-07 +================== + + * Fixed `render()` setting inheritance. + Mounted apps would not inherit "view engine" + +1.0.6 / 2011-02-07 +================== + + * Fixed `view engine` setting bug when period is in dirname + +1.0.5 / 2011-02-05 +================== + + * Added secret to generated app `session()` call + +1.0.4 / 2011-02-05 +================== + + * Added `qs` dependency to _package.json_ + * Fixed namespaced `require()`s for latest connect support + +1.0.3 / 2011-01-13 +================== + + * Remove unsafe characters from JSONP callback names [Ryan Grove] + +1.0.2 / 2011-01-10 +================== + + * Removed nested require, using `connect.router` + +1.0.1 / 2010-12-29 +================== + + * Fixed for middleware stacked via `createServer()` + previously the `foo` middleware passed to `createServer(foo)` + would not have access to Express methods such as `res.send()` + or props like `req.query` etc. + +1.0.0 / 2010-11-16 +================== + + * Added; deduce partial object names from the last segment. + For example by default `partial('forum/post', postObject)` will + give you the _post_ object, providing a meaningful default. + * Added http status code string representation to `res.redirect()` body + * Added; `res.redirect()` supporting _text/plain_ and _text/html_ via __Accept__. + * Added `req.is()` to aid in content negotiation + * Added partial local inheritance [suggested by masylum]. Closes #102 + providing access to parent template locals. + * Added _-s, --session[s]_ flag to express(1) to add session related middleware + * Added _--template_ flag to express(1) to specify the + template engine to use. + * Added _--css_ flag to express(1) to specify the + stylesheet engine to use (or just plain css by default). + * Added `app.all()` support [thanks aheckmann] + * Added partial direct object support. + You may now `partial('user', user)` providing the "user" local, + vs previously `partial('user', { object: user })`. + * Added _route-separation_ example since many people question ways + to do this with CommonJS modules. Also view the _blog_ example for + an alternative. + * Performance; caching view path derived partial object names + * Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454 + * Fixed jsonp support; _text/javascript_ as per mailinglist discussion + +1.0.0rc4 / 2010-10-14 +================== + + * Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0 + * Added route-middleware support (very helpful, see the [docs](http://expressjs.com/guide.html#Route-Middleware)) + * Added _jsonp callback_ setting to enable/disable jsonp autowrapping [Dav Glass] + * Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass] + * Added `partial()` support for array-like collections. Closes #434 + * Added support for swappable querystring parsers + * Added session usage docs. Closes #443 + * Added dynamic helper caching. Closes #439 [suggested by maritz] + * Added authentication example + * Added basic Range support to `res.sendfile()` (and `res.download()` etc) + * Changed; `express(1)` generated app using 2 spaces instead of 4 + * Default env to "development" again [aheckmann] + * Removed _context_ option is no more, use "scope" + * Fixed; exposing _./support_ libs to examples so they can run without installs + * Fixed mvc example + +1.0.0rc3 / 2010-09-20 +================== + + * Added confirmation for `express(1)` app generation. Closes #391 + * Added extending of flash formatters via `app.flashFormatters` + * Added flash formatter support. Closes #411 + * Added streaming support to `res.sendfile()` using `sys.pump()` when >= "stream threshold" + * Added _stream threshold_ setting for `res.sendfile()` + * Added `res.send()` __HEAD__ support + * Added `res.clearCookie()` + * Added `res.cookie()` + * Added `res.render()` headers option + * Added `res.redirect()` response bodies + * Added `res.render()` status option support. Closes #425 [thanks aheckmann] + * Fixed `res.sendfile()` responding with 403 on malicious path + * Fixed `res.download()` bug; when an error occurs remove _Content-Disposition_ + * Fixed; mounted apps settings now inherit from parent app [aheckmann] + * Fixed; stripping Content-Length / Content-Type when 204 + * Fixed `res.send()` 204. Closes #419 + * Fixed multiple _Set-Cookie_ headers via `res.header()`. Closes #402 + * Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo] + + +1.0.0rc2 / 2010-08-17 +================== + + * Added `app.register()` for template engine mapping. Closes #390 + * Added `res.render()` callback support as second argument (no options) + * Added callback support to `res.download()` + * Added callback support for `res.sendfile()` + * Added support for middleware access via `express.middlewareName()` vs `connect.middlewareName()` + * Added "partials" setting to docs + * Added default expresso tests to `express(1)` generated app. Closes #384 + * Fixed `res.sendfile()` error handling, defer via `next()` + * Fixed `res.render()` callback when a layout is used [thanks guillermo] + * Fixed; `make install` creating ~/.node_libraries when not present + * Fixed issue preventing error handlers from being defined anywhere. Closes #387 + +1.0.0rc / 2010-07-28 +================== + + * Added mounted hook. Closes #369 + * Added connect dependency to _package.json_ + + * Removed "reload views" setting and support code + development env never caches, production always caches. + + * Removed _param_ in route callbacks, signature is now + simply (req, res, next), previously (req, res, params, next). + Use _req.params_ for path captures, _req.query_ for GET params. + + * Fixed "home" setting + * Fixed middleware/router precedence issue. Closes #366 + * Fixed; _configure()_ callbacks called immediately. Closes #368 + +1.0.0beta2 / 2010-07-23 +================== + + * Added more examples + * Added; exporting `Server` constructor + * Added `Server#helpers()` for view locals + * Added `Server#dynamicHelpers()` for dynamic view locals. Closes #349 + * Added support for absolute view paths + * Added; _home_ setting defaults to `Server#route` for mounted apps. Closes #363 + * Added Guillermo Rauch to the contributor list + * Added support for "as" for non-collection partials. Closes #341 + * Fixed _install.sh_, ensuring _~/.node_libraries_ exists. Closes #362 [thanks jf] + * Fixed `res.render()` exceptions, now passed to `next()` when no callback is given [thanks guillermo] + * Fixed instanceof `Array` checks, now `Array.isArray()` + * Fixed express(1) expansion of public dirs. Closes #348 + * Fixed middleware precedence. Closes #345 + * Fixed view watcher, now async [thanks aheckmann] + +1.0.0beta / 2010-07-15 +================== + + * Re-write + - much faster + - much lighter + - Check [ExpressJS.com](http://expressjs.com) for migration guide and updated docs + +0.14.0 / 2010-06-15 +================== + + * Utilize relative requires + * Added Static bufferSize option [aheckmann] + * Fixed caching of view and partial subdirectories [aheckmann] + * Fixed mime.type() comments now that ".ext" is not supported + * Updated haml submodule + * Updated class submodule + * Removed bin/express + +0.13.0 / 2010-06-01 +================== + + * Added node v0.1.97 compatibility + * Added support for deleting cookies via Request#cookie('key', null) + * Updated haml submodule + * Fixed not-found page, now using using charset utf-8 + * Fixed show-exceptions page, now using using charset utf-8 + * Fixed view support due to fs.readFile Buffers + * Changed; mime.type() no longer accepts ".type" due to node extname() changes + +0.12.0 / 2010-05-22 +================== + + * Added node v0.1.96 compatibility + * Added view `helpers` export which act as additional local variables + * Updated haml submodule + * Changed ETag; removed inode, modified time only + * Fixed LF to CRLF for setting multiple cookies + * Fixed cookie complation; values are now urlencoded + * Fixed cookies parsing; accepts quoted values and url escaped cookies + +0.11.0 / 2010-05-06 +================== + + * Added support for layouts using different engines + - this.render('page.html.haml', { layout: 'super-cool-layout.html.ejs' }) + - this.render('page.html.haml', { layout: 'foo' }) // assumes 'foo.html.haml' + - this.render('page.html.haml', { layout: false }) // no layout + * Updated ext submodule + * Updated haml submodule + * Fixed EJS partial support by passing along the context. Issue #307 + +0.10.1 / 2010-05-03 +================== + + * Fixed binary uploads. + +0.10.0 / 2010-04-30 +================== + + * Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s + encoding is set to 'utf8' or 'utf-8'. + * Added "encoding" option to Request#render(). Closes #299 + * Added "dump exceptions" setting, which is enabled by default. + * Added simple ejs template engine support + * Added error reponse support for text/plain, application/json. Closes #297 + * Added callback function param to Request#error() + * Added Request#sendHead() + * Added Request#stream() + * Added support for Request#respond(304, null) for empty response bodies + * Added ETag support to Request#sendfile() + * Added options to Request#sendfile(), passed to fs.createReadStream() + * Added filename arg to Request#download() + * Performance enhanced due to pre-reversing plugins so that plugins.reverse() is not called on each request + * Performance enhanced by preventing several calls to toLowerCase() in Router#match() + * Changed; Request#sendfile() now streams + * Changed; Renamed Request#halt() to Request#respond(). Closes #289 + * Changed; Using sys.inspect() instead of JSON.encode() for error output + * Changed; run() returns the http.Server instance. Closes #298 + * Changed; Defaulting Server#host to null (INADDR_ANY) + * Changed; Logger "common" format scale of 0.4f + * Removed Logger "request" format + * Fixed; Catching ENOENT in view caching, preventing error when "views/partials" is not found + * Fixed several issues with http client + * Fixed Logger Content-Length output + * Fixed bug preventing Opera from retaining the generated session id. Closes #292 + +0.9.0 / 2010-04-14 +================== + + * Added DSL level error() route support + * Added DSL level notFound() route support + * Added Request#error() + * Added Request#notFound() + * Added Request#render() callback function. Closes #258 + * Added "max upload size" setting + * Added "magic" variables to collection partials (\_\_index\_\_, \_\_length\_\_, \_\_isFirst\_\_, \_\_isLast\_\_). Closes #254 + * Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js + * Added callback function support to Request#halt() as 3rd/4th arg + * Added preprocessing of route param wildcards using param(). Closes #251 + * Added view partial support (with collections etc) + * Fixed bug preventing falsey params (such as ?page=0). Closes #286 + * Fixed setting of multiple cookies. Closes #199 + * Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml) + * Changed; session cookie is now httpOnly + * Changed; Request is no longer global + * Changed; Event is no longer global + * Changed; "sys" module is no longer global + * Changed; moved Request#download to Static plugin where it belongs + * Changed; Request instance created before body parsing. Closes #262 + * Changed; Pre-caching views in memory when "cache view contents" is enabled. Closes #253 + * Changed; Pre-caching view partials in memory when "cache view partials" is enabled + * Updated support to node --version 0.1.90 + * Updated dependencies + * Removed set("session cookie") in favour of use(Session, { cookie: { ... }}) + * Removed utils.mixin(); use Object#mergeDeep() + +0.8.0 / 2010-03-19 +================== + + * Added coffeescript example app. Closes #242 + * Changed; cache api now async friendly. Closes #240 + * Removed deprecated 'express/static' support. Use 'express/plugins/static' + +0.7.6 / 2010-03-19 +================== + + * Added Request#isXHR. Closes #229 + * Added `make install` (for the executable) + * Added `express` executable for setting up simple app templates + * Added "GET /public/*" to Static plugin, defaulting to /public + * Added Static plugin + * Fixed; Request#render() only calls cache.get() once + * Fixed; Namespacing View caches with "view:" + * Fixed; Namespacing Static caches with "static:" + * Fixed; Both example apps now use the Static plugin + * Fixed set("views"). Closes #239 + * Fixed missing space for combined log format + * Deprecated Request#sendfile() and 'express/static' + * Removed Server#running + +0.7.5 / 2010-03-16 +================== + + * Added Request#flash() support without args, now returns all flashes + * Updated ext submodule + +0.7.4 / 2010-03-16 +================== + + * Fixed session reaper + * Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft) + +0.7.3 / 2010-03-16 +================== + + * Added package.json + * Fixed requiring of haml / sass due to kiwi removal + +0.7.2 / 2010-03-16 +================== + + * Fixed GIT submodules (HAH!) + +0.7.1 / 2010-03-16 +================== + + * Changed; Express now using submodules again until a PM is adopted + * Changed; chat example using millisecond conversions from ext + +0.7.0 / 2010-03-15 +================== + + * Added Request#pass() support (finds the next matching route, or the given path) + * Added Logger plugin (default "common" format replaces CommonLogger) + * Removed Profiler plugin + * Removed CommonLogger plugin + +0.6.0 / 2010-03-11 +================== + + * Added seed.yml for kiwi package management support + * Added HTTP client query string support when method is GET. Closes #205 + + * Added support for arbitrary view engines. + For example "foo.engine.html" will now require('engine'), + the exports from this module are cached after the first require(). + + * Added async plugin support + + * Removed usage of RESTful route funcs as http client + get() etc, use http.get() and friends + + * Removed custom exceptions + +0.5.0 / 2010-03-10 +================== + + * Added ext dependency (library of js extensions) + * Removed extname() / basename() utils. Use path module + * Removed toArray() util. Use arguments.values + * Removed escapeRegexp() util. Use RegExp.escape() + * Removed process.mixin() dependency. Use utils.mixin() + * Removed Collection + * Removed ElementCollection + * Shameless self promotion of ebook "Advanced JavaScript" (http://dev-mag.com) ;) + +0.4.0 / 2010-02-11 +================== + + * Added flash() example to sample upload app + * Added high level restful http client module (express/http) + * Changed; RESTful route functions double as HTTP clients. Closes #69 + * Changed; throwing error when routes are added at runtime + * Changed; defaulting render() context to the current Request. Closes #197 + * Updated haml submodule + +0.3.0 / 2010-02-11 +================== + + * Updated haml / sass submodules. Closes #200 + * Added flash message support. Closes #64 + * Added accepts() now allows multiple args. fixes #117 + * Added support for plugins to halt. Closes #189 + * Added alternate layout support. Closes #119 + * Removed Route#run(). Closes #188 + * Fixed broken specs due to use(Cookie) missing + +0.2.1 / 2010-02-05 +================== + + * Added "plot" format option for Profiler (for gnuplot processing) + * Added request number to Profiler plugin + * Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8 + * Fixed issue with routes not firing when not files are present. Closes #184 + * Fixed process.Promise -> events.Promise + +0.2.0 / 2010-02-03 +================== + + * Added parseParam() support for name[] etc. (allows for file inputs with "multiple" attr) Closes #180 + * Added Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174 + * Added expiration support to cache api with reaper. Closes #133 + * Added cache Store.Memory#reap() + * Added Cache; cache api now uses first class Cache instances + * Added abstract session Store. Closes #172 + * Changed; cache Memory.Store#get() utilizing Collection + * Renamed MemoryStore -> Store.Memory + * Fixed use() of the same plugin several time will always use latest options. Closes #176 + +0.1.0 / 2010-02-03 +================== + + * Changed; Hooks (before / after) pass request as arg as well as evaluated in their context + * Updated node support to 0.1.27 Closes #169 + * Updated dirname(__filename) -> __dirname + * Updated libxmljs support to v0.2.0 + * Added session support with memory store / reaping + * Added quick uid() helper + * Added multi-part upload support + * Added Sass.js support / submodule + * Added production env caching view contents and static files + * Added static file caching. Closes #136 + * Added cache plugin with memory stores + * Added support to StaticFile so that it works with non-textual files. + * Removed dirname() helper + * Removed several globals (now their modules must be required) + +0.0.2 / 2010-01-10 +================== + + * Added view benchmarks; currently haml vs ejs + * Added Request#attachment() specs. Closes #116 + * Added use of node's parseQuery() util. Closes #123 + * Added `make init` for submodules + * Updated Haml + * Updated sample chat app to show messages on load + * Updated libxmljs parseString -> parseHtmlString + * Fixed `make init` to work with older versions of git + * Fixed specs can now run independant specs for those who cant build deps. Closes #127 + * Fixed issues introduced by the node url module changes. Closes 126. + * Fixed two assertions failing due to Collection#keys() returning strings + * Fixed faulty Collection#toArray() spec due to keys() returning strings + * Fixed `make test` now builds libxmljs.node before testing + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/dist/node_modules/express/LICENSE b/dist/node_modules/express/LICENSE new file mode 100644 index 0000000..36075a3 --- /dev/null +++ b/dist/node_modules/express/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2009-2011 TJ Holowaychuk + +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/dist/node_modules/express/Makefile b/dist/node_modules/express/Makefile new file mode 100644 index 0000000..e820e31 --- /dev/null +++ b/dist/node_modules/express/Makefile @@ -0,0 +1,33 @@ + +MOCHA_OPTS= +REPORTER = dot + +check: test + +test: test-unit test-acceptance + +test-unit: + @NODE_ENV=test ./node_modules/.bin/mocha \ + --reporter $(REPORTER) \ + $(MOCHA_OPTS) + +test-acceptance: + @NODE_ENV=test ./node_modules/.bin/mocha \ + --reporter $(REPORTER) \ + --bail \ + test/acceptance/*.js + +test-cov: lib-cov + @EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html + +lib-cov: + @jscoverage lib lib-cov + +benchmark: + @./support/bench + +clean: + rm -f coverage.html + rm -fr lib-cov + +.PHONY: test test-unit test-acceptance benchmark clean diff --git a/dist/node_modules/express/Readme.md b/dist/node_modules/express/Readme.md new file mode 100644 index 0000000..6cc91e8 --- /dev/null +++ b/dist/node_modules/express/Readme.md @@ -0,0 +1,183 @@ +![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png) + + Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) + +```js +var express = require('express'); +var app = express(); + +app.get('/', function(req, res){ + res.send('Hello World'); +}); + +app.listen(3000); +``` + +## Installation + + $ npm install -g express + + To install the 3.0 alpha: + + $ npm install -g express@3.0 + +## Quick Start + + The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below: + + Create the app: + + $ npm install -g express + $ express /tmp/foo && cd /tmp/foo + + Install dependencies: + + $ npm install + + Start the server: + + $ node app + +## Features + + * Built on [Connect](http://github.com/senchalabs/connect) + * Robust routing + * HTTP helpers (redirection, caching, etc) + * View system supporting 14+ template engines + * Content negotiation + * Focus on high performance + * Environment based configuration + * Executable for generating applications quickly + * High test coverage + +## Philosophy + + The Express philosophy is to provide small, robust tooling for HTTP servers. Making + it a great solution for single page applications, web sites, hybrids, or public + HTTP APIs. + + Built on Connect you can use _only_ what you need, and nothing more, applications + can be as big or as small as you like, even a single file. Express does + not force you to use any specific ORM or template engine. With support for over + 14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js) + you can quickly craft your perfect framework. + +## More Information + + * Join #express on freenode + * [Google Group](http://groups.google.com/group/express-js) for discussion + * Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates + * Visit the [Wiki](http://github.com/visionmedia/express/wiki) + * [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito) + * [Русскоязычная документация](http://express-js.ru/) + +## Viewing Examples + +Clone the Express repo, then install the dev dependencies to install all the example / test suite deps: + + $ git clone git://github.com/visionmedia/express.git --depth 1 + $ cd express + $ npm install + +then run whichever tests you want: + + $ node examples/content-negotiation + +## Running Tests + +To run the test suite first invoke the following command within the repo, installing the development dependencies: + + $ npm install + +then run the tests: + + $ make test + +## Contributors + +``` +project: express +commits: 3559 +active : 468 days +files : 237 +authors: + 1891 Tj Holowaychuk 53.1% + 1285 visionmedia 36.1% + 182 TJ Holowaychuk 5.1% + 54 Aaron Heckmann 1.5% + 34 csausdev 1.0% + 26 ciaranj 0.7% + 21 Robert Sköld 0.6% + 6 Guillermo Rauch 0.2% + 3 Dav Glass 0.1% + 3 Nick Poulden 0.1% + 2 Randy Merrill 0.1% + 2 Benny Wong 0.1% + 2 Hunter Loftis 0.1% + 2 Jake Gordon 0.1% + 2 Brian McKinney 0.1% + 2 Roman Shtylman 0.1% + 2 Ben Weaver 0.1% + 2 Dave Hoover 0.1% + 2 Eivind Fjeldstad 0.1% + 2 Daniel Shaw 0.1% + 1 Matt Colyer 0.0% + 1 Pau Ramon 0.0% + 1 Pero Pejovic 0.0% + 1 Peter Rekdal Sunde 0.0% + 1 Raynos 0.0% + 1 Teng Siong Ong 0.0% + 1 Viktor Kelemen 0.0% + 1 ctide 0.0% + 1 8bitDesigner 0.0% + 1 isaacs 0.0% + 1 mgutz 0.0% + 1 pikeas 0.0% + 1 shuwatto 0.0% + 1 tstrimple 0.0% + 1 ewoudj 0.0% + 1 Adam Sanderson 0.0% + 1 Andrii Kostenko 0.0% + 1 Andy Hiew 0.0% + 1 Arpad Borsos 0.0% + 1 Ashwin Purohit 0.0% + 1 Benjen 0.0% + 1 Darren Torpey 0.0% + 1 Greg Ritter 0.0% + 1 Gregory Ritter 0.0% + 1 James Herdman 0.0% + 1 Jim Snodgrass 0.0% + 1 Joe McCann 0.0% + 1 Jonathan Dumaine 0.0% + 1 Jonathan Palardy 0.0% + 1 Jonathan Zacsh 0.0% + 1 Justin Lilly 0.0% + 1 Ken Sato 0.0% + 1 Maciej Małecki 0.0% + 1 Masahiro Hayashi 0.0% +``` + +## License + +(The MIT License) + +Copyright (c) 2009-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. diff --git a/dist/node_modules/express/bin/express b/dist/node_modules/express/bin/express new file mode 100755 index 0000000..3c0090c --- /dev/null +++ b/dist/node_modules/express/bin/express @@ -0,0 +1,422 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var exec = require('child_process').exec + , program = require('commander') + , mkdirp = require('mkdirp') + , pkg = require('../package.json') + , version = pkg.version + , os = require('os') + , fs = require('fs'); + +// CLI + +program + .version(version) + .option('-s, --sessions', 'add session support') + .option('-e, --ejs', 'add ejs engine support (defaults to jade)') + .option('-J, --jshtml', 'add jshtml engine support (defaults to jade)') + .option('-H, --hogan', 'add hogan.js engine support') + .option('-c, --css ', 'add stylesheet support (less|stylus) (defaults to plain css)') + .option('-f, --force', 'force on non-empty directory') + .parse(process.argv); + +// Path + +var path = program.args.shift() || '.'; + +// end-of-line code + +var eol = 'win32' == os.platform() ? '\r\n' : '\n' + +// Template engine + +program.template = 'jade'; +if (program.ejs) program.template = 'ejs'; +if (program.jshtml) program.template = 'jshtml'; +if (program.hogan) program.template = 'hjs'; + +/** + * Routes index template. + */ + +var index = [ + '' + , '/*' + , ' * GET home page.' + , ' */' + , '' + , 'exports.index = function(req, res){' + , ' res.render(\'index\', { title: \'Express\' });' + , '};' +].join(eol); + +/** + * Routes users template. + */ + +var users = [ + '' + , '/*' + , ' * GET users listing.' + , ' */' + , '' + , 'exports.list = function(req, res){' + , ' res.send("respond with a resource");' + , '};' +].join(eol); + +/** + * Jade layout template. + */ + +var jadeLayout = [ + 'doctype 5' + , 'html' + , ' head' + , ' title= title' + , ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')' + , ' body' + , ' block content' +].join(eol); + +/** + * Jade index template. + */ + +var jadeIndex = [ + 'extends layout' + , '' + , 'block content' + , ' h1= title' + , ' p Welcome to #{title}' +].join(eol); + +/** + * EJS index template. + */ + +var ejsIndex = [ + '' + , '' + , ' ' + , ' <%= title %>' + , ' ' + , ' ' + , ' ' + , '

<%= title %>

' + , '

Welcome to <%= title %>

' + , ' ' + , '' +].join(eol); + +/** + * JSHTML layout template. + */ + +var jshtmlLayout = [ + '' + , '' + , ' ' + , ' @write(title) ' + , ' ' + , ' ' + , ' ' + , ' @write(body)' + , ' ' + , '' +].join(eol); + +/** + * JSHTML index template. + */ + +var jshtmlIndex = [ + '

@write(title)

' + , '

Welcome to @write(title)

' +].join(eol); + +/** + * Hogan.js index template. + */ +var hoganIndex = [ + '' + , '' + , ' ' + , ' {{ title }}' + , ' ' + , ' ' + , ' ' + , '

{{ title }}

' + , '

Welcome to {{ title }}

' + , ' ' + , '' +].join(eol); + +/** + * Default css template. + */ + +var css = [ + 'body {' + , ' padding: 50px;' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;' + , '}' + , '' + , 'a {' + , ' color: #00B7FF;' + , '}' +].join(eol); + +/** + * Default less template. + */ + +var less = [ + 'body {' + , ' padding: 50px;' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;' + , '}' + , '' + , 'a {' + , ' color: #00B7FF;' + , '}' +].join(eol); + +/** + * Default stylus template. + */ + +var stylus = [ + 'body' + , ' padding: 50px' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif' + , 'a' + , ' color: #00B7FF' +].join(eol); + +/** + * App template. + */ + +var app = [ + '' + , '/**' + , ' * Module dependencies.' + , ' */' + , '' + , 'var express = require(\'express\')' + , ' , routes = require(\'./routes\')' + , ' , user = require(\'./routes/user\')' + , ' , http = require(\'http\')' + , ' , path = require(\'path\');' + , '' + , 'var app = express();' + , '' + , 'app.configure(function(){' + , ' app.set(\'port\', process.env.PORT || 3000);' + , ' app.set(\'views\', __dirname + \'/views\');' + , ' app.set(\'view engine\', \':TEMPLATE\');' + , ' app.use(express.favicon());' + , ' app.use(express.logger(\'dev\'));' + , ' app.use(express.bodyParser());' + , ' app.use(express.methodOverride());{sess}' + , ' app.use(app.router);{css}' + , ' app.use(express.static(path.join(__dirname, \'public\')));' + , '});' + , '' + , 'app.configure(\'development\', function(){' + , ' app.use(express.errorHandler());' + , '});' + , '' + , 'app.get(\'/\', routes.index);' + , 'app.get(\'/users\', user.list);' + , '' + , 'http.createServer(app).listen(app.get(\'port\'), function(){' + , ' console.log("Express server listening on port " + app.get(\'port\'));' + , '});' + , '' +].join(eol); + +// Generate application + +(function createApplication(path) { + emptyDirectory(path, function(empty){ + if (empty || program.force) { + createApplicationAt(path); + } else { + program.confirm('destination is not empty, continue? ', function(ok){ + if (ok) { + process.stdin.destroy(); + createApplicationAt(path); + } else { + abort('aborting'); + } + }); + } + }); +})(path); + +/** + * Create application at the given directory `path`. + * + * @param {String} path + */ + +function createApplicationAt(path) { + console.log(); + process.on('exit', function(){ + console.log(); + console.log(' install dependencies:'); + console.log(' $ cd %s && npm install', path); + console.log(); + console.log(' run the app:'); + console.log(' $ node app'); + console.log(); + }); + + mkdir(path, function(){ + mkdir(path + '/public'); + mkdir(path + '/public/javascripts'); + mkdir(path + '/public/images'); + mkdir(path + '/public/stylesheets', function(){ + switch (program.css) { + case 'less': + write(path + '/public/stylesheets/style.less', less); + break; + case 'stylus': + write(path + '/public/stylesheets/style.styl', stylus); + break; + default: + write(path + '/public/stylesheets/style.css', css); + } + }); + + mkdir(path + '/routes', function(){ + write(path + '/routes/index.js', index); + write(path + '/routes/user.js', users); + }); + + mkdir(path + '/views', function(){ + switch (program.template) { + case 'ejs': + write(path + '/views/index.ejs', ejsIndex); + break; + case 'jade': + write(path + '/views/layout.jade', jadeLayout); + write(path + '/views/index.jade', jadeIndex); + break; + case 'jshtml': + write(path + '/views/layout.jshtml', jshtmlLayout); + write(path + '/views/index.jshtml', jshtmlIndex); + break; + case 'hjs': + write(path + '/views/index.hjs', hoganIndex); + break; + + } + }); + + // CSS Engine support + switch (program.css) { + case 'less': + app = app.replace('{css}', eol + ' app.use(require(\'less-middleware\')({ src: __dirname + \'/public\' }));'); + break; + case 'stylus': + app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware(__dirname + \'/public\'));'); + break; + default: + app = app.replace('{css}', ''); + } + + // Session support + app = app.replace('{sess}', program.sessions + ? eol + ' app.use(express.cookieParser(\'your secret here\'));' + eol + ' app.use(express.session());' + : ''); + + // Template support + app = app.replace(':TEMPLATE', program.template); + + // package.json + var pkg = { + name: 'application-name' + , version: '0.0.1' + , private: true + , scripts: { start: 'node app' } + , dependencies: { + express: version + } + } + + if (program.template) pkg.dependencies[program.template] = '*'; + + // CSS Engine support + switch (program.css) { + case 'less': + pkg.dependencies['less-middleware'] = '*'; + break; + default: + if (program.css) { + pkg.dependencies[program.css] = '*'; + } + } + + write(path + '/package.json', JSON.stringify(pkg, null, 2)); + write(path + '/app.js', app); + }); +} + +/** + * Check if the given directory `path` is empty. + * + * @param {String} path + * @param {Function} fn + */ + +function emptyDirectory(path, fn) { + fs.readdir(path, function(err, files){ + if (err && 'ENOENT' != err.code) throw err; + fn(!files || !files.length); + }); +} + +/** + * echo str > path. + * + * @param {String} path + * @param {String} str + */ + +function write(path, str) { + fs.writeFile(path, str); + console.log(' \x1b[36mcreate\x1b[0m : ' + path); +} + +/** + * Mkdir -p. + * + * @param {String} path + * @param {Function} fn + */ + +function mkdir(path, fn) { + mkdirp(path, 0755, function(err){ + if (err) throw err; + console.log(' \033[36mcreate\033[0m : ' + path); + fn && fn(); + }); +} + +/** + * Exit with the given `str`. + * + * @param {String} str + */ + +function abort(str) { + console.error(str); + process.exit(1); +} diff --git a/dist/node_modules/express/client.js b/dist/node_modules/express/client.js new file mode 100644 index 0000000..8984c44 --- /dev/null +++ b/dist/node_modules/express/client.js @@ -0,0 +1,25 @@ + +var http = require('http'); + +var times = 50; + +while (times--) { + var req = http.request({ + port: 3000 + , method: 'POST' + , headers: { 'Content-Type': 'application/x-www-form-urlencoded' } + }); + + req.on('response', function(res){ + console.log(res.statusCode); + }); + + var n = 500000; + while (n--) { + req.write('foo=bar&bar=baz&'); + } + + req.write('foo=bar&bar=baz'); + + req.end(); +} \ No newline at end of file diff --git a/dist/node_modules/express/index.js b/dist/node_modules/express/index.js new file mode 100644 index 0000000..bfe9934 --- /dev/null +++ b/dist/node_modules/express/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.EXPRESS_COV + ? require('./lib-cov/express') + : require('./lib/express'); \ No newline at end of file diff --git a/dist/node_modules/express/lib/application.js b/dist/node_modules/express/lib/application.js new file mode 100644 index 0000000..340f0c0 --- /dev/null +++ b/dist/node_modules/express/lib/application.js @@ -0,0 +1,535 @@ +/** + * Module dependencies. + */ + +var connect = require('connect') + , Router = require('./router') + , methods = require('methods') + , middleware = require('./middleware') + , debug = require('debug')('express:application') + , locals = require('./utils').locals + , View = require('./view') + , url = require('url') + , utils = connect.utils + , path = require('path') + , http = require('http') + , join = path.join + , fs = require('fs'); + +/** + * Application prototype. + */ + +var app = exports = module.exports = {}; + +/** + * Initialize the server. + * + * - setup default configuration + * - setup default middleware + * - setup route reflection methods + * + * @api private + */ + +app.init = function(){ + var self = this; + this.cache = {}; + this.settings = {}; + this.engines = {}; + this.viewCallbacks = []; + this.defaultConfiguration(); +}; + +/** + * Initialize application configuration. + * + * @api private + */ + +app.defaultConfiguration = function(){ + var self = this; + + // default settings + this.enable('x-powered-by'); + this.set('env', process.env.NODE_ENV || 'development'); + debug('booting in %s mode', this.get('env')); + + // implicit middleware + this.use(connect.query()); + this.use(middleware.init(this)); + + // inherit protos + this.on('mount', function(parent){ + this.request.__proto__ = parent.request; + this.response.__proto__ = parent.response; + this.engines.__proto__ = parent.engines; + }); + + // router + this._router = new Router(this); + this.routes = this._router.map; + this.__defineGetter__('router', function(){ + this._usedRouter = true; + this._router.caseSensitive = this.enabled('case sensitive routing'); + this._router.strict = this.enabled('strict routing'); + return this._router.middleware; + }); + + // setup locals + this.locals = locals(this); + + // default locals + this.locals.settings = this.settings; + + // default configuration + this.set('views', process.cwd() + '/views'); + this.set('jsonp callback name', 'callback'); + + this.configure('development', function(){ + this.set('json spaces', 2); + }); + + this.configure('production', function(){ + this.enable('view cache'); + }); +}; + +/** + * Proxy `connect#use()` to apply settings to + * mounted applications. + * + * @param {String|Function|Server} route + * @param {Function|Server} fn + * @return {app} for chaining + * @api public + */ + +app.use = function(route, fn){ + var app, home, handle; + + // default route to '/' + if ('string' != typeof route) fn = route, route = '/'; + + // express app + if (fn.handle && fn.set) app = fn; + + // restore .app property on req and res + if (app) { + app.route = route; + fn = function(req, res, next) { + var orig = req.app; + app.handle(req, res, function(err){ + req.app = res.app = orig; + req.__proto__ = orig.request; + res.__proto__ = orig.response; + next(err); + }); + }; + } + + connect.proto.use.call(this, route, fn); + + // mounted an app + if (app) { + app.parent = this; + app.emit('mount', this); + } + + return this; +}; + +/** + * Register the given template engine callback `fn` + * as `ext`. + * + * By default will `require()` the engine based on the + * file extension. For example if you try to render + * a "foo.jade" file Express will invoke the following internally: + * + * app.engine('jade', require('jade').__express); + * + * For engines that do not provide `.__express` out of the box, + * or if you wish to "map" a different extension to the template engine + * you may use this method. For example mapping the EJS template engine to + * ".html" files: + * + * app.engine('html', require('ejs').renderFile); + * + * In this case EJS provides a `.renderFile()` method with + * the same signature that Express expects: `(path, options, callback)`, + * though note that it aliases this method as `ejs.__express` internally + * so if you're using ".ejs" extensions you dont need to do anything. + * + * Some template engines do not follow this convention, the + * [Consolidate.js](https://github.com/visionmedia/consolidate.js) + * library was created to map all of node's popular template + * engines to follow this convention, thus allowing them to + * work seemlessly within Express. + * + * @param {String} ext + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.engine = function(ext, fn){ + if ('function' != typeof fn) throw new Error('callback function required'); + if ('.' != ext[0]) ext = '.' + ext; + this.engines[ext] = fn; + return this; +}; + +/** + * Map the given param placeholder `name`(s) to the given callback(s). + * + * Parameter mapping is used to provide pre-conditions to routes + * which use normalized placeholders. For example a _:user_id_ parameter + * could automatically load a user's information from the database without + * any additional code, + * + * The callback uses the samesignature as middleware, the only differencing + * being that the value of the placeholder is passed, in this case the _id_ + * of the user. Once the `next()` function is invoked, just like middleware + * it will continue on to execute the route, or subsequent parameter functions. + * + * app.param('user_id', function(req, res, next, id){ + * User.find(id, function(err, user){ + * if (err) { + * next(err); + * } else if (user) { + * req.user = user; + * next(); + * } else { + * next(new Error('failed to load user')); + * } + * }); + * }); + * + * @param {String|Array} name + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.param = function(name, fn){ + var self = this + , fns = [].slice.call(arguments, 1); + + // array + if (Array.isArray(name)) { + name.forEach(function(name){ + fns.forEach(function(fn){ + self.param(name, fn); + }); + }); + // param logic + } else if ('function' == typeof name) { + this._router.param(name); + // single + } else { + if (':' == name[0]) name = name.substr(1); + fns.forEach(function(fn){ + self._router.param(name, fn); + }); + } + + return this; +}; + +/** + * Assign `setting` to `val`, or return `setting`'s value. + * + * app.set('foo', 'bar'); + * app.get('foo'); + * // => "bar" + * + * Mounted servers inherit their parent server's settings. + * + * @param {String} setting + * @param {String} val + * @return {Server} for chaining + * @api public + */ + +app.set = function(setting, val){ + if (1 == arguments.length) { + if (this.settings.hasOwnProperty(setting)) { + return this.settings[setting]; + } else if (this.parent) { + return this.parent.set(setting); + } + } else { + this.settings[setting] = val; + return this; + } +}; + +/** + * Return the app's absolute pathname + * based on the parent(s) that have + * mounted it. + * + * For example if the application was + * mounted as "/admin", which itself + * was mounted as "/blog" then the + * return value would be "/blog/admin". + * + * @return {String} + * @api private + */ + +app.path = function(){ + return this.parent + ? this.parent.path() + this.route + : ''; +}; + +/** + * Check if `setting` is enabled (truthy). + * + * app.enabled('foo') + * // => false + * + * app.enable('foo') + * app.enabled('foo') + * // => true + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.enabled = function(setting){ + return !!this.set(setting); +}; + +/** + * Check if `setting` is disabled. + * + * app.disabled('foo') + * // => true + * + * app.enable('foo') + * app.disabled('foo') + * // => false + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.disabled = function(setting){ + return !this.set(setting); +}; + +/** + * Enable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.enable = function(setting){ + return this.set(setting, true); +}; + +/** + * Disable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.disable = function(setting){ + return this.set(setting, false); +}; + +/** + * Configure callback for zero or more envs, + * when no `env` is specified that callback will + * be invoked for all environments. Any combination + * can be used multiple times, in any order desired. + * + * Examples: + * + * app.configure(function(){ + * // executed for all envs + * }); + * + * app.configure('stage', function(){ + * // executed staging env + * }); + * + * app.configure('stage', 'production', function(){ + * // executed for stage and production + * }); + * + * Note: + * + * These callbacks are invoked immediately, and + * are effectively sugar for the following: + * + * var env = process.env.NODE_ENV || 'development'; + * + * switch (env) { + * case 'development': + * ... + * break; + * case 'stage': + * ... + * break; + * case 'production': + * ... + * break; + * } + * + * @param {String} env... + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.configure = function(env, fn){ + var envs = 'all' + , args = [].slice.call(arguments); + fn = args.pop(); + if (args.length) envs = args; + if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this); + return this; +}; + +/** + * Delegate `.VERB(...)` calls to `.route(VERB, ...)`. + */ + +methods.forEach(function(method){ + app[method] = function(path){ + if ('get' == method && 1 == arguments.length) return this.set(path); + var args = [method].concat([].slice.call(arguments)); + if (!this._usedRouter) this.use(this.router); + return this._router.route.apply(this._router, args); + } +}); + +/** + * Special-cased "all" method, applying the given route `path`, + * middleware, and callback to _every_ HTTP method. + * + * @param {String} path + * @param {Function} ... + * @return {app} for chaining + * @api public + */ + +app.all = function(path){ + var args = arguments; + methods.forEach(function(method){ + app[method].apply(this, args); + }, this); + return this; +}; + +// del -> delete alias + +app.del = app.delete; + +/** + * Render the given view `name` name with `options` + * and a callback accepting an error and the + * rendered template string. + * + * Example: + * + * app.render('email', { name: 'Tobi' }, function(err, html){ + * // ... + * }) + * + * @param {String} name + * @param {String|Function} options or fn + * @param {Function} fn + * @api public + */ + +app.render = function(name, options, fn){ + var self = this + , opts = {} + , cache = this.cache + , engines = this.engines + , view; + + // support callback function as second arg + if ('function' == typeof options) { + fn = options, options = {}; + } + + // merge app.locals + utils.merge(opts, this.locals); + + // merge options._locals + if (options._locals) utils.merge(opts, options._locals); + + // merge options + utils.merge(opts, options); + + // set .cache unless explicitly provided + opts.cache = null == opts.cache + ? this.enabled('view cache') + : opts.cache; + + // primed cache + if (opts.cache) view = cache[name]; + + // view + if (!view) { + view = new View(name, { + defaultEngine: this.get('view engine'), + root: this.get('views'), + engines: engines + }); + + if (!view.path) { + var err = new Error('Failed to lookup view "' + name + '"'); + err.view = view; + return fn(err); + } + + // prime the cache + if (opts.cache) cache[name] = view; + } + + // render + try { + view.render(opts, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Listen for connections. + * + * A node `http.Server` is returned, with this + * application (which is a `Function`) as its + * callback. If you wish to create both an HTTP + * and HTTPS server you may do so with the "http" + * and "https" modules as shown here: + * + * var http = require('http') + * , https = require('https') + * , express = require('express') + * , app = express(); + * + * http.createServer(app).listen(80); + * https.createServer({ ... }, app).listen(443); + * + * @return {http.Server} + * @api public + */ + +app.listen = function(){ + var server = http.createServer(this); + return server.listen.apply(server, arguments); +}; diff --git a/dist/node_modules/express/lib/express.js b/dist/node_modules/express/lib/express.js new file mode 100644 index 0000000..4d3bbc9 --- /dev/null +++ b/dist/node_modules/express/lib/express.js @@ -0,0 +1,92 @@ +/** + * Module dependencies. + */ + +var connect = require('connect') + , proto = require('./application') + , Route = require('./router/route') + , Router = require('./router') + , req = require('./request') + , res = require('./response') + , utils = connect.utils; + +/** + * Expose `createApplication()`. + */ + +exports = module.exports = createApplication; + +/** + * Framework version. + */ + +exports.version = '3.0.0'; + +/** + * Expose mime. + */ + +exports.mime = connect.mime; + +/** + * Create an express application. + * + * @return {Function} + * @api public + */ + +function createApplication() { + var app = connect(); + utils.merge(app, proto); + app.request = { __proto__: req }; + app.response = { __proto__: res }; + app.init(); + return app; +} + +/** + * Expose connect.middleware as express.* + * for example `express.logger` etc. + */ + +for (var key in connect.middleware) { + Object.defineProperty( + exports + , key + , Object.getOwnPropertyDescriptor(connect.middleware, key)); +} + +/** + * Error on createServer(). + */ + +exports.createServer = function(){ + console.warn('Warning: express.createServer() is deprecated, express'); + console.warn('applications no longer inherit from http.Server,'); + console.warn('please use:'); + console.warn(''); + console.warn(' var express = require("express");'); + console.warn(' var app = express();'); + console.warn(''); + return createApplication(); +}; + +/** + * Expose the prototypes. + */ + +exports.application = proto; +exports.request = req; +exports.response = res; + +/** + * Expose constructors. + */ + +exports.Route = Route; +exports.Router = Router; + +// Error handler title + +exports.errorHandler.title = 'Express'; + diff --git a/dist/node_modules/express/lib/middleware.js b/dist/node_modules/express/lib/middleware.js new file mode 100644 index 0000000..8cf085a --- /dev/null +++ b/dist/node_modules/express/lib/middleware.js @@ -0,0 +1,33 @@ + +/** + * Module dependencies. + */ + +var utils = require('./utils'); + +/** + * Initialization middleware, exposing the + * request and response to eachother, as well + * as defaulting the X-Powered-By header field. + * + * @param {Function} app + * @return {Function} + * @api private + */ + +exports.init = function(app){ + return function expressInit(req, res, next){ + req.app = res.app = app; + if (app.settings['x-powered-by']) res.setHeader('X-Powered-By', 'Express'); + req.res = res; + res.req = req; + req.next = next; + + req.__proto__ = app.request; + res.__proto__ = app.response; + + res.locals = res.locals || utils.locals(res); + + next(); + } +}; diff --git a/dist/node_modules/express/lib/request.js b/dist/node_modules/express/lib/request.js new file mode 100644 index 0000000..8362b32 --- /dev/null +++ b/dist/node_modules/express/lib/request.js @@ -0,0 +1,486 @@ + +/** + * Module dependencies. + */ + +var http = require('http') + , utils = require('./utils') + , connect = require('connect') + , fresh = require('fresh') + , parseRange = require('range-parser') + , parse = connect.utils.parseUrl + , mime = connect.mime; + +/** + * Request prototype. + */ + +var req = exports = module.exports = { + __proto__: http.IncomingMessage.prototype +}; + +/** + * Return request header. + * + * The `Referrer` header field is special-cased, + * both `Referrer` and `Referer` are interchangeable. + * + * Examples: + * + * req.get('Content-Type'); + * // => "text/plain" + * + * req.get('content-type'); + * // => "text/plain" + * + * req.get('Something'); + * // => undefined + * + * Aliased as `req.header()`. + * + * @param {String} name + * @return {String} + * @api public + */ + +req.get = +req.header = function(name){ + switch (name = name.toLowerCase()) { + case 'referer': + case 'referrer': + return this.headers.referrer + || this.headers.referer; + default: + return this.headers[name]; + } +}; + +/** + * 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", a comma-delimted list such as "json, html, text/plain", + * 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 + * req.accepts('html'); + * // => "html" + * + * // Accept: text/*, application/json + * req.accepts('html'); + * // => "html" + * req.accepts('text/html'); + * // => "text/html" + * req.accepts('json, text'); + * // => "json" + * req.accepts('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * req.accepts('image/png'); + * req.accepts('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * req.accepts(['html', 'json']); + * req.accepts('html, json'); + * // => "json" + * + * @param {String|Array} type(s) + * @return {String} + * @api public + */ + +req.accepts = function(type){ + return utils.accepts(type, this.get('Accept')); +}; + +/** + * Check if the given `charset` is acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} charset + * @return {Boolean} + * @api public + */ + +req.acceptsCharset = function(charset){ + var accepted = this.acceptedCharsets; + return accepted.length + ? ~accepted.indexOf(charset) + : true; +}; + +/** + * Check if the given `lang` is acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} lang + * @return {Boolean} + * @api public + */ + +req.acceptsLanguage = function(lang){ + var accepted = this.acceptedLanguages; + return accepted.length + ? ~accepted.indexOf(lang) + : true; +}; + +/** + * Parse Range header field, + * capping to the given `size`. + * + * Unspecified ranges such as "0-" require + * knowledge of your resource length. In + * the case of a byte range this is of course + * the total number of bytes. If the Range + * header field is not given `null` is returned, + * `-1` when unsatisfiable, `-2` when syntactically invalid. + * + * NOTE: remember that ranges are inclusive, so + * for example "Range: users=0-3" should respond + * with 4 users when available, not 3. + * + * @param {Number} size + * @return {Array} + * @api public + */ + +req.range = function(size){ + var range = this.get('Range'); + if (!range) return; + return parseRange(size, range); +}; + +/** + * Return an array of Accepted media types + * ordered from highest quality to lowest. + * + * Examples: + * + * [ { value: 'application/json', + * quality: 1, + * type: 'application', + * subtype: 'json' }, + * { value: 'text/html', + * quality: 0.5, + * type: 'text', + * subtype: 'html' } ] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('accepted', function(){ + var accept = this.get('Accept'); + return accept + ? utils.parseAccept(accept) + : []; +}); + +/** + * Return an array of Accepted languages + * ordered from highest quality to lowest. + * + * Examples: + * + * Accept-Language: en;q=.5, en-us + * ['en-us', 'en'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedLanguages', function(){ + var accept = this.get('Accept-Language'); + return accept + ? utils + .parseQuality(accept) + .map(function(obj){ + return obj.value; + }) + : []; +}); + +/** + * Return an array of Accepted charsets + * ordered from highest quality to lowest. + * + * Examples: + * + * Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8 + * ['unicode-1-1', 'iso-8859-5'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedCharsets', function(){ + var accept = this.get('Accept-Charset'); + return accept + ? utils + .parseQuality(accept) + .map(function(obj){ + return obj.value; + }) + : []; +}); + +/** + * Return the value of param `name` when present or `defaultValue`. + * + * - Checks route placeholders, ex: _/user/:id_ + * - Checks body params, ex: id=12, {"id":12} + * - Checks query string params, ex: ?id=12 + * + * To utilize request bodies, `req.body` + * should be an object. This can be done by using + * the `connect.bodyParser()` middleware. + * + * @param {String} name + * @param {Mixed} defaultValue + * @return {String} + * @api public + */ + +req.param = function(name, defaultValue){ + var params = this.params || {}; + var body = this.body || {}; + var query = this.query || {}; + if (null != params[name] && params.hasOwnProperty(name)) return params[name]; + if (null != body[name]) return body[name]; + if (null != query[name]) return query[name]; + return defaultValue; +}; + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains the give mime `type`. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * req.is('html'); + * req.is('text/html'); + * req.is('text/*'); + * // => true + * + * // When Content-Type is application/json + * req.is('json'); + * req.is('application/json'); + * req.is('application/*'); + * // => true + * + * req.is('html'); + * // => false + * + * @param {String} type + * @return {Boolean} + * @api public + */ + +req.is = function(type){ + var ct = this.get('Content-Type'); + if (!ct) return false; + ct = ct.split(';')[0]; + if (!~type.indexOf('/')) type = mime.lookup(type); + if (~type.indexOf('*')) { + type = type.split('/'); + ct = ct.split('/'); + if ('*' == type[0] && type[1] == ct[1]) return true; + if ('*' == type[1] && type[0] == ct[0]) return true; + return false; + } + return !! ~ct.indexOf(type); +}; + +/** + * Return the protocol string "http" or "https" + * when requested with TLS. When the "trust proxy" + * setting is enabled the "X-Forwarded-Proto" header + * field will be trusted. If you're running behind + * a reverse proxy that supplies https for you this + * may be enabled. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('protocol', function(){ + var trustProxy = this.app.get('trust proxy'); + return this.connection.encrypted + ? 'https' + : trustProxy + ? (this.get('X-Forwarded-Proto') || 'http') + : 'http'; +}); + +/** + * Short-hand for: + * + * req.protocol == 'https' + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('secure', function(){ + return 'https' == this.protocol; +}); + +/** + * Return the remote address, or when + * "trust proxy" is `true` return + * the upstream addr. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('ip', function(){ + return this.ips[0] || this.connection.remoteAddress; +}); + +/** + * When "trust proxy" is `true`, parse + * the "X-Forwarded-For" ip address list. + * + * For example if the value were "client, proxy1, proxy2" + * you would receive the array `["client", "proxy1", "proxy2"]` + * where "proxy2" is the furthest down-stream. + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('ips', function(){ + var trustProxy = this.app.get('trust proxy'); + var val = this.get('X-Forwarded-For'); + return trustProxy && val + ? val.split(/ *, */) + : []; +}); + +/** + * Return basic auth credentials. + * + * Examples: + * + * // http://tobi:hello@example.com + * req.auth + * // => { username: 'tobi', password: 'hello' } + * + * @return {Object} or undefined + * @api public + */ + +req.__defineGetter__('auth', function(){ + // missing + var auth = this.get('Authorization'); + if (!auth) return; + + // malformed + var parts = auth.split(' '); + if ('basic' != parts[0].toLowerCase()) return; + if (!parts[1]) return; + auth = parts[1]; + + // credentials + auth = new Buffer(auth, 'base64').toString().split(':'); + return { username: auth[0], password: auth[1] }; +}); + +/** + * Return subdomains as an array. + * + * For example "tobi.ferrets.example.com" + * would provide `["ferrets", "tobi"]`. + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('subdomains', function(){ + return this.get('Host') + .split('.') + .slice(0, -2) + .reverse(); +}); + +/** + * Short-hand for `url.parse(req.url).pathname`. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('path', function(){ + return parse(this).pathname; +}); + +/** + * Parse the "Host" header field hostname. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('host', function(){ + return this.get('Host').split(':')[0]; +}); + +/** + * Check if the request is fresh, aka + * Last-Modified and/or the ETag + * still match. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('fresh', function(){ + var method = this.method; + var s = this.res.statusCode; + + // GET or HEAD for weak freshness validation only + if ('GET' != method && 'HEAD' != method) return false; + + // 2xx or 304 as per rfc2616 14.26 + if ((s >= 200 && s < 300) || 304 == s) { + return fresh(this.headers, this.res._headers); + } + + return false; +}); + +/** + * Check if the request is stale, aka + * "Last-Modified" and / or the "ETag" for the + * resource has changed. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('stale', function(){ + return !this.fresh; +}); + +/** + * Check if the request was an _XMLHttpRequest_. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('xhr', function(){ + var val = this.get('X-Requested-With') || ''; + return 'xmlhttprequest' == val.toLowerCase(); +}); diff --git a/dist/node_modules/express/lib/response.js b/dist/node_modules/express/lib/response.js new file mode 100644 index 0000000..fb3480a --- /dev/null +++ b/dist/node_modules/express/lib/response.js @@ -0,0 +1,719 @@ +/** + * Module dependencies. + */ + +var fs = require('fs') + , http = require('http') + , path = require('path') + , connect = require('connect') + , utils = connect.utils + , sign = require('cookie-signature').sign + , normalizeType = require('./utils').normalizeType + , normalizeTypes = require('./utils').normalizeTypes + , etag = require('./utils').etag + , statusCodes = http.STATUS_CODES + , send = connect.static.send + , cookie = require('cookie') + , send = require('send') + , crc = require('crc') + , mime = connect.mime + , basename = path.basename + , extname = path.extname + , join = path.join; + +/** + * Response prototype. + */ + +var res = module.exports = { + __proto__: http.ServerResponse.prototype +}; + +/** + * Set status `code`. + * + * @param {Number} code + * @return {ServerResponse} + * @api public + */ + +res.status = function(code){ + this.statusCode = code; + return this; +}; + +/** + * Set Link header field with the given `links`. + * + * Examples: + * + * res.links({ + * next: 'http://api.example.com/users?page=2', + * last: 'http://api.example.com/users?page=5' + * }); + * + * @param {Object} links + * @return {ServerResponse} + * @api public + */ + +res.links = function(links){ + return this.set('Link', Object.keys(links).map(function(rel){ + return '<' + links[rel] + '>; rel="' + rel + '"'; + }).join(', ')); +}; + +/** + * Send a response. + * + * Examples: + * + * res.send(new Buffer('wahoo')); + * res.send({ some: 'json' }); + * res.send('

some html

'); + * res.send(404, 'Sorry, cant find that'); + * res.send(404); + * + * @param {Mixed} body or status + * @param {Mixed} body + * @return {ServerResponse} + * @api public + */ + +res.send = function(body){ + var req = this.req + , head = 'HEAD' == req.method + , len; + + // allow status / body + if (2 == arguments.length) { + // res.send(body, status) backwards compat + if ('number' != typeof body && 'number' == typeof arguments[1]) { + this.statusCode = arguments[1]; + } else { + this.statusCode = body; + body = arguments[1]; + } + } + + switch (typeof body) { + // response status + case 'number': + this.get('Content-Type') || this.type('txt'); + this.statusCode = body; + body = http.STATUS_CODES[body]; + break; + // string defaulting to html + case 'string': + if (!this.get('Content-Type')) { + this.charset = this.charset || 'utf-8'; + this.type('html'); + } + break; + case 'boolean': + case 'object': + if (null == body) { + body = ''; + } else if (Buffer.isBuffer(body)) { + this.get('Content-Type') || this.type('bin'); + } else { + return this.json(body); + } + break; + } + + // populate Content-Length + if (undefined !== body && !this.get('Content-Length')) { + this.set('Content-Length', len = Buffer.isBuffer(body) + ? body.length + : Buffer.byteLength(body)); + } + + // ETag support + // TODO: W/ support + if (len > 1024) { + if (!this.get('ETag')) { + this.set('ETag', etag(body)); + } + } + + // freshness + if (req.fresh) this.statusCode = 304; + + // strip irrelevant headers + if (204 == this.statusCode || 304 == this.statusCode) { + this.removeHeader('Content-Type'); + this.removeHeader('Content-Length'); + body = ''; + } + + // respond + this.end(head ? null : body); + return this; +}; + +/** + * Send JSON response. + * + * Examples: + * + * res.json(null); + * res.json({ user: 'tj' }); + * res.json(500, 'oh noes!'); + * res.json(404, 'I dont have that'); + * + * @param {Mixed} obj or status + * @param {Mixed} obj + * @return {ServerResponse} + * @api public + */ + +res.json = function(obj){ + // allow status / body + if (2 == arguments.length) { + // res.json(body, status) backwards compat + if ('number' == typeof arguments[1]) { + this.statusCode = arguments[1]; + } else { + this.statusCode = obj; + obj = arguments[1]; + } + } + + // settings + var app = this.app; + var replacer = app.get('json replacer'); + var spaces = app.get('json spaces'); + var body = JSON.stringify(obj, replacer, spaces); + + // content-type + this.charset = this.charset || 'utf-8'; + this.get('Content-Type') || this.set('Content-Type', 'application/json'); + + return this.send(body); +}; + +/** + * Send JSON response with JSONP callback support. + * + * Examples: + * + * res.jsonp(null); + * res.jsonp({ user: 'tj' }); + * res.jsonp(500, 'oh noes!'); + * res.jsonp(404, 'I dont have that'); + * + * @param {Mixed} obj or status + * @param {Mixed} obj + * @return {ServerResponse} + * @api public + */ + +res.jsonp = function(obj){ + // allow status / body + if (2 == arguments.length) { + // res.json(body, status) backwards compat + if ('number' == typeof arguments[1]) { + this.statusCode = arguments[1]; + } else { + this.statusCode = obj; + obj = arguments[1]; + } + } + + // settings + var app = this.app; + var replacer = app.get('json replacer'); + var spaces = app.get('json spaces'); + var body = JSON.stringify(obj, replacer, spaces); + var callback = this.req.query[app.get('jsonp callback name')]; + + // content-type + this.charset = this.charset || 'utf-8'; + this.set('Content-Type', 'application/json'); + + // jsonp + if (callback) { + this.set('Content-Type', 'text/javascript'); + var cb = callback.replace(/[^\[\]\w$.]/g, ''); + body = cb + ' && ' + cb + '(' + body + ');'; + } + + return this.send(body); +}; + +/** + * Transfer the file at the given `path`. + * + * Automatically sets the _Content-Type_ response header field. + * The callback `fn(err)` is invoked when the transfer is complete + * or when an error occurs. Be sure to check `res.sentHeader` + * if you wish to attempt responding, as the header and some data + * may have already been transferred. + * + * Options: + * + * - `maxAge` defaulting to 0 + * - `root` root directory for relative filenames + * + * Examples: + * + * The following example illustrates how `res.sendfile()` may + * be used as an alternative for the `static()` middleware for + * dynamic situations. The code backing `res.sendfile()` is actually + * the same code, so HTTP cache support etc is identical. + * + * app.get('/user/:uid/photos/:file', function(req, res){ + * var uid = req.params.uid + * , file = req.params.file; + * + * req.user.mayViewFilesFrom(uid, function(yes){ + * if (yes) { + * res.sendfile('/uploads/' + uid + '/' + file); + * } else { + * res.send(403, 'Sorry! you cant see that.'); + * } + * }); + * }); + * + * @param {String} path + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +res.sendfile = function(path, options, fn){ + var self = this + , req = self.req + , next = this.req.next + , options = options || {} + , done; + + // support function as second arg + if ('function' == typeof options) { + fn = options; + options = {}; + } + + // socket errors + req.socket.on('error', error); + + // errors + function error(err) { + if (done) return; + done = true; + + // clean up + cleanup(); + if (!self.headerSent) self.removeHeader('Content-Disposition'); + + // callback available + if (fn) return fn(err); + + // list in limbo if there's no callback + if (self.headerSent) return; + + // delegate + next(err); + } + + // streaming + function stream() { + if (done) return; + cleanup(); + if (fn) self.on('finish', fn); + } + + // cleanup + function cleanup() { + req.socket.removeListener('error', error); + } + + // transfer + var file = send(req, path); + if (options.root) file.root(options.root); + file.maxage(options.maxAge || 0); + file.on('error', error); + file.on('directory', next); + file.on('stream', stream); + file.pipe(this); + this.on('finish', cleanup); +}; + +/** + * Transfer the file at the given `path` as an attachment. + * + * Optionally providing an alternate attachment `filename`, + * and optional callback `fn(err)`. The callback is invoked + * when the data transfer is complete, or when an error has + * ocurred. Be sure to check `res.headerSent` if you plan to respond. + * + * This method uses `res.sendfile()`. + * + * @param {String} path + * @param {String|Function} filename or fn + * @param {Function} fn + * @api public + */ + +res.download = function(path, filename, fn){ + // support function as second arg + if ('function' == typeof filename) { + fn = filename; + filename = null; + } + + filename = filename || path; + this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"'); + return this.sendfile(path, fn); +}; + +/** + * Set _Content-Type_ response header with `type` through `mime.lookup()` + * when it does not contain "/", or set the Content-Type to `type` otherwise. + * + * Examples: + * + * res.type('.html'); + * res.type('html'); + * res.type('json'); + * res.type('application/json'); + * res.type('png'); + * + * @param {String} type + * @return {ServerResponse} for chaining + * @api public + */ + +res.contentType = +res.type = function(type){ + return this.set('Content-Type', ~type.indexOf('/') + ? type + : mime.lookup(type)); +}; + +/** + * Respond to the Acceptable formats using an `obj` + * of mime-type callbacks. + * + * This method uses `req.accepted`, an array of + * acceptable types ordered by their quality values. + * When "Accept" is not present the _first_ callback + * is invoked, otherwise the first match is used. When + * no match is performed the server responds with + * 406 "Not Acceptable". + * + * Content-Type is set for you, however if you choose + * you may alter this within the callback using `res.type()` + * or `res.set('Content-Type', ...)`. + * + * res.format({ + * 'text/plain': function(){ + * res.send('hey'); + * }, + * + * 'text/html': function(){ + * res.send('

hey

'); + * }, + * + * 'appliation/json': function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * In addition to canonicalized MIME types you may + * also use extnames mapped to these types: + * + * res.format({ + * text: function(){ + * res.send('hey'); + * }, + * + * html: function(){ + * res.send('

hey

'); + * }, + * + * json: function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * By default Express passes an `Error` + * with a `.status` of 406 to `next(err)` + * if a match is not made. If you provide + * a `.default` callback it will be invoked + * instead. + * + * @param {Object} obj + * @return {ServerResponse} for chaining + * @api public + */ + +res.format = function(obj){ + var req = this.req + , next = req.next; + + var fn = obj.default; + if (fn) delete obj.default; + var keys = Object.keys(obj); + + var key = req.accepts(keys); + + this.set('Vary', 'Accept'); + + if (key) { + this.set('Content-Type', normalizeType(key)); + obj[key](req, this, next); + } else if (fn) { + fn(); + } else { + var err = new Error('Not Acceptable'); + err.status = 406; + err.types = normalizeTypes(keys); + next(err); + } + + return this; +}; + +/** + * Set _Content-Disposition_ header to _attachment_ with optional `filename`. + * + * @param {String} filename + * @return {ServerResponse} + * @api public + */ + +res.attachment = function(filename){ + if (filename) this.type(extname(filename)); + this.set('Content-Disposition', filename + ? 'attachment; filename="' + basename(filename) + '"' + : 'attachment'); + return this; +}; + +/** + * Set header `field` to `val`, or pass + * an object of header fields. + * + * Examples: + * + * res.set('Accept', 'application/json'); + * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); + * + * Aliased as `res.header()`. + * + * @param {String|Object} field + * @param {String} val + * @return {ServerResponse} for chaining + * @api public + */ + +res.set = +res.header = function(field, val){ + if (2 == arguments.length) { + this.setHeader(field, '' + val); + } else { + for (var key in field) { + this.setHeader(key, '' + field[key]); + } + } + return this; +}; + +/** + * Get value for header `field`. + * + * @param {String} field + * @return {String} + * @api public + */ + +res.get = function(field){ + return this.getHeader(field); +}; + +/** + * Clear cookie `name`. + * + * @param {String} name + * @param {Object} options + * @param {ServerResponse} for chaining + * @api public + */ + +res.clearCookie = function(name, options){ + var opts = { expires: new Date(1), path: '/' }; + return this.cookie(name, '', options + ? utils.merge(opts, options) + : opts); +}; + +/** + * Set cookie `name` to `val`, with the given `options`. + * + * Options: + * + * - `maxAge` max-age in milliseconds, converted to `expires` + * - `signed` sign the cookie + * - `path` defaults to "/" + * + * Examples: + * + * // "Remember Me" for 15 minutes + * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); + * + * // save as above + * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) + * + * @param {String} name + * @param {String|Object} val + * @param {Options} options + * @api public + */ + +res.cookie = function(name, val, options){ + options = options || {}; + var secret = this.req.secret; + var signed = options.signed; + if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies'); + if ('object' == typeof val) val = 'j:' + JSON.stringify(val); + if (signed) val = 's:' + sign(val, secret); + if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge); + if (null == options.path) options.path = '/'; + this.set('Set-Cookie', cookie.serialize(name, String(val), options)); + return this; +}; + +/** + * Redirect to the given `url` with optional response `status` + * defaulting to 302. + * + * The given `url` can also be the name of a mapped url, for + * example by default express supports "back" which redirects + * to the _Referrer_ or _Referer_ headers or "/". + * + * Examples: + * + * res.redirect('/foo/bar'); + * res.redirect('http://example.com'); + * res.redirect(301, 'http://example.com'); + * res.redirect('http://example.com', 301); + * res.redirect('../login'); // /blog/post/1 -> /blog/login + * + * Mounting: + * + * When an application is mounted, and `res.redirect()` + * is given a path that does _not_ lead with "/". For + * example suppose a "blog" app is mounted at "/blog", + * the following redirect would result in "/blog/login": + * + * res.redirect('login'); + * + * While the leading slash would result in a redirect to "/login": + * + * res.redirect('/login'); + * + * @param {String} url + * @param {Number} code + * @api public + */ + +res.redirect = function(url){ + var app = this.app + , req = this.req + , head = 'HEAD' == req.method + , status = 302 + , body; + + // allow status / url + if (2 == arguments.length) { + if ('number' == typeof url) { + status = url; + url = arguments[1]; + } else { + status = arguments[1]; + } + } + + // setup redirect map + var map = { back: req.get('Referrer') || '/' }; + + // perform redirect + url = map[url] || url; + + // relative + if (!~url.indexOf('://') && 0 != url.indexOf('//')) { + var path = app.path(); + + // relative to path + if ('.' == url[0]) { + url = req.path + '/' + url; + // relative to mount-point + } else if ('/' != url[0]) { + url = path + '/' + url; + } + } + + // Support text/{plain,html} by default + this.format({ + text: function(){ + body = statusCodes[status] + '. Redirecting to ' + url; + }, + + html: function(){ + var u = utils.escape(url); + body = '

' + statusCodes[status] + '. Redirecting to ' + u + '

'; + }, + + default: function(){ + body = ''; + } + }); + + // Respond + this.statusCode = status; + this.set('Location', url); + this.set('Content-Length', Buffer.byteLength(body)); + this.end(head ? null : body); +}; + +/** + * Render `view` with the given `options` and optional callback `fn`. + * When a callback function is given a response will _not_ be made + * automatically, otherwise a response of _200_ and _text/html_ is given. + * + * Options: + * + * - `cache` boolean hinting to the engine it should cache + * - `filename` filename of the view being rendered + * + * @param {String} view + * @param {Object|Function} options or callback function + * @param {Function} fn + * @api public + */ + +res.render = function(view, options, fn){ + var self = this + , options = options || {} + , req = this.req + , app = req.app; + + // support callback function as second arg + if ('function' == typeof options) { + fn = options, options = {}; + } + + // merge res.locals + options._locals = self.locals; + + // default callback to respond + fn = fn || function(err, str){ + if (err) return req.next(err); + self.send(str); + }; + + // render + app.render(view, options, fn); +}; \ No newline at end of file diff --git a/dist/node_modules/express/lib/router/index.js b/dist/node_modules/express/lib/router/index.js new file mode 100644 index 0000000..6003b2b --- /dev/null +++ b/dist/node_modules/express/lib/router/index.js @@ -0,0 +1,258 @@ +/** + * Module dependencies. + */ + +var Route = require('./route') + , utils = require('../utils') + , debug = require('debug')('express:router') + , parse = require('connect').utils.parseUrl + , methods = require('methods'); + +/** + * Expose `Router` constructor. + */ + +exports = module.exports = Router; + +/** + * Initialize a new `Router` with the given `options`. + * + * @param {Object} options + * @api private + */ + +function Router(options) { + options = options || {}; + var self = this; + this.map = {}; + this.params = {}; + this._params = []; + this.caseSensitive = options.caseSensitive; + this.strict = options.strict; + this.middleware = function router(req, res, next){ + self._dispatch(req, res, next); + }; +} + +/** + * Register a param callback `fn` for the given `name`. + * + * @param {String|Function} name + * @param {Function} fn + * @return {Router} for chaining + * @api public + */ + +Router.prototype.param = function(name, fn){ + // param logic + if ('function' == typeof name) { + this._params.push(name); + return; + } + + // apply param functions + var params = this._params + , len = params.length + , ret; + + for (var i = 0; i < len; ++i) { + if (ret = params[i](name, fn)) { + fn = ret; + } + } + + // ensure we end up with a + // middleware function + if ('function' != typeof fn) { + throw new Error('invalid param() call for ' + name + ', got ' + fn); + } + + (this.params[name] = this.params[name] || []).push(fn); + return this; +}; + +/** + * Route dispatcher aka the route "middleware". + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @param {Function} next + * @api private + */ + +Router.prototype._dispatch = function(req, res, next){ + var params = this.params + , self = this; + + debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl); + + // route dispatch + (function pass(i, err){ + var paramCallbacks + , paramIndex = 0 + , paramVal + , route + , keys + , key + , ret; + + // match next route + function nextRoute(err) { + pass(req._route_index + 1, err); + } + + // match route + req.route = route = self.matchRequest(req, i); + + // no route + if (!route) return next(err); + debug('matched %s %s', route.method, route.path); + + // we have a route + // start at param 0 + req.params = route.params; + keys = route.keys; + i = 0; + + // param callbacks + function param(err) { + paramIndex = 0; + key = keys[i++]; + paramVal = key && req.params[key.name]; + paramCallbacks = key && params[key.name]; + + try { + if ('route' == err) { + nextRoute(); + } else if (err) { + i = 0; + callbacks(err); + } else if (paramCallbacks && undefined !== paramVal) { + paramCallback(); + } else if (key) { + param(); + } else { + i = 0; + callbacks(); + } + } catch (err) { + param(err); + } + }; + + param(err); + + // single param callbacks + function paramCallback(err) { + var fn = paramCallbacks[paramIndex++]; + if (err || !fn) return param(err); + fn(req, res, paramCallback, paramVal, key.name); + } + + // invoke route callbacks + function callbacks(err) { + var fn = route.callbacks[i++]; + try { + if ('route' == err) { + nextRoute(); + } else if (err && fn) { + if (fn.length < 4) return callbacks(err); + fn(err, req, res, callbacks); + } else if (fn) { + if (fn.length < 4) return fn(req, res, callbacks); + callbacks(); + } else { + nextRoute(err); + } + } catch (err) { + callbacks(err); + } + } + })(0); +}; + +/** + * Attempt to match a route for `req` + * with optional starting index of `i` + * defaulting to 0. + * + * @param {IncomingMessage} req + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype.matchRequest = function(req, i, head){ + var method = req.method.toLowerCase() + , url = parse(req) + , path = url.pathname + , routes = this.map + , i = i || 0 + , route; + + // HEAD support + if (!head && 'head' == method) { + route = this.matchRequest(req, i, true); + if (route) return route; + method = 'get'; + } + + // routes for this method + if (routes = routes[method]) { + + // matching routes + for (var len = routes.length; i < len; ++i) { + route = routes[i]; + if (route.match(path)) { + req._route_index = i; + return route; + } + } + } +}; + +/** + * Attempt to match a route for `method` + * and `url` with optional starting + * index of `i` defaulting to 0. + * + * @param {String} method + * @param {String} url + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype.match = function(method, url, i, head){ + var req = { method: method, url: url }; + return this.matchRequest(req, i, head); +}; + +/** + * Route `method`, `path`, and one or more callbacks. + * + * @param {String} method + * @param {String} path + * @param {Function} callback... + * @return {Router} for chaining + * @api private + */ + +Router.prototype.route = function(method, path, callbacks){ + var method = method.toLowerCase() + , callbacks = utils.flatten([].slice.call(arguments, 2)); + + // ensure path was given + if (!path) throw new Error('Router#' + method + '() requires a path'); + + // create the route + debug('defined %s %s', method, path); + var route = new Route(method, path, callbacks, { + sensitive: this.caseSensitive + , strict: this.strict + }); + + // add it + (this.map[method] = this.map[method] || []).push(route); + return this; +}; diff --git a/dist/node_modules/express/lib/router/route.js b/dist/node_modules/express/lib/router/route.js new file mode 100644 index 0000000..c1a0b5e --- /dev/null +++ b/dist/node_modules/express/lib/router/route.js @@ -0,0 +1,72 @@ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); + +/** + * Expose `Route`. + */ + +module.exports = Route; + +/** + * Initialize `Route` with the given HTTP `method`, `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + * - `sensitive` enable case-sensitive routes + * - `strict` enable strict matching for trailing slashes + * + * @param {String} method + * @param {String} path + * @param {Array} callbacks + * @param {Object} options. + * @api private + */ + +function Route(method, path, callbacks, options) { + options = options || {}; + this.path = path; + this.method = method; + this.callbacks = callbacks; + this.regexp = utils.pathRegexp(path + , this.keys = [] + , options.sensitive + , options.strict); +} + +/** + * Check if this route matches `path`, if so + * populate `.params`. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +Route.prototype.match = function(path){ + var keys = this.keys + , params = this.params = [] + , m = this.regexp.exec(path); + + if (!m) return false; + + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + + var val = 'string' == typeof m[i] + ? decodeURIComponent(m[i]) + : m[i]; + + if (key) { + params[key.name] = val; + } else { + params.push(val); + } + } + + return true; +}; diff --git a/dist/node_modules/express/lib/utils.js b/dist/node_modules/express/lib/utils.js new file mode 100644 index 0000000..eb32c83 --- /dev/null +++ b/dist/node_modules/express/lib/utils.js @@ -0,0 +1,284 @@ + +/** + * Module dependencies. + */ + +var mime = require('connect').mime + , crc = require('crc'); + +/** + * Return ETag for `body`. + * + * @param {String|Buffer} body + * @return {String} + * @api private + */ + +exports.etag = function(body){ + return '"' + (Buffer.isBuffer(body) + ? crc.buffer.crc32(body) + : crc.crc32(body)) + '"'; +}; + +/** + * Make `locals()` bound to the given `obj`. + * + * This is used for `app.locals` and `res.locals`. + * + * @param {Object} obj + * @return {Function} + * @api private + */ + +exports.locals = function(obj){ + obj.viewCallbacks = obj.viewCallbacks || []; + + function locals(obj){ + for (var key in obj) locals[key] = obj[key]; + return obj; + }; + + return locals; +}; + +/** + * Check if `path` looks absolute. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +exports.isAbsolute = function(path){ + if ('/' == path[0]) return true; + if (':' == path[1] && '\\' == path[2]) return true; +}; + +/** + * Flatten the given `arr`. + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.flatten = function(arr, ret){ + var ret = ret || [] + , len = arr.length; + for (var i = 0; i < len; ++i) { + if (Array.isArray(arr[i])) { + exports.flatten(arr[i], ret); + } else { + ret.push(arr[i]); + } + } + return ret; +}; + +/** + * Normalize the given `type`, for example "html" becomes "text/html". + * + * @param {String} type + * @return {String} + * @api private + */ + +exports.normalizeType = function(type){ + return ~type.indexOf('/') ? type : mime.lookup(type); +}; + +/** + * Normalize `types`, for example "html" becomes "text/html". + * + * @param {Array} types + * @return {Array} + * @api private + */ + +exports.normalizeTypes = function(types){ + var ret = []; + + for (var i = 0; i < types.length; ++i) { + ret.push(~types[i].indexOf('/') + ? types[i] + : mime.lookup(types[i])); + } + + return ret; +}; + +/** + * Return the acceptable type in `types`, if any. + * + * @param {Array} types + * @param {String} str + * @return {String} + * @api private + */ + +exports.acceptsArray = function(types, str){ + // accept anything when Accept is not present + if (!str) return types[0]; + + // parse + var accepted = exports.parseAccept(str) + , normalized = exports.normalizeTypes(types) + , len = accepted.length; + + for (var i = 0; i < len; ++i) { + for (var j = 0, jlen = types.length; j < jlen; ++j) { + if (exports.accept(normalized[j].split('/'), accepted[i])) { + return types[j]; + } + } + } +}; + +/** + * Check if `type(s)` are acceptable based on + * the given `str`. + * + * @param {String|Array} type(s) + * @param {String} str + * @return {Boolean|String} + * @api private + */ + +exports.accepts = function(type, str){ + if ('string' == typeof type) type = type.split(/ *, */); + return exports.acceptsArray(type, str); +}; + +/** + * Check if `type` array is acceptable for `other`. + * + * @param {Array} type + * @param {Object} other + * @return {Boolean} + * @api private + */ + +exports.accept = function(type, other){ + return (type[0] == other.type || '*' == other.type) + && (type[1] == other.subtype || '*' == other.subtype); +}; + +/** + * Parse accept `str`, returning + * an array objects containing + * `.type` and `.subtype` along + * with the values provided by + * `parseQuality()`. + * + * @param {Type} name + * @return {Type} + * @api private + */ + +exports.parseAccept = function(str){ + return exports + .parseQuality(str) + .map(function(obj){ + var parts = obj.value.split('/'); + obj.type = parts[0]; + obj.subtype = parts[1]; + return obj; + }); +}; + +/** + * Parse quality `str`, returning an + * array of objects with `.value` and + * `.quality`. + * + * @param {Type} name + * @return {Type} + * @api private + */ + +exports.parseQuality = function(str){ + return str + .split(/ *, */) + .map(quality) + .filter(function(obj){ + return obj.quality; + }) + .sort(function(a, b){ + return b.quality - a.quality; + }); +}; + +/** + * Parse quality `str` returning an + * object with `.value` and `.quality`. + * + * @param {String} str + * @return {Object} + * @api private + */ + +function quality(str) { + var parts = str.split(/ *; */) + , val = parts[0]; + + var q = parts[1] + ? parseFloat(parts[1].split(/ *= */)[1]) + : 1; + + return { value: val, quality: q }; +} + +/** + * Escape special characters in the given string of html. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param {String|RegExp|Array} path + * @param {Array} keys + * @param {Boolean} sensitive + * @param {Boolean} strict + * @return {RegExp} + * @api private + */ + +exports.pathRegexp = function(path, keys, sensitive, strict) { + if (path instanceof RegExp) return path; + if (Array.isArray(path)) path = '(' + path.join('|') + ')'; + path = path + .concat(strict ? '' : '/?') + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){ + keys.push({ name: key, optional: !! optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + + (optional || '') + + (star ? '(/*)?' : ''); + }) + .replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.*)'); + return new RegExp('^' + path + '$', sensitive ? '' : 'i'); +} \ No newline at end of file diff --git a/dist/node_modules/express/lib/view.js b/dist/node_modules/express/lib/view.js new file mode 100644 index 0000000..4007ecc --- /dev/null +++ b/dist/node_modules/express/lib/view.js @@ -0,0 +1,76 @@ +/** + * Module dependencies. + */ + +var path = require('path') + , fs = require('fs') + , utils = require('./utils') + , dirname = path.dirname + , basename = path.basename + , extname = path.extname + , exists = fs.existsSync || path.existsSync + , join = path.join; + +/** + * Expose `View`. + */ + +module.exports = View; + +/** + * Initialize a new `View` with the given `name`. + * + * Options: + * + * - `defaultEngine` the default template engine name + * - `engines` template engine require() cache + * - `root` root path for view lookup + * + * @param {String} name + * @param {Object} options + * @api private + */ + +function View(name, options) { + options = options || {}; + this.name = name; + this.root = options.root; + var engines = options.engines; + this.defaultEngine = options.defaultEngine; + var ext = this.ext = extname(name); + if (!ext) name += (ext = this.ext = '.' + this.defaultEngine); + this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express); + this.path = this.lookup(name); +} + +/** + * Lookup view by the given `path` + * + * @param {String} path + * @return {String} + * @api private + */ + +View.prototype.lookup = function(path){ + var ext = this.ext; + + // . + if (!utils.isAbsolute(path)) path = join(this.root, path); + if (exists(path)) return path; + + // /index. + path = join(dirname(path), basename(path, ext), 'index' + ext); + if (exists(path)) return path; +}; + +/** + * Render with the given `options` and callback `fn(err, str)`. + * + * @param {Object} options + * @param {Function} fn + * @api private + */ + +View.prototype.render = function(options, fn){ + this.engine(this.path, options, fn); +}; diff --git a/dist/node_modules/express/node_modules/commander/.npmignore b/dist/node_modules/express/node_modules/commander/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/commander/.travis.yml b/dist/node_modules/express/node_modules/commander/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/dist/node_modules/express/node_modules/commander/History.md b/dist/node_modules/express/node_modules/commander/History.md new file mode 100644 index 0000000..4961d2e --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/History.md @@ -0,0 +1,107 @@ + +0.6.1 / 2012-06-01 +================== + + * Added: append (yes or no) on confirmation + * Added: allow node.js v0.7.x + +0.6.0 / 2012-04-10 +================== + + * Added `.prompt(obj, callback)` support. Closes #49 + * Added default support to .choose(). Closes #41 + * Fixed the choice example + +0.5.1 / 2011-12-20 +================== + + * Fixed `password()` for recent nodes. Closes #36 + +0.5.0 / 2011-12-04 +================== + + * Added sub-command option support [itay] + +0.4.3 / 2011-12-04 +================== + + * Fixed custom help ordering. Closes #32 + +0.4.2 / 2011-11-24 +================== + + * Added travis support + * Fixed: line-buffered input automatically trimmed. Closes #31 + +0.4.1 / 2011-11-18 +================== + + * Removed listening for "close" on --help + +0.4.0 / 2011-11-15 +================== + + * Added support for `--`. Closes #24 + +0.3.3 / 2011-11-14 +================== + + * Fixed: wait for close event when writing help info [Jerry Hamlet] + +0.3.2 / 2011-11-01 +================== + + * Fixed long flag definitions with values [felixge] + +0.3.1 / 2011-10-31 +================== + + * Changed `--version` short flag to `-V` from `-v` + * Changed `.version()` so it's configurable [felixge] + +0.3.0 / 2011-10-31 +================== + + * Added support for long flags only. Closes #18 + +0.2.1 / 2011-10-24 +================== + + * "node": ">= 0.4.x < 0.7.0". Closes #20 + +0.2.0 / 2011-09-26 +================== + + * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] + +0.1.0 / 2011-08-24 +================== + + * Added support for custom `--help` output + +0.0.5 / 2011-08-18 +================== + + * Changed: when the user enters nothing prompt for password again + * Fixed issue with passwords beginning with numbers [NuckChorris] + +0.0.4 / 2011-08-15 +================== + + * Fixed `Commander#args` + +0.0.3 / 2011-08-15 +================== + + * Added default option value support + +0.0.2 / 2011-08-15 +================== + + * Added mask support to `Command#password(str[, mask], fn)` + * Added `Command#password(str, fn)` + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/dist/node_modules/express/node_modules/commander/Makefile b/dist/node_modules/express/node_modules/commander/Makefile new file mode 100644 index 0000000..0074625 --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/Makefile @@ -0,0 +1,7 @@ + +TESTS = $(shell find test/test.*.js) + +test: + @./test/run $(TESTS) + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/commander/Readme.md b/dist/node_modules/express/node_modules/commander/Readme.md new file mode 100644 index 0000000..b8328c3 --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/Readme.md @@ -0,0 +1,262 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineappe'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineappe + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .prompt(msg, fn) + + Single-line prompt: + +```js +program.prompt('name: ', function(name){ + console.log('hi %s', name); +}); +``` + + Multi-line prompt: + +```js +program.prompt('description:', function(name){ + console.log('hi %s', name); +}); +``` + + Coercion: + +```js +program.prompt('Age: ', Number, function(age){ + console.log('age: %j', age); +}); +``` + +```js +program.prompt('Birthdate: ', Date, function(date){ + console.log('date: %s', date); +}); +``` + +## .password(msg[, mask], fn) + +Prompt for password without echoing: + +```js +program.password('Password: ', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +Prompt for password with mask char "*": + +```js +program.password('Password: ', '*', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +## .confirm(msg, fn) + + Confirm with the given `msg`: + +```js +program.confirm('continue? ', function(ok){ + console.log(' got %j', ok); +}); +``` + +## .choose(list, fn) + + Let the user choose from a `list`: + +```js +var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + +console.log('Choose the coolest pet:'); +program.choose(list, function(i){ + console.log('you chose %d "%s"', i, list[i]); +}); +``` + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 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/dist/node_modules/express/node_modules/commander/index.js b/dist/node_modules/express/node_modules/commander/index.js new file mode 100644 index 0000000..06ec1e4 --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/commander'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/commander/lib/commander.js b/dist/node_modules/express/node_modules/commander/lib/commander.js new file mode 100644 index 0000000..5ba87eb --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/lib/commander.js @@ -0,0 +1,1026 @@ + +/*! + * commander + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , path = require('path') + , tty = require('tty') + , basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this.args = []; + this.name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name){ + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + return cmd; +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self.args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self.args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + this.parent.on(this.name, function(args, unknown){ + // Parse any so-far unknown options + unknown = unknown || []; + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + self.args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self.args.length) { + args[self.args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to false + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => true + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null != val && fn) val = fn(val); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // store raw args + this.rawArgs = argv; + + // guess name + if (!this.name) this.name = basename(argv[1]); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + this.args = parsed.args; + return this.parseArgs(this.args, parsed.unknown); +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + if ('-' == arg[0]) return this.optionMissingArgument(option, arg); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || '-' == arg[0]) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this.args.length ? ' ' + args : ''); + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd.args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return cmd.name + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args + + (cmd.description() + ? '\n' + cmd.description() + : ''); + }).join('\n\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this.name + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Prompt for a `Number`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForNumber = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseNumber(val){ + val = Number(val); + if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber); + fn(val); + }); +}; + +/** + * Prompt for a `Date`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForDate = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseDate(val){ + val = new Date(val); + if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate); + fn(val); + }); +}; + +/** + * Single-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptSingleLine = function(str, fn){ + if ('function' == typeof arguments[2]) { + return this['promptFor' + (fn.name || fn)](str, arguments[2]); + } + + process.stdout.write(str); + process.stdin.setEncoding('utf8'); + process.stdin.once('data', function(val){ + fn(val.trim()); + }).resume(); +}; + +/** + * Multi-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptMultiLine = function(str, fn){ + var buf = []; + console.log(str); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(val){ + if ('\n' == val || '\r\n' == val) { + process.stdin.removeAllListeners('data'); + fn(buf.join('\n')); + } else { + buf.push(val.trimRight()); + } + }).resume(); +}; + +/** + * Prompt `str` and callback `fn(val)` + * + * Commander supports single-line and multi-line prompts. + * To issue a single-line prompt simply add white-space + * to the end of `str`, something like "name: ", whereas + * for a multi-line prompt omit this "description:". + * + * + * Examples: + * + * program.prompt('Username: ', function(name){ + * console.log('hi %s', name); + * }); + * + * program.prompt('Description:', function(desc){ + * console.log('description was "%s"', desc.trim()); + * }); + * + * @param {String|Object} str + * @param {Function} fn + * @api public + */ + +Command.prototype.prompt = function(str, fn){ + var self = this; + + if ('string' == typeof str) { + if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments); + this.promptMultiLine(str, fn); + } else { + var keys = Object.keys(str) + , obj = {}; + + function next() { + var key = keys.shift() + , label = str[key]; + + if (!key) return fn(obj); + self.prompt(label, function(val){ + obj[key] = val; + next(); + }); + } + + next(); + } +}; + +/** + * Prompt for password with `str`, `mask` char and callback `fn(val)`. + * + * The mask string defaults to '', aka no output is + * written while typing, you may want to use "*" etc. + * + * Examples: + * + * program.password('Password: ', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * program.password('Password: ', '*', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {String} mask + * @param {Function} fn + * @api public + */ + +Command.prototype.password = function(str, mask, fn){ + var self = this + , buf = ''; + + // default mask + if ('function' == typeof mask) { + fn = mask; + mask = ''; + } + + process.stdin.resume(); + tty.setRawMode(true); + process.stdout.write(str); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' == key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + if (!buf.trim().length) return self.password(str, mask, fn); + fn(buf); + return; + } + + if (key && key.ctrl && 'c' == key.name) { + console.log('%s', buf); + process.exit(); + } + + process.stdout.write(mask); + buf += c; + }).resume(); +}; + +/** + * Confirmation prompt with `str` and callback `fn(bool)` + * + * Examples: + * + * program.confirm('continue? ', function(ok){ + * console.log(' got %j', ok); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {Function} fn + * @api public + */ + + +Command.prototype.confirm = function(str, fn, verbose){ + var self = this; + this.prompt(str, function(ok){ + if (!ok.trim()) { + if (!verbose) str += '(yes or no) '; + return self.confirm(str, fn, true); + } + fn(parseBool(ok)); + }); +}; + +/** + * Choice prompt with `list` of items and callback `fn(index, item)` + * + * Examples: + * + * var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + * + * console.log('Choose the coolest pet:'); + * program.choose(list, function(i){ + * console.log('you chose %d "%s"', i, list[i]); + * process.stdin.destroy(); + * }); + * + * @param {Array} list + * @param {Number|Function} index or fn + * @param {Function} fn + * @api public + */ + +Command.prototype.choose = function(list, index, fn){ + var self = this + , hasDefault = 'number' == typeof index; + + if (!hasDefault) { + fn = index; + index = null; + } + + list.forEach(function(item, i){ + if (hasDefault && i == index) { + console.log('* %d) %s', i + 1, item); + } else { + console.log(' %d) %s', i + 1, item); + } + }); + + function again() { + self.prompt(' : ', function(val){ + val = parseInt(val, 10) - 1; + if (hasDefault && isNaN(val)) val = index; + + if (null == list[val]) { + again(); + } else { + fn(val, list[val]); + } + }); + } + + again(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Parse a boolean `str`. + * + * @param {String} str + * @return {Boolean} + * @api private + */ + +function parseBool(str) { + return /^y|yes|ok|true$/i.test(str); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + process.stdout.write(cmd.helpInformation()); + cmd.emit('--help'); + process.exit(0); + } + } +} diff --git a/dist/node_modules/express/node_modules/commander/package.json b/dist/node_modules/express/node_modules/commander/package.json new file mode 100644 index 0000000..7161a8b --- /dev/null +++ b/dist/node_modules/express/node_modules/commander/package.json @@ -0,0 +1,13 @@ +{ + "name": "commander" + , "version": "0.6.1" + , "description": "the complete solution for node.js command-line programs" + , "keywords": ["command", "option", "parser", "prompt", "stdin"] + , "author": "TJ Holowaychuk " + , "repository": { "type": "git", "url": "https://github.com/visionmedia/commander.js.git" } + , "dependencies": {} + , "devDependencies": { "should": ">= 0.0.1" } + , "scripts": { "test": "make test" } + , "main": "index" + , "engines": { "node": ">= 0.4.x" } +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/.npmignore b/dist/node_modules/express/node_modules/connect/.npmignore new file mode 100644 index 0000000..9046dde --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/.npmignore @@ -0,0 +1,12 @@ +*.markdown +*.md +.git* +Makefile +benchmarks/ +docs/ +examples/ +install.sh +support/ +test/ +.DS_Store +coverage.html diff --git a/dist/node_modules/express/node_modules/connect/.travis.yml b/dist/node_modules/express/node_modules/connect/.travis.yml new file mode 100644 index 0000000..8111245 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/LICENSE b/dist/node_modules/express/node_modules/connect/LICENSE new file mode 100644 index 0000000..0c5d22d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2010 Sencha Inc. +Copyright (c) 2011 LearnBoost +Copyright (c) 2011 TJ Holowaychuk + +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/dist/node_modules/express/node_modules/connect/Readme.md b/dist/node_modules/express/node_modules/connect/Readme.md new file mode 100644 index 0000000..7d65f9c --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/Readme.md @@ -0,0 +1,133 @@ +[![build status](https://secure.travis-ci.org/senchalabs/connect.png)](http://travis-ci.org/senchalabs/connect) +# Connect + + Connect is an extensible HTTP server framework for [node](http://nodejs.org), providing high performance "plugins" known as _middleware_. + + Connect is bundled with over _20_ commonly used middleware, including + a logger, session support, cookie parser, and [more](http://senchalabs.github.com/connect). Be sure to view the 2.x [documentation](http://senchalabs.github.com/connect/). + +```js +var connect = require('connect') + , http = require('http'); + +var app = connect() + .use(connect.favicon()) + .use(connect.logger('dev')) + .use(connect.static('public')) + .use(connect.directory('public')) + .use(connect.cookieParser()) + .use(connect.session({ secret: 'my secret here' })) + .use(function(req, res){ + res.end('Hello from Connect!\n'); + }); + +http.createServer(app).listen(3000); +``` + +## Middleware + + - [csrf](http://www.senchalabs.org/connect/csrf.html) + - [basicAuth](http://www.senchalabs.org/connect/basicAuth.html) + - [bodyParser](http://www.senchalabs.org/connect/bodyParser.html) + - [json](http://www.senchalabs.org/connect/json.html) + - [multipart](http://www.senchalabs.org/connect/multipart.html) + - [urlencoded](http://www.senchalabs.org/connect/urlencoded.html) + - [cookieParser](http://www.senchalabs.org/connect/cookieParser.html) + - [directory](http://www.senchalabs.org/connect/directory.html) + - [compress](http://www.senchalabs.org/connect/compress.html) + - [errorHandler](http://www.senchalabs.org/connect/errorHandler.html) + - [favicon](http://www.senchalabs.org/connect/favicon.html) + - [limit](http://www.senchalabs.org/connect/limit.html) + - [logger](http://www.senchalabs.org/connect/logger.html) + - [methodOverride](http://www.senchalabs.org/connect/methodOverride.html) + - [query](http://www.senchalabs.org/connect/query.html) + - [responseTime](http://www.senchalabs.org/connect/responseTime.html) + - [session](http://www.senchalabs.org/connect/session.html) + - [static](http://www.senchalabs.org/connect/static.html) + - [staticCache](http://www.senchalabs.org/connect/staticCache.html) + - [vhost](http://www.senchalabs.org/connect/vhost.html) + - [subdomains](http://www.senchalabs.org/connect/subdomains.html) + - [cookieSession](http://www.senchalabs.org/connect/cookieSession.html) + +## Running Tests + +first: + + $ npm install -d + +then: + + $ make test + +## Authors + + Below is the output from [git-summary](http://github.com/visionmedia/git-extras). + + + project: connect + commits: 2033 + active : 301 days + files : 171 + authors: + 1414 Tj Holowaychuk 69.6% + 298 visionmedia 14.7% + 191 Tim Caswell 9.4% + 51 TJ Holowaychuk 2.5% + 10 Ryan Olds 0.5% + 8 Astro 0.4% + 5 Nathan Rajlich 0.2% + 5 Jakub Nešetřil 0.2% + 3 Daniel Dickison 0.1% + 3 David Rio Deiros 0.1% + 3 Alexander Simmerl 0.1% + 3 Andreas Lind Petersen 0.1% + 2 Aaron Heckmann 0.1% + 2 Jacques Crocker 0.1% + 2 Fabian Jakobs 0.1% + 2 Brian J Brennan 0.1% + 2 Adam Malcontenti-Wilson 0.1% + 2 Glen Mailer 0.1% + 2 James Campos 0.1% + 1 Trent Mick 0.0% + 1 Troy Kruthoff 0.0% + 1 Wei Zhu 0.0% + 1 comerc 0.0% + 1 darobin 0.0% + 1 nateps 0.0% + 1 Marco Sanson 0.0% + 1 Arthur Taylor 0.0% + 1 Aseem Kishore 0.0% + 1 Bart Teeuwisse 0.0% + 1 Cameron Howey 0.0% + 1 Chad Weider 0.0% + 1 Craig Barnes 0.0% + 1 Eran Hammer-Lahav 0.0% + 1 Gregory McWhirter 0.0% + 1 Guillermo Rauch 0.0% + 1 Jae Kwon 0.0% + 1 Jakub Nesetril 0.0% + 1 Joshua Peek 0.0% + 1 Jxck 0.0% + 1 AJ ONeal 0.0% + 1 Michael Hemesath 0.0% + 1 Morten Siebuhr 0.0% + 1 Samori Gorse 0.0% + 1 Tom Jensen 0.0% + +## Node Compatibility + + Connect `< 1.x` is compatible with node 0.2.x + + + Connect `1.x` is compatible with node 0.4.x + + + Connect (_master_) `2.x` is compatible with node 0.6.x + +## CLA + + [http://sencha.com/cla](http://sencha.com/cla) + +## License + +View the [LICENSE](https://github.com/senchalabs/connect/blob/master/LICENSE) file. The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons used by the `directory` middleware created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/). diff --git a/dist/node_modules/express/node_modules/connect/index.js b/dist/node_modules/express/node_modules/connect/index.js new file mode 100644 index 0000000..23240ee --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.CONNECT_COV + ? require('./lib-cov/connect') + : require('./lib/connect'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/lib/cache.js b/dist/node_modules/express/node_modules/connect/lib/cache.js new file mode 100644 index 0000000..052fcdb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/cache.js @@ -0,0 +1,81 @@ + +/*! + * Connect - Cache + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Expose `Cache`. + */ + +module.exports = Cache; + +/** + * LRU cache store. + * + * @param {Number} limit + * @api private + */ + +function Cache(limit) { + this.store = {}; + this.keys = []; + this.limit = limit; +} + +/** + * Touch `key`, promoting the object. + * + * @param {String} key + * @param {Number} i + * @api private + */ + +Cache.prototype.touch = function(key, i){ + this.keys.splice(i,1); + this.keys.push(key); +}; + +/** + * Remove `key`. + * + * @param {String} key + * @api private + */ + +Cache.prototype.remove = function(key){ + delete this.store[key]; +}; + +/** + * Get the object stored for `key`. + * + * @param {String} key + * @return {Array} + * @api private + */ + +Cache.prototype.get = function(key){ + return this.store[key]; +}; + +/** + * Add a cache `key`. + * + * @param {String} key + * @return {Array} + * @api private + */ + +Cache.prototype.add = function(key){ + // initialize store + var len = this.keys.push(key); + + // limit reached, invalidate LRU + if (len > this.limit) this.remove(this.keys.shift()); + + var arr = this.store[key] = []; + arr.createdAt = new Date; + return arr; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/connect.js b/dist/node_modules/express/node_modules/connect/lib/connect.js new file mode 100644 index 0000000..d31ffbb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/connect.js @@ -0,0 +1,93 @@ + +/*! + * Connect + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , proto = require('./proto') + , utils = require('./utils') + , path = require('path') + , basename = path.basename + , fs = require('fs'); + +// node patches + +require('./patch'); + +// expose createServer() as the module + +exports = module.exports = createServer; + +/** + * Framework version. + */ + +exports.version = '2.6.1'; + +/** + * Expose mime module. + */ + +exports.mime = require('./middleware/static').mime; + +/** + * Expose the prototype. + */ + +exports.proto = proto; + +/** + * Auto-load middleware getters. + */ + +exports.middleware = {}; + +/** + * Expose utilities. + */ + +exports.utils = utils; + +/** + * Create a new connect server. + * + * @return {Function} + * @api public + */ + +function createServer() { + function app(req, res){ app.handle(req, res); } + utils.merge(app, proto); + utils.merge(app, EventEmitter.prototype); + app.route = '/'; + app.stack = []; + for (var i = 0; i < arguments.length; ++i) { + app.use(arguments[i]); + } + return app; +}; + +/** + * Support old `.createServer()` method. + */ + +createServer.createServer = createServer; + +/** + * Auto-load bundled middleware with getters. + */ + +fs.readdirSync(__dirname + '/middleware').forEach(function(filename){ + if (!/\.js$/.test(filename)) return; + var name = basename(filename, '.js'); + function load(){ return require('./middleware/' + name); } + exports.middleware.__defineGetter__(name, load); + exports.__defineGetter__(name, load); +}); diff --git a/dist/node_modules/express/node_modules/connect/lib/index.js b/dist/node_modules/express/node_modules/connect/lib/index.js new file mode 100644 index 0000000..2618ddc --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/index.js @@ -0,0 +1,50 @@ + +/** + * Connect is a middleware framework for node, + * shipping with over 18 bundled middleware and a rich selection of + * 3rd-party middleware. + * + * var app = connect() + * .use(connect.logger('dev')) + * .use(connect.static('public')) + * .use(function(req, res){ + * res.end('hello world\n'); + * }) + * .listen(3000); + * + * Installation: + * + * $ npm install connect + * + * Middleware: + * + * - [logger](logger.html) request logger with custom format support + * - [csrf](csrf.html) Cross-site request forgery protection + * - [compress](compress.html) Gzip compression middleware + * - [basicAuth](basicAuth.html) basic http authentication + * - [bodyParser](bodyParser.html) extensible request body parser + * - [json](json.html) application/json parser + * - [urlencoded](urlencoded.html) application/x-www-form-urlencoded parser + * - [multipart](multipart.html) multipart/form-data parser + * - [timeout](timeout.html) request timeouts + * - [cookieParser](cookieParser.html) cookie parser + * - [session](session.html) session management support with bundled MemoryStore + * - [cookieSession](cookieSession.html) cookie-based session support + * - [methodOverride](methodOverride.html) faux HTTP method support + * - [responseTime](responseTime.html) calculates response-time and exposes via X-Response-Time + * - [staticCache](staticCache.html) memory cache layer for the static() middleware + * - [static](static.html) streaming static file server supporting `Range` and more + * - [directory](directory.html) directory listing middleware + * - [vhost](vhost.html) virtual host sub-domain mapping middleware + * - [favicon](favicon.html) efficient favicon server (with default icon) + * - [limit](limit.html) limit the bytesize of request bodies + * - [query](query.html) automatic querystring parser, populating `req.query` + * - [errorHandler](errorHandler.html) flexible error handler + * + * Links: + * + * - list of [3rd-party](https://github.com/senchalabs/connect/wiki) middleware + * - GitHub [repository](http://github.com/senchalabs/connect) + * - [test documentation](https://github.com/senchalabs/connect/blob/gh-pages/tests.md) + * + */ \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js b/dist/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js new file mode 100644 index 0000000..fbd26f2 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js @@ -0,0 +1,101 @@ + +/*! + * Connect - basicAuth + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , unauthorized = utils.unauthorized; + +/** + * Basic Auth: + * + * Enfore basic authentication by providing a `callback(user, pass)`, + * which must return `true` in order to gain access. Alternatively an async + * method is provided as well, invoking `callback(user, pass, callback)`. Populates + * `req.user`. The final alternative is simply passing username / password + * strings. + * + * Simple username and password + * + * connect(connect.basicAuth('username', 'password')); + * + * Callback verification + * + * connect() + * .use(connect.basicAuth(function(user, pass){ + * return 'tj' == user & 'wahoo' == pass; + * })) + * + * Async callback verification, accepting `fn(err, user)`. + * + * connect() + * .use(connect.basicAuth(function(user, pass, fn){ + * User.authenticate({ user: user, pass: pass }, fn); + * })) + * + * @param {Function|String} callback or username + * @param {String} realm + * @api public + */ + +module.exports = function basicAuth(callback, realm) { + var username, password; + + // user / pass strings + if ('string' == typeof callback) { + username = callback; + password = realm; + if ('string' != typeof password) throw new Error('password argument required'); + realm = arguments[2]; + callback = function(user, pass){ + return user == username && pass == password; + } + } + + realm = realm || 'Authorization Required'; + + return function(req, res, next) { + var authorization = req.headers.authorization; + + if (req.user) return next(); + if (!authorization) return unauthorized(res, realm); + + var parts = authorization.split(' ') + + if (parts.length !== 2) return next(utils.error(400)); + + var scheme = parts[0] + , credentials = new Buffer(parts[1], 'base64').toString().split(':') + , user = credentials[0] + , pass = credentials[1]; + + if ('Basic' != scheme) return next(utils.error(400)); + + // async + if (callback.length >= 3) { + var pause = utils.pause(req); + callback(user, pass, function(err, user){ + if (err || !user) return unauthorized(res, realm); + req.user = req.remoteUser = user; + next(); + pause.resume(); + }); + // sync + } else { + if (callback(user, pass)) { + req.user = req.remoteUser = user; + next(); + } else { + unauthorized(res, realm); + } + } + } +}; + diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js b/dist/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js new file mode 100644 index 0000000..9f692cd --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js @@ -0,0 +1,61 @@ + +/*! + * Connect - bodyParser + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var multipart = require('./multipart') + , urlencoded = require('./urlencoded') + , json = require('./json'); + +/** + * Body parser: + * + * Parse request bodies, supports _application/json_, + * _application/x-www-form-urlencoded_, and _multipart/form-data_. + * + * This is equivalent to: + * + * app.use(connect.json()); + * app.use(connect.urlencoded()); + * app.use(connect.multipart()); + * + * Examples: + * + * connect() + * .use(connect.bodyParser()) + * .use(function(req, res) { + * res.end('viewing user ' + req.body.user.name); + * }); + * + * $ curl -d 'user[name]=tj' http://local/ + * $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://local/ + * + * View [json](json.html), [urlencoded](urlencoded.html), and [multipart](multipart.html) for more info. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function bodyParser(options){ + var _urlencoded = urlencoded(options) + , _multipart = multipart(options) + , _json = json(options); + + return function bodyParser(req, res, next) { + _json(req, res, function(err){ + if (err) return next(err); + _urlencoded(req, res, function(err){ + if (err) return next(err); + _multipart(req, res, next); + }); + }); + } +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/compress.js b/dist/node_modules/express/node_modules/connect/lib/middleware/compress.js new file mode 100644 index 0000000..621eea9 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/compress.js @@ -0,0 +1,147 @@ +/*! + * Connect - compress + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var zlib = require('zlib'); + +/** + * Supported content-encoding methods. + */ + +exports.methods = { + gzip: zlib.createGzip + , deflate: zlib.createDeflate +}; + +/** + * Default filter function. + */ + +exports.filter = function(req, res){ + return /json|text|javascript/.test(res.getHeader('Content-Type')); +}; + +/** + * Compress: + * + * Compress response data with gzip/deflate. + * + * Filter: + * + * A `filter` callback function may be passed to + * replace the default logic of: + * + * exports.filter = function(req, res){ + * return /json|text|javascript/.test(res.getHeader('Content-Type')); + * }; + * + * Options: + * + * All remaining options are passed to the gzip/deflate + * creation functions. Consult node's docs for additional details. + * + * - `chunkSize` (default: 16*1024) + * - `windowBits` + * - `level`: 0-9 where 0 is no compression, and 9 is slow but best compression + * - `memLevel`: 1-9 low is slower but uses less memory, high is fast but uses more + * - `strategy`: compression strategy + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function compress(options) { + var options = options || {} + , names = Object.keys(exports.methods) + , filter = options.filter || exports.filter; + + return function(req, res, next){ + var accept = req.headers['accept-encoding'] + , write = res.write + , end = res.end + , stream + , method; + + // vary + res.setHeader('Vary', 'Accept-Encoding'); + + // proxy + + res.write = function(chunk, encoding){ + if (!this.headerSent) this._implicitHeader(); + return stream + ? stream.write(new Buffer(chunk, encoding)) + : write.call(res, chunk, encoding); + }; + + res.end = function(chunk, encoding){ + if (chunk) this.write(chunk, encoding); + return stream + ? stream.end() + : end.call(res); + }; + + res.on('header', function(){ + var encoding = res.getHeader('Content-Encoding') || 'identity'; + + // already encoded + if ('identity' != encoding) return; + + // default request filter + if (!filter(req, res)) return; + + // SHOULD use identity + if (!accept) return; + + // head + if ('HEAD' == req.method) return; + + // default to gzip + if ('*' == accept.trim()) method = 'gzip'; + + // compression method + if (!method) { + for (var i = 0, len = names.length; i < len; ++i) { + if (~accept.indexOf(names[i])) { + method = names[i]; + break; + } + } + } + + // compression method + if (!method) return; + + // compression stream + stream = exports.methods[method](options); + + // header fields + res.setHeader('Content-Encoding', method); + res.removeHeader('Content-Length'); + + // compression + + stream.on('data', function(chunk){ + write.call(res, chunk); + }); + + stream.on('end', function(){ + end.call(res); + }); + + stream.on('drain', function() { + res.emit('drain'); + }); + }); + + next(); + }; +} diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js b/dist/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js new file mode 100644 index 0000000..03d53e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js @@ -0,0 +1,63 @@ + +/*! + * Connect - cookieParser + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('./../utils') + , cookie = require('cookie'); + +/** + * Cookie parser: + * + * Parse _Cookie_ header and populate `req.cookies` + * with an object keyed by the cookie names. Optionally + * you may enabled signed cookie support by passing + * a `secret` string, which assigns `req.secret` so + * it may be used by other middleware. + * + * Examples: + * + * connect() + * .use(connect.cookieParser('optional secret string')) + * .use(function(req, res, next){ + * res.end(JSON.stringify(req.cookies)); + * }) + * + * @param {String} secret + * @return {Function} + * @api public + */ + +module.exports = function cookieParser(secret){ + return function cookieParser(req, res, next) { + if (req.cookies) return next(); + var cookies = req.headers.cookie; + + req.secret = secret; + req.cookies = {}; + req.signedCookies = {}; + + if (cookies) { + try { + req.cookies = cookie.parse(cookies); + if (secret) { + req.signedCookies = utils.parseSignedCookies(req.cookies, secret); + var obj = utils.parseJSONCookies(req.signedCookies); + req.signedCookies = obj; + } + req.cookies = utils.parseJSONCookies(req.cookies); + } catch (err) { + err.status = 400; + return next(err); + } + } + next(); + }; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/cookieSession.js b/dist/node_modules/express/node_modules/connect/lib/middleware/cookieSession.js new file mode 100644 index 0000000..bb0bd8b --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/cookieSession.js @@ -0,0 +1,119 @@ + +/*! + * Connect - cookieSession + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('./../utils') + , Cookie = require('./session/cookie') + , debug = require('debug')('connect:cookieSession') + , signature = require('cookie-signature') + , crc16 = require('crc').crc16; + +// environment + +var env = process.env.NODE_ENV; + +/** + * Cookie Session: + * + * Cookie session middleware. + * + * var app = connect(); + * app.use(connect.cookieParser()); + * app.use(connect.cookieSession({ secret: 'tobo!', cookie: { maxAge: 60 * 60 * 1000 }})); + * + * Options: + * + * - `key` cookie name defaulting to `connect.sess` + * - `secret` prevents cookie tampering + * - `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }` + * - `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto") + * + * Clearing sessions: + * + * To clear the session simply set its value to `null`, + * `cookieSession()` will then respond with a 1970 Set-Cookie. + * + * req.session = null; + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function cookieSession(options){ + // TODO: utilize Session/Cookie to unify API + var options = options || {} + , key = options.key || 'connect.sess' + , trustProxy = options.proxy; + + return function cookieSession(req, res, next) { + + // req.secret is for backwards compatibility + var secret = options.secret || req.secret; + if (!secret) throw new Error('`secret` option required for cookie sessions'); + + // default session + req.session = {}; + var cookie = req.session.cookie = new Cookie(options.cookie); + + // pathname mismatch + if (0 != req.originalUrl.indexOf(cookie.path)) return next(); + + // cookieParser secret + if (!options.secret && req.secret) { + req.session = req.signedCookies[key] || {}; + } else { + // TODO: refactor + var rawCookie = req.cookies[key]; + if (rawCookie) { + var unsigned = utils.parseSignedCookie(rawCookie, secret); + if (unsigned) { + var originalHash = crc16(unsigned); + req.session = utils.parseJSONCookie(unsigned) || {}; + } + } + } + + res.on('header', function(){ + // removed + if (!req.session) { + debug('clear session'); + cookie.expires = new Date(0); + res.setHeader('Set-Cookie', cookie.serialize(key, '')); + return; + } + + delete req.session.cookie; + + // check security + var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase() + , tls = req.connection.encrypted || (trustProxy && 'https' == proto) + , secured = cookie.secure && tls; + + // only send secure cookies via https + if (cookie.secure && !secured) return debug('not secured'); + + // serialize + debug('serializing %j', req.session); + var val = 'j:' + JSON.stringify(req.session); + + // compare hashes, no need to set-cookie if unchanged + if (originalHash == crc16(val)) return debug('unmodified session'); + + // set-cookie + val = 's:' + signature.sign(val, secret); + val = cookie.serialize(key, val); + debug('set-cookie %j', cookie); + res.setHeader('Set-Cookie', val); + }); + + next(); + }; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/csrf.js b/dist/node_modules/express/node_modules/connect/lib/middleware/csrf.js new file mode 100644 index 0000000..d7a5e05 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/csrf.js @@ -0,0 +1,73 @@ +/*! + * Connect - csrf + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); + +/** + * Anti CSRF: + * + * CRSF protection middleware. + * + * By default this middleware generates a token named "_csrf" + * which should be added to requests which mutate + * state, within a hidden form field, query-string etc. This + * token is validated against the visitor's `req.session._csrf` + * property. + * + * The default `value` function checks `req.body` generated + * by the `bodyParser()` middleware, `req.query` generated + * by `query()`, and the "X-CSRF-Token" header field. + * + * This middleware requires session support, thus should be added + * somewhere _below_ `session()` and `cookieParser()`. + * + * Options: + * + * - `value` a function accepting the request, returning the token + * + * @param {Object} options + * @api public + */ + +module.exports = function csrf(options) { + var options = options || {} + , value = options.value || defaultValue; + + return function(req, res, next){ + // generate CSRF token + var token = req.session._csrf || (req.session._csrf = utils.uid(24)); + + // ignore these methods + if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next(); + + // determine value + var val = value(req); + + // check + if (val != token) return next(utils.error(403)); + + next(); + } +}; + +/** + * Default value function, checking the `req.body` + * and `req.query` for the CSRF token. + * + * @param {IncomingMessage} req + * @return {String} + * @api private + */ + +function defaultValue(req) { + return (req.body && req.body._csrf) + || (req.query && req.query._csrf) + || (req.headers['x-csrf-token']); +} diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/directory.js b/dist/node_modules/express/node_modules/connect/lib/middleware/directory.js new file mode 100644 index 0000000..1c925a7 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/directory.js @@ -0,0 +1,229 @@ + +/*! + * Connect - directory + * Copyright(c) 2011 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +// TODO: icon / style for directories +// TODO: arrow key navigation +// TODO: make icons extensible + +/** + * Module dependencies. + */ + +var fs = require('fs') + , parse = require('url').parse + , utils = require('../utils') + , path = require('path') + , normalize = path.normalize + , extname = path.extname + , join = path.join; + +/*! + * Icon cache. + */ + +var cache = {}; + +/** + * Directory: + * + * Serve directory listings with the given `root` path. + * + * Options: + * + * - `hidden` display hidden (dot) files. Defaults to false. + * - `icons` display icons. Defaults to false. + * - `filter` Apply this filter function to files. Defaults to false. + * + * @param {String} root + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function directory(root, options){ + options = options || {}; + + // root required + if (!root) throw new Error('directory() root path required'); + var hidden = options.hidden + , icons = options.icons + , filter = options.filter + , root = normalize(root); + + return function directory(req, res, next) { + if ('GET' != req.method && 'HEAD' != req.method) return next(); + + var accept = req.headers.accept || 'text/plain' + , url = parse(req.url) + , dir = decodeURIComponent(url.pathname) + , path = normalize(join(root, dir)) + , originalUrl = parse(req.originalUrl) + , originalDir = decodeURIComponent(originalUrl.pathname) + , showUp = path != root && path != root + '/'; + + // null byte(s), bad request + if (~path.indexOf('\0')) return next(utils.error(400)); + + // malicious path, forbidden + if (0 != path.indexOf(root)) return next(utils.error(403)); + + // check if we have a directory + fs.stat(path, function(err, stat){ + if (err) return 'ENOENT' == err.code + ? next() + : next(err); + + if (!stat.isDirectory()) return next(); + + // fetch files + fs.readdir(path, function(err, files){ + if (err) return next(err); + if (!hidden) files = removeHidden(files); + if (filter) files = files.filter(filter); + files.sort(); + + // content-negotiation + for (var key in exports) { + if (~accept.indexOf(key) || ~accept.indexOf('*/*')) { + exports[key](req, res, files, next, originalDir, showUp, icons); + return; + } + } + + // not acceptable + next(utils.error(406)); + }); + }); + }; +}; + +/** + * Respond with text/html. + */ + +exports.html = function(req, res, files, next, dir, showUp, icons){ + fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){ + if (err) return next(err); + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){ + if (err) return next(err); + if (showUp) files.unshift('..'); + str = str + .replace('{style}', style) + .replace('{files}', html(files, dir, icons)) + .replace('{directory}', dir) + .replace('{linked-path}', htmlPath(dir)); + res.setHeader('Content-Type', 'text/html'); + res.setHeader('Content-Length', str.length); + res.end(str); + }); + }); +}; + +/** + * Respond with application/json. + */ + +exports.json = function(req, res, files){ + files = JSON.stringify(files); + res.setHeader('Content-Type', 'application/json'); + res.setHeader('Content-Length', files.length); + res.end(files); +}; + +/** + * Respond with text/plain. + */ + +exports.plain = function(req, res, files){ + files = files.join('\n') + '\n'; + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Length', files.length); + res.end(files); +}; + +/** + * Map html `dir`, returning a linked path. + */ + +function htmlPath(dir) { + var curr = []; + return dir.split('/').map(function(part){ + curr.push(part); + return '' + part + ''; + }).join(' / '); +} + +/** + * Map html `files`, returning an html unordered list. + */ + +function html(files, dir, useIcons) { + return '
    ' + files.map(function(file){ + var icon = '' + , classes = []; + + if (useIcons && '..' != file) { + icon = icons[extname(file)] || icons.default; + icon = ''; + classes.push('icon'); + } + + return '
  • ' + + icon + file + '
  • '; + + }).join('\n') + '
'; +} + +/** + * Load and cache the given `icon`. + * + * @param {String} icon + * @return {String} + * @api private + */ + +function load(icon) { + if (cache[icon]) return cache[icon]; + return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64'); +} + +/** + * Filter "hidden" `files`, aka files + * beginning with a `.`. + * + * @param {Array} files + * @return {Array} + * @api private + */ + +function removeHidden(files) { + return files.filter(function(file){ + return '.' != file[0]; + }); +} + +/** + * Icon map. + */ + +var icons = { + '.js': 'page_white_code_red.png' + , '.c': 'page_white_c.png' + , '.h': 'page_white_h.png' + , '.cc': 'page_white_cplusplus.png' + , '.php': 'page_white_php.png' + , '.rb': 'page_white_ruby.png' + , '.cpp': 'page_white_cplusplus.png' + , '.swf': 'page_white_flash.png' + , '.pdf': 'page_white_acrobat.png' + , 'default': 'page_white.png' +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js b/dist/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js new file mode 100644 index 0000000..4a84edc --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js @@ -0,0 +1,86 @@ +/*! + * Connect - errorHandler + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , fs = require('fs'); + +// environment + +var env = process.env.NODE_ENV || 'development'; + +/** + * Error handler: + * + * Development error handler, providing stack traces + * and error message responses for requests accepting text, html, + * or json. + * + * Text: + * + * By default, and when _text/plain_ is accepted a simple stack trace + * or error message will be returned. + * + * JSON: + * + * When _application/json_ is accepted, connect will respond with + * an object in the form of `{ "error": error }`. + * + * HTML: + * + * When accepted connect will output a nice html stack trace. + * + * @return {Function} + * @api public + */ + +exports = module.exports = function errorHandler(){ + return function errorHandler(err, req, res, next){ + if (err.status) res.statusCode = err.status; + if (res.statusCode < 400) res.statusCode = 500; + if ('test' != env) console.error(err.stack); + var accept = req.headers.accept || ''; + // html + if (~accept.indexOf('html')) { + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){ + fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){ + var stack = (err.stack || '') + .split('\n').slice(1) + .map(function(v){ return '
  • ' + v + '
  • '; }).join(''); + html = html + .replace('{style}', style) + .replace('{stack}', stack) + .replace('{title}', exports.title) + .replace('{statusCode}', res.statusCode) + .replace(/\{error\}/g, utils.escape(err.toString())); + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + res.end(html); + }); + }); + // json + } else if (~accept.indexOf('json')) { + var error = { message: err.message, stack: err.stack }; + for (var prop in err) error[prop] = err[prop]; + var json = JSON.stringify({ error: error }); + res.setHeader('Content-Type', 'application/json'); + res.end(json); + // plain text + } else { + res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' }); + res.end(err.stack); + } + }; +}; + +/** + * Template title, framework authors may override this value. + */ + +exports.title = 'Connect'; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/favicon.js b/dist/node_modules/express/node_modules/connect/lib/middleware/favicon.js new file mode 100644 index 0000000..c57bf34 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/favicon.js @@ -0,0 +1,81 @@ + +/*! + * Connect - favicon + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , utils = require('../utils'); + +/** + * Favicon: + * + * By default serves the connect favicon, or the favicon + * located by the given `path`. + * + * Options: + * + * - `maxAge` cache-control max-age directive, defaulting to 1 day + * + * Examples: + * + * Serve default favicon: + * + * connect() + * .use(connect.favicon()) + * + * Serve favicon before logging for brevity: + * + * connect() + * .use(connect.favicon()) + * .use(connect.logger('dev')) + * + * Serve custom favicon: + * + * connect() + * .use(connect.favicon('public/favicon.ico)) + * + * @param {String} path + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function favicon(path, options){ + var options = options || {} + , path = path || __dirname + '/../public/favicon.ico' + , maxAge = options.maxAge || 86400000 + , icon; // favicon cache + + return function favicon(req, res, next){ + if ('/favicon.ico' == req.url) { + if (icon) { + res.writeHead(200, icon.headers); + res.end(icon.body); + } else { + fs.readFile(path, function(err, buf){ + if (err) return next(err); + icon = { + headers: { + 'Content-Type': 'image/x-icon' + , 'Content-Length': buf.length + , 'ETag': '"' + utils.md5(buf) + '"' + , 'Cache-Control': 'public, max-age=' + (maxAge / 1000) + }, + body: buf + }; + res.writeHead(200, icon.headers); + res.end(icon.body); + }); + } + } else { + next(); + } + }; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/json.js b/dist/node_modules/express/node_modules/connect/lib/middleware/json.js new file mode 100644 index 0000000..1659cf8 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/json.js @@ -0,0 +1,80 @@ + +/*! + * Connect - json + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , _limit = require('./limit'); + +/** + * noop middleware. + */ + +function noop(req, res, next) { + next(); +} + +/** + * JSON: + * + * Parse JSON request bodies, providing the + * parsed object as `req.body`. + * + * Options: + * + * - `strict` when `false` anything `JSON.parse()` accepts will be parsed + * - `reviver` used as the second "reviver" argument for JSON.parse + * - `limit` byte limit disabled by default + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(options){ + var options = options || {} + , strict = options.strict === false + ? false + : true; + + var limit = options.limit + ? _limit(options.limit) + : noop; + + return function json(req, res, next) { + if (req._body) return next(); + req.body = req.body || {}; + + // check Content-Type + if ('application/json' != utils.mime(req)) return next(); + + // flag as parsed + req._body = true; + + // parse + limit(req, res, function(err){ + if (err) return next(err); + var buf = ''; + req.setEncoding('utf8'); + req.on('data', function(chunk){ buf += chunk }); + req.on('end', function(){ + if (strict && '{' != buf[0] && '[' != buf[0]) return next(utils.error(400, 'invalid json')); + try { + req.body = JSON.parse(buf, options.reviver); + next(); + } catch (err){ + err.body = buf; + err.status = 400; + next(err); + } + }); + }); + } +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/limit.js b/dist/node_modules/express/node_modules/connect/lib/middleware/limit.js new file mode 100644 index 0000000..8233b4d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/limit.js @@ -0,0 +1,55 @@ + +/*! + * Connect - limit + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); + +/** + * Limit: + * + * Limit request bodies to the given size in `bytes`. + * + * A string representation of the bytesize may also be passed, + * for example "5mb", "200kb", "1gb", etc. + * + * connect() + * .use(connect.limit('5.5mb')) + * .use(handleImageUpload) + * + * @param {Number|String} bytes + * @return {Function} + * @api public + */ + +module.exports = function limit(bytes){ + if ('string' == typeof bytes) bytes = utils.parseBytes(bytes); + if ('number' != typeof bytes) throw new Error('limit() bytes required'); + return function limit(req, res, next){ + var received = 0 + , len = req.headers['content-length'] + ? parseInt(req.headers['content-length'], 10) + : null; + + // self-awareness + if (req._limit) return next(); + req._limit = true; + + // limit by content-length + if (len && len > bytes) return next(utils.error(413)); + + // limit + req.on('data', function(chunk){ + received += chunk.length; + if (received > bytes) req.destroy(); + }); + + next(); + }; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/logger.js b/dist/node_modules/express/node_modules/connect/lib/middleware/logger.js new file mode 100644 index 0000000..d29958f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/logger.js @@ -0,0 +1,336 @@ +/*! + * Connect - logger + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var bytes = require('bytes'); + +/*! + * Log buffer. + */ + +var buf = []; + +/*! + * Default log buffer duration. + */ + +var defaultBufferDuration = 1000; + +/** + * Logger: + * + * Log requests with the given `options` or a `format` string. + * + * Options: + * + * - `format` Format string, see below for tokens + * - `stream` Output stream, defaults to _stdout_ + * - `buffer` Buffer duration, defaults to 1000ms when _true_ + * - `immediate` Write log line on request instead of response (for response times) + * + * Tokens: + * + * - `:req[header]` ex: `:req[Accept]` + * - `:res[header]` ex: `:res[Content-Length]` + * - `:http-version` + * - `:response-time` + * - `:remote-addr` + * - `:date` + * - `:method` + * - `:url` + * - `:referrer` + * - `:user-agent` + * - `:status` + * + * Formats: + * + * Pre-defined formats that ship with connect: + * + * - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"' + * - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms' + * - `tiny` ':method :url :status :res[content-length] - :response-time ms' + * - `dev` concise output colored by response status for development use + * + * Examples: + * + * connect.logger() // default + * connect.logger('short') + * connect.logger('tiny') + * connect.logger({ immediate: true, format: 'dev' }) + * connect.logger(':method :url - :referrer') + * connect.logger(':req[content-type] -> :res[content-type]') + * connect.logger(function(req, res){ return 'some format string' }) + * + * Defining Tokens: + * + * To define a token, simply invoke `connect.logger.token()` with the + * name and a callback function. The value returned is then available + * as ":type" in this case. + * + * connect.logger.token('type', function(req, res){ return req.headers['content-type']; }) + * + * Defining Formats: + * + * All default formats are defined this way, however it's public API as well: + * + * connect.logger.format('name', 'string or function') + * + * @param {String|Function|Object} format or options + * @return {Function} + * @api public + */ + +exports = module.exports = function logger(options) { + if ('object' == typeof options) { + options = options || {}; + } else if (options) { + options = { format: options }; + } else { + options = {}; + } + + // output on request instead of response + var immediate = options.immediate; + + // format name + var fmt = exports[options.format] || options.format || exports.default; + + // compile format + if ('function' != typeof fmt) fmt = compile(fmt); + + // options + var stream = options.stream || process.stdout + , buffer = options.buffer; + + // buffering support + if (buffer) { + var realStream = stream + , interval = 'number' == typeof buffer + ? buffer + : defaultBufferDuration; + + // flush interval + setInterval(function(){ + if (buf.length) { + realStream.write(buf.join('')); + buf.length = 0; + } + }, interval); + + // swap the stream + stream = { + write: function(str){ + buf.push(str); + } + }; + } + + return function logger(req, res, next) { + req._startTime = new Date; + + // immediate + if (immediate) { + var line = fmt(exports, req, res); + if (null == line) return; + stream.write(line + '\n'); + // proxy end to output logging + } else { + var end = res.end; + res.end = function(chunk, encoding){ + res.end = end; + res.end(chunk, encoding); + var line = fmt(exports, req, res); + if (null == line) return; + stream.write(line + '\n'); + }; + } + + + next(); + }; +}; + +/** + * Compile `fmt` into a function. + * + * @param {String} fmt + * @return {Function} + * @api private + */ + +function compile(fmt) { + fmt = fmt.replace(/"/g, '\\"'); + var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){ + return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "'; + }) + '";' + return new Function('tokens, req, res', js); +}; + +/** + * Define a token function with the given `name`, + * and callback `fn(req, res)`. + * + * @param {String} name + * @param {Function} fn + * @return {Object} exports for chaining + * @api public + */ + +exports.token = function(name, fn) { + exports[name] = fn; + return this; +}; + +/** + * Define a `fmt` with the given `name`. + * + * @param {String} name + * @param {String|Function} fmt + * @return {Object} exports for chaining + * @api public + */ + +exports.format = function(name, str){ + exports[name] = str; + return this; +}; + +/** + * Default format. + */ + +exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'); + +/** + * Short format. + */ + +exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'); + +/** + * Tiny format. + */ + +exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms'); + +/** + * dev (colored) + */ + +exports.format('dev', function(tokens, req, res){ + var status = res.statusCode + , len = parseInt(res.getHeader('Content-Length'), 10) + , color = 32; + + if (status >= 500) color = 31 + else if (status >= 400) color = 33 + else if (status >= 300) color = 36; + + len = isNaN(len) + ? '' + : len = ' - ' + bytes(len); + + return '\033[90m' + req.method + + ' ' + req.originalUrl + ' ' + + '\033[' + color + 'm' + res.statusCode + + ' \033[90m' + + (new Date - req._startTime) + + 'ms' + len + + '\033[0m'; +}); + +/** + * request url + */ + +exports.token('url', function(req){ + return req.originalUrl || req.url; +}); + +/** + * request method + */ + +exports.token('method', function(req){ + return req.method; +}); + +/** + * response time in milliseconds + */ + +exports.token('response-time', function(req){ + return new Date - req._startTime; +}); + +/** + * UTC date + */ + +exports.token('date', function(){ + return new Date().toUTCString(); +}); + +/** + * response status code + */ + +exports.token('status', function(req, res){ + return res.statusCode; +}); + +/** + * normalized referrer + */ + +exports.token('referrer', function(req){ + return req.headers['referer'] || req.headers['referrer']; +}); + +/** + * remote address + */ + +exports.token('remote-addr', function(req){ + return req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)); +}); + +/** + * HTTP version + */ + +exports.token('http-version', function(req){ + return req.httpVersionMajor + '.' + req.httpVersionMinor; +}); + +/** + * UA string + */ + +exports.token('user-agent', function(req){ + return req.headers['user-agent']; +}); + +/** + * request header + */ + +exports.token('req', function(req, res, field){ + return req.headers[field.toLowerCase()]; +}); + +/** + * response header + */ + +exports.token('res', function(req, res, field){ + return (res._headers || {})[field.toLowerCase()]; +}); + diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js b/dist/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js new file mode 100644 index 0000000..aaf4014 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js @@ -0,0 +1,40 @@ + +/*! + * Connect - methodOverride + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Method Override: + * + * Provides faux HTTP method support. + * + * Pass an optional `key` to use when checking for + * a method override, othewise defaults to _\_method_. + * The original method is available via `req.originalMethod`. + * + * @param {String} key + * @return {Function} + * @api public + */ + +module.exports = function methodOverride(key){ + key = key || "_method"; + return function methodOverride(req, res, next) { + req.originalMethod = req.originalMethod || req.method; + + // req.body + if (req.body && key in req.body) { + req.method = req.body[key].toUpperCase(); + delete req.body[key]; + // check X-HTTP-Method-Override + } else if (req.headers['x-http-method-override']) { + req.method = req.headers['x-http-method-override'].toUpperCase(); + } + + next(); + }; +}; + diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/multipart.js b/dist/node_modules/express/node_modules/connect/lib/middleware/multipart.js new file mode 100644 index 0000000..1a0e3d0 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/multipart.js @@ -0,0 +1,131 @@ +/*! + * Connect - multipart + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var formidable = require('formidable') + , _limit = require('./limit') + , utils = require('../utils') + , qs = require('qs'); + +/** + * noop middleware. + */ + +function noop(req, res, next) { + next(); +} + +/** + * Multipart: + * + * Parse multipart/form-data request bodies, + * providing the parsed object as `req.body` + * and `req.files`. + * + * Configuration: + * + * The options passed are merged with [formidable](https://github.com/felixge/node-formidable)'s + * `IncomingForm` object, allowing you to configure the upload directory, + * size limits, etc. For example if you wish to change the upload dir do the following. + * + * app.use(connect.multipart({ uploadDir: path })); + * + * Options: + * + * - `limit` byte limit defaulting to none + * - `defer` defers processing and exposes the Formidable form object as `req.form`. + * `next()` is called without waiting for the form's "end" event. + * This option is useful if you need to bind to the "progress" event, for example. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(options){ + options = options || {}; + + var limit = options.limit + ? _limit(options.limit) + : noop; + + return function multipart(req, res, next) { + if (req._body) return next(); + req.body = req.body || {}; + req.files = req.files || {}; + + // ignore GET + if ('GET' == req.method || 'HEAD' == req.method) return next(); + + // check Content-Type + if ('multipart/form-data' != utils.mime(req)) return next(); + + // flag as parsed + req._body = true; + + // parse + limit(req, res, function(err){ + if (err) return next(err); + + var form = new formidable.IncomingForm + , data = {} + , files = {} + , done; + + Object.keys(options).forEach(function(key){ + form[key] = options[key]; + }); + + function ondata(name, val, data){ + if (Array.isArray(data[name])) { + data[name].push(val); + } else if (data[name]) { + data[name] = [data[name], val]; + } else { + data[name] = val; + } + } + + form.on('field', function(name, val){ + ondata(name, val, data); + }); + + form.on('file', function(name, val){ + ondata(name, val, files); + }); + + form.on('error', function(err){ + if (!options.defer) { + err.status = 400; + next(err); + } + done = true; + }); + + form.on('end', function(){ + if (done) return; + try { + req.body = qs.parse(data); + req.files = qs.parse(files); + if (!options.defer) next(); + } catch (err) { + form.emit('error', err); + } + }); + + form.parse(req); + + if (options.defer) { + req.form = form; + next(); + } + }); + } +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/query.js b/dist/node_modules/express/node_modules/connect/lib/middleware/query.js new file mode 100644 index 0000000..93fc5d3 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/query.js @@ -0,0 +1,46 @@ +/*! + * Connect - query + * Copyright(c) 2011 TJ Holowaychuk + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var qs = require('qs') + , parse = require('../utils').parseUrl; + +/** + * Query: + * + * Automatically parse the query-string when available, + * populating the `req.query` object. + * + * Examples: + * + * connect() + * .use(connect.query()) + * .use(function(req, res){ + * res.end(JSON.stringify(req.query)); + * }); + * + * The `options` passed are provided to qs.parse function. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function query(options){ + return function query(req, res, next){ + if (!req.query) { + req.query = ~req.url.indexOf('?') + ? qs.parse(parse(req).query, options) + : {}; + } + + next(); + }; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/responseTime.js b/dist/node_modules/express/node_modules/connect/lib/middleware/responseTime.js new file mode 100644 index 0000000..62abc04 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/responseTime.js @@ -0,0 +1,32 @@ + +/*! + * Connect - responseTime + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Reponse time: + * + * Adds the `X-Response-Time` header displaying the response + * duration in milliseconds. + * + * @return {Function} + * @api public + */ + +module.exports = function responseTime(){ + return function(req, res, next){ + var start = new Date; + + if (res._responseTime) return next(); + res._responseTime = true; + + res.on('header', function(){ + var duration = new Date - start; + res.setHeader('X-Response-Time', duration + 'ms'); + }); + + next(); + }; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/session.js b/dist/node_modules/express/node_modules/connect/lib/middleware/session.js new file mode 100644 index 0000000..8bf3609 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/session.js @@ -0,0 +1,354 @@ + +/*! + * Connect - session + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Session = require('./session/session') + , debug = require('debug')('connect:session') + , MemoryStore = require('./session/memory') + , signature = require('cookie-signature') + , Cookie = require('./session/cookie') + , Store = require('./session/store') + , utils = require('./../utils') + , parse = utils.parseUrl + , crc16 = require('crc').crc16; + +// environment + +var env = process.env.NODE_ENV; + +/** + * Expose the middleware. + */ + +exports = module.exports = session; + +/** + * Expose constructors. + */ + +exports.Store = Store; +exports.Cookie = Cookie; +exports.Session = Session; +exports.MemoryStore = MemoryStore; + +/** + * Warning message for `MemoryStore` usage in production. + */ + +var warning = 'Warning: connection.session() MemoryStore is not\n' + + 'designed for a production environment, as it will leak\n' + + 'memory, and will not scale past a single process.'; + +/** + * Session: + * + * Setup session store with the given `options`. + * + * Session data is _not_ saved in the cookie itself, however + * cookies are used, so we must use the [cookieParser()](cookieParser.html) + * middleware _before_ `session()`. + * + * Examples: + * + * connect() + * .use(connect.cookieParser()) + * .use(connect.session({ secret: 'keyboard cat', key: 'sid', cookie: { secure: true }})) + * + * Options: + * + * - `key` cookie name defaulting to `connect.sid` + * - `store` session store instance + * - `secret` session cookie is signed with this secret to prevent tampering + * - `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }` + * - `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto") + * + * Cookie option: + * + * By default `cookie.maxAge` is `null`, meaning no "expires" parameter is set + * so the cookie becomes a browser-session cookie. When the user closes the + * browser the cookie (and session) will be removed. + * + * ## req.session + * + * To store or access session data, simply use the request property `req.session`, + * which is (generally) serialized as JSON by the store, so nested objects + * are typically fine. For example below is a user-specific view counter: + * + * connect() + * .use(connect.favicon()) + * .use(connect.cookieParser()) + * .use(connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }})) + * .use(function(req, res, next){ + * var sess = req.session; + * if (sess.views) { + * res.setHeader('Content-Type', 'text/html'); + * res.write('

    views: ' + sess.views + '

    '); + * res.write('

    expires in: ' + (sess.cookie.maxAge / 1000) + 's

    '); + * res.end(); + * sess.views++; + * } else { + * sess.views = 1; + * res.end('welcome to the session demo. refresh!'); + * } + * } + * )).listen(3000); + * + * ## Session#regenerate() + * + * To regenerate the session simply invoke the method, once complete + * a new SID and `Session` instance will be initialized at `req.session`. + * + * req.session.regenerate(function(err){ + * // will have a new session here + * }); + * + * ## Session#destroy() + * + * Destroys the session, removing `req.session`, will be re-generated next request. + * + * req.session.destroy(function(err){ + * // cannot access session here + * }); + * + * ## Session#reload() + * + * Reloads the session data. + * + * req.session.reload(function(err){ + * // session updated + * }); + * + * ## Session#save() + * + * Save the session. + * + * req.session.save(function(err){ + * // session saved + * }); + * + * ## Session#touch() + * + * Updates the `.maxAge` property. Typically this is + * not necessary to call, as the session middleware does this for you. + * + * ## Session#cookie + * + * Each session has a unique cookie object accompany it. This allows + * you to alter the session cookie per visitor. For example we can + * set `req.session.cookie.expires` to `false` to enable the cookie + * to remain for only the duration of the user-agent. + * + * ## Session#maxAge + * + * Alternatively `req.session.cookie.maxAge` will return the time + * remaining in milliseconds, which we may also re-assign a new value + * to adjust the `.expires` property appropriately. The following + * are essentially equivalent + * + * var hour = 3600000; + * req.session.cookie.expires = new Date(Date.now() + hour); + * req.session.cookie.maxAge = hour; + * + * For example when `maxAge` is set to `60000` (one minute), and 30 seconds + * has elapsed it will return `30000` until the current request has completed, + * at which time `req.session.touch()` is called to reset `req.session.maxAge` + * to its original value. + * + * req.session.cookie.maxAge; + * // => 30000 + * + * Session Store Implementation: + * + * Every session store _must_ implement the following methods + * + * - `.get(sid, callback)` + * - `.set(sid, session, callback)` + * - `.destroy(sid, callback)` + * + * Recommended methods include, but are not limited to: + * + * - `.length(callback)` + * - `.clear(callback)` + * + * For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +function session(options){ + var options = options || {} + , key = options.key || 'connect.sid' + , store = options.store || new MemoryStore + , cookie = options.cookie || {} + , trustProxy = options.proxy + , storeReady = true; + + // notify user that this store is not + // meant for a production environment + if ('production' == env && store instanceof MemoryStore) { + console.warn(warning); + } + + // generates the new session + store.generate = function(req){ + req.sessionID = utils.uid(24); + req.session = new Session(req); + req.session.cookie = new Cookie(cookie); + }; + + store.on('disconnect', function(){ storeReady = false; }); + store.on('connect', function(){ storeReady = true; }); + + return function session(req, res, next) { + // self-awareness + if (req.session) return next(); + + // Handle connection as if there is no session if + // the store has temporarily disconnected etc + if (!storeReady) return debug('store is disconnected'), next(); + + // pathname mismatch + if (0 != req.originalUrl.indexOf(cookie.path || '/')) return next(); + + // backwards compatibility for signed cookies + // req.secret is passed from the cookie parser middleware + var secret = options.secret || req.secret; + + // ensure secret is available or bail + if (!secret) throw new Error('`secret` option required for sessions'); + + // parse url + var url = parse(req) + , path = url.pathname + , originalHash + , originalId; + + // expose store + req.sessionStore = store; + + // grab the session cookie value and check the signature + var rawCookie = req.cookies[key]; + + // get signedCookies for backwards compat with signed cookies + var unsignedCookie = req.signedCookies[key]; + + if (!unsignedCookie && rawCookie) { + unsignedCookie = utils.parseSignedCookie(rawCookie, secret); + } + + // set-cookie + res.on('header', function(){ + if (!req.session) return; + var cookie = req.session.cookie + , proto = (req.headers['x-forwarded-proto'] || '').toLowerCase() + , tls = req.connection.encrypted || (trustProxy && 'https' == proto) + , secured = cookie.secure && tls + , isNew = unsignedCookie != req.sessionID; + + // only send secure cookies via https + if (cookie.secure && !secured) return debug('not secured'); + + // browser-session length cookie + if (null == cookie.expires) { + if (!isNew) return debug('already set browser-session cookie'); + // compare hashes and ids + } else if (originalHash == hash(req.session) && originalId == req.session.id) { + return debug('unmodified session'); + } + + var val = 's:' + signature.sign(req.sessionID, secret); + val = cookie.serialize(key, val); + debug('set-cookie %s', val); + res.setHeader('Set-Cookie', val); + }); + + // proxy end() to commit the session + var end = res.end; + res.end = function(data, encoding){ + res.end = end; + if (!req.session) return res.end(data, encoding); + debug('saving'); + req.session.resetMaxAge(); + req.session.save(function(){ + debug('saved'); + res.end(data, encoding); + }); + }; + + // generate the session + function generate() { + store.generate(req); + } + + // get the sessionID from the cookie + req.sessionID = unsignedCookie; + + // generate a session if the browser doesn't send a sessionID + if (!req.sessionID) { + debug('no SID sent, generating session'); + generate(); + next(); + return; + } + + // generate the session object + var pause = utils.pause(req); + debug('fetching %s', req.sessionID); + store.get(req.sessionID, function(err, sess){ + // proxy to resume() events + var _next = next; + next = function(err){ + _next(err); + pause.resume(); + } + + // error handling + if (err) { + debug('error'); + if ('ENOENT' == err.code) { + generate(); + next(); + } else { + next(err); + } + // no session + } else if (!sess) { + debug('no session found'); + generate(); + next(); + // populate req.session + } else { + debug('session found'); + store.createSession(req, sess); + originalId = req.sessionID; + originalHash = hash(sess); + next(); + } + }); + }; +}; + +/** + * Hash the given `sess` object omitting changes + * to `.cookie`. + * + * @param {Object} sess + * @return {String} + * @api private + */ + +function hash(sess) { + return crc16(JSON.stringify(sess, function(key, val){ + if ('cookie' != key) return val; + })); +} diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js b/dist/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js new file mode 100644 index 0000000..e8ff862 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js @@ -0,0 +1,128 @@ + +/*! + * Connect - session - Cookie + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../../utils') + , cookie = require('cookie'); + +/** + * Initialize a new `Cookie` with the given `options`. + * + * @param {IncomingMessage} req + * @param {Object} options + * @api private + */ + +var Cookie = module.exports = function Cookie(options) { + this.path = '/'; + this.maxAge = null; + this.httpOnly = true; + if (options) utils.merge(this, options); + this.originalMaxAge = undefined == this.originalMaxAge + ? this.maxAge + : this.originalMaxAge; +}; + +/*! + * Prototype. + */ + +Cookie.prototype = { + + /** + * Set expires `date`. + * + * @param {Date} date + * @api public + */ + + set expires(date) { + this._expires = date; + this.originalMaxAge = this.maxAge; + }, + + /** + * Get expires `date`. + * + * @return {Date} + * @api public + */ + + get expires() { + return this._expires; + }, + + /** + * Set expires via max-age in `ms`. + * + * @param {Number} ms + * @api public + */ + + set maxAge(ms) { + this.expires = 'number' == typeof ms + ? new Date(Date.now() + ms) + : ms; + }, + + /** + * Get expires max-age in `ms`. + * + * @return {Number} + * @api public + */ + + get maxAge() { + return this.expires instanceof Date + ? this.expires.valueOf() - Date.now() + : this.expires; + }, + + /** + * Return cookie data object. + * + * @return {Object} + * @api private + */ + + get data() { + return { + originalMaxAge: this.originalMaxAge + , expires: this._expires + , secure: this.secure + , httpOnly: this.httpOnly + , domain: this.domain + , path: this.path + } + }, + + /** + * Return a serialized cookie string. + * + * @return {String} + * @api public + */ + + serialize: function(name, val){ + return cookie.serialize(name, val, this.data); + }, + + /** + * Return JSON representation of this cookie. + * + * @return {Object} + * @api private + */ + + toJSON: function(){ + return this.data; + } +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/session/memory.js b/dist/node_modules/express/node_modules/connect/lib/middleware/session/memory.js new file mode 100644 index 0000000..fb93939 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/session/memory.js @@ -0,0 +1,129 @@ + +/*! + * Connect - session - MemoryStore + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Store = require('./store'); + +/** + * Initialize a new `MemoryStore`. + * + * @api public + */ + +var MemoryStore = module.exports = function MemoryStore() { + this.sessions = {}; +}; + +/** + * Inherit from `Store.prototype`. + */ + +MemoryStore.prototype.__proto__ = Store.prototype; + +/** + * Attempt to fetch session by the given `sid`. + * + * @param {String} sid + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.get = function(sid, fn){ + var self = this; + process.nextTick(function(){ + var expires + , sess = self.sessions[sid]; + if (sess) { + sess = JSON.parse(sess); + expires = 'string' == typeof sess.cookie.expires + ? new Date(sess.cookie.expires) + : sess.cookie.expires; + if (!expires || new Date < expires) { + fn(null, sess); + } else { + self.destroy(sid, fn); + } + } else { + fn(); + } + }); +}; + +/** + * Commit the given `sess` object associated with the given `sid`. + * + * @param {String} sid + * @param {Session} sess + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.set = function(sid, sess, fn){ + var self = this; + process.nextTick(function(){ + self.sessions[sid] = JSON.stringify(sess); + fn && fn(); + }); +}; + +/** + * Destroy the session associated with the given `sid`. + * + * @param {String} sid + * @api public + */ + +MemoryStore.prototype.destroy = function(sid, fn){ + var self = this; + process.nextTick(function(){ + delete self.sessions[sid]; + fn && fn(); + }); +}; + +/** + * Invoke the given callback `fn` with all active sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.all = function(fn){ + var arr = [] + , keys = Object.keys(this.sessions); + for (var i = 0, len = keys.length; i < len; ++i) { + arr.push(this.sessions[keys[i]]); + } + fn(null, arr); +}; + +/** + * Clear all sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.clear = function(fn){ + this.sessions = {}; + fn && fn(); +}; + +/** + * Fetch number of sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.length = function(fn){ + fn(null, Object.keys(this.sessions).length); +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/session/session.js b/dist/node_modules/express/node_modules/connect/lib/middleware/session/session.js new file mode 100644 index 0000000..0dd4b40 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/session/session.js @@ -0,0 +1,116 @@ + +/*! + * Connect - session - Session + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../../utils'); + +/** + * Create a new `Session` with the given request and `data`. + * + * @param {IncomingRequest} req + * @param {Object} data + * @api private + */ + +var Session = module.exports = function Session(req, data) { + Object.defineProperty(this, 'req', { value: req }); + Object.defineProperty(this, 'id', { value: req.sessionID }); + if ('object' == typeof data) utils.merge(this, data); +}; + +/** + * Update reset `.cookie.maxAge` to prevent + * the cookie from expiring when the + * session is still active. + * + * @return {Session} for chaining + * @api public + */ + +Session.prototype.touch = function(){ + return this.resetMaxAge(); +}; + +/** + * Reset `.maxAge` to `.originalMaxAge`. + * + * @return {Session} for chaining + * @api public + */ + +Session.prototype.resetMaxAge = function(){ + this.cookie.maxAge = this.cookie.originalMaxAge; + return this; +}; + +/** + * Save the session data with optional callback `fn(err)`. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.save = function(fn){ + this.req.sessionStore.set(this.id, this, fn || function(){}); + return this; +}; + +/** + * Re-loads the session data _without_ altering + * the maxAge properties. Invokes the callback `fn(err)`, + * after which time if no exception has occurred the + * `req.session` property will be a new `Session` object, + * although representing the same session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.reload = function(fn){ + var req = this.req + , store = this.req.sessionStore; + store.get(this.id, function(err, sess){ + if (err) return fn(err); + if (!sess) return fn(new Error('failed to load session')); + store.createSession(req, sess); + fn(); + }); + return this; +}; + +/** + * Destroy `this` session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.destroy = function(fn){ + delete this.req.session; + this.req.sessionStore.destroy(this.id, fn); + return this; +}; + +/** + * Regenerate this request's session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.regenerate = function(fn){ + this.req.sessionStore.regenerate(this.req, fn); + return this; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/session/store.js b/dist/node_modules/express/node_modules/connect/lib/middleware/session/store.js new file mode 100644 index 0000000..a9c08f4 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/session/store.js @@ -0,0 +1,85 @@ + +/*! + * Connect - session - Store + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , Session = require('./session') + , Cookie = require('./cookie'); + +/** + * Initialize abstract `Store`. + * + * @api private + */ + +var Store = module.exports = function Store(options){}; + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Store.prototype.__proto__ = EventEmitter.prototype; + +/** + * Re-generate the given requests's session. + * + * @param {IncomingRequest} req + * @return {Function} fn + * @api public + */ + +Store.prototype.regenerate = function(req, fn){ + var self = this; + this.destroy(req.sessionID, function(err){ + self.generate(req); + fn(err); + }); +}; + +/** + * Load a `Session` instance via the given `sid` + * and invoke the callback `fn(err, sess)`. + * + * @param {String} sid + * @param {Function} fn + * @api public + */ + +Store.prototype.load = function(sid, fn){ + var self = this; + this.get(sid, function(err, sess){ + if (err) return fn(err); + if (!sess) return fn(); + var req = { sessionID: sid, sessionStore: self }; + sess = self.createSession(req, sess); + fn(null, sess); + }); +}; + +/** + * Create session from JSON `sess` data. + * + * @param {IncomingRequest} req + * @param {Object} sess + * @return {Session} + * @api private + */ + +Store.prototype.createSession = function(req, sess){ + var expires = sess.cookie.expires + , orig = sess.cookie.originalMaxAge + , update = null == update ? true : false; + sess.cookie = new Cookie(sess.cookie); + if ('string' == typeof expires) sess.cookie.expires = new Date(expires); + sess.cookie.originalMaxAge = orig; + req.session = new Session(req, sess); + return req.session; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/static.js b/dist/node_modules/express/node_modules/connect/lib/middleware/static.js new file mode 100644 index 0000000..281a783 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/static.js @@ -0,0 +1,94 @@ + +/*! + * Connect - static + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var send = require('send') + , utils = require('../utils') + , parse = utils.parseUrl + , url = require('url'); + +/** + * Static: + * + * Static file server with the given `root` path. + * + * Examples: + * + * var oneDay = 86400000; + * + * connect() + * .use(connect.static(__dirname + '/public')) + * + * connect() + * .use(connect.static(__dirname + '/public', { maxAge: oneDay })) + * + * Options: + * + * - `maxAge` Browser cache maxAge in milliseconds. defaults to 0 + * - `hidden` Allow transfer of hidden files. defaults to false + * - `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to true + * + * @param {String} root + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function static(root, options){ + options = options || {}; + + // root required + if (!root) throw new Error('static() root path required'); + + // default redirect + var redirect = false === options.redirect ? false : true; + + return function static(req, res, next) { + if ('GET' != req.method && 'HEAD' != req.method) return next(); + var path = parse(req).pathname; + var pause = utils.pause(req); + + function resume() { + next(); + pause.resume(); + } + + function directory() { + if (!redirect) return resume(); + var pathname = url.parse(req.originalUrl).pathname; + res.statusCode = 301; + res.setHeader('Location', pathname + '/'); + res.end('Redirecting to ' + utils.escape(pathname) + '/'); + } + + function error(err) { + if (404 == err.status) return resume(); + next(err); + } + + send(req, path) + .maxage(options.maxAge || 0) + .root(root) + .hidden(options.hidden) + .on('error', error) + .on('directory', directory) + .pipe(res); + }; +}; + +/** + * Expose mime module. + * + * If you wish to extend the mime table use this + * reference to the "mime" module in the npm registry. + */ + +exports.mime = send.mime; diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/staticCache.js b/dist/node_modules/express/node_modules/connect/lib/middleware/staticCache.js new file mode 100644 index 0000000..06032c6 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/staticCache.js @@ -0,0 +1,231 @@ + +/*! + * Connect - staticCache + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , Cache = require('../cache') + , fresh = require('fresh'); + +/** + * Static cache: + * + * Enables a memory cache layer on top of + * the `static()` middleware, serving popular + * static files. + * + * By default a maximum of 128 objects are + * held in cache, with a max of 256k each, + * totalling ~32mb. + * + * A Least-Recently-Used (LRU) cache algo + * is implemented through the `Cache` object, + * simply rotating cache objects as they are + * hit. This means that increasingly popular + * objects maintain their positions while + * others get shoved out of the stack and + * garbage collected. + * + * Benchmarks: + * + * static(): 2700 rps + * node-static: 5300 rps + * static() + staticCache(): 7500 rps + * + * Options: + * + * - `maxObjects` max cache objects [128] + * - `maxLength` max cache object length 256kb + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function staticCache(options){ + var options = options || {} + , cache = new Cache(options.maxObjects || 128) + , maxlen = options.maxLength || 1024 * 256; + + console.warn('connect.staticCache() is deprecated and will be removed in 3.0'); + console.warn('use varnish or similar reverse proxy caches.'); + + return function staticCache(req, res, next){ + var key = cacheKey(req) + , ranges = req.headers.range + , hasCookies = req.headers.cookie + , hit = cache.get(key); + + // cache static + // TODO: change from staticCache() -> cache() + // and make this work for any request + req.on('static', function(stream){ + var headers = res._headers + , cc = utils.parseCacheControl(headers['cache-control'] || '') + , contentLength = headers['content-length'] + , hit; + + // dont cache set-cookie responses + if (headers['set-cookie']) return hasCookies = true; + + // dont cache when cookies are present + if (hasCookies) return; + + // ignore larger files + if (!contentLength || contentLength > maxlen) return; + + // don't cache partial files + if (headers['content-range']) return; + + // dont cache items we shouldn't be + // TODO: real support for must-revalidate / no-cache + if ( cc['no-cache'] + || cc['no-store'] + || cc['private'] + || cc['must-revalidate']) return; + + // if already in cache then validate + if (hit = cache.get(key)){ + if (headers.etag == hit[0].etag) { + hit[0].date = new Date; + return; + } else { + cache.remove(key); + } + } + + // validation notifiactions don't contain a steam + if (null == stream) return; + + // add the cache object + var arr = []; + + // store the chunks + stream.on('data', function(chunk){ + arr.push(chunk); + }); + + // flag it as complete + stream.on('end', function(){ + var cacheEntry = cache.add(key); + delete headers['x-cache']; // Clean up (TODO: others) + cacheEntry.push(200); + cacheEntry.push(headers); + cacheEntry.push.apply(cacheEntry, arr); + }); + }); + + if (req.method == 'GET' || req.method == 'HEAD') { + if (ranges) { + next(); + } else if (!hasCookies && hit && !mustRevalidate(req, hit)) { + res.setHeader('X-Cache', 'HIT'); + respondFromCache(req, res, hit); + } else { + res.setHeader('X-Cache', 'MISS'); + next(); + } + } else { + next(); + } + } +}; + +/** + * Respond with the provided cached value. + * TODO: Assume 200 code, that's iffy. + * + * @param {Object} req + * @param {Object} res + * @param {Object} cacheEntry + * @return {String} + * @api private + */ + +function respondFromCache(req, res, cacheEntry) { + var status = cacheEntry[0] + , headers = utils.merge({}, cacheEntry[1]) + , content = cacheEntry.slice(2); + + headers.age = (new Date - new Date(headers.date)) / 1000 || 0; + + switch (req.method) { + case 'HEAD': + res.writeHead(status, headers); + res.end(); + break; + case 'GET': + if (utils.conditionalGET(req) && fresh(req.headers, headers)) { + headers['content-length'] = 0; + res.writeHead(304, headers); + res.end(); + } else { + res.writeHead(status, headers); + + function write() { + while (content.length) { + if (false === res.write(content.shift())) { + res.once('drain', write); + return; + } + } + res.end(); + } + + write(); + } + break; + default: + // This should never happen. + res.writeHead(500, ''); + res.end(); + } +} + +/** + * Determine whether or not a cached value must be revalidated. + * + * @param {Object} req + * @param {Object} cacheEntry + * @return {String} + * @api private + */ + +function mustRevalidate(req, cacheEntry) { + var cacheHeaders = cacheEntry[1] + , reqCC = utils.parseCacheControl(req.headers['cache-control'] || '') + , cacheCC = utils.parseCacheControl(cacheHeaders['cache-control'] || '') + , cacheAge = (new Date - new Date(cacheHeaders.date)) / 1000 || 0; + + if ( cacheCC['no-cache'] + || cacheCC['must-revalidate'] + || cacheCC['proxy-revalidate']) return true; + + if (reqCC['no-cache']) return true + + if (null != reqCC['max-age']) return reqCC['max-age'] < cacheAge; + + if (null != cacheCC['max-age']) return cacheCC['max-age'] < cacheAge; + + return false; +} + +/** + * The key to use in the cache. For now, this is the URL path and query. + * + * 'http://example.com?key=value' -> '/?key=value' + * + * @param {Object} req + * @return {String} + * @api private + */ + +function cacheKey(req) { + return utils.parseUrl(req).path; +} diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/timeout.js b/dist/node_modules/express/node_modules/connect/lib/middleware/timeout.js new file mode 100644 index 0000000..c6cbf09 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/timeout.js @@ -0,0 +1,56 @@ + +/*! + * Connect - timeout + * Ported from https://github.com/LearnBoost/connect-timeout + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var debug = require('debug')('connect:timeout'); + +/** + * Timeout: + * + * Times out the request in `ms`, defaulting to `5000`. The + * method `req.clearTimeout()` is added to revert this behaviour + * programmatically within your application's middleware, routes, etc. + * + * The timeout error is passed to `next()` so that you may customize + * the response behaviour. This error has the `.timeout` property as + * well as `.status == 408`. + * + * @param {Number} ms + * @return {Function} + * @api public + */ + +module.exports = function timeout(ms) { + ms = ms || 5000; + + return function(req, res, next) { + var id = setTimeout(function(){ + req.emit('timeout', ms); + }, ms); + + req.on('timeout', function(){ + if (req.headerSent) return debug('response started, cannot timeout'); + var err = new Error('Request timeout'); + err.timeout = ms; + err.status = 408; + next(err); + }); + + req.clearTimeout = function(){ + clearTimeout(id); + }; + + res.on('header', function(){ + clearTimeout(id); + }); + + next(); + }; +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js b/dist/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js new file mode 100644 index 0000000..011c8c0 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js @@ -0,0 +1,76 @@ + +/*! + * Connect - urlencoded + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , _limit = require('./limit') + , qs = require('qs'); + +/** + * noop middleware. + */ + +function noop(req, res, next) { + next(); +} + +/** + * Urlencoded: + * + * Parse x-ww-form-urlencoded request bodies, + * providing the parsed object as `req.body`. + * + * Options: + * + * - `limit` byte limit disabled by default + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(options){ + options = options || {}; + + var limit = options.limit + ? _limit(options.limit) + : noop; + + return function urlencoded(req, res, next) { + if (req._body) return next(); + req.body = req.body || {}; + + // check Content-Type + if ('application/x-www-form-urlencoded' != utils.mime(req)) return next(); + + // flag as parsed + req._body = true; + + // parse + limit(req, res, function(err){ + if (err) return next(err); + var buf = ''; + req.setEncoding('utf8'); + req.on('data', function(chunk){ buf += chunk }); + req.on('end', function(){ + try { + req.body = buf.length + ? qs.parse(buf, options) + : {}; + next(); + } catch (err){ + err.body = buf; + next(err); + } + }); + }); + } +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/lib/middleware/vhost.js b/dist/node_modules/express/node_modules/connect/lib/middleware/vhost.js new file mode 100644 index 0000000..897a9d8 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/middleware/vhost.js @@ -0,0 +1,40 @@ + +/*! + * Connect - vhost + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Vhost: + * + * Setup vhost for the given `hostname` and `server`. + * + * connect() + * .use(connect.vhost('foo.com', fooApp)) + * .use(connect.vhost('bar.com', barApp)) + * .use(connect.vhost('*.com', mainApp)) + * + * The `server` may be a Connect server or + * a regular Node `http.Server`. + * + * @param {String} hostname + * @param {Server} server + * @return {Function} + * @api public + */ + +module.exports = function vhost(hostname, server){ + if (!hostname) throw new Error('vhost hostname required'); + if (!server) throw new Error('vhost server required'); + var regexp = new RegExp('^' + hostname.replace(/[*]/g, '(.*?)') + '$', 'i'); + if (server.onvhost) server.onvhost(hostname); + return function vhost(req, res, next){ + if (!req.headers.host) return next(); + var host = req.headers.host.split(':')[0]; + if (!regexp.test(host)) return next(); + if ('function' == typeof server) return server(req, res, next); + server.emit('request', req, res); + }; +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/patch.js b/dist/node_modules/express/node_modules/connect/lib/patch.js new file mode 100644 index 0000000..7cf0012 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/patch.js @@ -0,0 +1,79 @@ + +/*! + * Connect + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , res = http.ServerResponse.prototype + , setHeader = res.setHeader + , _renderHeaders = res._renderHeaders + , writeHead = res.writeHead; + +// apply only once + +if (!res._hasConnectPatch) { + + /** + * Provide a public "header sent" flag + * until node does. + * + * @return {Boolean} + * @api public + */ + + res.__defineGetter__('headerSent', function(){ + return this._header; + }); + + /** + * Set header `field` to `val`, special-casing + * the `Set-Cookie` field for multiple support. + * + * @param {String} field + * @param {String} val + * @api public + */ + + res.setHeader = function(field, val){ + var key = field.toLowerCase() + , prev; + + // special-case Set-Cookie + if (this._headers && 'set-cookie' == key) { + if (prev = this.getHeader(field)) { + val = Array.isArray(prev) + ? prev.concat(val) + : [prev, val]; + } + // charset + } else if ('content-type' == key && this.charset) { + val += '; charset=' + this.charset; + } + + return setHeader.call(this, field, val); + }; + + /** + * Proxy to emit "header" event. + */ + + res._renderHeaders = function(){ + if (!this._emittedHeader) this.emit('header'); + this._emittedHeader = true; + return _renderHeaders.call(this); + }; + + res.writeHead = function(){ + if (!this._emittedHeader) this.emit('header'); + this._emittedHeader = true; + return writeHead.apply(this, arguments); + }; + + res._hasConnectPatch = true; +} diff --git a/dist/node_modules/express/node_modules/connect/lib/proto.js b/dist/node_modules/express/node_modules/connect/lib/proto.js new file mode 100644 index 0000000..b304cf7 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/proto.js @@ -0,0 +1,230 @@ + +/*! + * Connect - HTTPServer + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , utils = require('./utils') + , debug = require('debug')('connect:dispatcher'); + +// prototype + +var app = module.exports = {}; + +// environment + +var env = process.env.NODE_ENV || 'development'; + +/** + * Utilize the given middleware `handle` to the given `route`, + * defaulting to _/_. This "route" is the mount-point for the + * middleware, when given a value other than _/_ the middleware + * is only effective when that segment is present in the request's + * pathname. + * + * For example if we were to mount a function at _/admin_, it would + * be invoked on _/admin_, and _/admin/settings_, however it would + * not be invoked for _/_, or _/posts_. + * + * Examples: + * + * var app = connect(); + * app.use(connect.favicon()); + * app.use(connect.logger()); + * app.use(connect.static(__dirname + '/public')); + * + * If we wanted to prefix static files with _/public_, we could + * "mount" the `static()` middleware: + * + * app.use('/public', connect.static(__dirname + '/public')); + * + * This api is chainable, so the following is valid: + * + * connect() + * .use(connect.favicon()) + * .use(connect.logger()) + * .use(connect.static(__dirname + '/public')) + * .listen(3000); + * + * @param {String|Function|Server} route, callback or server + * @param {Function|Server} callback or server + * @return {Server} for chaining + * @api public + */ + +app.use = function(route, fn){ + // default route to '/' + if ('string' != typeof route) { + fn = route; + route = '/'; + } + + // wrap sub-apps + if ('function' == typeof fn.handle) { + var server = fn; + fn.route = route; + fn = function(req, res, next){ + server.handle(req, res, next); + }; + } + + // wrap vanilla http.Servers + if (fn instanceof http.Server) { + fn = fn.listeners('request')[0]; + } + + // strip trailing slash + if ('/' == route[route.length - 1]) { + route = route.slice(0, -1); + } + + // add the middleware + debug('use %s %s', route || '/', fn.name || 'anonymous'); + this.stack.push({ route: route, handle: fn }); + + return this; +}; + +/** + * Handle server requests, punting them down + * the middleware stack. + * + * @api private + */ + +app.handle = function(req, res, out) { + var stack = this.stack + , fqdn = ~req.url.indexOf('://') + , removed = '' + , slashAdded = false + , index = 0; + + function next(err) { + var layer, path, status, c; + + if (slashAdded) { + req.url = req.url.substr(1); + slashAdded = false; + } + + req.url = removed + req.url; + req.originalUrl = req.originalUrl || req.url; + removed = ''; + + // next callback + layer = stack[index++]; + + // all done + if (!layer || res.headerSent) { + // delegate to parent + if (out) return out(err); + + // unhandled error + if (err) { + // default to 500 + if (res.statusCode < 400) res.statusCode = 500; + debug('default %s', res.statusCode); + + // respect err.status + if (err.status) res.statusCode = err.status; + + // production gets a basic error message + var msg = 'production' == env + ? http.STATUS_CODES[res.statusCode] + : err.stack || err.toString(); + + // log to stderr in a non-test env + if ('test' != env) console.error(err.stack || err.toString()); + if (res.headerSent) return req.socket.destroy(); + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Length', Buffer.byteLength(msg)); + if ('HEAD' == req.method) return res.end(); + res.end(msg); + } else { + debug('default 404'); + res.statusCode = 404; + res.setHeader('Content-Type', 'text/plain'); + if ('HEAD' == req.method) return res.end(); + res.end('Cannot ' + req.method + ' ' + utils.escape(req.originalUrl)); + } + return; + } + + try { + path = utils.parseUrl(req).pathname; + if (undefined == path) path = '/'; + + // skip this layer if the route doesn't match. + if (0 != path.toLowerCase().indexOf(layer.route.toLowerCase())) return next(err); + + c = path[layer.route.length]; + if (c && '/' != c && '.' != c) return next(err); + + // Call the layer handler + // Trim off the part of the url that matches the route + removed = layer.route; + req.url = req.url.substr(removed.length); + + // Ensure leading slash + if (!fqdn && '/' != req.url[0]) { + req.url = '/' + req.url; + slashAdded = true; + } + + debug('%s', layer.handle.name || 'anonymous'); + var arity = layer.handle.length; + if (err) { + if (arity === 4) { + layer.handle(err, req, res, next); + } else { + next(err); + } + } else if (arity < 4) { + layer.handle(req, res, next); + } else { + next(); + } + } catch (e) { + next(e); + } + } + next(); +}; + +/** + * Listen for connections. + * + * This method takes the same arguments + * as node's `http.Server#listen()`. + * + * HTTP and HTTPS: + * + * If you run your application both as HTTP + * and HTTPS you may wrap them individually, + * since your Connect "server" is really just + * a JavaScript `Function`. + * + * var connect = require('connect') + * , http = require('http') + * , https = require('https'); + * + * var app = connect(); + * + * http.createServer(app).listen(80); + * https.createServer(options, app).listen(443); + * + * @return {http.Server} + * @api public + */ + +app.listen = function(){ + var server = http.createServer(this); + return server.listen.apply(server, arguments); +}; diff --git a/dist/node_modules/express/node_modules/connect/lib/public/directory.html b/dist/node_modules/express/node_modules/connect/lib/public/directory.html new file mode 100644 index 0000000..15164bb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/public/directory.html @@ -0,0 +1,75 @@ + + + listing directory {directory} + + + + + +
    +

    {linked-path}

    + {files} +
    + + \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/lib/public/error.html b/dist/node_modules/express/node_modules/connect/lib/public/error.html new file mode 100644 index 0000000..c5ae73a --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/public/error.html @@ -0,0 +1,13 @@ + + + {error} + + + +
    +

    {title}

    +

    {statusCode} {error}

    +
      {stack}
    +
    + + diff --git a/dist/node_modules/express/node_modules/connect/lib/public/favicon.ico b/dist/node_modules/express/node_modules/connect/lib/public/favicon.ico new file mode 100644 index 0000000..895fc96 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/favicon.ico differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page.png new file mode 100644 index 0000000..03ddd79 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_add.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_add.png new file mode 100644 index 0000000..d5bfa07 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_add.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png new file mode 100644 index 0000000..89ee2da Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_code.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_code.png new file mode 100644 index 0000000..f7ea904 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_code.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png new file mode 100644 index 0000000..195dc6d Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png new file mode 100644 index 0000000..3141467 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png new file mode 100644 index 0000000..046811e Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_error.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_error.png new file mode 100644 index 0000000..f07f449 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_error.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png new file mode 100644 index 0000000..eb6158e Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_find.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_find.png new file mode 100644 index 0000000..2f19388 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_find.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png new file mode 100644 index 0000000..8e83281 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_go.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_go.png new file mode 100644 index 0000000..80fe1ed Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_go.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_green.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_green.png new file mode 100644 index 0000000..de8e003 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_green.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_key.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_key.png new file mode 100644 index 0000000..d6626cb Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_key.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png new file mode 100644 index 0000000..7e56870 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_link.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_link.png new file mode 100644 index 0000000..312eab0 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_link.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png new file mode 100644 index 0000000..246a2f0 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png new file mode 100644 index 0000000..968f073 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_red.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_red.png new file mode 100644 index 0000000..0b18247 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_red.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png new file mode 100644 index 0000000..cf347c7 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_save.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_save.png new file mode 100644 index 0000000..caea546 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_save.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white.png new file mode 100644 index 0000000..8b8b1ca Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png new file mode 100644 index 0000000..8f8095e Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png new file mode 100644 index 0000000..159b240 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png new file mode 100644 index 0000000..aa23dde Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png new file mode 100644 index 0000000..34a05cc Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png new file mode 100644 index 0000000..f501a59 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png new file mode 100644 index 0000000..848bdaf Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png new file mode 100644 index 0000000..0c76bd1 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png new file mode 100644 index 0000000..87a6914 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png new file mode 100644 index 0000000..c66011f Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png new file mode 100644 index 0000000..2b6b100 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png new file mode 100644 index 0000000..a9f31a2 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png new file mode 100644 index 0000000..a87cf84 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png new file mode 100644 index 0000000..ffb8fc9 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png new file mode 100644 index 0000000..0a7d6f4 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png new file mode 100644 index 0000000..bddba1f Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png new file mode 100644 index 0000000..af1ecaf Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png new file mode 100644 index 0000000..4cc537a Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png new file mode 100644 index 0000000..b93e776 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png new file mode 100644 index 0000000..9fc5a0a Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png new file mode 100644 index 0000000..b977d7e Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png new file mode 100644 index 0000000..5818436 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png new file mode 100644 index 0000000..5769120 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png new file mode 100644 index 0000000..8d719df Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png new file mode 100644 index 0000000..106f5aa Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png new file mode 100644 index 0000000..e4a1ecb Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png new file mode 100644 index 0000000..7e62a92 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png new file mode 100644 index 0000000..e902abb Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png new file mode 100644 index 0000000..1d2d0a4 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png new file mode 100644 index 0000000..d616484 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png new file mode 100644 index 0000000..7215d1e Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png new file mode 100644 index 0000000..bf7bd1c Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png new file mode 100644 index 0000000..f6b74cc Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png new file mode 100644 index 0000000..d3fffb6 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png new file mode 100644 index 0000000..a65bcb3 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png new file mode 100644 index 0000000..23a37b8 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png new file mode 100644 index 0000000..f907e44 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png new file mode 100644 index 0000000..5b2cbb3 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png new file mode 100644 index 0000000..7868a25 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png new file mode 100644 index 0000000..134b669 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png new file mode 100644 index 0000000..c4eff03 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png new file mode 100644 index 0000000..884ffd6 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png new file mode 100644 index 0000000..f59b7c4 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png new file mode 100644 index 0000000..44084ad Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png new file mode 100644 index 0000000..3a1441c Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png new file mode 100644 index 0000000..e770829 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png new file mode 100644 index 0000000..813f712 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png new file mode 100644 index 0000000..d9cf132 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png new file mode 100644 index 0000000..52699bf Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png new file mode 100644 index 0000000..4a05955 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png new file mode 100644 index 0000000..a0a433d Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png new file mode 100644 index 0000000..1eb8809 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png new file mode 100644 index 0000000..ae8ecbf Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png new file mode 100644 index 0000000..6ed2490 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png new file mode 100644 index 0000000..fecadd0 Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png new file mode 100644 index 0000000..fd4bbcc Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_word.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_word.png new file mode 100644 index 0000000..834cdfa Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_word.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/icons/page_world.png b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_world.png new file mode 100644 index 0000000..b8895dd Binary files /dev/null and b/dist/node_modules/express/node_modules/connect/lib/public/icons/page_world.png differ diff --git a/dist/node_modules/express/node_modules/connect/lib/public/style.css b/dist/node_modules/express/node_modules/connect/lib/public/style.css new file mode 100644 index 0000000..32b6507 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/public/style.css @@ -0,0 +1,141 @@ +body { + margin: 0; + padding: 80px 100px; + font: 13px "Helvetica Neue", "Lucida Grande", "Arial"; + background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9)); + background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9); + background-repeat: no-repeat; + color: #555; + -webkit-font-smoothing: antialiased; +} +h1, h2, h3 { + margin: 0; + font-size: 22px; + color: #343434; +} +h1 em, h2 em { + padding: 0 5px; + font-weight: normal; +} +h1 { + font-size: 60px; +} +h2 { + margin-top: 10px; +} +h3 { + margin: 5px 0 10px 0; + padding-bottom: 5px; + border-bottom: 1px solid #eee; + font-size: 18px; +} +ul { + margin: 0; + padding: 0; +} +ul li { + margin: 5px 0; + padding: 3px 8px; + list-style: none; +} +ul li:hover { + cursor: pointer; + color: #2e2e2e; +} +ul li .path { + padding-left: 5px; + font-weight: bold; +} +ul li .line { + padding-right: 5px; + font-style: italic; +} +ul li:first-child .path { + padding-left: 0; +} +p { + line-height: 1.5; +} +a { + color: #555; + text-decoration: none; +} +a:hover { + color: #303030; +} +#stacktrace { + margin-top: 15px; +} +.directory h1 { + margin-bottom: 15px; + font-size: 18px; +} +ul#files { + width: 100%; + height: 500px; +} +ul#files li { + padding: 0; +} +ul#files li img { + position: absolute; + top: 5px; + left: 5px; +} +ul#files li a { + position: relative; + display: block; + margin: 1px; + width: 30%; + height: 25px; + line-height: 25px; + text-indent: 8px; + float: left; + border: 1px solid transparent; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + overflow: hidden; + text-overflow: ellipsis; +} +ul#files li a.icon { + text-indent: 25px; +} +ul#files li a:focus, +ul#files li a:hover { + outline: none; + background: rgba(255,255,255,0.65); + border: 1px solid #ececec; +} +ul#files li a.highlight { + -webkit-transition: background .4s ease-in-out; + background: #ffff4f; + border-color: #E9DC51; +} +#search { + display: block; + position: fixed; + top: 20px; + right: 20px; + width: 90px; + -webkit-transition: width ease 0.2s, opacity ease 0.4s; + -moz-transition: width ease 0.2s, opacity ease 0.4s; + -webkit-border-radius: 32px; + -moz-border-radius: 32px; + -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -webkit-font-smoothing: antialiased; + text-align: left; + font: 13px "Helvetica Neue", Arial, sans-serif; + padding: 4px 10px; + border: none; + background: transparent; + margin-bottom: 0; + outline: none; + opacity: 0.7; + color: #888; +} +#search:focus { + width: 120px; + opacity: 1.0; +} diff --git a/dist/node_modules/express/node_modules/connect/lib/utils.js b/dist/node_modules/express/node_modules/connect/lib/utils.js new file mode 100644 index 0000000..4dba843 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/lib/utils.js @@ -0,0 +1,374 @@ + +/*! + * Connect - utils + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , crypto = require('crypto') + , parse = require('url').parse + , signature = require('cookie-signature'); + +/** + * Extract the mime type from the given request's + * _Content-Type_ header. + * + * @param {IncomingMessage} req + * @return {String} + * @api private + */ + +exports.mime = function(req) { + var str = req.headers['content-type'] || ''; + return str.split(';')[0]; +}; + +/** + * Generate an `Error` from the given status `code` + * and optional `msg`. + * + * @param {Number} code + * @param {String} msg + * @return {Error} + * @api private + */ + +exports.error = function(code, msg){ + var err = new Error(msg || http.STATUS_CODES[code]); + err.status = code; + return err; +}; + +/** + * Return md5 hash of the given string and optional encoding, + * defaulting to hex. + * + * utils.md5('wahoo'); + * // => "e493298061761236c96b02ea6aa8a2ad" + * + * @param {String} str + * @param {String} encoding + * @return {String} + * @api private + */ + +exports.md5 = function(str, encoding){ + return crypto + .createHash('md5') + .update(str) + .digest(encoding || 'hex'); +}; + +/** + * Merge object b with object a. + * + * var a = { foo: 'bar' } + * , b = { bar: 'baz' }; + * + * utils.merge(a, b); + * // => { foo: 'bar', bar: 'baz' } + * + * @param {Object} a + * @param {Object} b + * @return {Object} + * @api private + */ + +exports.merge = function(a, b){ + if (a && b) { + for (var key in b) { + a[key] = b[key]; + } + } + return a; +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + + +/** + * Return a unique identifier with the given `len`. + * + * utils.uid(10); + * // => "FDaS435D2z" + * + * @param {Number} len + * @return {String} + * @api private + */ + +exports.uid = function(len) { + return crypto.randomBytes(Math.ceil(len * 3 / 4)) + .toString('base64') + .slice(0, len); +}; + +/** + * Sign the given `val` with `secret`. + * + * @param {String} val + * @param {String} secret + * @return {String} + * @api private + */ + +exports.sign = function(val, secret){ + console.warn('do not use utils.sign(), use https://github.com/visionmedia/node-cookie-signature') + return val + '.' + crypto + .createHmac('sha256', secret) + .update(val) + .digest('base64') + .replace(/=+$/, ''); +}; + +/** + * Unsign and decode the given `val` with `secret`, + * returning `false` if the signature is invalid. + * + * @param {String} val + * @param {String} secret + * @return {String|Boolean} + * @api private + */ + +exports.unsign = function(val, secret){ + console.warn('do not use utils.unsign(), use https://github.com/visionmedia/node-cookie-signature') + var str = val.slice(0, val.lastIndexOf('.')); + return exports.sign(str, secret) == val + ? str + : false; +}; + +/** + * Parse signed cookies, returning an object + * containing the decoded key/value pairs, + * while removing the signed key from `obj`. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +exports.parseSignedCookies = function(obj, secret){ + var ret = {}; + Object.keys(obj).forEach(function(key){ + var val = obj[key]; + if (0 == val.indexOf('s:')) { + val = signature.unsign(val.slice(2), secret); + if (val) { + ret[key] = val; + delete obj[key]; + } + } + }); + return ret; +}; + +/** + * Parse a signed cookie string, return the decoded value + * + * @param {String} str signed cookie string + * @param {String} secret + * @return {String} decoded value + * @api private + */ + +exports.parseSignedCookie = function(str, secret){ + return 0 == str.indexOf('s:') + ? signature.unsign(str.slice(2), secret) + : str; +}; + +/** + * Parse JSON cookies. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +exports.parseJSONCookies = function(obj){ + Object.keys(obj).forEach(function(key){ + var val = obj[key]; + var res = exports.parseJSONCookie(val); + if (res) obj[key] = res; + }); + return obj; +}; + +/** + * Parse JSON cookie string + * + * @param {String} str + * @return {Object} Parsed object or null if not json cookie + * @api private + */ + +exports.parseJSONCookie = function(str) { + if (0 == str.indexOf('j:')) { + try { + return JSON.parse(str.slice(2)); + } catch (err) { + // no op + } + } +} + +/** + * Pause `data` and `end` events on the given `obj`. + * Middleware performing async tasks _should_ utilize + * this utility (or similar), to re-emit data once + * the async operation has completed, otherwise these + * events may be lost. + * + * var pause = utils.pause(req); + * fs.readFile(path, function(){ + * next(); + * pause.resume(); + * }); + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +exports.pause = require('pause'); + +/** + * Strip `Content-*` headers from `res`. + * + * @param {ServerResponse} res + * @api private + */ + +exports.removeContentHeaders = function(res){ + Object.keys(res._headers).forEach(function(field){ + if (0 == field.indexOf('content')) { + res.removeHeader(field); + } + }); +}; + +/** + * Check if `req` is a conditional GET request. + * + * @param {IncomingMessage} req + * @return {Boolean} + * @api private + */ + +exports.conditionalGET = function(req) { + return req.headers['if-modified-since'] + || req.headers['if-none-match']; +}; + +/** + * Respond with 401 "Unauthorized". + * + * @param {ServerResponse} res + * @param {String} realm + * @api private + */ + +exports.unauthorized = function(res, realm) { + res.statusCode = 401; + res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"'); + res.end('Unauthorized'); +}; + +/** + * Respond with 304 "Not Modified". + * + * @param {ServerResponse} res + * @param {Object} headers + * @api private + */ + +exports.notModified = function(res) { + exports.removeContentHeaders(res); + res.statusCode = 304; + res.end(); +}; + +/** + * Return an ETag in the form of `"-"` + * from the given `stat`. + * + * @param {Object} stat + * @return {String} + * @api private + */ + +exports.etag = function(stat) { + return '"' + stat.size + '-' + Number(stat.mtime) + '"'; +}; + +/** + * Parse the given Cache-Control `str`. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.parseCacheControl = function(str){ + var directives = str.split(',') + , obj = {}; + + for(var i = 0, len = directives.length; i < len; i++) { + var parts = directives[i].split('=') + , key = parts.shift().trim() + , val = parseInt(parts.shift(), 10); + + obj[key] = isNaN(val) ? true : val; + } + + return obj; +}; + +/** + * Parse the `req` url with memoization. + * + * @param {ServerRequest} req + * @return {Object} + * @api private + */ + +exports.parseUrl = function(req){ + var parsed = req._parsedUrl; + if (parsed && parsed.href == req.url) { + return parsed; + } else { + return req._parsedUrl = parse(req.url); + } +}; + +/** + * Parse byte `size` string. + * + * @param {String} size + * @return {Number} + * @api private + */ + +exports.parseBytes = require('bytes'); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/bytes/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/bytes/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/bytes/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/connect/node_modules/bytes/History.md b/dist/node_modules/express/node_modules/connect/node_modules/bytes/History.md new file mode 100644 index 0000000..db1f759 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/bytes/History.md @@ -0,0 +1,5 @@ + +0.1.0 / 2012-07-04 +================== + + * add bytes to string conversion [yields] diff --git a/dist/node_modules/express/node_modules/connect/node_modules/bytes/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/bytes/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/bytes/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/bytes/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/bytes/Readme.md new file mode 100644 index 0000000..9325d5b --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/bytes/Readme.md @@ -0,0 +1,51 @@ +# node-bytes + + Byte string parser / formatter. + +## Example: + +```js +bytes('1kb') +// => 1024 + +bytes('2mb') +// => 2097152 + +bytes('1gb') +// => 1073741824 + +bytes(1073741824) +// => 1gb +``` + +## Installation + +``` +$ npm install bytes +$ component install visionmedia/bytes.js +``` + +## 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. diff --git a/dist/node_modules/express/node_modules/connect/node_modules/bytes/component.json b/dist/node_modules/express/node_modules/connect/node_modules/bytes/component.json new file mode 100644 index 0000000..76a6057 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/bytes/component.json @@ -0,0 +1,7 @@ +{ + "name": "bytes", + "description": "byte size string parser / serializer", + "keywords": ["bytes", "utility"], + "version": "0.1.0", + "scripts": ["index.js"] +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/bytes/index.js b/dist/node_modules/express/node_modules/connect/node_modules/bytes/index.js new file mode 100644 index 0000000..3eaafc7 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/bytes/index.js @@ -0,0 +1,39 @@ + +/** + * Parse byte `size` string. + * + * @param {String} size + * @return {Number} + * @api public + */ + +module.exports = function(size) { + if ('number' == typeof size) return convert(size); + var parts = size.match(/^(\d+(?:\.\d+)?) *(kb|mb|gb)$/) + , n = parseFloat(parts[1]) + , type = parts[2]; + + var map = { + kb: 1 << 10 + , mb: 1 << 20 + , gb: 1 << 30 + }; + + return map[type] * n; +}; + +/** + * convert bytes into string. + * + * @param {Number} b - bytes to convert + * @return {String}i + * @api public + */ + +function convert (b) { + var gb = 1 << 30, mb = 1 << 20, kb = 1 << 10; + if (b >= gb) return (Math.round(b / gb * 100) / 100) + 'gb'; + if (b >= mb) return (Math.round(b / mb * 100) / 100) + 'mb'; + if (b >= kb) return (Math.round(b / kb * 100) / 100) + 'kb'; + return b; +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/bytes/package.json b/dist/node_modules/express/node_modules/connect/node_modules/bytes/package.json new file mode 100644 index 0000000..5326178 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/bytes/package.json @@ -0,0 +1,17 @@ +{ + "name": "bytes", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "byte size string parser / serializer", + "version": "0.1.0", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "component": { + "scripts": { + "bytes": "index.js" + } + } +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/History.md b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/History.md new file mode 100644 index 0000000..c8aa68f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/History.md @@ -0,0 +1,5 @@ + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/Makefile new file mode 100644 index 0000000..4e9c8d3 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/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/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/Readme.md new file mode 100644 index 0000000..2559e84 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/Readme.md @@ -0,0 +1,42 @@ + +# cookie-signature + + Sign and unsign cookies. + +## Example + +```js +var cookie = require('cookie-signature'); + +var val = cookie.sign('hello', 'tobiiscool'); +val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI'); + +var val = cookie.sign('hello', 'tobiiscool'); +cookie.unsign(val, 'tobiiscool').should.equal('hello'); +cookie.unsign(val, 'luna').should.be.false; +``` + +## License + +(The MIT License) + +Copyright (c) 2012 LearnBoost <tj@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE 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/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/index.js b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/index.js new file mode 100644 index 0000000..93206a0 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/index.js @@ -0,0 +1,42 @@ + +/** + * Module dependencies. + */ + +var crypto = require('crypto'); + +/** + * Sign the given `val` with `secret`. + * + * @param {String} val + * @param {String} secret + * @return {String} + * @api private + */ + +exports.sign = function(val, secret){ + if ('string' != typeof val) throw new TypeError('cookie required'); + if ('string' != typeof secret) throw new TypeError('secret required'); + return val + '.' + crypto + .createHmac('sha256', secret) + .update(val) + .digest('base64') + .replace(/\=+$/, ''); +}; + +/** + * Unsign and decode the given `val` with `secret`, + * returning `false` if the signature is invalid. + * + * @param {String} val + * @param {String} secret + * @return {String|Boolean} + * @api private + */ + +exports.unsign = function(val, secret){ + if ('string' != typeof val) throw new TypeError('cookie required'); + if ('string' != typeof secret) throw new TypeError('secret required'); + var str = val.slice(0, val.lastIndexOf('.')); + return exports.sign(str, secret) == val ? str : false; +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/package.json b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/package.json new file mode 100644 index 0000000..6ddb349 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie-signature/package.json @@ -0,0 +1,13 @@ +{ + "name": "cookie-signature", + "version": "0.0.1", + "description": "Sign and unsign cookies", + "keywords": ["cookie", "sign", "unsign"], + "author": "TJ Holowaychuk ", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "main": "index" +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/cookie/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/.travis.yml b/dist/node_modules/express/node_modules/connect/node_modules/cookie/.travis.yml new file mode 100644 index 0000000..bced151 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.6 diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/README.md b/dist/node_modules/express/node_modules/connect/node_modules/cookie/README.md new file mode 100644 index 0000000..5187ed1 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/README.md @@ -0,0 +1,44 @@ +# cookie [![Build Status](https://secure.travis-ci.org/shtylman/node-cookie.png?branch=master)](http://travis-ci.org/shtylman/node-cookie) # + +cookie is a basic cookie parser and serializer. It doesn't make assumptions about how you are going to deal with your cookies. It basically just provides a way to read and write the HTTP cookie headers. + +See [RFC6265](http://tools.ietf.org/html/rfc6265) for details about the http header for cookies. + +## how? + +``` +npm install cookie +``` + +```javascript +var cookie = require('cookie'); + +var hdr = cookie.serialize('foo', 'bar'); +// hdr = 'foo=bar'; + +var cookies = cookie.parse('foo=bar; cat=meow; dog=ruff'); +// cookies = { foo: 'bar', cat: 'meow', dog: 'ruff' }; +``` + +## more + +The serialize function takes a third parameter, an object, to set cookie options. See the RFC for valid values. + +### path +> cookie path + +### expires +> absolute expiration date for the cookie (Date object) + +### maxAge +> relative max age of the cookie from when the client receives it (seconds) + +### domain +> domain for the cookie + +### secure +> true or false + +### httpOnly +> true or false + diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/index.js b/dist/node_modules/express/node_modules/connect/node_modules/cookie/index.js new file mode 100644 index 0000000..ce6c926 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/index.js @@ -0,0 +1,57 @@ + +/// Serialize the a name value pair into a cookie string suitable for +/// http headers. An optional options object specified cookie parameters +/// +/// serialize('foo', 'bar', { httpOnly: true }) +/// => "foo=bar; httpOnly" +/// +/// @param {String} name +/// @param {String} val +/// @param {Object} options +/// @return {String} +var serialize = function(name, val, opt){ + var pairs = [name + '=' + encode(val)]; + opt = opt || {}; + + if (opt.maxAge) pairs.push('Max-Age=' + opt.maxAge); + if (opt.domain) pairs.push('Domain=' + opt.domain); + if (opt.path) pairs.push('Path=' + opt.path); + if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString()); + if (opt.httpOnly) pairs.push('HttpOnly'); + if (opt.secure) pairs.push('Secure'); + + return pairs.join('; '); +}; + +/// Parse the given cookie header string into an object +/// The object has the various cookies as keys(names) => values +/// @param {String} str +/// @return {Object} +var parse = function(str) { + var obj = {} + var pairs = str.split(/[;,] */); + + pairs.forEach(function(pair) { + var eq_idx = pair.indexOf('=') + var key = pair.substr(0, eq_idx).trim() + var val = pair.substr(++eq_idx, pair.length).trim(); + + // quoted values + if ('"' == val[0]) { + val = val.slice(1, -1); + } + + // only assign once + if (undefined == obj[key]) { + obj[key] = decode(val); + } + }); + + return obj; +}; + +var encode = encodeURIComponent; +var decode = decodeURIComponent; + +module.exports.serialize = serialize; +module.exports.parse = parse; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/package.json b/dist/node_modules/express/node_modules/connect/node_modules/cookie/package.json new file mode 100644 index 0000000..416a03c --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/package.json @@ -0,0 +1,26 @@ +{ + "author": "Roman Shtylman ", + "name": "cookie", + "description": "cookie parsing and serialization", + "version": "0.0.4", + "repository": { + "type": "git", + "url": "git://github.com/shtylman/node-cookie.git" + }, + "keywords": [ + "cookie", + "cookies" + ], + "main": "index.js", + "scripts": { + "test": "mocha" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "1.x.x" + }, + "optionalDependencies": {}, + "engines": { + "node": "*" + } +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/mocha.opts b/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/mocha.opts new file mode 100644 index 0000000..e2bfcc5 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/mocha.opts @@ -0,0 +1 @@ +--ui qunit diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/parse.js b/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/parse.js new file mode 100644 index 0000000..4b98a20 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/parse.js @@ -0,0 +1,25 @@ + +var assert = require('assert'); + +var cookie = require('..'); + +suite('parse'); + +test('basic', function() { + assert.deepEqual({ foo: 'bar' }, cookie.parse('foo=bar')); + assert.deepEqual({ foo: '123' }, cookie.parse('foo=123')); +}); + +test('ignore spaces', function() { + assert.deepEqual({ FOO: 'bar', baz: 'raz' }, + cookie.parse('FOO = bar; baz = raz')); +}); + +test('escaping', function() { + assert.deepEqual({ foo: 'bar=123456789&name=Magic+Mouse' }, + cookie.parse('foo="bar=123456789&name=Magic+Mouse"')); + + assert.deepEqual({ email: ' ",;/' }, + cookie.parse('email=%20%22%2c%3b%2f')); +}); + diff --git a/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/serialize.js b/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/serialize.js new file mode 100644 index 0000000..d38768d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/cookie/test/serialize.js @@ -0,0 +1,59 @@ +// builtin +var assert = require('assert'); + +var cookie = require('..'); + +suite('serialize'); + +test('basic', function() { + assert.equal('foo=bar', cookie.serialize('foo', 'bar')); + assert.equal('foo=bar%20baz', cookie.serialize('foo', 'bar baz')); +}); + +test('path', function() { + assert.equal('foo=bar; Path=/', cookie.serialize('foo', 'bar', { + path: '/' + })); +}); + +test('secure', function() { + assert.equal('foo=bar; Secure', cookie.serialize('foo', 'bar', { + secure: true + })); + + assert.equal('foo=bar', cookie.serialize('foo', 'bar', { + secure: false + })); +}); + +test('domain', function() { + assert.equal('foo=bar; Domain=example.com', cookie.serialize('foo', 'bar', { + domain: 'example.com' + })); +}); + +test('httpOnly', function() { + assert.equal('foo=bar; HttpOnly', cookie.serialize('foo', 'bar', { + httpOnly: true + })); +}); + +test('maxAge', function() { + assert.equal('foo=bar; Max-Age=1000', cookie.serialize('foo', 'bar', { + maxAge: 1000 + })); +}); + +test('escaping', function() { + assert.deepEqual('cat=%2B%20', cookie.serialize('cat', '+ ')); +}); + +test('parse->serialize', function() { + + assert.deepEqual({ cat: 'foo=123&name=baz five' }, cookie.parse( + cookie.serialize('cat', 'foo=123&name=baz five'))); + + assert.deepEqual({ cat: ' ";/' }, cookie.parse( + cookie.serialize('cat', ' ";/'))); +}); + diff --git a/dist/node_modules/express/node_modules/connect/node_modules/crc/.gitmodules b/dist/node_modules/express/node_modules/connect/node_modules/crc/.gitmodules new file mode 100644 index 0000000..2319e18 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/crc/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests/nodeunit"] + path = tests/nodeunit + url = git://github.com/caolan/nodeunit.git diff --git a/dist/node_modules/express/node_modules/connect/node_modules/crc/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/crc/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/crc/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/express/node_modules/connect/node_modules/crc/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/crc/Makefile new file mode 100644 index 0000000..720bf85 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/crc/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/dist/node_modules/express/node_modules/connect/node_modules/crc/README.md b/dist/node_modules/express/node_modules/connect/node_modules/crc/README.md new file mode 100644 index 0000000..26ce22f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/crc/README.md @@ -0,0 +1,31 @@ +# JavaScript CRC 8, 16 and 32. + +This is a basic port/copy of the JavaScript CRC implementation. The module works with any CommonJS system supporting `module.exports` notation as well as in the browser. When loaded in the browser, all functions end up under the `window.crc` "namespace". + +Original code is taken from http://www.digsys.se/JavaScript/CRC.aspx + +## Functions + +The following functions are implemented: + + crc8(String) #=> Number + crcArc(String) #=> Number + crc16(String) #=> Number + fcs16(String) #=> Number + crc32(String) #=> Number + hex8(Number) #=> String + hex16(Number) #=> String + hex32(Number) #=> String + +## Installation + + git clone git://github.com/alexgorbatchev/node-crc.git + +or + + npm install crc + +## Running tests + + $ npm install + $ make test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/crc/lib/crc.js b/dist/node_modules/express/node_modules/connect/node_modules/crc/lib/crc.js new file mode 100644 index 0000000..8feb542 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/crc/lib/crc.js @@ -0,0 +1,403 @@ +(function() +{ + // CRC-8 in table form + // + // Copyright (c) 1989 AnDan Software. You may use this program, or + // code or tables extracted from it, as long as this notice is not + // removed or changed. + var CRC8_TAB = new Array( + // C/C++ language: + // + // unsigned char CRC8_TAB[] = {...}; + 0x00,0x1B,0x36,0x2D,0x6C,0x77,0x5A,0x41,0xD8,0xC3,0xEE,0xF5,0xB4,0xAF,0x82,0x99,0xD3,0xC8,0xE5, + 0xFE,0xBF,0xA4,0x89,0x92,0x0B,0x10,0x3D,0x26,0x67,0x7C,0x51,0x4A,0xC5,0xDE,0xF3,0xE8,0xA9,0xB2, + 0x9F,0x84,0x1D,0x06,0x2B,0x30,0x71,0x6A,0x47,0x5C,0x16,0x0D,0x20,0x3B,0x7A,0x61,0x4C,0x57,0xCE, + 0xD5,0xF8,0xE3,0xA2,0xB9,0x94,0x8F,0xE9,0xF2,0xDF,0xC4,0x85,0x9E,0xB3,0xA8,0x31,0x2A,0x07,0x1C, + 0x5D,0x46,0x6B,0x70,0x3A,0x21,0x0C,0x17,0x56,0x4D,0x60,0x7B,0xE2,0xF9,0xD4,0xCF,0x8E,0x95,0xB8, + 0xA3,0x2C,0x37,0x1A,0x01,0x40,0x5B,0x76,0x6D,0xF4,0xEF,0xC2,0xD9,0x98,0x83,0xAE,0xB5,0xFF,0xE4, + 0xC9,0xD2,0x93,0x88,0xA5,0xBE,0x27,0x3C,0x11,0x0A,0x4B,0x50,0x7D,0x66,0xB1,0xAA,0x87,0x9C,0xDD, + 0xC6,0xEB,0xF0,0x69,0x72,0x5F,0x44,0x05,0x1E,0x33,0x28,0x62,0x79,0x54,0x4F,0x0E,0x15,0x38,0x23, + 0xBA,0xA1,0x8C,0x97,0xD6,0xCD,0xE0,0xFB,0x74,0x6F,0x42,0x59,0x18,0x03,0x2E,0x35,0xAC,0xB7,0x9A, + 0x81,0xC0,0xDB,0xF6,0xED,0xA7,0xBC,0x91,0x8A,0xCB,0xD0,0xFD,0xE6,0x7F,0x64,0x49,0x52,0x13,0x08, + 0x25,0x3E,0x58,0x43,0x6E,0x75,0x34,0x2F,0x02,0x19,0x80,0x9B,0xB6,0xAD,0xEC,0xF7,0xDA,0xC1,0x8B, + 0x90,0xBD,0xA6,0xE7,0xFC,0xD1,0xCA,0x53,0x48,0x65,0x7E,0x3F,0x24,0x09,0x12,0x9D,0x86,0xAB,0xB0, + 0xF1,0xEA,0xC7,0xDC,0x45,0x5E,0x73,0x68,0x29,0x32,0x1F,0x04,0x4E,0x55,0x78,0x63,0x22,0x39,0x14, + 0x0F,0x96,0x8D,0xA0,0xBB,0xFA,0xE1,0xCC,0xD7 + ); + + function crc8Add(crc,c) + // 'crc' should be initialized to 0x00. + { + return CRC8_TAB[(crc^c)&0xFF]; + }; + // C/C++ language: + // + // inline unsigned char crc8Add(unsigned char crc, unsigned char c) + // { + // return CRC8_TAB[crc^c]; + // } + + // CRC-16 (as it is in SEA's ARC) in table form + // + // The logic for this method of calculating the CRC 16 bit polynomial + // is taken from an article by David Schwaderer in the April 1985 + // issue of PC Tech Journal. + var CRC_ARC_TAB = new Array( + // C/C++ language: + // + // unsigned short CRC_ARC_TAB[] = {...}; + 0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,0xC601,0x06C0,0x0780,0xC741,0x0500, + 0xC5C1,0xC481,0x0440,0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,0x0A00,0xCAC1, + 0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81, + 0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,0x1400,0xD4C1,0xD581,0x1540, + 0xD701,0x17C0,0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,0xF001, + 0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0, + 0x3480,0xF441,0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0,0x3B80, + 0xFB41,0x3900,0xF9C1,0xF881,0x3840,0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41, + 0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,0xE401,0x24C0,0x2580,0xE541,0x2700, + 0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,0xA001,0x60C0, + 0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480, + 0xA441,0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01,0x6AC0,0x6B80,0xAB41, + 0x6900,0xA9C1,0xA881,0x6840,0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01, + 0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1, + 0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,0x5000,0x90C1,0x9181, + 0x5140,0x9301,0x53C0,0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440, + 0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,0x5A00,0x9AC1,0x9B81,0x5B40,0x9901, + 0x59C0,0x5880,0x9841,0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1, + 0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680, + 0x8641,0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040 + ); + + function crcArcAdd(crc,c) + // 'crc' should be initialized to 0x0000. + { + return CRC_ARC_TAB[(crc^c)&0xFF]^((crc>>8)&0xFF); + }; + // C/C++ language: + // + // inline unsigned short crcArcAdd(unsigned short crc, unsigned char c) + // { + // return CRC_ARC_TAB[(unsigned char)crc^c]^(unsigned short)(crc>>8); + // } + + // CRC-16 (as it is in ZMODEM) in table form + // + // Copyright (c) 1989 AnDan Software. You may use this program, or + // code or tables extracted from it, as long as this notice is not + // removed or changed. + var CRC16_TAB = new Array( + // C/C++ language: + // + // unsigned short CRC16_TAB[] = {...}; + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,0x8108,0x9129,0xA14A,0xB16B,0xC18C, + 0xD1AD,0xE1CE,0xF1EF,0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,0x9339,0x8318, + 0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE,0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4, + 0x5485,0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,0x3653,0x2672,0x1611,0x0630, + 0x76D7,0x66F6,0x5695,0x46B4,0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC,0x48C4, + 0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969, + 0xA90A,0xB92B,0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,0xDBFD,0xCBDC,0xFBBF, + 0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41, + 0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49,0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13, + 0x2E32,0x1E51,0x0E70,0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,0x9188,0x81A9, + 0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046, + 0x6067,0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,0x02B1,0x1290,0x22F3,0x32D2, + 0x4235,0x5214,0x6277,0x7256,0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,0x34E2, + 0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E, + 0xC71D,0xD73C,0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634,0xD94C,0xC96D,0xF90E, + 0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3, + 0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1, + 0x1AD0,0x2AB3,0x3A92,0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,0x7C26,0x6C07, + 0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9, + 0x9FF8,0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0 + ); + + function crc16Add(crc,c) + // 'crc' should be initialized to 0x0000. + { + return CRC16_TAB[((crc>>8)^c)&0xFF]^((crc<<8)&0xFFFF); + }; + // C/C++ language: + // + // inline unsigned short crc16Add(unsigned short crc, unsigned char c) + // { + // return CRC16_TAB[(unsigned char)(crc>>8)^c]^(unsigned short)(crc<<8); + // } + + // FCS-16 (as it is in PPP) in table form + // + // Described in RFC-1662 by William Allen Simpson, see RFC-1662 for references. + // + // Modified by Anders Danielsson, March 10, 2006. + var FCS_16_TAB = new Array( + // C/C++ language: + // + // unsigned short FCS_16_TAB[256] = {...}; + 0x0000,0x1189,0x2312,0x329B,0x4624,0x57AD,0x6536,0x74BF,0x8C48,0x9DC1,0xAF5A,0xBED3,0xCA6C, + 0xDBE5,0xE97E,0xF8F7,0x1081,0x0108,0x3393,0x221A,0x56A5,0x472C,0x75B7,0x643E,0x9CC9,0x8D40, + 0xBFDB,0xAE52,0xDAED,0xCB64,0xF9FF,0xE876,0x2102,0x308B,0x0210,0x1399,0x6726,0x76AF,0x4434, + 0x55BD,0xAD4A,0xBCC3,0x8E58,0x9FD1,0xEB6E,0xFAE7,0xC87C,0xD9F5,0x3183,0x200A,0x1291,0x0318, + 0x77A7,0x662E,0x54B5,0x453C,0xBDCB,0xAC42,0x9ED9,0x8F50,0xFBEF,0xEA66,0xD8FD,0xC974,0x4204, + 0x538D,0x6116,0x709F,0x0420,0x15A9,0x2732,0x36BB,0xCE4C,0xDFC5,0xED5E,0xFCD7,0x8868,0x99E1, + 0xAB7A,0xBAF3,0x5285,0x430C,0x7197,0x601E,0x14A1,0x0528,0x37B3,0x263A,0xDECD,0xCF44,0xFDDF, + 0xEC56,0x98E9,0x8960,0xBBFB,0xAA72,0x6306,0x728F,0x4014,0x519D,0x2522,0x34AB,0x0630,0x17B9, + 0xEF4E,0xFEC7,0xCC5C,0xDDD5,0xA96A,0xB8E3,0x8A78,0x9BF1,0x7387,0x620E,0x5095,0x411C,0x35A3, + 0x242A,0x16B1,0x0738,0xFFCF,0xEE46,0xDCDD,0xCD54,0xB9EB,0xA862,0x9AF9,0x8B70,0x8408,0x9581, + 0xA71A,0xB693,0xC22C,0xD3A5,0xE13E,0xF0B7,0x0840,0x19C9,0x2B52,0x3ADB,0x4E64,0x5FED,0x6D76, + 0x7CFF,0x9489,0x8500,0xB79B,0xA612,0xD2AD,0xC324,0xF1BF,0xE036,0x18C1,0x0948,0x3BD3,0x2A5A, + 0x5EE5,0x4F6C,0x7DF7,0x6C7E,0xA50A,0xB483,0x8618,0x9791,0xE32E,0xF2A7,0xC03C,0xD1B5,0x2942, + 0x38CB,0x0A50,0x1BD9,0x6F66,0x7EEF,0x4C74,0x5DFD,0xB58B,0xA402,0x9699,0x8710,0xF3AF,0xE226, + 0xD0BD,0xC134,0x39C3,0x284A,0x1AD1,0x0B58,0x7FE7,0x6E6E,0x5CF5,0x4D7C,0xC60C,0xD785,0xE51E, + 0xF497,0x8028,0x91A1,0xA33A,0xB2B3,0x4A44,0x5BCD,0x6956,0x78DF,0x0C60,0x1DE9,0x2F72,0x3EFB, + 0xD68D,0xC704,0xF59F,0xE416,0x90A9,0x8120,0xB3BB,0xA232,0x5AC5,0x4B4C,0x79D7,0x685E,0x1CE1, + 0x0D68,0x3FF3,0x2E7A,0xE70E,0xF687,0xC41C,0xD595,0xA12A,0xB0A3,0x8238,0x93B1,0x6B46,0x7ACF, + 0x4854,0x59DD,0x2D62,0x3CEB,0x0E70,0x1FF9,0xF78F,0xE606,0xD49D,0xC514,0xB1AB,0xA022,0x92B9, + 0x8330,0x7BC7,0x6A4E,0x58D5,0x495C,0x3DE3,0x2C6A,0x1EF1,0x0F78 + ); + + function fcs16Add(fcs,c) + // 'fcs' should be initialized to 0xFFFF and after the computation it should be + // complemented (inverted). + // + // If the FCS-16 is calculated over the data and over the complemented FCS-16, the + // result will always be 0xF0B8 (without the complementation). + { + return FCS_16_TAB[(fcs^c)&0xFF]^((fcs>>8)&0xFF); + }; + + // C/C++ language: + // + // inline unsigned short fcs16Add(unsigned short fcs, unsigned char c) + // { + // return FCS_16_TAB[(unsigned char)fcs^c]^(unsigned short)(fcs>>8); + // } + + // + // CRC-32 (as it is in ZMODEM) in table form + // + // Copyright (C) 1986 Gary S. Brown. You may use this program, or + // code or tables extracted from it, as desired without restriction. + // + // Modified by Anders Danielsson, February 5, 1989 and March 10, 2006. + // + // This is also known as FCS-32 (as it is in PPP), described in + // RFC-1662 by William Allen Simpson, see RFC-1662 for references. + // + var CRC32_TAB = new Array( /* CRC polynomial 0xEDB88320 */ + // C/C++ language: + // + // unsigned long CRC32_TAB[] = {...}; + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, + 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, + 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7, + 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, + 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B, + 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, + 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, + 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, + 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433, + 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, + 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457, + 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, + 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB, + 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F, + 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, + 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683, + 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, + 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7, + 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, + 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, + 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, + 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F, + 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, + 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713, + 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, + 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777, + 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB, + 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, + 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF, + 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D + ); + + function crc32Add(crc,c) + // 'crc' should be initialized to 0xFFFFFFFF and after the computation it should be + // complemented (inverted). + // + // CRC-32 is also known as FCS-32. + // + // If the FCS-32 is calculated over the data and over the complemented FCS-32, the + // result will always be 0xDEBB20E3 (without the complementation). + { + return CRC32_TAB[(crc^c)&0xFF]^((crc>>8)&0xFFFFFF); + }; + // + // C/C++ language: + // + // inline unsigned long crc32Add(unsigned long crc, unsigned char c) + // { + // return CRC32_TAB[(unsigned char)crc^c]^(crc>>8); + // } + // + + function crc8(str) + { + var n, + len = str.length, + crc = 0 + ; + + for(var i = 0; i < len; i++) + crc = crc8Add(crc, str.charCodeAt(i)); + + return crc; + }; + + function crc8Buffer(buf) + { + var crc = 0; + + for (var i = 0, len = buf.length; i < len; ++i) + { + crc = crc8Add(crc, buf[i]); + } + + return crc; + } + + function crcArc(str) + { + var i, + len = str.length, + crc = 0 + ; + + for(i = 0; i < len; i++) + crc = crcArcAdd(crc, str.charCodeAt(i)); + + return crc; + }; + + function crc16(str) + { + var i, + len = str.length, + crc = 0 + ; + + for(i = 0; i < len; i++) + crc = crc16Add(crc, str.charCodeAt(i)); + + return crc; + }; + + function crc16Buffer(buf) + { + var crc = 0; + + for (var i = 0, len = buf.length; i < len; ++i) + { + crc = crc16Add(crc, buf[i]); + } + + return crc; + } + + function fcs16(str) + { + var i, + len = str.length, + fcs = 0xFFFF + ; + + for(i = 0; i < len; i++) + fcs = fcs16Add(fcs,str.charCodeAt(i)); + + return fcs^0xFFFF; + }; + + function crc32(str) + { + var i, + len = str.length, + crc = 0xFFFFFFFF + ; + + for(i = 0; i < len; i++) + crc = crc32Add(crc, str.charCodeAt(i)); + + return crc^0xFFFFFFFF; + }; + + function crc32Buffer(buf) + { + var crc = 0xFFFFFFFF; + + for (var i = 0, len = buf.length; i < len; ++i) + { + crc = crc32Add(crc, buf[i]); + } + + return crc ^ 0xFFFFFFFF; + } + + /** + * Convert value as 8-bit unsigned integer to 2 digit hexadecimal number. + */ + function hex8(val) + { + var n = val & 0xFF, + str = n.toString(16).toUpperCase() + ; + + while(str.length < 2) + str = "0" + str; + + return str; + }; + + /** + * Convert value as 16-bit unsigned integer to 4 digit hexadecimal number. + */ + function hex16(val) + { + return hex8(val >> 8) + hex8(val); + }; + + /** + * Convert value as 32-bit unsigned integer to 8 digit hexadecimal number. + */ + function hex32(val) + { + return hex16(val >> 16) + hex16(val); + }; + + var target, property; + + if(typeof(window) == 'undefined') + { + target = module; + property = 'exports'; + } + else + { + target = window; + property = 'crc'; + } + + target[property] = { + 'crc8' : crc8, + 'crcArc' : crcArc, + 'crc16' : crc16, + 'fcs16' : fcs16, + 'crc32' : crc32, + 'hex8' : hex8, + 'hex16' : hex16, + 'hex32' : hex32, + 'buffer' : { + crc8 : crc8Buffer, + crc16 : crc16Buffer, + crc32 : crc32Buffer + } + }; +})(); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/crc/package.json b/dist/node_modules/express/node_modules/connect/node_modules/crc/package.json new file mode 100644 index 0000000..27c3f7c --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/crc/package.json @@ -0,0 +1,18 @@ +{ + "name" : "crc", + "version" : "0.2.0", + "description" : "CRC JavaScript implementation", + "author": "Alex Gorbatchev ", + "contributors": [], + "main": "./lib/crc.js", + "scripts": {}, + "directories" : {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "repository": { + "type": "git", + "url": "git://github.com/alexgorbatchev/node-crc.git" + } +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/crc/test/crc.js b/dist/node_modules/express/node_modules/connect/node_modules/crc/test/crc.js new file mode 100755 index 0000000..faf2d1d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/crc/test/crc.js @@ -0,0 +1,63 @@ +#!/usr/bin/env ./nodeunit/bin/nodeunit + +var crc = require('../lib/crc'); + +describe('crc8()', function(){ + it('should work with strings', function(){ + crc.crc8('hello world').should.equal(64); + }) + + it('should work with Buffers', function(){ + crc.buffer.crc8(new Buffer('hello world')).should.equal(64); + }) +}) + +describe('crc16()', function(){ + it('should work with strings', function(){ + crc.crc16('hello world').should.equal(15332); + }) + + it('should work with Buffers', function(){ + crc.buffer.crc16(new Buffer('hello world')).should.equal(15332); + }) +}) + +describe('crc32()', function(){ + it('should work with strings', function(){ + crc.crc32('hello world').should.equal(222957957); + }) + + it('should work with Buffers', function(){ + crc.buffer.crc32(new Buffer('hello world')).should.equal(222957957); + }) +}) + +describe('crcArc()', function(){ + it('should work with strings', function(){ + crc.crcArc('hello world').should.equal(14785); + }) +}) + +describe('fcs16()', function(){ + it('should work with strings', function(){ + crc.fcs16('hello world').should.equal(44550); + }) +}) + +describe('hex8()', function(){ + it('should work with strings', function(){ + crc.hex8(64).should.equal('40'); + }) +}) + +describe('hex16()', function(){ + it('should work with strings', function(){ + crc.hex16(15332).should.equal('3BE4'); + }) +}) + +describe('hex32()', function(){ + it('should work with strings', function(){ + crc.hex32(222957957).should.equal('0D4A1185'); + }) +}) diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/debug/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/History.md b/dist/node_modules/express/node_modules/connect/node_modules/debug/History.md new file mode 100644 index 0000000..2220632 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/History.md @@ -0,0 +1,47 @@ + +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/dist/node_modules/express/node_modules/connect/node_modules/debug/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/debug/Makefile new file mode 100644 index 0000000..692f2c1 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/Makefile @@ -0,0 +1,4 @@ + +debug.component.js: head.js debug.js tail.js + cat $^ > $@ + diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/debug/Readme.md new file mode 100644 index 0000000..419fcdf --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/Readme.md @@ -0,0 +1,130 @@ + +# debug + + tiny node.js debugging utility. + +## Installation + +``` +$ npm install debug +``` + +## Example + + This module is modelled after node core's debugging technique, allowing you to enable one or more topic-specific debugging functions, for example core does the following within many modules: + +```js +var debug; +if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) { + debug = function(x) { + var prefix = process.pid + ',' + + (process.env.NODE_WORKER_ID ? 'Worker' : 'Master'); + console.error(prefix, x); + }; +} else { + debug = function() { }; +} +``` + + This concept is extremely simple but it works well. 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) + +## 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`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + a('doing some work'); +}, 1200); +``` + +## License + +(The MIT License) + +Copyright (c) 2011 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/dist/node_modules/express/node_modules/connect/node_modules/debug/debug.component.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/debug.component.js new file mode 100644 index 0000000..e6e9dbf --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/debug.component.js @@ -0,0 +1,120 @@ +;(function(){ + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + localStorage.debug = name; + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +// persist + +if (window.localStorage) debug.enable(localStorage.debug); + module.exports = debug; + +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/debug.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/debug.js new file mode 100644 index 0000000..905fbd4 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/debug.js @@ -0,0 +1,116 @@ + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + localStorage.debug = name; + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +// persist + +if (window.localStorage) debug.enable(localStorage.debug); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/example/app.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/app.js new file mode 100644 index 0000000..05374d9 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/app.js @@ -0,0 +1,19 @@ + +var debug = require('../')('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'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/example/browser.html b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/browser.html new file mode 100644 index 0000000..7510eee --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/browser.html @@ -0,0 +1,24 @@ + + + debug() + + + + + + + diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/example/wildcards.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/wildcards.js new file mode 100644 index 0000000..1fdac20 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/wildcards.js @@ -0,0 +1,10 @@ + +var debug = { + foo: require('../')('test:foo'), + bar: require('../')('test:bar'), + baz: require('../')('test:baz') +}; + +debug.foo('foo') +debug.bar('bar') +debug.baz('baz') \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/example/worker.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/worker.js new file mode 100644 index 0000000..7f6d288 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/example/worker.js @@ -0,0 +1,22 @@ + +// DEBUG=* node example/worker +// DEBUG=worker:* node example/worker +// DEBUG=worker:a node example/worker +// DEBUG=worker:b node example/worker + +var a = require('../')('worker:a') + , b = require('../')('worker:b'); + +function work() { + a('doing lots of uninteresting work'); + setTimeout(work, Math.random() * 1000); +} + +work(); + +function workb() { + b('doing some work'); + setTimeout(workb, Math.random() * 2000); +} + +workb(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/head.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/head.js new file mode 100644 index 0000000..55d3817 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/head.js @@ -0,0 +1 @@ +;(function(){ diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/index.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/index.js new file mode 100644 index 0000000..ee54454 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/debug'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/lib/debug.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/lib/debug.js new file mode 100644 index 0000000..969d122 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/lib/debug.js @@ -0,0 +1,135 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Enabled debuggers. + */ + +var names = [] + , skips = []; + +(process.env.DEBUG || '') + .split(/[\s,]+/) + .forEach(function(name){ + name = name.replace('*', '.*?'); + if (name[0] === '-') { + skips.push(new RegExp('^' + name.substr(1) + '$')); + } else { + names.push(new RegExp('^' + name + '$')); + } + }); + +/** + * Colors. + */ + +var colors = [6, 2, 3, 4, 5, 1]; + +/** + * Previous debug() call. + */ + +var prev = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Is stdout a TTY? Colored output is disabled when `true`. + */ + +var isatty = tty.isatty(2); + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function color() { + return colors[prevColor++ % colors.length]; +} + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +function humanize(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +} + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + function disabled(){} + disabled.enabled = false; + + var match = skips.some(function(re){ + return re.test(name); + }); + + if (match) return disabled; + + match = names.some(function(re){ + return re.test(name); + }); + + if (!match) return disabled; + var c = color(); + + function colored(fmt) { + var curr = new Date; + var ms = curr - (prev[name] || curr); + prev[name] = curr; + + fmt = ' \033[9' + c + 'm' + name + ' ' + + '\033[3' + c + 'm\033[90m' + + fmt + '\033[3' + c + 'm' + + ' +' + humanize(ms) + '\033[0m'; + + console.error.apply(this, arguments); + } + + function plain(fmt) { + fmt = new Date().toUTCString() + + ' ' + name + ' ' + fmt; + console.error.apply(this, arguments); + } + + colored.enabled = plain.enabled = true; + + return isatty + ? colored + : plain; +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/package.json b/dist/node_modules/express/node_modules/connect/node_modules/debug/package.json new file mode 100644 index 0000000..f226656 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/package.json @@ -0,0 +1,17 @@ +{ + "name": "debug" + , "version": "0.7.0" + , "description": "small debugging utility" + , "keywords": ["debug", "log", "debugger"] + , "author": "TJ Holowaychuk " + , "dependencies": {} + , "devDependencies": { "mocha": "*" } + , "main": "index" + , "browserify": "debug.component.js" + , "engines": { "node": "*" } + , "component": { + "scripts": { + "debug": "debug.component.js" + } + } +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/debug/tail.js b/dist/node_modules/express/node_modules/connect/node_modules/debug/tail.js new file mode 100644 index 0000000..5bf3fd3 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/debug/tail.js @@ -0,0 +1,4 @@ + + module.exports = debug; + +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/formidable/.npmignore new file mode 100644 index 0000000..4fbabb3 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/.npmignore @@ -0,0 +1,4 @@ +/test/tmp/ +*.upload +*.un~ +*.http diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/.travis.yml b/dist/node_modules/express/node_modules/connect/node_modules/formidable/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/formidable/Makefile new file mode 100644 index 0000000..8945872 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/Makefile @@ -0,0 +1,14 @@ +SHELL := /bin/bash + +test: + @./test/run.js + +build: npm test + +npm: + npm install . + +clean: + rm test/tmp/* + +.PHONY: test clean build diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md new file mode 100644 index 0000000..a5ca104 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md @@ -0,0 +1,311 @@ +# Formidable + +[![Build Status](https://secure.travis-ci.org/felixge/node-formidable.png?branch=master)](http://travis-ci.org/felixge/node-formidable) + +## Purpose + +A node.js module for parsing form data, especially file uploads. + +## Current status + +This module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading +and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from +a large variety of clients and is considered production-ready. + +## Features + +* Fast (~500mb/sec), non-buffering multipart parser +* Automatically writing file uploads to disk +* Low memory footprint +* Graceful error handling +* Very high test coverage + +## Changelog + +### v1.0.9 + +* Emit progress when content length header parsed (Tim Koschützki) +* Fix Readme syntax due to GitHub changes (goob) +* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara) + +### v1.0.8 + +* Strip potentially unsafe characters when using `keepExtensions: true`. +* Switch to utest / urun for testing +* Add travis build + +### v1.0.7 + +* Remove file from package that was causing problems when installing on windows. (#102) +* Fix typos in Readme (Jason Davies). + +### v1.0.6 + +* Do not default to the default to the field name for file uploads where + filename="". + +### v1.0.5 + +* Support filename="" in multipart parts +* Explain unexpected end() errors in parser better + +**Note:** Starting with this version, formidable emits 'file' events for empty +file input fields. Previously those were incorrectly emitted as regular file +input fields with value = "". + +### v1.0.4 + +* Detect a good default tmp directory regardless of platform. (#88) + +### v1.0.3 + +* Fix problems with utf8 characters (#84) / semicolons in filenames (#58) +* Small performance improvements +* New test suite and fixture system + +### v1.0.2 + +* Exclude node\_modules folder from git +* Implement new `'aborted'` event +* Fix files in example folder to work with recent node versions +* Make gently a devDependency + +[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2) + +### v1.0.1 + +* Fix package.json to refer to proper main directory. (#68, Dean Landolt) + +[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1) + +### v1.0.0 + +* Add support for multipart boundaries that are quoted strings. (Jeff Craig) + +This marks the beginning of development on version 2.0 which will include +several architectural improvements. + +[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0) + +### v0.9.11 + +* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki) +* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class + +**Important:** The old property names of the File class will be removed in a +future release. + +[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11) + +### Older releases + +These releases were done before starting to maintain the above Changelog: + +* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10) +* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9) +* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8) +* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7) +* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6) +* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5) +* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4) +* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3) +* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2) +* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0) + +## Installation + +Via [npm](http://github.com/isaacs/npm): + + npm install formidable@latest + +Manually: + + git clone git://github.com/felixge/node-formidable.git formidable + vim my.js + # var formidable = require('./formidable'); + +Note: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library. + +## Example + +Parse an incoming file upload. + + var formidable = require('formidable'), + http = require('http'), + + util = require('util'); + + http.createServer(function(req, res) { + if (req.url == '/upload' && req.method.toLowerCase() == 'post') { + // parse a file upload + var form = new formidable.IncomingForm(); + form.parse(req, function(err, fields, files) { + res.writeHead(200, {'content-type': 'text/plain'}); + res.write('received upload:\n\n'); + res.end(util.inspect({fields: fields, files: files})); + }); + return; + } + + // show a file upload form + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + }).listen(80); + +## API + +### formidable.IncomingForm + +__new formidable.IncomingForm()__ + +Creates a new incoming form. + +__incomingForm.encoding = 'utf-8'__ + +The encoding to use for incoming form fields. + +__incomingForm.uploadDir = process.env.TMP || '/tmp' || process.cwd()__ + +The directory for placing file uploads in. You can move them later on using +`fs.rename()`. The default directory is picked at module load time depending on +the first existing directory from those listed above. + +__incomingForm.keepExtensions = false__ + +If you want the files written to `incomingForm.uploadDir` to include the extensions of the original files, set this property to `true`. + +__incomingForm.type__ + +Either 'multipart' or 'urlencoded' depending on the incoming request. + +__incomingForm.maxFieldsSize = 2 * 1024 * 1024__ + +Limits the amount of memory a field (not file) can allocate in bytes. +If this value is exceeded, an `'error'` event is emitted. The default +size is 2MB. + +__incomingForm.hash = false__ + +If you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`. + +__incomingForm.bytesReceived__ + +The amount of bytes received for this form so far. + +__incomingForm.bytesExpected__ + +The expected number of bytes in this form. + +__incomingForm.parse(request, [cb])__ + +Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields an files are collected and passed to the callback: + + incomingForm.parse(req, function(err, fields, files) { + // ... + }); + +__incomingForm.onPart(part)__ + +You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing. + + incomingForm.onPart = function(part) { + part.addListener('data', function() { + // ... + }); + } + +If you want to use formidable to only handle certain parts for you, you can do so: + + incomingForm.onPart = function(part) { + if (!part.filename) { + // let formidable handle all non-file parts + incomingForm.handlePart(part); + } + } + +Check the code in this method for further inspiration. + +__Event: 'progress' (bytesReceived, bytesExpected)__ + +Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar. + +__Event: 'field' (name, value)__ + +Emitted whenever a field / value pair has been received. + +__Event: 'fileBegin' (name, file)__ + +Emitted whenever a new file is detected in the upload stream. Use this even if +you want to stream the file to somewhere else while buffering the upload on +the file system. + +__Event: 'file' (name, file)__ + +Emitted whenever a field / file pair has been received. `file` is an instance of `File`. + +__Event: 'error' (err)__ + +Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events. + +__Event: 'aborted'__ + +Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. In the future there will be a separate 'timeout' event (needs a change in the node core). + +__Event: 'end' ()__ + +Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response. + +### formidable.File + +__file.size = 0__ + +The size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet. + +__file.path = null__ + +The path this file is being written to. You can modify this in the `'fileBegin'` event in +case you are unhappy with the way formidable generates a temporary path for your files. + +__file.name = null__ + +The name this file had according to the uploading client. + +__file.type = null__ + +The mime type of this file, according to the uploading client. + +__file.lastModifiedDate = null__ + +A date object (or `null`) containing the time this file was last written to. Mostly +here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/). + +__file.hash = null__ + +If hash calculation was set, you can read the hex digest out of this var. + +## License + +Formidable is licensed under the MIT license. + +## Ports + +* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable + +## Credits + +* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/TODO b/dist/node_modules/express/node_modules/connect/node_modules/formidable/TODO new file mode 100644 index 0000000..e1107f2 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/TODO @@ -0,0 +1,3 @@ +- Better bufferMaxSize handling approach +- Add tests for JSON parser pull request and merge it +- Implement QuerystringParser the same way as MultipartParser diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js new file mode 100644 index 0000000..bff41f1 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/benchmark/bench-multipart-parser.js @@ -0,0 +1,70 @@ +require('../test/common'); +var multipartParser = require('../lib/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + parser = new MultipartParser(), + Buffer = require('buffer').Buffer, + boundary = '-----------------------------168072824752491622650073', + mb = 100, + buffer = createMultipartBuffer(boundary, mb * 1024 * 1024), + callbacks = + { partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, + }; + + +parser.initWithBoundary(boundary); +parser.onHeaderField = function() { + callbacks.headerField++; +}; + +parser.onHeaderValue = function() { + callbacks.headerValue++; +}; + +parser.onPartBegin = function() { + callbacks.partBegin++; +}; + +parser.onPartData = function() { + callbacks.partData++; +}; + +parser.onPartEnd = function() { + callbacks.partEnd++; +}; + +parser.onEnd = function() { + callbacks.end++; +}; + +var start = +new Date(), + nparsed = parser.write(buffer), + duration = +new Date - start, + mbPerSec = (mb / (duration / 1000)).toFixed(2); + +console.log(mbPerSec+' mb/sec'); + +assert.equal(nparsed, buffer.length); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +process.on('exit', function() { + for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + } +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js new file mode 100644 index 0000000..f6c15a6 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/example/post.js @@ -0,0 +1,43 @@ +require('../test/common'); +var http = require('http'), + util = require('util'), + formidable = require('formidable'), + server; + +server = http.createServer(function(req, res) { + if (req.url == '/') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + } else if (req.url == '/post') { + var form = new formidable.IncomingForm(), + fields = []; + + form + .on('error', function(err) { + res.writeHead(200, {'content-type': 'text/plain'}); + res.end('error:\n\n'+util.inspect(err)); + }) + .on('field', function(field, value) { + console.log(field, value); + fields.push([field, value]); + }) + .on('end', function() { + console.log('-> post done'); + res.writeHead(200, {'content-type': 'text/plain'}); + res.end('received fields:\n\n '+util.inspect(fields)); + }); + form.parse(req); + } else { + res.writeHead(404, {'content-type': 'text/plain'}); + res.end('404'); + } +}); +server.listen(TEST_PORT); + +console.log('listening on http://localhost:'+TEST_PORT+'/'); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js new file mode 100644 index 0000000..050cdd9 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/example/upload.js @@ -0,0 +1,48 @@ +require('../test/common'); +var http = require('http'), + util = require('util'), + formidable = require('formidable'), + server; + +server = http.createServer(function(req, res) { + if (req.url == '/') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + } else if (req.url == '/upload') { + var form = new formidable.IncomingForm(), + files = [], + fields = []; + + form.uploadDir = TEST_TMP; + + form + .on('field', function(field, value) { + console.log(field, value); + fields.push([field, value]); + }) + .on('file', function(field, file) { + console.log(field, file); + files.push([field, file]); + }) + .on('end', function() { + console.log('-> upload done'); + res.writeHead(200, {'content-type': 'text/plain'}); + res.write('received fields:\n\n '+util.inspect(fields)); + res.write('\n\n'); + res.end('received files:\n\n '+util.inspect(files)); + }); + form.parse(req); + } else { + res.writeHead(404, {'content-type': 'text/plain'}); + res.end('404'); + } +}); +server.listen(TEST_PORT); + +console.log('listening on http://localhost:'+TEST_PORT+'/'); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/index.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/index.js new file mode 100644 index 0000000..be41032 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/formidable'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js new file mode 100644 index 0000000..dad8d5f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/file.js @@ -0,0 +1,73 @@ +if (global.GENTLY) require = GENTLY.hijack(require); + +var util = require('./util'), + WriteStream = require('fs').WriteStream, + EventEmitter = require('events').EventEmitter, + crypto = require('crypto'); + +function File(properties) { + EventEmitter.call(this); + + this.size = 0; + this.path = null; + this.name = null; + this.type = null; + this.hash = null; + this.lastModifiedDate = null; + + this._writeStream = null; + + for (var key in properties) { + this[key] = properties[key]; + } + + if(typeof this.hash === 'string') { + this.hash = crypto.createHash(properties.hash); + } + + this._backwardsCompatibility(); +} +module.exports = File; +util.inherits(File, EventEmitter); + +// @todo Next release: Show error messages when accessing these +File.prototype._backwardsCompatibility = function() { + var self = this; + this.__defineGetter__('length', function() { + return self.size; + }); + this.__defineGetter__('filename', function() { + return self.name; + }); + this.__defineGetter__('mime', function() { + return self.type; + }); +}; + +File.prototype.open = function() { + this._writeStream = new WriteStream(this.path); +}; + +File.prototype.write = function(buffer, cb) { + var self = this; + this._writeStream.write(buffer, function() { + if(self.hash) { + self.hash.update(buffer); + } + self.lastModifiedDate = new Date(); + self.size += buffer.length; + self.emit('progress', self.size); + cb(); + }); +}; + +File.prototype.end = function(cb) { + var self = this; + this._writeStream.end(function() { + if(self.hash) { + self.hash = self.hash.digest('hex'); + } + self.emit('end'); + cb(); + }); +}; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js new file mode 100644 index 0000000..060eac2 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js @@ -0,0 +1,384 @@ +if (global.GENTLY) require = GENTLY.hijack(require); + +var fs = require('fs'); +var util = require('./util'), + path = require('path'), + File = require('./file'), + MultipartParser = require('./multipart_parser').MultipartParser, + QuerystringParser = require('./querystring_parser').QuerystringParser, + StringDecoder = require('string_decoder').StringDecoder, + EventEmitter = require('events').EventEmitter, + Stream = require('stream').Stream; + +function IncomingForm(opts) { + if (!(this instanceof IncomingForm)) return new IncomingForm; + EventEmitter.call(this); + + opts=opts||{}; + + this.error = null; + this.ended = false; + + this.maxFieldsSize = opts.maxFieldsSize || 2 * 1024 * 1024; + this.keepExtensions = opts.keepExtensions || false; + this.uploadDir = opts.uploadDir || IncomingForm.UPLOAD_DIR; + this.encoding = opts.encoding || 'utf-8'; + this.headers = null; + this.type = null; + this.hash = false; + + this.bytesReceived = null; + this.bytesExpected = null; + + this._parser = null; + this._flushing = 0; + this._fieldsSize = 0; +}; +util.inherits(IncomingForm, EventEmitter); +exports.IncomingForm = IncomingForm; + +IncomingForm.UPLOAD_DIR = (function() { + var dirs = [process.env.TMP, '/tmp', process.cwd()]; + for (var i = 0; i < dirs.length; i++) { + var dir = dirs[i]; + var isDirectory = false; + + try { + isDirectory = fs.statSync(dir).isDirectory(); + } catch (e) {} + + if (isDirectory) return dir; + } +})(); + +IncomingForm.prototype.parse = function(req, cb) { + this.pause = function() { + try { + req.pause(); + } catch (err) { + // the stream was destroyed + if (!this.ended) { + // before it was completed, crash & burn + this._error(err); + } + return false; + } + return true; + }; + + this.resume = function() { + try { + req.resume(); + } catch (err) { + // the stream was destroyed + if (!this.ended) { + // before it was completed, crash & burn + this._error(err); + } + return false; + } + + return true; + }; + + this.writeHeaders(req.headers); + + var self = this; + req + .on('error', function(err) { + self._error(err); + }) + .on('aborted', function() { + self.emit('aborted'); + }) + .on('data', function(buffer) { + self.write(buffer); + }) + .on('end', function() { + if (self.error) { + return; + } + + var err = self._parser.end(); + if (err) { + self._error(err); + } + }); + + if (cb) { + var fields = {}, files = {}; + this + .on('field', function(name, value) { + fields[name] = value; + }) + .on('file', function(name, file) { + files[name] = file; + }) + .on('error', function(err) { + cb(err, fields, files); + }) + .on('end', function() { + cb(null, fields, files); + }); + } + + return this; +}; + +IncomingForm.prototype.writeHeaders = function(headers) { + this.headers = headers; + this._parseContentLength(); + this._parseContentType(); +}; + +IncomingForm.prototype.write = function(buffer) { + if (!this._parser) { + this._error(new Error('unintialized parser')); + return; + } + + this.bytesReceived += buffer.length; + this.emit('progress', this.bytesReceived, this.bytesExpected); + + var bytesParsed = this._parser.write(buffer); + if (bytesParsed !== buffer.length) { + this._error(new Error('parser error, '+bytesParsed+' of '+buffer.length+' bytes parsed')); + } + + return bytesParsed; +}; + +IncomingForm.prototype.pause = function() { + // this does nothing, unless overwritten in IncomingForm.parse + return false; +}; + +IncomingForm.prototype.resume = function() { + // this does nothing, unless overwritten in IncomingForm.parse + return false; +}; + +IncomingForm.prototype.onPart = function(part) { + // this method can be overwritten by the user + this.handlePart(part); +}; + +IncomingForm.prototype.handlePart = function(part) { + var self = this; + + if (part.filename === undefined) { + var value = '' + , decoder = new StringDecoder(this.encoding); + + part.on('data', function(buffer) { + self._fieldsSize += buffer.length; + if (self._fieldsSize > self.maxFieldsSize) { + self._error(new Error('maxFieldsSize exceeded, received '+self._fieldsSize+' bytes of field data')); + return; + } + value += decoder.write(buffer); + }); + + part.on('end', function() { + self.emit('field', part.name, value); + }); + return; + } + + this._flushing++; + + var file = new File({ + path: this._uploadPath(part.filename), + name: part.filename, + type: part.mime, + hash: self.hash + }); + + this.emit('fileBegin', part.name, file); + + file.open(); + + part.on('data', function(buffer) { + self.pause(); + file.write(buffer, function() { + self.resume(); + }); + }); + + part.on('end', function() { + file.end(function() { + self._flushing--; + self.emit('file', part.name, file); + self._maybeEnd(); + }); + }); +}; + +IncomingForm.prototype._parseContentType = function() { + if (!this.headers['content-type']) { + this._error(new Error('bad content-type header, no content-type')); + return; + } + + if (this.headers['content-type'].match(/urlencoded/i)) { + this._initUrlencoded(); + return; + } + + if (this.headers['content-type'].match(/multipart/i)) { + var m; + if (m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i)) { + this._initMultipart(m[1] || m[2]); + } else { + this._error(new Error('bad content-type header, no multipart boundary')); + } + return; + } + + this._error(new Error('bad content-type header, unknown content-type: '+this.headers['content-type'])); +}; + +IncomingForm.prototype._error = function(err) { + if (this.error) { + return; + } + + this.error = err; + this.pause(); + this.emit('error', err); +}; + +IncomingForm.prototype._parseContentLength = function() { + if (this.headers['content-length']) { + this.bytesReceived = 0; + this.bytesExpected = parseInt(this.headers['content-length'], 10); + this.emit('progress', this.bytesReceived, this.bytesExpected); + } +}; + +IncomingForm.prototype._newParser = function() { + return new MultipartParser(); +}; + +IncomingForm.prototype._initMultipart = function(boundary) { + this.type = 'multipart'; + + var parser = new MultipartParser(), + self = this, + headerField, + headerValue, + part; + + parser.initWithBoundary(boundary); + + parser.onPartBegin = function() { + part = new Stream(); + part.readable = true; + part.headers = {}; + part.name = null; + part.filename = null; + part.mime = null; + headerField = ''; + headerValue = ''; + }; + + parser.onHeaderField = function(b, start, end) { + headerField += b.toString(self.encoding, start, end); + }; + + parser.onHeaderValue = function(b, start, end) { + headerValue += b.toString(self.encoding, start, end); + }; + + parser.onHeaderEnd = function() { + headerField = headerField.toLowerCase(); + part.headers[headerField] = headerValue; + + var m; + if (headerField == 'content-disposition') { + if (m = headerValue.match(/name="([^"]+)"/i)) { + part.name = m[1]; + } + + part.filename = self._fileName(headerValue); + } else if (headerField == 'content-type') { + part.mime = headerValue; + } + + headerField = ''; + headerValue = ''; + }; + + parser.onHeadersEnd = function() { + self.onPart(part); + }; + + parser.onPartData = function(b, start, end) { + part.emit('data', b.slice(start, end)); + }; + + parser.onPartEnd = function() { + part.emit('end'); + }; + + parser.onEnd = function() { + self.ended = true; + self._maybeEnd(); + }; + + this._parser = parser; +}; + +IncomingForm.prototype._fileName = function(headerValue) { + var m = headerValue.match(/filename="(.*?)"($|; )/i) + if (!m) return; + + var filename = m[1].substr(m[1].lastIndexOf('\\') + 1); + filename = filename.replace(/%22/g, '"'); + filename = filename.replace(/&#([\d]{4});/g, function(m, code) { + return String.fromCharCode(code); + }); + return filename; +}; + +IncomingForm.prototype._initUrlencoded = function() { + this.type = 'urlencoded'; + + var parser = new QuerystringParser() + , self = this; + + parser.onField = function(key, val) { + self.emit('field', key, val); + }; + + parser.onEnd = function() { + self.ended = true; + self._maybeEnd(); + }; + + this._parser = parser; +}; + +IncomingForm.prototype._uploadPath = function(filename) { + var name = ''; + for (var i = 0; i < 32; i++) { + name += Math.floor(Math.random() * 16).toString(16); + } + + if (this.keepExtensions) { + var ext = path.extname(filename); + ext = ext.replace(/(\.[a-z0-9]+).*/, '$1') + + name += ext; + } + + return path.join(this.uploadDir, name); +}; + +IncomingForm.prototype._maybeEnd = function() { + if (!this.ended || this._flushing) { + return; + } + + this.emit('end'); +}; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js new file mode 100644 index 0000000..7a6e3e1 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/index.js @@ -0,0 +1,3 @@ +var IncomingForm = require('./incoming_form').IncomingForm; +IncomingForm.IncomingForm = IncomingForm; +module.exports = IncomingForm; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js new file mode 100644 index 0000000..9ca567c --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/multipart_parser.js @@ -0,0 +1,312 @@ +var Buffer = require('buffer').Buffer, + s = 0, + S = + { PARSER_UNINITIALIZED: s++, + START: s++, + START_BOUNDARY: s++, + HEADER_FIELD_START: s++, + HEADER_FIELD: s++, + HEADER_VALUE_START: s++, + HEADER_VALUE: s++, + HEADER_VALUE_ALMOST_DONE: s++, + HEADERS_ALMOST_DONE: s++, + PART_DATA_START: s++, + PART_DATA: s++, + PART_END: s++, + END: s++, + }, + + f = 1, + F = + { PART_BOUNDARY: f, + LAST_BOUNDARY: f *= 2, + }, + + LF = 10, + CR = 13, + SPACE = 32, + HYPHEN = 45, + COLON = 58, + A = 97, + Z = 122, + + lower = function(c) { + return c | 0x20; + }; + +for (var s in S) { + exports[s] = S[s]; +} + +function MultipartParser() { + this.boundary = null; + this.boundaryChars = null; + this.lookbehind = null; + this.state = S.PARSER_UNINITIALIZED; + + this.index = null; + this.flags = 0; +}; +exports.MultipartParser = MultipartParser; + +MultipartParser.stateToString = function(stateNumber) { + for (var state in S) { + var number = S[state]; + if (number === stateNumber) return state; + } +}; + +MultipartParser.prototype.initWithBoundary = function(str) { + this.boundary = new Buffer(str.length+4); + this.boundary.write('\r\n--', 'ascii', 0); + this.boundary.write(str, 'ascii', 4); + this.lookbehind = new Buffer(this.boundary.length+8); + this.state = S.START; + + this.boundaryChars = {}; + for (var i = 0; i < this.boundary.length; i++) { + this.boundaryChars[this.boundary[i]] = true; + } +}; + +MultipartParser.prototype.write = function(buffer) { + var self = this, + i = 0, + len = buffer.length, + prevIndex = this.index, + index = this.index, + state = this.state, + flags = this.flags, + lookbehind = this.lookbehind, + boundary = this.boundary, + boundaryChars = this.boundaryChars, + boundaryLength = this.boundary.length, + boundaryEnd = boundaryLength - 1, + bufferLength = buffer.length, + c, + cl, + + mark = function(name) { + self[name+'Mark'] = i; + }, + clear = function(name) { + delete self[name+'Mark']; + }, + callback = function(name, buffer, start, end) { + if (start !== undefined && start === end) { + return; + } + + var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1); + if (callbackSymbol in self) { + self[callbackSymbol](buffer, start, end); + } + }, + dataCallback = function(name, clear) { + var markSymbol = name+'Mark'; + if (!(markSymbol in self)) { + return; + } + + if (!clear) { + callback(name, buffer, self[markSymbol], buffer.length); + self[markSymbol] = 0; + } else { + callback(name, buffer, self[markSymbol], i); + delete self[markSymbol]; + } + }; + + for (i = 0; i < len; i++) { + c = buffer[i]; + switch (state) { + case S.PARSER_UNINITIALIZED: + return i; + case S.START: + index = 0; + state = S.START_BOUNDARY; + case S.START_BOUNDARY: + if (index == boundary.length - 2) { + if (c != CR) { + return i; + } + index++; + break; + } else if (index - 1 == boundary.length - 2) { + if (c != LF) { + return i; + } + index = 0; + callback('partBegin'); + state = S.HEADER_FIELD_START; + break; + } + + if (c != boundary[index+2]) { + return i; + } + index++; + break; + case S.HEADER_FIELD_START: + state = S.HEADER_FIELD; + mark('headerField'); + index = 0; + case S.HEADER_FIELD: + if (c == CR) { + clear('headerField'); + state = S.HEADERS_ALMOST_DONE; + break; + } + + index++; + if (c == HYPHEN) { + break; + } + + if (c == COLON) { + if (index == 1) { + // empty header field + return i; + } + dataCallback('headerField', true); + state = S.HEADER_VALUE_START; + break; + } + + cl = lower(c); + if (cl < A || cl > Z) { + return i; + } + break; + case S.HEADER_VALUE_START: + if (c == SPACE) { + break; + } + + mark('headerValue'); + state = S.HEADER_VALUE; + case S.HEADER_VALUE: + if (c == CR) { + dataCallback('headerValue', true); + callback('headerEnd'); + state = S.HEADER_VALUE_ALMOST_DONE; + } + break; + case S.HEADER_VALUE_ALMOST_DONE: + if (c != LF) { + return i; + } + state = S.HEADER_FIELD_START; + break; + case S.HEADERS_ALMOST_DONE: + if (c != LF) { + return i; + } + + callback('headersEnd'); + state = S.PART_DATA_START; + break; + case S.PART_DATA_START: + state = S.PART_DATA + mark('partData'); + case S.PART_DATA: + prevIndex = index; + + if (index == 0) { + // boyer-moore derrived algorithm to safely skip non-boundary data + i += boundaryEnd; + while (i < bufferLength && !(buffer[i] in boundaryChars)) { + i += boundaryLength; + } + i -= boundaryEnd; + c = buffer[i]; + } + + if (index < boundary.length) { + if (boundary[index] == c) { + if (index == 0) { + dataCallback('partData', true); + } + index++; + } else { + index = 0; + } + } else if (index == boundary.length) { + index++; + if (c == CR) { + // CR = part boundary + flags |= F.PART_BOUNDARY; + } else if (c == HYPHEN) { + // HYPHEN = end boundary + flags |= F.LAST_BOUNDARY; + } else { + index = 0; + } + } else if (index - 1 == boundary.length) { + if (flags & F.PART_BOUNDARY) { + index = 0; + if (c == LF) { + // unset the PART_BOUNDARY flag + flags &= ~F.PART_BOUNDARY; + callback('partEnd'); + callback('partBegin'); + state = S.HEADER_FIELD_START; + break; + } + } else if (flags & F.LAST_BOUNDARY) { + if (c == HYPHEN) { + callback('partEnd'); + callback('end'); + state = S.END; + } else { + index = 0; + } + } else { + index = 0; + } + } + + if (index > 0) { + // when matching a possible boundary, keep a lookbehind reference + // in case it turns out to be a false lead + lookbehind[index-1] = c; + } else if (prevIndex > 0) { + // if our boundary turned out to be rubbish, the captured lookbehind + // belongs to partData + callback('partData', lookbehind, 0, prevIndex); + prevIndex = 0; + mark('partData'); + + // reconsider the current character even so it interrupted the sequence + // it could be the beginning of a new sequence + i--; + } + + break; + case S.END: + break; + default: + return i; + } + } + + dataCallback('headerField'); + dataCallback('headerValue'); + dataCallback('partData'); + + this.index = index; + this.state = state; + this.flags = flags; + + return len; +}; + +MultipartParser.prototype.end = function() { + if (this.state != S.END) { + return new Error('MultipartParser.end(): stream ended unexpectedly: ' + this.explain()); + } +}; + +MultipartParser.prototype.explain = function() { + return 'state = ' + MultipartParser.stateToString(this.state); +}; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js new file mode 100644 index 0000000..63f109e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/querystring_parser.js @@ -0,0 +1,25 @@ +if (global.GENTLY) require = GENTLY.hijack(require); + +// This is a buffering parser, not quite as nice as the multipart one. +// If I find time I'll rewrite this to be fully streaming as well +var querystring = require('querystring'); + +function QuerystringParser() { + this.buffer = ''; +}; +exports.QuerystringParser = QuerystringParser; + +QuerystringParser.prototype.write = function(buffer) { + this.buffer += buffer.toString('ascii'); + return buffer.length; +}; + +QuerystringParser.prototype.end = function() { + var fields = querystring.parse(this.buffer); + for (var field in fields) { + this.onField(field, fields[field]); + } + this.buffer = ''; + + this.onEnd(); +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/util.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/util.js new file mode 100644 index 0000000..e9493e9 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/lib/util.js @@ -0,0 +1,6 @@ +// Backwards compatibility ... +try { + module.exports = require('util'); +} catch (e) { + module.exports = require('sys'); +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Makefile new file mode 100644 index 0000000..01f7140 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Makefile @@ -0,0 +1,4 @@ +test: + @find test/simple/test-*.js | xargs -n 1 -t node + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Readme.md new file mode 100644 index 0000000..f8f0c66 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/Readme.md @@ -0,0 +1,167 @@ +# Gently + +## Purpose + +A node.js module that helps with stubbing and behavior verification. It allows you to test the most remote and nested corners of your code while keeping being fully unobtrusive. + +## Features + +* Overwrite and stub individual object functions +* Verify that all expected calls have been made in the expected order +* Restore stubbed functions to their original behavior +* Detect object / class names from obj.constructor.name and obj.toString() +* Hijack any required module function or class constructor + +## Installation + +Via [npm](http://github.com/isaacs/npm): + + npm install gently@latest + +## Example + +Make sure your dog is working properly: + + function Dog() {} + + Dog.prototype.seeCat = function() { + this.bark('whuf, whuf'); + this.run(); + } + + Dog.prototype.bark = function(bark) { + require('sys').puts(bark); + } + + var gently = new (require('gently')) + , assert = require('assert') + , dog = new Dog(); + + gently.expect(dog, 'bark', function(bark) { + assert.equal(bark, 'whuf, whuf'); + }); + gently.expect(dog, 'run'); + + dog.seeCat(); + +You can also easily test event emitters with this, for example a simple sequence of 2 events emitted by `fs.WriteStream`: + + var gently = new (require('gently')) + , stream = new (require('fs').WriteStream)('my_file.txt'); + + gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'open'); + }); + + gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'drain'); + }); + +For a full read world example, check out this test case: [test-incoming-form.js](http://github.com/felixge/node-formidable/blob/master/test/simple/test-incoming-form.js) (in [node-formdiable](http://github.com/felixge/node-formidable)). + +## API + +### Gently + +#### new Gently() + +Creates a new gently instance. It listens to the process `'exit'` event to make sure all expectations have been verified. + +#### gently.expect(obj, method, [[count], stubFn]) + +Creates an expectation for an objects method to be called. You can optionally specify the call `count` you are expecting, as well as `stubFn` function that will run instead of the original function. + +Returns a reference to the function that is getting overwritten. + +#### gently.expect([count], stubFn) + +Returns a function that is supposed to be executed `count` times, delegating any calls to the provided `stubFn` function. Naming your stubFn closure will help to properly diagnose errors that are being thrown: + + childProcess.exec('ls', gently.expect(function lsCallback(code) { + assert.equal(0, code); + })); + +#### gently.restore(obj, method) + +Restores an object method that has been previously overwritten using `gently.expect()`. + +#### gently.hijack(realRequire) + +Returns a new require functions that catches a reference to all required modules into `gently.hijacked`. + +To use this function, include a line like this in your `'my-module.js'`. + + if (global.GENTLY) require = GENTLY.hijack(require); + + var sys = require('sys'); + exports.hello = function() { + sys.log('world'); + }; + +Now you can write a test for the module above: + + var gently = global.GENTLY = new (require('gently')) + , myModule = require('./my-module'); + + gently.expect(gently.hijacked.sys, 'log', function(str) { + assert.equal(str, 'world'); + }); + + myModule.hello(); + +#### gently.stub(location, [exportsName]) + +Returns a stub class that will be used instead of the real class from the module at `location` with the given `exportsName`. + +This allows to test an OOP version of the previous example, where `'my-module.js'`. + + if (global.GENTLY) require = GENTLY.hijack(require); + + var World = require('./world'); + + exports.hello = function() { + var world = new World(); + world.hello(); + } + +And `world.js` looks like this: + + var sys = require('sys'); + + function World() { + + } + module.exports = World; + + World.prototype.hello = function() { + sys.log('world'); + }; + +Testing `'my-module.js'` can now easily be accomplished: + + var gently = global.GENTLY = new (require('gently')) + , WorldStub = gently.stub('./world') + , myModule = require('./my-module') + , WORLD; + + gently.expect(WorldStub, 'new', function() { + WORLD = this; + }); + + gently.expect(WORLD, 'hello'); + + myModule.hello(); + +#### gently.hijacked + +An object that holds the references to all hijacked modules. + +#### gently.verify([msg]) + +Verifies that all expectations of this gently instance have been satisfied. If not called manually, this method is called when the process `'exit'` event is fired. + +If `msg` is given, it will appear in any error that might be thrown. + +## License + +Gently is licensed under the MIT license. \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/dog.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/dog.js new file mode 100644 index 0000000..022fae0 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/dog.js @@ -0,0 +1,22 @@ +require('../test/common'); +function Dog() {} + +Dog.prototype.seeCat = function() { + this.bark('whuf, whuf'); + this.run(); +} + +Dog.prototype.bark = function(bark) { + require('sys').puts(bark); +} + +var gently = new (require('gently')) + , assert = require('assert') + , dog = new Dog(); + +gently.expect(dog, 'bark', function(bark) { + assert.equal(bark, 'whuf, whuf'); +}); +gently.expect(dog, 'run'); + +dog.seeCat(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/event_emitter.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/event_emitter.js new file mode 100644 index 0000000..7def134 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/example/event_emitter.js @@ -0,0 +1,11 @@ +require('../test/common'); +var gently = new (require('gently')) + , stream = new (require('fs').WriteStream)('my_file.txt'); + +gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'open'); +}); + +gently.expect(stream, 'emit', function(event) { + assert.equal(event, 'drain'); +}); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/index.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/index.js new file mode 100644 index 0000000..69122bd --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/gently'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/gently.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/gently.js new file mode 100644 index 0000000..8af0e1e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/gently.js @@ -0,0 +1,184 @@ +var path = require('path'); + +function Gently() { + this.expectations = []; + this.hijacked = {}; + + var self = this; + process.addListener('exit', function() { + self.verify('process exit'); + }); +}; +module.exports = Gently; + +Gently.prototype.stub = function(location, exportsName) { + function Stub() { + return Stub['new'].apply(this, arguments); + }; + + Stub['new'] = function () {}; + + var stubName = 'require('+JSON.stringify(location)+')'; + if (exportsName) { + stubName += '.'+exportsName; + } + + Stub.prototype.toString = Stub.toString = function() { + return stubName; + }; + + var exports = this.hijacked[location] || {}; + if (exportsName) { + exports[exportsName] = Stub; + } else { + exports = Stub; + } + + this.hijacked[location] = exports; + return Stub; +}; + +Gently.prototype.hijack = function(realRequire) { + var self = this; + return function(location) { + return self.hijacked[location] = (self.hijacked[location]) + ? self.hijacked[location] + : realRequire(location); + }; +}; + +Gently.prototype.expect = function(obj, method, count, stubFn) { + if (typeof obj != 'function' && typeof obj != 'object' && typeof obj != 'number') { + throw new Error + ( 'Bad 1st argument for gently.expect(), ' + + 'object, function, or number expected, got: '+(typeof obj) + ); + } else if (typeof obj == 'function' && (typeof method != 'string')) { + // expect(stubFn) interface + stubFn = obj; + obj = null; + method = null; + count = 1; + } else if (typeof method == 'function') { + // expect(count, stubFn) interface + count = obj; + stubFn = method; + obj = null; + method = null; + } else if (typeof count == 'function') { + // expect(obj, method, stubFn) interface + stubFn = count; + count = 1; + } else if (count === undefined) { + // expect(obj, method) interface + count = 1; + } + + var name = this._name(obj, method, stubFn); + this.expectations.push({obj: obj, method: method, stubFn: stubFn, name: name, count: count}); + + var self = this; + function delegate() { + return self._stubFn(this, obj, method, name, Array.prototype.slice.call(arguments)); + } + + if (!obj) { + return delegate; + } + + var original = (obj[method]) + ? obj[method]._original || obj[method] + : undefined; + + obj[method] = delegate; + return obj[method]._original = original; +}; + +Gently.prototype.restore = function(obj, method) { + if (!obj[method] || !obj[method]._original) { + throw new Error(this._name(obj, method)+' is not gently stubbed'); + } + obj[method] = obj[method]._original; +}; + +Gently.prototype.verify = function(msg) { + if (!this.expectations.length) { + return; + } + + var validExpectations = []; + for (var i = 0, l = this.expectations.length; i < l; i++) { + var expectation = this.expectations[i]; + + if (expectation.count > 0) { + validExpectations.push(expectation); + } + } + + this.expectations = []; // reset so that no duplicate verification attempts are made + + if (!validExpectations.length) { + return; + } + + var expectation = validExpectations[0]; + + throw new Error + ( 'Expected call to '+expectation.name+' did not happen' + + ( (msg) + ? ' ('+msg+')' + : '' + ) + ); +}; + +Gently.prototype._stubFn = function(self, obj, method, name, args) { + var expectation = this.expectations[0], obj, method; + + if (!expectation) { + throw new Error('Unexpected call to '+name+', no call was expected'); + } + + if (expectation.obj !== obj || expectation.method !== method) { + throw new Error('Unexpected call to '+name+', expected call to '+ expectation.name); + } + + expectation.count -= 1; + if (expectation.count === 0) { + this.expectations.shift(); + + // autorestore original if its not a closure + // and no more expectations on that object + var has_more_expectations = this.expectations.reduce(function (memo, expectation) { + return memo || (expectation.obj === obj && expectation.method === method); + }, false); + if (obj !== null && method !== null && !has_more_expectations) { + if (typeof obj[method]._original !== 'undefined') { + obj[method] = obj[method]._original; + delete obj[method]._original; + } else { + delete obj[method]; + } + } + } + + if (expectation.stubFn) { + return expectation.stubFn.apply(self, args); + } +}; + +Gently.prototype._name = function(obj, method, stubFn) { + if (obj) { + var objectName = obj.toString(); + if (objectName == '[object Object]' && obj.constructor.name) { + objectName = '['+obj.constructor.name+']'; + } + return (objectName)+'.'+method+'()'; + } + + if (stubFn.name) { + return stubFn.name+'()'; + } + + return '>> '+stubFn.toString()+' <<'; +}; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/index.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/index.js new file mode 100644 index 0000000..64c1977 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/lib/gently/index.js @@ -0,0 +1 @@ +module.exports = require('./gently'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/package.json b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/package.json new file mode 100644 index 0000000..9c1b7a0 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/package.json @@ -0,0 +1,14 @@ +{ + "name": "gently", + "version": "0.9.2", + "directories": { + "lib": "./lib/gently" + }, + "main": "./lib/gently/index", + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": "*" + }, + "optionalDependencies": {} +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/common.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/common.js new file mode 100644 index 0000000..978b5c5 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/common.js @@ -0,0 +1,8 @@ +var path = require('path') + , sys = require('sys'); + +require.paths.unshift(path.dirname(__dirname)+'/lib'); + +global.puts = sys.puts; +global.p = function() {sys.error(sys.inspect.apply(null, arguments))};; +global.assert = require('assert'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/simple/test-gently.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/simple/test-gently.js new file mode 100644 index 0000000..4f8fe2d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/node-gently/test/simple/test-gently.js @@ -0,0 +1,348 @@ +require('../common'); +var Gently = require('gently') + , gently; + +function test(test) { + process.removeAllListeners('exit'); + gently = new Gently(); + test(); +} + +test(function constructor() { + assert.deepEqual(gently.expectations, []); + assert.deepEqual(gently.hijacked, {}); + assert.equal(gently.constructor.name, 'Gently'); +}); + +test(function expectBadArgs() { + var BAD_ARG = 'oh no'; + try { + gently.expect(BAD_ARG); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Bad 1st argument for gently.expect(), object, function, or number expected, got: '+(typeof BAD_ARG)); + } +}); + +test(function expectObjMethod() { + var OBJ = {}, NAME = 'foobar'; + OBJ.foo = function(x) { + return x; + }; + + gently._name = function() { + return NAME; + }; + + var original = OBJ.foo + , stubFn = function() {}; + + (function testAddOne() { + assert.strictEqual(gently.expect(OBJ, 'foo', stubFn), original); + + assert.equal(gently.expectations.length, 1); + var expectation = gently.expectations[0]; + assert.strictEqual(expectation.obj, OBJ); + assert.strictEqual(expectation.method, 'foo'); + assert.strictEqual(expectation.stubFn, stubFn); + assert.strictEqual(expectation.name, NAME); + assert.strictEqual(OBJ.foo._original, original); + })(); + + (function testAddTwo() { + gently.expect(OBJ, 'foo', 2, stubFn); + assert.equal(gently.expectations.length, 2); + assert.strictEqual(OBJ.foo._original, original); + })(); + + (function testAddOneWithoutMock() { + gently.expect(OBJ, 'foo'); + assert.equal(gently.expectations.length, 3); + })(); + + var stubFnCalled = 0, SELF = {}; + gently._stubFn = function(self, obj, method, name, args) { + stubFnCalled++; + assert.strictEqual(self, SELF); + assert.strictEqual(obj, OBJ); + assert.strictEqual(method, 'foo'); + assert.strictEqual(name, NAME); + assert.deepEqual(args, [1, 2]); + return 23; + }; + assert.equal(OBJ.foo.apply(SELF, [1, 2]), 23); + assert.equal(stubFnCalled, 1); +}); + +test(function expectClosure() { + var NAME = 'MY CLOSURE'; + function closureFn() {}; + + gently._name = function() { + return NAME; + }; + + var fn = gently.expect(closureFn); + assert.equal(gently.expectations.length, 1); + var expectation = gently.expectations[0]; + assert.strictEqual(expectation.obj, null); + assert.strictEqual(expectation.method, null); + assert.strictEqual(expectation.stubFn, closureFn); + assert.strictEqual(expectation.name, NAME); + + var stubFnCalled = 0, SELF = {}; + gently._stubFn = function(self, obj, method, name, args) { + stubFnCalled++; + assert.strictEqual(self, SELF); + assert.strictEqual(obj, null); + assert.strictEqual(method, null); + assert.strictEqual(name, NAME); + assert.deepEqual(args, [1, 2]); + return 23; + }; + assert.equal(fn.apply(SELF, [1, 2]), 23); + assert.equal(stubFnCalled, 1); +}); + +test(function expectClosureCount() { + var stubFnCalled = 0; + function closureFn() {stubFnCalled++}; + + var fn = gently.expect(2, closureFn); + assert.equal(gently.expectations.length, 1); + fn(); + assert.equal(gently.expectations.length, 1); + fn(); + assert.equal(stubFnCalled, 2); +}); + +test(function restore() { + var OBJ = {}, NAME = '[my object].myFn()'; + OBJ.foo = function(x) { + return x; + }; + + gently._name = function() { + return NAME; + }; + + var original = OBJ.foo; + gently.expect(OBJ, 'foo'); + gently.restore(OBJ, 'foo'); + assert.strictEqual(OBJ.foo, original); + + (function testError() { + try { + gently.restore(OBJ, 'foo'); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, NAME+' is not gently stubbed'); + } + })(); +}); + +test(function _stubFn() { + var OBJ1 = {toString: function() {return '[OBJ 1]'}} + , OBJ2 = {toString: function() {return '[OBJ 2]'}, foo: function () {return 'bar';}} + , SELF = {}; + + gently.expect(OBJ1, 'foo', function(x) { + assert.strictEqual(this, SELF); + return x * 2; + }); + + assert.equal(gently._stubFn(SELF, OBJ1, 'foo', 'dummy_name', [5]), 10); + + (function testAutorestore() { + assert.equal(OBJ2.foo(), 'bar'); + + gently.expect(OBJ2, 'foo', function() { + return 'stubbed foo'; + }); + + gently.expect(OBJ2, 'foo', function() { + return "didn't restore yet"; + }); + + assert.equal(gently._stubFn(SELF, OBJ2, 'foo', 'dummy_name', []), 'stubbed foo'); + assert.equal(gently._stubFn(SELF, OBJ2, 'foo', 'dummy_name', []), "didn't restore yet"); + assert.equal(OBJ2.foo(), 'bar'); + assert.deepEqual(gently.expectations, []); + })(); + + (function testNoMoreCallExpected() { + try { + gently._stubFn(SELF, OBJ1, 'foo', 'dummy_name', [5]); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Unexpected call to dummy_name, no call was expected'); + } + })(); + + (function testDifferentCallExpected() { + gently.expect(OBJ2, 'bar'); + try { + gently._stubFn(SELF, OBJ1, 'foo', 'dummy_name', [5]); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Unexpected call to dummy_name, expected call to '+gently._name(OBJ2, 'bar')); + } + + assert.equal(gently.expectations.length, 1); + })(); + + (function testNoMockCallback() { + OBJ2.bar(); + assert.equal(gently.expectations.length, 0); + })(); +}); + +test(function stub() { + var LOCATION = './my_class'; + + (function testRegular() { + var Stub = gently.stub(LOCATION); + assert.ok(Stub instanceof Function); + assert.strictEqual(gently.hijacked[LOCATION], Stub); + assert.ok(Stub['new'] instanceof Function); + assert.equal(Stub.toString(), 'require('+JSON.stringify(LOCATION)+')'); + + (function testConstructor() { + var newCalled = 0 + , STUB + , ARGS = ['foo', 'bar']; + + Stub['new'] = function(a, b) { + assert.equal(a, ARGS[0]); + assert.equal(b, ARGS[1]); + newCalled++; + STUB = this; + }; + + var stub = new Stub(ARGS[0], ARGS[1]); + assert.strictEqual(stub, STUB); + assert.equal(newCalled, 1); + assert.equal(stub.toString(), 'require('+JSON.stringify(LOCATION)+')'); + })(); + + (function testUseReturnValueAsInstance() { + var R = {}; + + Stub['new'] = function() { + return R; + }; + + var stub = new Stub(); + assert.strictEqual(stub, R); + + })(); + })(); + + var EXPORTS_NAME = 'MyClass'; + test(function testExportsName() { + var Stub = gently.stub(LOCATION, EXPORTS_NAME); + assert.strictEqual(gently.hijacked[LOCATION][EXPORTS_NAME], Stub); + assert.equal(Stub.toString(), 'require('+JSON.stringify(LOCATION)+').'+EXPORTS_NAME); + + (function testConstructor() { + var stub = new Stub(); + assert.equal(Stub.toString(), 'require('+JSON.stringify(LOCATION)+').'+EXPORTS_NAME); + })(); + }); +}); + +test(function hijack() { + var LOCATION = './foo' + , REQUIRE_CALLS = 0 + , EXPORTS = {} + , REQUIRE = function() { + REQUIRE_CALLS++; + return EXPORTS; + }; + + var hijackedRequire = gently.hijack(REQUIRE); + hijackedRequire(LOCATION); + assert.strictEqual(gently.hijacked[LOCATION], EXPORTS); + + assert.equal(REQUIRE_CALLS, 1); + + // make sure we are caching the hijacked module + hijackedRequire(LOCATION); + assert.equal(REQUIRE_CALLS, 1); +}); + +test(function verify() { + var OBJ = {toString: function() {return '[OBJ]'}}; + gently.verify(); + + gently.expect(OBJ, 'foo'); + try { + gently.verify(); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Expected call to [OBJ].foo() did not happen'); + } + + try { + gently.verify('foo'); + assert.ok(false, 'throw needs to happen'); + } catch (e) { + assert.equal(e.message, 'Expected call to [OBJ].foo() did not happen (foo)'); + } +}); + +test(function processExit() { + var verifyCalled = 0; + gently.verify = function(msg) { + verifyCalled++; + assert.equal(msg, 'process exit'); + }; + + process.emit('exit'); + assert.equal(verifyCalled, 1); +}); + +test(function _name() { + (function testNamedClass() { + function Foo() {}; + var foo = new Foo(); + assert.equal(gently._name(foo, 'bar'), '[Foo].bar()'); + })(); + + (function testToStringPreference() { + function Foo() {}; + Foo.prototype.toString = function() { + return '[Superman 123]'; + }; + var foo = new Foo(); + assert.equal(gently._name(foo, 'bar'), '[Superman 123].bar()'); + })(); + + (function testUnamedClass() { + var Foo = function() {}; + var foo = new Foo(); + assert.equal(gently._name(foo, 'bar'), foo.toString()+'.bar()'); + })(); + + (function testNamedClosure() { + function myClosure() {}; + assert.equal(gently._name(null, null, myClosure), myClosure.name+'()'); + })(); + + (function testUnamedClosure() { + var myClosure = function() {2+2 == 5}; + assert.equal(gently._name(null, null, myClosure), '>> '+myClosure.toString()+' <<'); + })(); +}); + +test(function verifyExpectNone() { + var OBJ = {toString: function() {return '[OBJ]'}}; + gently.verify(); + + gently.expect(OBJ, 'foo', 0); + try { + gently.verify(); + } catch (e) { + assert.fail('Exception should not have been thrown'); + } +}); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/package.json b/dist/node_modules/express/node_modules/connect/node_modules/formidable/package.json new file mode 100644 index 0000000..4d94297 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/package.json @@ -0,0 +1,23 @@ +{ + "name": "formidable", + "version": "1.0.11", + "dependencies": {}, + "devDependencies": { + "gently": "0.8.0", + "findit": "0.1.1", + "hashish": "0.0.4", + "urun": "0.0.4", + "utest": "0.0.3" + }, + "directories": { + "lib": "./lib" + }, + "main": "./lib/index", + "scripts": { + "test": "make test" + }, + "engines": { + "node": "*" + }, + "optionalDependencies": {} +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js new file mode 100644 index 0000000..eb432ad --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/common.js @@ -0,0 +1,19 @@ +var mysql = require('..'); +var path = require('path'); + +var root = path.join(__dirname, '../'); +exports.dir = { + root : root, + lib : root + '/lib', + fixture : root + '/test/fixture', + tmp : root + '/test/tmp', +}; + +exports.port = 13532; + +exports.formidable = require('..'); +exports.assert = require('assert'); + +exports.require = function(lib) { + return require(exports.dir.lib + '/' + lib); +}; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt new file mode 100644 index 0000000..e7a4785 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/funkyfilename.txt @@ -0,0 +1 @@ +I am a text file with a funky name! diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt new file mode 100644 index 0000000..9b6903e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/file/plain.txt @@ -0,0 +1 @@ +I am a plain text file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md new file mode 100644 index 0000000..3c9dbe3 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/info.md @@ -0,0 +1,3 @@ +* Opera does not allow submitting this file, it shows a warning to the + user that the file could not be found instead. Tested in 9.8, 11.51 on OSX. + Reported to Opera on 08.09.2011 (tracking email DSK-346009@bugs.opera.com). diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js new file mode 100644 index 0000000..0bae449 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/no-filename.js @@ -0,0 +1,3 @@ +module.exports['generic.http'] = [ + {type: 'file', name: 'upload', filename: '', fixture: 'plain.txt'}, +]; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js new file mode 100644 index 0000000..eb76fdc --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/js/special-chars-in-filename.js @@ -0,0 +1,21 @@ +var properFilename = 'funkyfilename.txt'; + +function expect(filename) { + return [ + {type: 'field', name: 'title', value: 'Weird filename'}, + {type: 'file', name: 'upload', filename: filename, fixture: properFilename}, + ]; +}; + +var webkit = " ? % * | \" < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"; +var ffOrIe = " ? % * | \" < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"; + +module.exports = { + 'osx-chrome-13.http' : expect(webkit), + 'osx-firefox-3.6.http' : expect(ffOrIe), + 'osx-safari-5.http' : expect(webkit), + 'xp-chrome-12.http' : expect(webkit), + 'xp-ie-7.http' : expect(ffOrIe), + 'xp-ie-8.http' : expect(ffOrIe), + 'xp-safari-5.http' : expect(webkit), +}; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js new file mode 100644 index 0000000..a476169 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multipart.js @@ -0,0 +1,72 @@ +exports['rfc1867'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--\r\n', + parts: + [ { headers: { + 'content-disposition': 'form-data; name="field1"', + }, + data: 'Joe Blow\r\nalmost tricked you!', + }, + { headers: { + 'content-disposition': 'form-data; name="pics"; filename="file1.txt"', + 'Content-Type': 'text/plain', + }, + data: '... contents of file1.txt ...\r', + } + ] + }; + +exports['noTrailing\r\n'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--', + parts: + [ { headers: { + 'content-disposition': 'form-data; name="field1"', + }, + data: 'Joe Blow\r\nalmost tricked you!', + }, + { headers: { + 'content-disposition': 'form-data; name="pics"; filename="file1.txt"', + 'Content-Type': 'text/plain', + }, + data: '... contents of file1.txt ...\r', + } + ] + }; + +exports['emptyHeader'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + ': foo\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--\r\n', + expectError: true, + }; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js new file mode 100644 index 0000000..66ad259 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/integration/test-fixtures.js @@ -0,0 +1,89 @@ +var hashish = require('hashish'); +var fs = require('fs'); +var findit = require('findit'); +var path = require('path'); +var http = require('http'); +var net = require('net'); +var assert = require('assert'); + +var common = require('../common'); +var formidable = common.formidable; + +var server = http.createServer(); +server.listen(common.port, findFixtures); + +function findFixtures() { + var fixtures = []; + findit + .sync(common.dir.fixture + '/js') + .forEach(function(jsPath) { + if (!/\.js$/.test(jsPath)) return; + + var group = path.basename(jsPath, '.js'); + hashish.forEach(require(jsPath), function(fixture, name) { + fixtures.push({ + name : group + '/' + name, + fixture : fixture, + }); + }); + }); + + testNext(fixtures); +} + +function testNext(fixtures) { + var fixture = fixtures.shift(); + if (!fixture) return server.close(); + + var name = fixture.name; + var fixture = fixture.fixture; + + uploadFixture(name, function(err, parts) { + if (err) throw err; + + fixture.forEach(function(expectedPart, i) { + var parsedPart = parts[i]; + assert.equal(parsedPart.type, expectedPart.type); + assert.equal(parsedPart.name, expectedPart.name); + + if (parsedPart.type === 'file') { + var filename = parsedPart.value.name; + assert.equal(filename, expectedPart.filename); + } + }); + + testNext(fixtures); + }); +}; + +function uploadFixture(name, cb) { + server.once('request', function(req, res) { + var form = new formidable.IncomingForm(); + form.uploadDir = common.dir.tmp; + form.parse(req); + + function callback() { + var realCallback = cb; + cb = function() {}; + realCallback.apply(null, arguments); + } + + var parts = []; + form + .on('error', callback) + .on('fileBegin', function(name, value) { + parts.push({type: 'file', name: name, value: value}); + }) + .on('field', function(name, value) { + parts.push({type: 'field', name: name, value: value}); + }) + .on('end', function() { + callback(null, parts); + }); + }); + + var socket = net.createConnection(common.port); + var file = fs.createReadStream(common.dir.fixture + '/http/' + name); + + file.pipe(socket); +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js new file mode 100644 index 0000000..2b98598 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/common.js @@ -0,0 +1,24 @@ +var path = require('path'), + fs = require('fs'); + +try { + global.Gently = require('gently'); +} catch (e) { + throw new Error('this test suite requires node-gently'); +} + +exports.lib = path.join(__dirname, '../../lib'); + +global.GENTLY = new Gently(); + +global.assert = require('assert'); +global.TEST_PORT = 13532; +global.TEST_FIXTURES = path.join(__dirname, '../fixture'); +global.TEST_TMP = path.join(__dirname, '../tmp'); + +// Stupid new feature in node that complains about gently attaching too many +// listeners to process 'exit'. This is a workaround until I can think of a +// better way to deal with this. +if (process.setMaxListeners) { + process.setMaxListeners(10000); +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js new file mode 100644 index 0000000..75232aa --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/integration/test-multipart-parser.js @@ -0,0 +1,80 @@ +var common = require('../common'); +var CHUNK_LENGTH = 10, + multipartParser = require(common.lib + '/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + parser = new MultipartParser(), + fixtures = require(TEST_FIXTURES + '/multipart'), + Buffer = require('buffer').Buffer; + +Object.keys(fixtures).forEach(function(name) { + var fixture = fixtures[name], + buffer = new Buffer(Buffer.byteLength(fixture.raw, 'binary')), + offset = 0, + chunk, + nparsed, + + parts = [], + part = null, + headerField, + headerValue, + endCalled = ''; + + parser.initWithBoundary(fixture.boundary); + parser.onPartBegin = function() { + part = {headers: {}, data: ''}; + parts.push(part); + headerField = ''; + headerValue = ''; + }; + + parser.onHeaderField = function(b, start, end) { + headerField += b.toString('ascii', start, end); + }; + + parser.onHeaderValue = function(b, start, end) { + headerValue += b.toString('ascii', start, end); + } + + parser.onHeaderEnd = function() { + part.headers[headerField] = headerValue; + headerField = ''; + headerValue = ''; + }; + + parser.onPartData = function(b, start, end) { + var str = b.toString('ascii', start, end); + part.data += b.slice(start, end); + } + + parser.onEnd = function() { + endCalled = true; + } + + buffer.write(fixture.raw, 'binary', 0); + + while (offset < buffer.length) { + if (offset + CHUNK_LENGTH < buffer.length) { + chunk = buffer.slice(offset, offset+CHUNK_LENGTH); + } else { + chunk = buffer.slice(offset, buffer.length); + } + offset = offset + CHUNK_LENGTH; + + nparsed = parser.write(chunk); + if (nparsed != chunk.length) { + if (fixture.expectError) { + return; + } + puts('-- ERROR --'); + p(chunk.toString('ascii')); + throw new Error(chunk.length+' bytes written, but only '+nparsed+' bytes parsed!'); + } + } + + if (fixture.expectError) { + throw new Error('expected parse error did not happen'); + } + + assert.ok(endCalled); + assert.deepEqual(parts, fixture.parts); +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js new file mode 100644 index 0000000..52ceedb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-file.js @@ -0,0 +1,104 @@ +var common = require('../common'); +var WriteStreamStub = GENTLY.stub('fs', 'WriteStream'); + +var File = require(common.lib + '/file'), + EventEmitter = require('events').EventEmitter, + file, + gently; + +function test(test) { + gently = new Gently(); + file = new File(); + test(); + gently.verify(test.name); +} + +test(function constructor() { + assert.ok(file instanceof EventEmitter); + assert.strictEqual(file.size, 0); + assert.strictEqual(file.path, null); + assert.strictEqual(file.name, null); + assert.strictEqual(file.type, null); + assert.strictEqual(file.lastModifiedDate, null); + + assert.strictEqual(file._writeStream, null); + + (function testSetProperties() { + var file2 = new File({foo: 'bar'}); + assert.equal(file2.foo, 'bar'); + })(); +}); + +test(function open() { + var WRITE_STREAM; + file.path = '/foo'; + + gently.expect(WriteStreamStub, 'new', function (path) { + WRITE_STREAM = this; + assert.strictEqual(path, file.path); + }); + + file.open(); + assert.strictEqual(file._writeStream, WRITE_STREAM); +}); + +test(function write() { + var BUFFER = {length: 10}, + CB_STUB, + CB = function() { + CB_STUB.apply(this, arguments); + }; + + file._writeStream = {}; + + gently.expect(file._writeStream, 'write', function (buffer, cb) { + assert.strictEqual(buffer, BUFFER); + + gently.expect(file, 'emit', function (event, bytesWritten) { + assert.ok(file.lastModifiedDate instanceof Date); + assert.equal(event, 'progress'); + assert.equal(bytesWritten, file.size); + }); + + CB_STUB = gently.expect(function writeCb() { + assert.equal(file.size, 10); + }); + + cb(); + + gently.expect(file, 'emit', function (event, bytesWritten) { + assert.equal(event, 'progress'); + assert.equal(bytesWritten, file.size); + }); + + CB_STUB = gently.expect(function writeCb() { + assert.equal(file.size, 20); + }); + + cb(); + }); + + file.write(BUFFER, CB); +}); + +test(function end() { + var CB_STUB, + CB = function() { + CB_STUB.apply(this, arguments); + }; + + file._writeStream = {}; + + gently.expect(file._writeStream, 'end', function (cb) { + gently.expect(file, 'emit', function (event) { + assert.equal(event, 'end'); + }); + + CB_STUB = gently.expect(function endCb() { + }); + + cb(); + }); + + file.end(CB); +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js new file mode 100644 index 0000000..84de439 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js @@ -0,0 +1,727 @@ +var common = require('../common'); +var MultipartParserStub = GENTLY.stub('./multipart_parser', 'MultipartParser'), + QuerystringParserStub = GENTLY.stub('./querystring_parser', 'QuerystringParser'), + EventEmitterStub = GENTLY.stub('events', 'EventEmitter'), + StreamStub = GENTLY.stub('stream', 'Stream'), + FileStub = GENTLY.stub('./file'); + +var formidable = require(common.lib + '/index'), + IncomingForm = formidable.IncomingForm, + events = require('events'), + fs = require('fs'), + path = require('path'), + Buffer = require('buffer').Buffer, + fixtures = require(TEST_FIXTURES + '/multipart'), + form, + gently; + +function test(test) { + gently = new Gently(); + gently.expect(EventEmitterStub, 'call'); + form = new IncomingForm(); + test(); + gently.verify(test.name); +} + +test(function constructor() { + assert.strictEqual(form.error, null); + assert.strictEqual(form.ended, false); + assert.strictEqual(form.type, null); + assert.strictEqual(form.headers, null); + assert.strictEqual(form.keepExtensions, false); + assert.strictEqual(form.uploadDir, '/tmp'); + assert.strictEqual(form.encoding, 'utf-8'); + assert.strictEqual(form.bytesReceived, null); + assert.strictEqual(form.bytesExpected, null); + assert.strictEqual(form.maxFieldsSize, 2 * 1024 * 1024); + assert.strictEqual(form._parser, null); + assert.strictEqual(form._flushing, 0); + assert.strictEqual(form._fieldsSize, 0); + assert.ok(form instanceof EventEmitterStub); + assert.equal(form.constructor.name, 'IncomingForm'); + + (function testSimpleConstructor() { + gently.expect(EventEmitterStub, 'call'); + var form = IncomingForm(); + assert.ok(form instanceof IncomingForm); + })(); + + (function testSimpleConstructorShortcut() { + gently.expect(EventEmitterStub, 'call'); + var form = formidable(); + assert.ok(form instanceof IncomingForm); + })(); +}); + +test(function parse() { + var REQ = {headers: {}} + , emit = {}; + + gently.expect(form, 'writeHeaders', function(headers) { + assert.strictEqual(headers, REQ.headers); + }); + + var events = ['error', 'aborted', 'data', 'end']; + gently.expect(REQ, 'on', events.length, function(event, fn) { + assert.equal(event, events.shift()); + emit[event] = fn; + return this; + }); + + form.parse(REQ); + + (function testPause() { + gently.expect(REQ, 'pause'); + assert.strictEqual(form.pause(), true); + })(); + + (function testPauseCriticalException() { + form.ended = false; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'pause', function() { + throw ERR; + }); + + gently.expect(form, '_error', function(err) { + assert.strictEqual(err, ERR); + }); + + assert.strictEqual(form.pause(), false); + })(); + + (function testPauseHarmlessException() { + form.ended = true; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'pause', function() { + throw ERR; + }); + + assert.strictEqual(form.pause(), false); + })(); + + (function testResume() { + gently.expect(REQ, 'resume'); + assert.strictEqual(form.resume(), true); + })(); + + (function testResumeCriticalException() { + form.ended = false; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'resume', function() { + throw ERR; + }); + + gently.expect(form, '_error', function(err) { + assert.strictEqual(err, ERR); + }); + + assert.strictEqual(form.resume(), false); + })(); + + (function testResumeHarmlessException() { + form.ended = true; + + var ERR = new Error('dasdsa'); + gently.expect(REQ, 'resume', function() { + throw ERR; + }); + + assert.strictEqual(form.resume(), false); + })(); + + (function testEmitError() { + var ERR = new Error('something bad happened'); + gently.expect(form, '_error',function(err) { + assert.strictEqual(err, ERR); + }); + emit.error(ERR); + })(); + + (function testEmitAborted() { + gently.expect(form, 'emit',function(event) { + assert.equal(event, 'aborted'); + }); + + emit.aborted(); + })(); + + + (function testEmitData() { + var BUFFER = [1, 2, 3]; + gently.expect(form, 'write', function(buffer) { + assert.strictEqual(buffer, BUFFER); + }); + emit.data(BUFFER); + })(); + + (function testEmitEnd() { + form._parser = {}; + + (function testWithError() { + var ERR = new Error('haha'); + gently.expect(form._parser, 'end', function() { + return ERR; + }); + + gently.expect(form, '_error', function(err) { + assert.strictEqual(err, ERR); + }); + + emit.end(); + })(); + + (function testWithoutError() { + gently.expect(form._parser, 'end'); + emit.end(); + })(); + + (function testAfterError() { + form.error = true; + emit.end(); + })(); + })(); + + (function testWithCallback() { + gently.expect(EventEmitterStub, 'call'); + var form = new IncomingForm(), + REQ = {headers: {}}, + parseCalled = 0; + + gently.expect(form, 'writeHeaders'); + gently.expect(REQ, 'on', 4, function() { + return this; + }); + + gently.expect(form, 'on', 4, function(event, fn) { + if (event == 'field') { + fn('field1', 'foo'); + fn('field1', 'bar'); + fn('field2', 'nice'); + } + + if (event == 'file') { + fn('file1', '1'); + fn('file1', '2'); + fn('file2', '3'); + } + + if (event == 'end') { + fn(); + } + return this; + }); + + form.parse(REQ, gently.expect(function parseCbOk(err, fields, files) { + assert.deepEqual(fields, {field1: 'bar', field2: 'nice'}); + assert.deepEqual(files, {file1: '2', file2: '3'}); + })); + + gently.expect(form, 'writeHeaders'); + gently.expect(REQ, 'on', 4, function() { + return this; + }); + + var ERR = new Error('test'); + gently.expect(form, 'on', 3, function(event, fn) { + if (event == 'field') { + fn('foo', 'bar'); + } + + if (event == 'error') { + fn(ERR); + gently.expect(form, 'on'); + } + return this; + }); + + form.parse(REQ, gently.expect(function parseCbErr(err, fields, files) { + assert.strictEqual(err, ERR); + assert.deepEqual(fields, {foo: 'bar'}); + })); + })(); +}); + +test(function pause() { + assert.strictEqual(form.pause(), false); +}); + +test(function resume() { + assert.strictEqual(form.resume(), false); +}); + + +test(function writeHeaders() { + var HEADERS = {}; + gently.expect(form, '_parseContentLength'); + gently.expect(form, '_parseContentType'); + + form.writeHeaders(HEADERS); + assert.strictEqual(form.headers, HEADERS); +}); + +test(function write() { + var parser = {}, + BUFFER = [1, 2, 3]; + + form._parser = parser; + form.bytesExpected = 523423; + + (function testBasic() { + gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) { + assert.equal(event, 'progress'); + assert.equal(bytesReceived, BUFFER.length); + assert.equal(bytesExpected, form.bytesExpected); + }); + + gently.expect(parser, 'write', function(buffer) { + assert.strictEqual(buffer, BUFFER); + return buffer.length; + }); + + assert.equal(form.write(BUFFER), BUFFER.length); + assert.equal(form.bytesReceived, BUFFER.length); + })(); + + (function testParserError() { + gently.expect(form, 'emit'); + + gently.expect(parser, 'write', function(buffer) { + assert.strictEqual(buffer, BUFFER); + return buffer.length - 1; + }); + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/parser error/i)); + }); + + assert.equal(form.write(BUFFER), BUFFER.length - 1); + assert.equal(form.bytesReceived, BUFFER.length + BUFFER.length); + })(); + + (function testUninitialized() { + delete form._parser; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/unintialized parser/i)); + }); + form.write(BUFFER); + })(); +}); + +test(function parseContentType() { + var HEADERS = {}; + + form.headers = {'content-type': 'application/x-www-form-urlencoded'}; + gently.expect(form, '_initUrlencoded'); + form._parseContentType(); + + // accept anything that has 'urlencoded' in it + form.headers = {'content-type': 'broken-client/urlencoded-stupid'}; + gently.expect(form, '_initUrlencoded'); + form._parseContentType(); + + var BOUNDARY = '---------------------------57814261102167618332366269'; + form.headers = {'content-type': 'multipart/form-data; boundary='+BOUNDARY}; + + gently.expect(form, '_initMultipart', function(boundary) { + assert.equal(boundary, BOUNDARY); + }); + form._parseContentType(); + + (function testQuotedBoundary() { + form.headers = {'content-type': 'multipart/form-data; boundary="' + BOUNDARY + '"'}; + + gently.expect(form, '_initMultipart', function(boundary) { + assert.equal(boundary, BOUNDARY); + }); + form._parseContentType(); + })(); + + (function testNoBoundary() { + form.headers = {'content-type': 'multipart/form-data'}; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/no multipart boundary/i)); + }); + form._parseContentType(); + })(); + + (function testNoContentType() { + form.headers = {}; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/no content-type/i)); + }); + form._parseContentType(); + })(); + + (function testUnknownContentType() { + form.headers = {'content-type': 'invalid'}; + + gently.expect(form, '_error', function(err) { + assert.ok(err.message.match(/unknown content-type/i)); + }); + form._parseContentType(); + })(); +}); + +test(function parseContentLength() { + var HEADERS = {}; + + form.headers = {}; + form._parseContentLength(); + assert.strictEqual(form.bytesReceived, null); + assert.strictEqual(form.bytesExpected, null); + + form.headers['content-length'] = '8'; + gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) { + assert.equal(event, 'progress'); + assert.equal(bytesReceived, 0); + assert.equal(bytesExpected, 8); + }); + form._parseContentLength(); + assert.strictEqual(form.bytesReceived, 0); + assert.strictEqual(form.bytesExpected, 8); + + // JS can be evil, lets make sure we are not + form.headers['content-length'] = '08'; + gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) { + assert.equal(event, 'progress'); + assert.equal(bytesReceived, 0); + assert.equal(bytesExpected, 8); + }); + form._parseContentLength(); + assert.strictEqual(form.bytesExpected, 8); +}); + +test(function _initMultipart() { + var BOUNDARY = '123', + PARSER; + + gently.expect(MultipartParserStub, 'new', function() { + PARSER = this; + }); + + gently.expect(MultipartParserStub.prototype, 'initWithBoundary', function(boundary) { + assert.equal(boundary, BOUNDARY); + }); + + form._initMultipart(BOUNDARY); + assert.equal(form.type, 'multipart'); + assert.strictEqual(form._parser, PARSER); + + (function testRegularField() { + var PART; + gently.expect(StreamStub, 'new', function() { + PART = this; + }); + + gently.expect(form, 'onPart', function(part) { + assert.strictEqual(part, PART); + assert.deepEqual + ( part.headers + , { 'content-disposition': 'form-data; name="field1"' + , 'foo': 'bar' + } + ); + assert.equal(part.name, 'field1'); + + var strings = ['hello', ' world']; + gently.expect(part, 'emit', 2, function(event, b) { + assert.equal(event, 'data'); + assert.equal(b.toString(), strings.shift()); + }); + + gently.expect(part, 'emit', function(event, b) { + assert.equal(event, 'end'); + }); + }); + + PARSER.onPartBegin(); + PARSER.onHeaderField(new Buffer('content-disposition'), 0, 10); + PARSER.onHeaderField(new Buffer('content-disposition'), 10, 19); + PARSER.onHeaderValue(new Buffer('form-data; name="field1"'), 0, 14); + PARSER.onHeaderValue(new Buffer('form-data; name="field1"'), 14, 24); + PARSER.onHeaderEnd(); + PARSER.onHeaderField(new Buffer('foo'), 0, 3); + PARSER.onHeaderValue(new Buffer('bar'), 0, 3); + PARSER.onHeaderEnd(); + PARSER.onHeadersEnd(); + PARSER.onPartData(new Buffer('hello world'), 0, 5); + PARSER.onPartData(new Buffer('hello world'), 5, 11); + PARSER.onPartEnd(); + })(); + + (function testFileField() { + var PART; + gently.expect(StreamStub, 'new', function() { + PART = this; + }); + + gently.expect(form, 'onPart', function(part) { + assert.deepEqual + ( part.headers + , { 'content-disposition': 'form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"' + , 'content-type': 'text/plain' + } + ); + assert.equal(part.name, 'field2'); + assert.equal(part.filename, 'Sun"et.jpg'); + assert.equal(part.mime, 'text/plain'); + + gently.expect(part, 'emit', function(event, b) { + assert.equal(event, 'data'); + assert.equal(b.toString(), '... contents of file1.txt ...'); + }); + + gently.expect(part, 'emit', function(event, b) { + assert.equal(event, 'end'); + }); + }); + + PARSER.onPartBegin(); + PARSER.onHeaderField(new Buffer('content-disposition'), 0, 19); + PARSER.onHeaderValue(new Buffer('form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"'), 0, 85); + PARSER.onHeaderEnd(); + PARSER.onHeaderField(new Buffer('Content-Type'), 0, 12); + PARSER.onHeaderValue(new Buffer('text/plain'), 0, 10); + PARSER.onHeaderEnd(); + PARSER.onHeadersEnd(); + PARSER.onPartData(new Buffer('... contents of file1.txt ...'), 0, 29); + PARSER.onPartEnd(); + })(); + + (function testEnd() { + gently.expect(form, '_maybeEnd'); + PARSER.onEnd(); + assert.ok(form.ended); + })(); +}); + +test(function _fileName() { + // TODO + return; +}); + +test(function _initUrlencoded() { + var PARSER; + + gently.expect(QuerystringParserStub, 'new', function() { + PARSER = this; + }); + + form._initUrlencoded(); + assert.equal(form.type, 'urlencoded'); + assert.strictEqual(form._parser, PARSER); + + (function testOnField() { + var KEY = 'KEY', VAL = 'VAL'; + gently.expect(form, 'emit', function(field, key, val) { + assert.equal(field, 'field'); + assert.equal(key, KEY); + assert.equal(val, VAL); + }); + + PARSER.onField(KEY, VAL); + })(); + + (function testOnEnd() { + gently.expect(form, '_maybeEnd'); + + PARSER.onEnd(); + assert.equal(form.ended, true); + })(); +}); + +test(function _error() { + var ERR = new Error('bla'); + + gently.expect(form, 'pause'); + gently.expect(form, 'emit', function(event, err) { + assert.equal(event, 'error'); + assert.strictEqual(err, ERR); + }); + + form._error(ERR); + assert.strictEqual(form.error, ERR); + + // make sure _error only does its thing once + form._error(ERR); +}); + +test(function onPart() { + var PART = {}; + gently.expect(form, 'handlePart', function(part) { + assert.strictEqual(part, PART); + }); + + form.onPart(PART); +}); + +test(function handlePart() { + (function testUtf8Field() { + var PART = new events.EventEmitter(); + PART.name = 'my_field'; + + gently.expect(form, 'emit', function(event, field, value) { + assert.equal(event, 'field'); + assert.equal(field, 'my_field'); + assert.equal(value, 'hello world: €'); + }); + + form.handlePart(PART); + PART.emit('data', new Buffer('hello')); + PART.emit('data', new Buffer(' world: ')); + PART.emit('data', new Buffer([0xE2])); + PART.emit('data', new Buffer([0x82, 0xAC])); + PART.emit('end'); + })(); + + (function testBinaryField() { + var PART = new events.EventEmitter(); + PART.name = 'my_field2'; + + gently.expect(form, 'emit', function(event, field, value) { + assert.equal(event, 'field'); + assert.equal(field, 'my_field2'); + assert.equal(value, 'hello world: '+new Buffer([0xE2, 0x82, 0xAC]).toString('binary')); + }); + + form.encoding = 'binary'; + form.handlePart(PART); + PART.emit('data', new Buffer('hello')); + PART.emit('data', new Buffer(' world: ')); + PART.emit('data', new Buffer([0xE2])); + PART.emit('data', new Buffer([0x82, 0xAC])); + PART.emit('end'); + })(); + + (function testFieldSize() { + form.maxFieldsSize = 8; + var PART = new events.EventEmitter(); + PART.name = 'my_field'; + + gently.expect(form, '_error', function(err) { + assert.equal(err.message, 'maxFieldsSize exceeded, received 9 bytes of field data'); + }); + + form.handlePart(PART); + form._fieldsSize = 1; + PART.emit('data', new Buffer(7)); + PART.emit('data', new Buffer(1)); + })(); + + (function testFilePart() { + var PART = new events.EventEmitter(), + FILE = new events.EventEmitter(), + PATH = '/foo/bar'; + + PART.name = 'my_file'; + PART.filename = 'sweet.txt'; + PART.mime = 'sweet.txt'; + + gently.expect(form, '_uploadPath', function(filename) { + assert.equal(filename, PART.filename); + return PATH; + }); + + gently.expect(FileStub, 'new', function(properties) { + assert.equal(properties.path, PATH); + assert.equal(properties.name, PART.filename); + assert.equal(properties.type, PART.mime); + FILE = this; + + gently.expect(form, 'emit', function (event, field, file) { + assert.equal(event, 'fileBegin'); + assert.strictEqual(field, PART.name); + assert.strictEqual(file, FILE); + }); + + gently.expect(FILE, 'open'); + }); + + form.handlePart(PART); + assert.equal(form._flushing, 1); + + var BUFFER; + gently.expect(form, 'pause'); + gently.expect(FILE, 'write', function(buffer, cb) { + assert.strictEqual(buffer, BUFFER); + gently.expect(form, 'resume'); + // @todo handle cb(new Err) + cb(); + }); + + PART.emit('data', BUFFER = new Buffer('test')); + + gently.expect(FILE, 'end', function(cb) { + gently.expect(form, 'emit', function(event, field, file) { + assert.equal(event, 'file'); + assert.strictEqual(file, FILE); + }); + + gently.expect(form, '_maybeEnd'); + + cb(); + assert.equal(form._flushing, 0); + }); + + PART.emit('end'); + })(); +}); + +test(function _uploadPath() { + (function testUniqueId() { + var UUID_A, UUID_B; + gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) { + assert.equal(uploadDir, form.uploadDir); + UUID_A = uuid; + }); + form._uploadPath(); + + gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) { + UUID_B = uuid; + }); + form._uploadPath(); + + assert.notEqual(UUID_A, UUID_B); + })(); + + (function testFileExtension() { + form.keepExtensions = true; + var FILENAME = 'foo.jpg', + EXT = '.bar'; + + gently.expect(GENTLY.hijacked.path, 'extname', function(filename) { + assert.equal(filename, FILENAME); + gently.restore(path, 'extname'); + + return EXT; + }); + + gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, name) { + assert.equal(path.extname(name), EXT); + }); + form._uploadPath(FILENAME); + })(); +}); + +test(function _maybeEnd() { + gently.expect(form, 'emit', 0); + form._maybeEnd(); + + form.ended = true; + form._flushing = 1; + form._maybeEnd(); + + gently.expect(form, 'emit', function(event) { + assert.equal(event, 'end'); + }); + + form.ended = true; + form._flushing = 0; + form._maybeEnd(); +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js new file mode 100644 index 0000000..d8dc968 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-multipart-parser.js @@ -0,0 +1,50 @@ +var common = require('../common'); +var multipartParser = require(common.lib + '/multipart_parser'), + MultipartParser = multipartParser.MultipartParser, + events = require('events'), + Buffer = require('buffer').Buffer, + parser; + +function test(test) { + parser = new MultipartParser(); + test(); +} + +test(function constructor() { + assert.equal(parser.boundary, null); + assert.equal(parser.state, 0); + assert.equal(parser.flags, 0); + assert.equal(parser.boundaryChars, null); + assert.equal(parser.index, null); + assert.equal(parser.lookbehind, null); + assert.equal(parser.constructor.name, 'MultipartParser'); +}); + +test(function initWithBoundary() { + var boundary = 'abc'; + parser.initWithBoundary(boundary); + assert.deepEqual(Array.prototype.slice.call(parser.boundary), [13, 10, 45, 45, 97, 98, 99]); + assert.equal(parser.state, multipartParser.START); + + assert.deepEqual(parser.boundaryChars, {10: true, 13: true, 45: true, 97: true, 98: true, 99: true}); +}); + +test(function parserError() { + var boundary = 'abc', + buffer = new Buffer(5); + + parser.initWithBoundary(boundary); + buffer.write('--ad', 'ascii', 0); + assert.equal(parser.write(buffer), 3); +}); + +test(function end() { + (function testError() { + assert.equal(parser.end().message, 'MultipartParser.end(): stream ended unexpectedly: ' + parser.explain()); + })(); + + (function testRegular() { + parser.state = multipartParser.END; + assert.strictEqual(parser.end(), undefined); + })(); +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js new file mode 100644 index 0000000..54d3e2d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-querystring-parser.js @@ -0,0 +1,45 @@ +var common = require('../common'); +var QuerystringParser = require(common.lib + '/querystring_parser').QuerystringParser, + Buffer = require('buffer').Buffer, + gently, + parser; + +function test(test) { + gently = new Gently(); + parser = new QuerystringParser(); + test(); + gently.verify(test.name); +} + +test(function constructor() { + assert.equal(parser.buffer, ''); + assert.equal(parser.constructor.name, 'QuerystringParser'); +}); + +test(function write() { + var a = new Buffer('a=1'); + assert.equal(parser.write(a), a.length); + + var b = new Buffer('&b=2'); + parser.write(b); + assert.equal(parser.buffer, a + b); +}); + +test(function end() { + var FIELDS = {a: ['b', {c: 'd'}], e: 'f'}; + + gently.expect(GENTLY.hijacked.querystring, 'parse', function(str) { + assert.equal(str, parser.buffer); + return FIELDS; + }); + + gently.expect(parser, 'onField', Object.keys(FIELDS).length, function(key, val) { + assert.deepEqual(FIELDS[key], val); + }); + + gently.expect(parser, 'onEnd'); + + parser.buffer = 'my buffer'; + parser.end(); + assert.equal(parser.buffer, ''); +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js new file mode 100644 index 0000000..479e46d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/system/test-multi-video-upload.js @@ -0,0 +1,75 @@ +var common = require('../common'); +var BOUNDARY = '---------------------------10102754414578508781458777923', + FIXTURE = TEST_FIXTURES+'/multi_video.upload', + fs = require('fs'), + util = require(common.lib + '/util'), + http = require('http'), + formidable = require(common.lib + '/index'), + server = http.createServer(); + +server.on('request', function(req, res) { + var form = new formidable.IncomingForm(), + uploads = {}; + + form.uploadDir = TEST_TMP; + form.hash = 'sha1'; + form.parse(req); + + form + .on('fileBegin', function(field, file) { + assert.equal(field, 'upload'); + + var tracker = {file: file, progress: [], ended: false}; + uploads[file.filename] = tracker; + file + .on('progress', function(bytesReceived) { + tracker.progress.push(bytesReceived); + assert.equal(bytesReceived, file.length); + }) + .on('end', function() { + tracker.ended = true; + }); + }) + .on('field', function(field, value) { + assert.equal(field, 'title'); + assert.equal(value, ''); + }) + .on('file', function(field, file) { + assert.equal(field, 'upload'); + assert.strictEqual(uploads[file.filename].file, file); + }) + .on('end', function() { + assert.ok(uploads['shortest_video.flv']); + assert.ok(uploads['shortest_video.flv'].ended); + assert.ok(uploads['shortest_video.flv'].progress.length > 3); + assert.equal(uploads['shortest_video.flv'].file.hash, 'd6a17616c7143d1b1438ceeef6836d1a09186b3a'); + assert.equal(uploads['shortest_video.flv'].progress.slice(-1), uploads['shortest_video.flv'].file.length); + assert.ok(uploads['shortest_video.mp4']); + assert.ok(uploads['shortest_video.mp4'].ended); + assert.ok(uploads['shortest_video.mp4'].progress.length > 3); + assert.equal(uploads['shortest_video.mp4'].file.hash, '937dfd4db263f4887ceae19341dcc8d63bcd557f'); + + server.close(); + res.writeHead(200); + res.end('good'); + }); +}); + +server.listen(TEST_PORT, function() { + var client = http.createClient(TEST_PORT), + stat = fs.statSync(FIXTURE), + headers = { + 'content-type': 'multipart/form-data; boundary='+BOUNDARY, + 'content-length': stat.size, + } + request = client.request('POST', '/', headers), + fixture = new fs.ReadStream(FIXTURE); + + fixture + .on('data', function(b) { + request.write(b); + }) + .on('end', function() { + request.end(); + }); +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js new file mode 100755 index 0000000..50b2361 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/run.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('urun')(__dirname) diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js new file mode 100644 index 0000000..fe2ac1c --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/test/unit/test-incoming-form.js @@ -0,0 +1,63 @@ +var common = require('../common'); +var test = require('utest'); +var assert = common.assert; +var IncomingForm = common.require('incoming_form').IncomingForm; +var path = require('path'); + +var form; +test('IncomingForm', { + before: function() { + form = new IncomingForm(); + }, + + '#_fileName with regular characters': function() { + var filename = 'foo.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'foo.txt'); + }, + + '#_fileName with unescaped quote': function() { + var filename = 'my".txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my".txt'); + }, + + '#_fileName with escaped quote': function() { + var filename = 'my%22.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my".txt'); + }, + + '#_fileName with bad quote and additional sub-header': function() { + var filename = 'my".txt'; + var header = makeHeader(filename) + '; foo="bar"'; + assert.equal(form._fileName(header), filename); + }, + + '#_fileName with semicolon': function() { + var filename = 'my;.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my;.txt'); + }, + + '#_fileName with utf8 character': function() { + var filename = 'my☃.txt'; + assert.equal(form._fileName(makeHeader(filename)), 'my☃.txt'); + }, + + '#_uploadPath strips harmful characters from extension when keepExtensions': function() { + form.keepExtensions = true; + + var ext = path.extname(form._uploadPath('fine.jpg?foo=bar')); + assert.equal(ext, '.jpg'); + + var ext = path.extname(form._uploadPath('fine?foo=bar')); + assert.equal(ext, ''); + + var ext = path.extname(form._uploadPath('super.cr2+dsad')); + assert.equal(ext, '.cr2'); + + var ext = path.extname(form._uploadPath('super.bar')); + assert.equal(ext, '.bar'); + }, +}); + +function makeHeader(filename) { + return 'Content-Disposition: form-data; name="upload"; filename="' + filename + '"'; +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js b/dist/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js new file mode 100644 index 0000000..9f1cef8 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/formidable/tool/record.js @@ -0,0 +1,47 @@ +var http = require('http'); +var fs = require('fs'); +var connections = 0; + +var server = http.createServer(function(req, res) { + var socket = req.socket; + console.log('Request: %s %s -> %s', req.method, req.url, socket.filename); + + req.on('end', function() { + if (req.url !== '/') { + res.end(JSON.stringify({ + method: req.method, + url: req.url, + filename: socket.filename, + })); + return; + } + + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + }); +}); + +server.on('connection', function(socket) { + connections++; + + socket.id = connections; + socket.filename = 'connection-' + socket.id + '.http'; + socket.file = fs.createWriteStream(socket.filename); + socket.pipe(socket.file); + + console.log('--> %s', socket.filename); + socket.on('close', function() { + console.log('<-- %s', socket.filename); + }); +}); + +var port = process.env.PORT || 8080; +server.listen(port, function() { + console.log('Recording connections on port %s', port); +}); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/fresh/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/fresh/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/fresh/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/connect/node_modules/fresh/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/fresh/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/fresh/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/fresh/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/fresh/Readme.md new file mode 100644 index 0000000..273130d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/fresh/Readme.md @@ -0,0 +1,32 @@ + +# node-fresh + + HTTP response freshness testing + +## fresh(req, res) + + Check freshness of `req` and `res` headers. + + When the cache is "fresh" __true__ is returned, + otherwise __false__ is returned to indicate that + the cache is now stale. + +## Example: + +```js +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'luna' }; +fresh(req, res); +// => false + +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'tobi' }; +fresh(req, res); +// => true +``` + +## Installation + +``` +$ npm install fresh +``` \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/fresh/index.js b/dist/node_modules/express/node_modules/connect/node_modules/fresh/index.js new file mode 100644 index 0000000..b2f4d41 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/fresh/index.js @@ -0,0 +1,49 @@ + +/** + * Expose `fresh()`. + */ + +module.exports = fresh; + +/** + * Check freshness of `req` and `res` headers. + * + * When the cache is "fresh" __true__ is returned, + * otherwise __false__ is returned to indicate that + * the cache is now stale. + * + * @param {Object} req + * @param {Object} res + * @return {Boolean} + * @api public + */ + +function fresh(req, res) { + // defaults + var etagMatches = true; + var notModified = true; + + // fields + var modifiedSince = req['if-modified-since']; + var noneMatch = req['if-none-match']; + var lastModified = res['last-modified']; + var etag = res['etag']; + + // unconditional request + if (!modifiedSince && !noneMatch) return false; + + // parse if-none-match + if (noneMatch) noneMatch = noneMatch.split(/ *, */); + + // if-none-match + if (noneMatch) etagMatches = ~noneMatch.indexOf(etag) || '*' == noneMatch[0]; + + // if-modified-since + if (modifiedSince) { + modifiedSince = new Date(modifiedSince); + lastModified = new Date(lastModified); + notModified = lastModified <= modifiedSince; + } + + return !! (etagMatches && notModified); +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/fresh/package.json b/dist/node_modules/express/node_modules/connect/node_modules/fresh/package.json new file mode 100644 index 0000000..d81fc0d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/fresh/package.json @@ -0,0 +1,12 @@ +{ + "name": "fresh", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "HTTP response freshness testing", + "version": "0.1.0", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + } +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/pause/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/pause/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/pause/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/connect/node_modules/pause/History.md b/dist/node_modules/express/node_modules/connect/node_modules/pause/History.md new file mode 100644 index 0000000..c8aa68f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/pause/History.md @@ -0,0 +1,5 @@ + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/dist/node_modules/express/node_modules/connect/node_modules/pause/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/pause/Makefile new file mode 100644 index 0000000..4e9c8d3 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/pause/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/dist/node_modules/express/node_modules/connect/node_modules/pause/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/pause/Readme.md new file mode 100644 index 0000000..1cdd68a --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/pause/Readme.md @@ -0,0 +1,29 @@ + +# pause + + Pause streams... + +## 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/dist/node_modules/express/node_modules/connect/node_modules/pause/index.js b/dist/node_modules/express/node_modules/connect/node_modules/pause/index.js new file mode 100644 index 0000000..1b7b379 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/pause/index.js @@ -0,0 +1,29 @@ + +module.exports = function(obj){ + var onData + , onEnd + , events = []; + + // buffer data + obj.on('data', onData = function(data, encoding){ + events.push(['data', data, encoding]); + }); + + // buffer end + obj.on('end', onEnd = function(data, encoding){ + events.push(['end', data, encoding]); + }); + + return { + end: function(){ + obj.removeListener('data', onData); + obj.removeListener('end', onEnd); + }, + resume: function(){ + this.end(); + for (var i = 0, len = events.length; i < len; ++i) { + obj.emit.apply(obj, events[i]); + } + } + }; +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/pause/package.json b/dist/node_modules/express/node_modules/connect/node_modules/pause/package.json new file mode 100644 index 0000000..ae472ac --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/pause/package.json @@ -0,0 +1,13 @@ +{ + "name": "pause", + "version": "0.0.1", + "description": "Pause streams...", + "keywords": [], + "author": "TJ Holowaychuk ", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "main": "index" +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules b/dist/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules new file mode 100644 index 0000000..49e31da --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules @@ -0,0 +1,6 @@ +[submodule "support/expresso"] + path = support/expresso + url = git://github.com/visionmedia/expresso.git +[submodule "support/should"] + path = support/should + url = git://github.com/visionmedia/should.js.git diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/qs/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/.travis.yml b/dist/node_modules/express/node_modules/connect/node_modules/qs/.travis.yml new file mode 100644 index 0000000..2c0a8f6 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.4 \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/History.md b/dist/node_modules/express/node_modules/connect/node_modules/qs/History.md new file mode 100644 index 0000000..1feef45 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/History.md @@ -0,0 +1,83 @@ + +0.5.1 / 2012-09-18 +================== + + * fix encoded `=`. Closes #43 + +0.5.0 / 2012-05-04 +================== + + * Added component support + +0.4.2 / 2012-02-08 +================== + + * Fixed: ensure objects are created when appropriate not arrays [aheckmann] + +0.4.1 / 2012-01-26 +================== + + * Fixed stringify()ing numbers. Closes #23 + +0.4.0 / 2011-11-21 +================== + + * Allow parsing of an existing object (for `bodyParser()`) [jackyz] + * Replaced expresso with mocha + +0.3.2 / 2011-11-08 +================== + + * Fixed global variable leak + +0.3.1 / 2011-08-17 +================== + + * Added `try/catch` around malformed uri components + * Add test coverage for Array native method bleed-though + +0.3.0 / 2011-07-19 +================== + + * Allow `array[index]` and `object[property]` syntaxes [Aria Stewart] + +0.2.0 / 2011-06-29 +================== + + * Added `qs.stringify()` [Cory Forsyth] + +0.1.0 / 2011-04-13 +================== + + * Added jQuery-ish array support + +0.0.7 / 2011-03-13 +================== + + * Fixed; handle empty string and `== null` in `qs.parse()` [dmit] + allows for convenient `qs.parse(url.parse(str).query)` + +0.0.6 / 2011-02-14 +================== + + * Fixed; support for implicit arrays + +0.0.4 / 2011-02-09 +================== + + * Fixed `+` as a space + +0.0.3 / 2011-02-08 +================== + + * Fixed case when right-hand value contains "]" + +0.0.2 / 2011-02-07 +================== + + * Fixed "=" presence in key + +0.0.1 / 2011-02-07 +================== + + * Initial release \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/qs/Makefile new file mode 100644 index 0000000..0a21cf7 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/Makefile @@ -0,0 +1,12 @@ + +test/browser/qs.js: querystring.js + component build package.json test/browser/qs + +querystring.js: lib/head.js lib/querystring.js lib/tail.js + cat $^ > $@ + +test: + @./node_modules/.bin/mocha \ + --ui bdd + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/qs/Readme.md new file mode 100644 index 0000000..27e54a4 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/Readme.md @@ -0,0 +1,58 @@ +# node-querystring + + query string parser for node and the browser supporting nesting, as it was removed from `0.3.x`, so this library provides the previous and commonly desired behaviour (and twice as fast). Used by [express](http://expressjs.com), [connect](http://senchalabs.github.com/connect) and others. + +## Installation + + $ npm install qs + +## Examples + +```js +var qs = require('qs'); + +qs.parse('user[name][first]=Tobi&user[email]=tobi@learnboost.com'); +// => { user: { name: { first: 'Tobi' }, email: 'tobi@learnboost.com' } } + +qs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }}) +// => user[name]=Tobi&user[email]=tobi%40learnboost.com +``` + +## Testing + +Install dev dependencies: + + $ npm install -d + +and execute: + + $ make test + +browser: + + $ open test/browser/index.html + +## License + +(The MIT License) + +Copyright (c) 2010 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/dist/node_modules/express/node_modules/connect/node_modules/qs/benchmark.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/benchmark.js new file mode 100644 index 0000000..97e2c93 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/benchmark.js @@ -0,0 +1,17 @@ + +var qs = require('./'); + +var times = 100000 + , start = new Date + , n = times; + +console.log('times: %d', times); + +while (n--) qs.parse('foo=bar'); +console.log('simple: %dms', new Date - start); + +var start = new Date + , n = times; + +while (n--) qs.parse('user[name][first]=tj&user[name][last]=holowaychuk'); +console.log('nested: %dms', new Date - start); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/component.json b/dist/node_modules/express/node_modules/connect/node_modules/qs/component.json new file mode 100644 index 0000000..ba34ead --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/component.json @@ -0,0 +1,6 @@ +{ + "name": "querystring", + "description": "Querystring parser / stringifier with nesting support", + "keywords": ["querystring", "query", "parser"], + "main": "lib/querystring.js" +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/examples.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/examples.js new file mode 100644 index 0000000..27617b7 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/examples.js @@ -0,0 +1,51 @@ + +/** + * Module dependencies. + */ + +var qs = require('./'); + +var obj = qs.parse('foo'); +console.log(obj) + +var obj = qs.parse('foo=bar=baz'); +console.log(obj) + +var obj = qs.parse('users[]'); +console.log(obj) + +var obj = qs.parse('name=tj&email=tj@vision-media.ca'); +console.log(obj) + +var obj = qs.parse('users[]=tj&users[]=tobi&users[]=jane'); +console.log(obj) + +var obj = qs.parse('user[name][first]=tj&user[name][last]=holowaychuk'); +console.log(obj) + +var obj = qs.parse('users[][name][first]=tj&users[][name][last]=holowaychuk'); +console.log(obj) + +var obj = qs.parse('a=a&a=b&a=c'); +console.log(obj) + +var obj = qs.parse('user[tj]=tj&user[tj]=TJ'); +console.log(obj) + +var obj = qs.parse('user[names]=tj&user[names]=TJ&user[names]=Tyler'); +console.log(obj) + +var obj = qs.parse('user[name][first]=tj&user[name][first]=TJ'); +console.log(obj) + +var obj = qs.parse('user[0]=tj&user[1]=TJ'); +console.log(obj) + +var obj = qs.parse('user[0]=tj&user[]=TJ'); +console.log(obj) + +var obj = qs.parse('user[0]=tj&user[foo]=TJ'); +console.log(obj) + +var str = qs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }}); +console.log(str); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/index.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/index.js new file mode 100644 index 0000000..d177d20 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/querystring'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/head.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/head.js new file mode 100644 index 0000000..55d3817 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/head.js @@ -0,0 +1 @@ +;(function(){ diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/querystring.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/querystring.js new file mode 100644 index 0000000..d3689bb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/querystring.js @@ -0,0 +1,262 @@ + +/** + * Object#toString() ref for stringify(). + */ + +var toString = Object.prototype.toString; + +/** + * Cache non-integer test regexp. + */ + +var isint = /^[0-9]+$/; + +function promote(parent, key) { + if (parent[key].length == 0) return parent[key] = {}; + var t = {}; + for (var i in parent[key]) t[i] = parent[key][i]; + parent[key] = t; + return t; +} + +function parse(parts, parent, key, val) { + var part = parts.shift(); + // end + if (!part) { + if (Array.isArray(parent[key])) { + parent[key].push(val); + } else if ('object' == typeof parent[key]) { + parent[key] = val; + } else if ('undefined' == typeof parent[key]) { + parent[key] = val; + } else { + parent[key] = [parent[key], val]; + } + // array + } else { + var obj = parent[key] = parent[key] || []; + if (']' == part) { + if (Array.isArray(obj)) { + if ('' != val) obj.push(val); + } else if ('object' == typeof obj) { + obj[Object.keys(obj).length] = val; + } else { + obj = parent[key] = [parent[key], val]; + } + // prop + } else if (~part.indexOf(']')) { + part = part.substr(0, part.length - 1); + if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + // key + } else { + if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + } + } +} + +/** + * Merge parent key/val pair. + */ + +function merge(parent, key, val){ + if (~key.indexOf(']')) { + var parts = key.split('[') + , len = parts.length + , last = len - 1; + parse(parts, parent, 'base', val); + // optimize + } else { + if (!isint.test(key) && Array.isArray(parent.base)) { + var t = {}; + for (var k in parent.base) t[k] = parent.base[k]; + parent.base = t; + } + set(parent.base, key, val); + } + + return parent; +} + +/** + * Parse the given obj. + */ + +function parseObject(obj){ + var ret = { base: {} }; + Object.keys(obj).forEach(function(name){ + merge(ret, name, obj[name]); + }); + return ret.base; +} + +/** + * Parse the given str. + */ + +function parseString(str){ + return String(str) + .split('&') + .reduce(function(ret, pair){ + var eql = pair.indexOf('=') + , brace = lastBraceInKey(pair) + , key = pair.substr(0, brace || eql) + , val = pair.substr(brace || eql, pair.length) + , val = val.substr(val.indexOf('=') + 1, val.length); + + // ?foo + if ('' == key) key = pair, val = ''; + + return merge(ret, decode(key), decode(val)); + }, { base: {} }).base; +} + +/** + * Parse the given query `str` or `obj`, returning an object. + * + * @param {String} str | {Object} obj + * @return {Object} + * @api public + */ + +exports.parse = function(str){ + if (null == str || '' == str) return {}; + return 'object' == typeof str + ? parseObject(str) + : parseString(str); +}; + +/** + * Turn the given `obj` into a query string + * + * @param {Object} obj + * @return {String} + * @api public + */ + +var stringify = exports.stringify = function(obj, prefix) { + if (Array.isArray(obj)) { + return stringifyArray(obj, prefix); + } else if ('[object Object]' == toString.call(obj)) { + return stringifyObject(obj, prefix); + } else if ('string' == typeof obj) { + return stringifyString(obj, prefix); + } else { + return prefix + '=' + obj; + } +}; + +/** + * Stringify the given `str`. + * + * @param {String} str + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyString(str, prefix) { + if (!prefix) throw new TypeError('stringify expects an object'); + return prefix + '=' + encodeURIComponent(str); +} + +/** + * Stringify the given `arr`. + * + * @param {Array} arr + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyArray(arr, prefix) { + var ret = []; + if (!prefix) throw new TypeError('stringify expects an object'); + for (var i = 0; i < arr.length; i++) { + ret.push(stringify(arr[i], prefix + '['+i+']')); + } + return ret.join('&'); +} + +/** + * Stringify the given `obj`. + * + * @param {Object} obj + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyObject(obj, prefix) { + var ret = [] + , keys = Object.keys(obj) + , key; + + for (var i = 0, len = keys.length; i < len; ++i) { + key = keys[i]; + ret.push(stringify(obj[key], prefix + ? prefix + '[' + encodeURIComponent(key) + ']' + : encodeURIComponent(key))); + } + + return ret.join('&'); +} + +/** + * Set `obj`'s `key` to `val` respecting + * the weird and wonderful syntax of a qs, + * where "foo=bar&foo=baz" becomes an array. + * + * @param {Object} obj + * @param {String} key + * @param {String} val + * @api private + */ + +function set(obj, key, val) { + var v = obj[key]; + if (undefined === v) { + obj[key] = val; + } else if (Array.isArray(v)) { + v.push(val); + } else { + obj[key] = [v, val]; + } +} + +/** + * Locate last brace in `str` within the key. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function lastBraceInKey(str) { + var len = str.length + , brace + , c; + for (var i = 0; i < len; ++i) { + c = str[i]; + if (']' == c) brace = false; + if ('[' == c) brace = true; + if ('=' == c && !brace) return i; + } +} + +/** + * Decode `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +function decode(str) { + try { + return decodeURIComponent(str.replace(/\+/g, ' ')); + } catch (err) { + return str; + } +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/tail.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/tail.js new file mode 100644 index 0000000..158693a --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/lib/tail.js @@ -0,0 +1 @@ +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/package.json b/dist/node_modules/express/node_modules/connect/node_modules/qs/package.json new file mode 100644 index 0000000..6a4022e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/package.json @@ -0,0 +1,22 @@ +{ + "name": "qs", + "description": "querystring parser", + "version": "0.5.1", + "keywords": ["query string", "parser", "component"], + "repository": { + "type" : "git", + "url" : "git://github.com/visionmedia/node-querystring.git" + }, + "devDependencies": { + "mocha": "*" + , "expect.js": "*" + }, + "component": { + "scripts": { + "querystring": "querystring.js" + } + }, + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "main": "index", + "engines": { "node": "*" } +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/querystring.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/querystring.js new file mode 100644 index 0000000..7466b06 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/querystring.js @@ -0,0 +1,254 @@ +;(function(){ + +/** + * Object#toString() ref for stringify(). + */ + +var toString = Object.prototype.toString; + +/** + * Cache non-integer test regexp. + */ + +var isint = /^[0-9]+$/; + +function promote(parent, key) { + if (parent[key].length == 0) return parent[key] = {}; + var t = {}; + for (var i in parent[key]) t[i] = parent[key][i]; + parent[key] = t; + return t; +} + +function parse(parts, parent, key, val) { + var part = parts.shift(); + // end + if (!part) { + if (Array.isArray(parent[key])) { + parent[key].push(val); + } else if ('object' == typeof parent[key]) { + parent[key] = val; + } else if ('undefined' == typeof parent[key]) { + parent[key] = val; + } else { + parent[key] = [parent[key], val]; + } + // array + } else { + var obj = parent[key] = parent[key] || []; + if (']' == part) { + if (Array.isArray(obj)) { + if ('' != val) obj.push(val); + } else if ('object' == typeof obj) { + obj[Object.keys(obj).length] = val; + } else { + obj = parent[key] = [parent[key], val]; + } + // prop + } else if (~part.indexOf(']')) { + part = part.substr(0, part.length - 1); + if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + // key + } else { + if (!isint.test(part) && Array.isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + } + } +} + +/** + * Merge parent key/val pair. + */ + +function merge(parent, key, val){ + if (~key.indexOf(']')) { + var parts = key.split('[') + , len = parts.length + , last = len - 1; + parse(parts, parent, 'base', val); + // optimize + } else { + if (!isint.test(key) && Array.isArray(parent.base)) { + var t = {}; + for (var k in parent.base) t[k] = parent.base[k]; + parent.base = t; + } + set(parent.base, key, val); + } + + return parent; +} + +/** + * Parse the given obj. + */ + +function parseObject(obj){ + var ret = { base: {} }; + Object.keys(obj).forEach(function(name){ + merge(ret, name, obj[name]); + }); + return ret.base; +} + +/** + * Parse the given str. + */ + +function parseString(str){ + return String(str) + .split('&') + .reduce(function(ret, pair){ + try{ + pair = decodeURIComponent(pair.replace(/\+/g, ' ')); + } catch(e) { + // ignore + } + + var eql = pair.indexOf('=') + , brace = lastBraceInKey(pair) + , key = pair.substr(0, brace || eql) + , val = pair.substr(brace || eql, pair.length) + , val = val.substr(val.indexOf('=') + 1, val.length); + + // ?foo + if ('' == key) key = pair, val = ''; + + return merge(ret, key, val); + }, { base: {} }).base; +} + +/** + * Parse the given query `str` or `obj`, returning an object. + * + * @param {String} str | {Object} obj + * @return {Object} + * @api public + */ + +exports.parse = function(str){ + if (null == str || '' == str) return {}; + return 'object' == typeof str + ? parseObject(str) + : parseString(str); +}; + +/** + * Turn the given `obj` into a query string + * + * @param {Object} obj + * @return {String} + * @api public + */ + +var stringify = exports.stringify = function(obj, prefix) { + if (Array.isArray(obj)) { + return stringifyArray(obj, prefix); + } else if ('[object Object]' == toString.call(obj)) { + return stringifyObject(obj, prefix); + } else if ('string' == typeof obj) { + return stringifyString(obj, prefix); + } else { + return prefix + '=' + obj; + } +}; + +/** + * Stringify the given `str`. + * + * @param {String} str + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyString(str, prefix) { + if (!prefix) throw new TypeError('stringify expects an object'); + return prefix + '=' + encodeURIComponent(str); +} + +/** + * Stringify the given `arr`. + * + * @param {Array} arr + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyArray(arr, prefix) { + var ret = []; + if (!prefix) throw new TypeError('stringify expects an object'); + for (var i = 0; i < arr.length; i++) { + ret.push(stringify(arr[i], prefix + '['+i+']')); + } + return ret.join('&'); +} + +/** + * Stringify the given `obj`. + * + * @param {Object} obj + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyObject(obj, prefix) { + var ret = [] + , keys = Object.keys(obj) + , key; + + for (var i = 0, len = keys.length; i < len; ++i) { + key = keys[i]; + ret.push(stringify(obj[key], prefix + ? prefix + '[' + encodeURIComponent(key) + ']' + : encodeURIComponent(key))); + } + + return ret.join('&'); +} + +/** + * Set `obj`'s `key` to `val` respecting + * the weird and wonderful syntax of a qs, + * where "foo=bar&foo=baz" becomes an array. + * + * @param {Object} obj + * @param {String} key + * @param {String} val + * @api private + */ + +function set(obj, key, val) { + var v = obj[key]; + if (undefined === v) { + obj[key] = val; + } else if (Array.isArray(v)) { + v.push(val); + } else { + obj[key] = [v, val]; + } +} + +/** + * Locate last brace in `str` within the key. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function lastBraceInKey(str) { + var len = str.length + , brace + , c; + for (var i = 0; i < len; ++i) { + c = str[i]; + if (']' == c) brace = false; + if ('[' == c) brace = true; + if ('=' == c && !brace) return i; + } +} +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/expect.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/expect.js new file mode 100644 index 0000000..76aa4e8 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/expect.js @@ -0,0 +1,1202 @@ + +(function (global, module) { + + if ('undefined' == typeof module) { + var module = { exports: {} } + , exports = module.exports + } + + /** + * Exports. + */ + + module.exports = expect; + expect.Assertion = Assertion; + + /** + * Exports version. + */ + + expect.version = '0.1.2'; + + /** + * Possible assertion flags. + */ + + var flags = { + not: ['to', 'be', 'have', 'include', 'only'] + , to: ['be', 'have', 'include', 'only', 'not'] + , only: ['have'] + , have: ['own'] + , be: ['an'] + }; + + function expect (obj) { + return new Assertion(obj); + } + + /** + * Constructor + * + * @api private + */ + + function Assertion (obj, flag, parent) { + this.obj = obj; + this.flags = {}; + + if (undefined != parent) { + this.flags[flag] = true; + + for (var i in parent.flags) { + if (parent.flags.hasOwnProperty(i)) { + this.flags[i] = true; + } + } + } + + var $flags = flag ? flags[flag] : keys(flags) + , self = this + + if ($flags) { + for (var i = 0, l = $flags.length; i < l; i++) { + // avoid recursion + if (this.flags[$flags[i]]) continue; + + var name = $flags[i] + , assertion = new Assertion(this.obj, name, this) + + if ('function' == typeof Assertion.prototype[name]) { + // clone the function, make sure we dont touch the prot reference + var old = this[name]; + this[name] = function () { + return old.apply(self, arguments); + } + + for (var fn in Assertion.prototype) { + if (Assertion.prototype.hasOwnProperty(fn) && fn != name) { + this[name][fn] = bind(assertion[fn], assertion); + } + } + } else { + this[name] = assertion; + } + } + } + }; + + /** + * Performs an assertion + * + * @api private + */ + + Assertion.prototype.assert = function (truth, msg, error) { + var msg = this.flags.not ? error : msg + , ok = this.flags.not ? !truth : truth; + + if (!ok) { + throw new Error(msg); + } + + this.and = new Assertion(this.obj); + }; + + /** + * Check if the value is truthy + * + * @api public + */ + + Assertion.prototype.ok = function () { + this.assert( + !!this.obj + , 'expected ' + i(this.obj) + ' to be truthy' + , 'expected ' + i(this.obj) + ' to be falsy'); + }; + + /** + * Assert that the function throws. + * + * @param {Function|RegExp} callback, or regexp to match error string against + * @api public + */ + + Assertion.prototype.throwError = + Assertion.prototype.throwException = function (fn) { + expect(this.obj).to.be.a('function'); + + var thrown = false + , not = this.flags.not + + try { + this.obj(); + } catch (e) { + if ('function' == typeof fn) { + fn(e); + } else if ('object' == typeof fn) { + var subject = 'string' == typeof e ? e : e.message; + if (not) { + expect(subject).to.not.match(fn); + } else { + expect(subject).to.match(fn); + } + } + thrown = true; + } + + if ('object' == typeof fn && not) { + // in the presence of a matcher, ensure the `not` only applies to + // the matching. + this.flags.not = false; + } + + var name = this.obj.name || 'fn'; + this.assert( + thrown + , 'expected ' + name + ' to throw an exception' + , 'expected ' + name + ' not to throw an exception'); + }; + + /** + * Checks if the array is empty. + * + * @api public + */ + + Assertion.prototype.empty = function () { + var expectation; + + if ('object' == typeof this.obj && null !== this.obj && !isArray(this.obj)) { + if ('number' == typeof this.obj.length) { + expectation = !this.obj.length; + } else { + expectation = !keys(this.obj).length; + } + } else { + if ('string' != typeof this.obj) { + expect(this.obj).to.be.an('object'); + } + + expect(this.obj).to.have.property('length'); + expectation = !this.obj.length; + } + + this.assert( + expectation + , 'expected ' + i(this.obj) + ' to be empty' + , 'expected ' + i(this.obj) + ' to not be empty'); + return this; + }; + + /** + * Checks if the obj exactly equals another. + * + * @api public + */ + + Assertion.prototype.be = + Assertion.prototype.equal = function (obj) { + this.assert( + obj === this.obj + , 'expected ' + i(this.obj) + ' to equal ' + i(obj) + , 'expected ' + i(this.obj) + ' to not equal ' + i(obj)); + return this; + }; + + /** + * Checks if the obj sortof equals another. + * + * @api public + */ + + Assertion.prototype.eql = function (obj) { + this.assert( + expect.eql(obj, this.obj) + , 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj) + , 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj)); + return this; + }; + + /** + * Assert within start to finish (inclusive). + * + * @param {Number} start + * @param {Number} finish + * @api public + */ + + Assertion.prototype.within = function (start, finish) { + var range = start + '..' + finish; + this.assert( + this.obj >= start && this.obj <= finish + , 'expected ' + i(this.obj) + ' to be within ' + range + , 'expected ' + i(this.obj) + ' to not be within ' + range); + return this; + }; + + /** + * Assert typeof / instance of + * + * @api public + */ + + Assertion.prototype.a = + Assertion.prototype.an = function (type) { + if ('string' == typeof type) { + // proper english in error msg + var n = /^[aeiou]/.test(type) ? 'n' : ''; + + // typeof with support for 'array' + this.assert( + 'array' == type ? isArray(this.obj) : + 'object' == type + ? 'object' == typeof this.obj && null !== this.obj + : type == typeof this.obj + , 'expected ' + i(this.obj) + ' to be a' + n + ' ' + type + , 'expected ' + i(this.obj) + ' not to be a' + n + ' ' + type); + } else { + // instanceof + var name = type.name || 'supplied constructor'; + this.assert( + this.obj instanceof type + , 'expected ' + i(this.obj) + ' to be an instance of ' + name + , 'expected ' + i(this.obj) + ' not to be an instance of ' + name); + } + + return this; + }; + + /** + * Assert numeric value above _n_. + * + * @param {Number} n + * @api public + */ + + Assertion.prototype.greaterThan = + Assertion.prototype.above = function (n) { + this.assert( + this.obj > n + , 'expected ' + i(this.obj) + ' to be above ' + n + , 'expected ' + i(this.obj) + ' to be below ' + n); + return this; + }; + + /** + * Assert numeric value below _n_. + * + * @param {Number} n + * @api public + */ + + Assertion.prototype.lessThan = + Assertion.prototype.below = function (n) { + this.assert( + this.obj < n + , 'expected ' + i(this.obj) + ' to be below ' + n + , 'expected ' + i(this.obj) + ' to be above ' + n); + return this; + }; + + /** + * Assert string value matches _regexp_. + * + * @param {RegExp} regexp + * @api public + */ + + Assertion.prototype.match = function (regexp) { + this.assert( + regexp.exec(this.obj) + , 'expected ' + i(this.obj) + ' to match ' + regexp + , 'expected ' + i(this.obj) + ' not to match ' + regexp); + return this; + }; + + /** + * Assert property "length" exists and has value of _n_. + * + * @param {Number} n + * @api public + */ + + Assertion.prototype.length = function (n) { + expect(this.obj).to.have.property('length'); + var len = this.obj.length; + this.assert( + n == len + , 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len + , 'expected ' + i(this.obj) + ' to not have a length of ' + len); + return this; + }; + + /** + * Assert property _name_ exists, with optional _val_. + * + * @param {String} name + * @param {Mixed} val + * @api public + */ + + Assertion.prototype.property = function (name, val) { + if (this.flags.own) { + this.assert( + Object.prototype.hasOwnProperty.call(this.obj, name) + , 'expected ' + i(this.obj) + ' to have own property ' + i(name) + , 'expected ' + i(this.obj) + ' to not have own property ' + i(name)); + return this; + } + + if (this.flags.not && undefined !== val) { + if (undefined === this.obj[name]) { + throw new Error(i(this.obj) + ' has no property ' + i(name)); + } + } else { + var hasProp; + try { + hasProp = name in this.obj + } catch (e) { + hasProp = undefined !== this.obj[name] + } + + this.assert( + hasProp + , 'expected ' + i(this.obj) + ' to have a property ' + i(name) + , 'expected ' + i(this.obj) + ' to not have a property ' + i(name)); + } + + if (undefined !== val) { + this.assert( + val === this.obj[name] + , 'expected ' + i(this.obj) + ' to have a property ' + i(name) + + ' of ' + i(val) + ', but got ' + i(this.obj[name]) + , 'expected ' + i(this.obj) + ' to not have a property ' + i(name) + + ' of ' + i(val)); + } + + this.obj = this.obj[name]; + return this; + }; + + /** + * Assert that the array contains _obj_ or string contains _obj_. + * + * @param {Mixed} obj|string + * @api public + */ + + Assertion.prototype.string = + Assertion.prototype.contain = function (obj) { + if ('string' == typeof this.obj) { + this.assert( + ~this.obj.indexOf(obj) + , 'expected ' + i(this.obj) + ' to contain ' + i(obj) + , 'expected ' + i(this.obj) + ' to not contain ' + i(obj)); + } else { + this.assert( + ~indexOf(this.obj, obj) + , 'expected ' + i(this.obj) + ' to contain ' + i(obj) + , 'expected ' + i(this.obj) + ' to not contain ' + i(obj)); + } + return this; + }; + + /** + * Assert exact keys or inclusion of keys by using + * the `.own` modifier. + * + * @param {Array|String ...} keys + * @api public + */ + + Assertion.prototype.key = + Assertion.prototype.keys = function ($keys) { + var str + , ok = true; + + $keys = isArray($keys) + ? $keys + : Array.prototype.slice.call(arguments); + + if (!$keys.length) throw new Error('keys required'); + + var actual = keys(this.obj) + , len = $keys.length; + + // Inclusion + ok = every($keys, function (key) { + return ~indexOf(actual, key); + }); + + // Strict + if (!this.flags.not && this.flags.only) { + ok = ok && $keys.length == actual.length; + } + + // Key string + if (len > 1) { + $keys = map($keys, function (key) { + return i(key); + }); + var last = $keys.pop(); + str = $keys.join(', ') + ', and ' + last; + } else { + str = i($keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (!this.flags.only ? 'include ' : 'only have ') + str; + + // Assertion + this.assert( + ok + , 'expected ' + i(this.obj) + ' to ' + str + , 'expected ' + i(this.obj) + ' to not ' + str); + + return this; + }; + + /** + * Function bind implementation. + */ + + function bind (fn, scope) { + return function () { + return fn.apply(scope, arguments); + } + } + + /** + * Array every compatibility + * + * @see bit.ly/5Fq1N2 + * @api public + */ + + function every (arr, fn, thisObj) { + var scope = thisObj || global; + for (var i = 0, j = arr.length; i < j; ++i) { + if (!fn.call(scope, arr[i], i, arr)) { + return false; + } + } + return true; + }; + + /** + * Array indexOf compatibility. + * + * @see bit.ly/a5Dxa2 + * @api public + */ + + function indexOf (arr, o, i) { + if (Array.prototype.indexOf) { + return Array.prototype.indexOf.call(arr, o, i); + } + + if (arr.length === undefined) { + return -1; + } + + for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0 + ; i < j && arr[i] !== o; i++); + + return j <= i ? -1 : i; + }; + + /** + * Inspects an object. + * + * @see taken from node.js `util` module (copyright Joyent, MIT license) + * @api private + */ + + function i (obj, showHidden, depth) { + var seen = []; + + function stylize (str) { + return str; + }; + + function format (value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value !== exports && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + return value.inspect(recurseTimes); + } + + // Primitive types cannot have properties + switch (typeof value) { + case 'undefined': + return stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return stylize(simple, 'string'); + + case 'number': + return stylize('' + value, 'number'); + + case 'boolean': + return stylize('' + value, 'boolean'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return stylize('null', 'null'); + } + + // Look up the keys of the object. + var visible_keys = keys(value); + var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys; + + // Functions without properties can be shortcutted. + if (typeof value === 'function' && $keys.length === 0) { + if (isRegExp(value)) { + return stylize('' + value, 'regexp'); + } else { + var name = value.name ? ': ' + value.name : ''; + return stylize('[Function' + name + ']', 'special'); + } + } + + // Dates without properties can be shortcutted + if (isDate(value) && $keys.length === 0) { + return stylize(value.toUTCString(), 'date'); + } + + var base, type, braces; + // Determine the object type + if (isArray(value)) { + type = 'Array'; + braces = ['[', ']']; + } else { + type = 'Object'; + braces = ['{', '}']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + var n = value.name ? ': ' + value.name : ''; + base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; + } else { + base = ''; + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + value.toUTCString(); + } + + if ($keys.length === 0) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return stylize('' + value, 'regexp'); + } else { + return stylize('[Object]', 'special'); + } + } + + seen.push(value); + + var output = map($keys, function (key) { + var name, str; + if (value.__lookupGetter__) { + if (value.__lookupGetter__(key)) { + if (value.__lookupSetter__(key)) { + str = stylize('[Getter/Setter]', 'special'); + } else { + str = stylize('[Getter]', 'special'); + } + } else { + if (value.__lookupSetter__(key)) { + str = stylize('[Setter]', 'special'); + } + } + } + if (indexOf(visible_keys, key) < 0) { + name = '[' + key + ']'; + } + if (!str) { + if (indexOf(seen, value[key]) < 0) { + if (recurseTimes === null) { + str = format(value[key]); + } else { + str = format(value[key], recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (isArray(value)) { + str = map(str.split('\n'), function (line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + map(str.split('\n'), function (line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (type === 'Array' && key.match(/^\d+$/)) { + return str; + } + name = json.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = stylize(name, 'string'); + } + } + + return name + ': ' + str; + }); + + seen.pop(); + + var numLinesEst = 0; + var length = reduce(output, function (prev, cur) { + numLinesEst++; + if (indexOf(cur, '\n') >= 0) numLinesEst++; + return prev + cur.length + 1; + }, 0); + + if (length > 50) { + output = braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + + } else { + output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; + } + + return output; + } + return format(obj, (typeof depth === 'undefined' ? 2 : depth)); + }; + + function isArray (ar) { + return Object.prototype.toString.call(ar) == '[object Array]'; + }; + + function isRegExp(re) { + var s = '' + re; + return re instanceof RegExp || // easy case + // duck-type for context-switching evalcx case + typeof(re) === 'function' && + re.constructor.name === 'RegExp' && + re.compile && + re.test && + re.exec && + s.match(/^\/.*\/[gim]{0,3}$/); + }; + + function isDate(d) { + if (d instanceof Date) return true; + return false; + }; + + function keys (obj) { + if (Object.keys) { + return Object.keys(obj); + } + + var keys = []; + + for (var i in obj) { + if (Object.prototype.hasOwnProperty.call(obj, i)) { + keys.push(i); + } + } + + return keys; + } + + function map (arr, mapper, that) { + if (Array.prototype.map) { + return Array.prototype.map.call(arr, mapper, that); + } + + var other= new Array(arr.length); + + for (var i= 0, n = arr.length; i= 2) { + var rv = arguments[1]; + } else { + do { + if (i in this) { + rv = this[i++]; + break; + } + + // if array contains no values, no initial value to return + if (++i >= len) + throw new TypeError(); + } while (true); + } + + for (; i < len; i++) { + if (i in this) + rv = fun.call(null, rv, this[i], i, this); + } + + return rv; + }; + + /** + * Asserts deep equality + * + * @see taken from node.js `assert` module (copyright Joyent, MIT license) + * @api private + */ + + expect.eql = function eql (actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + } else if ('undefined' != typeof Buffer + && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } else if (typeof actual != 'object' && typeof expected != 'object') { + return actual == expected; + + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical "prototype" property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } + } + + function isUndefinedOrNull (value) { + return value === null || value === undefined; + } + + function isArguments (object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; + } + + function objEquiv (a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical "prototype" property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return expect.eql(a, b); + } + try{ + var ka = keys(a), + kb = keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!expect.eql(a[key], b[key])) + return false; + } + return true; + } + + var json = (function () { + "use strict"; + + if ('object' == typeof JSON && JSON.parse && JSON.stringify) { + return { + parse: nativeJSON.parse + , stringify: nativeJSON.stringify + } + } + + var JSON = {}; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + function date(d, key) { + return isFinite(d.valueOf()) ? + d.getUTCFullYear() + '-' + + f(d.getUTCMonth() + 1) + '-' + + f(d.getUTCDate()) + 'T' + + f(d.getUTCHours()) + ':' + + f(d.getUTCMinutes()) + ':' + + f(d.getUTCSeconds()) + 'Z' : null; + }; + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + 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. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(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. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + + // If the value has a toJSON method, call it to obtain a replacement value. + + if (value instanceof Date) { + value = date(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. + + 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. + + 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, v, 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); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, 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 (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + // 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'); + }; + + return JSON; + })(); + + if ('undefined' != typeof window) { + window.expect = module.exports; + } + +})( + this + , 'undefined' != typeof module ? module : {} + , 'undefined' != typeof exports ? exports : {} +); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/index.html b/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/index.html new file mode 100644 index 0000000..c73147a --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/index.html @@ -0,0 +1,18 @@ + + + Mocha + + + + + + + + + + + + +
    + + diff --git a/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/jquery.js b/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/jquery.js new file mode 100644 index 0000000..f3201aa --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/qs/test/browser/jquery.js @@ -0,0 +1,8981 @@ +/*! + * jQuery JavaScript Library v1.6.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Jun 30 14:16:56 2011 -0400 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z])/ig, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.6.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery._Deferred(); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNaN: function( obj ) { + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return (new Function( "return " + data ))(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Converts a dashed string to camelCased string; + // Used by both the css and data modules + camelCase: function( string ) { + return string.replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + + if ( indexOf ) { + return indexOf.call( array, elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return (new Date()).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +var // Promise methods + promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), + // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + // Create a simple deferred (one callbacks list) + _Deferred: function() { + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function() { + if ( !cancelled ) { + var args = arguments, + i, + length, + elem, + type, + _fired; + if ( fired ) { + _fired = fired; + fired = 0; + } + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred, elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + if ( _fired ) { + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); + } + } + return this; + }, + + // resolve with given context and args + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { + // make sure args are available (#8421) + args = args || []; + firing = 1; + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context, args ); + } + } + finally { + fired = [ context, args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.resolveWith( this, arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + Deferred: function( func ) { + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + pipe: function( fnDone, fnFail ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject ); + } else { + newDefer[ action ]( returned ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + var i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; + } + return obj; + } + }); + // Make sure only one callback list will be used + deferred.done( failDeferred.cancel ).fail( deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = arguments, + i = 0, + length = args.length, + count = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + // Strange bug in FF4: + // Values changed onto the arguments object sometimes end up as undefined values + // outside the $.when method. Cloning the object into a fresh array solves the issue + deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); + } + }; + } + if ( length > 1 ) { + for( ; i < length; i++ ) { + if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return deferred.promise(); + } +}); + + + +jQuery.support = (function() { + + var div = document.createElement( "div" ), + documentElement = document.documentElement, + all, + a, + select, + opt, + input, + marginDiv, + support, + fragment, + body, + testElementParent, + testElement, + testElementStyle, + tds, + events, + eventName, + i, + isSupported; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
    a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName( "tbody" ).length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName( "link" ).length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains it's value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + div.innerHTML = ""; + + // Figure out if the W3C box model works as expected + div.style.width = div.style.paddingLeft = "1px"; + + body = document.getElementsByTagName( "body" )[ 0 ]; + // We use our own, invisible, body unless the body is already present + // in which case we use a div (#9239) + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0 + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
    "; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.innerHTML = "
    t
    "; + tds = div.getElementsByTagName( "td" ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Remove the body element we added + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + } ) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Null connected elements to avoid leaks in IE + testElement = fragment = select = opt = body = marginDiv = div = input = null; + + return support; +})(); + +// Keep track of boxModel +jQuery.boxModel = jQuery.support.boxModel; + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([a-z])([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + } else { + cache[ id ] = jQuery.extend(cache[ id ], name); + } + } + + thisCache = cache[ id ]; + + // Internal jQuery data is stored in a separate object inside the object's data + // cache in order to avoid key collisions between internal data and user-defined + // data + if ( pvt ) { + if ( !thisCache[ internalKey ] ) { + thisCache[ internalKey ] = {}; + } + + thisCache = thisCache[ internalKey ]; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should + // not attempt to inspect the internal events object using jQuery.data, as this + // internal data object is undocumented and subject to change. + if ( name === "events" && !thisCache[name] ) { + return thisCache[ internalKey ] && thisCache[ internalKey ].events; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + delete thisCache[ name ]; + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !isEmptyDataObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( pvt ) { + delete cache[ id ][ internalKey ]; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + var internalCache = cache[ id ][ internalKey ]; + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + if ( jQuery.support.deleteExpando || cache != window ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the entire user cache at once because it's faster than + // iterating through each key, but we need to continue to persist internal + // data if it existed + if ( internalCache ) { + cache[ id ] = {}; + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + + cache[ id ][ internalKey ] = internalCache; + + // Otherwise, we need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + } else if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 ) { + var attr = this[0].attributes, name; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + !jQuery.isNaN( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON +// property to be considered empty objects; this property always exists in +// order to make sure JSON.stringify does not expose internal metadata +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery.data( elem, deferDataKey, undefined, true ); + if ( defer && + ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) && + ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery.data( elem, queueDataKey, undefined, true ) && + !jQuery.data( elem, markDataKey, undefined, true ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.resolve(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); + if ( count ) { + jQuery.data( elem, key, count, true ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + if ( elem ) { + type = (type || "fx") + "queue"; + var q = jQuery.data( elem, type, undefined, true ); + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery.data( elem, type, jQuery.makeArray(data), true ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + defer; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { + count++; + tmp.done( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + rinvalidChar = /\:|^on/, + formHook, boolHook; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = (value || "").split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return undefined; + } + + var isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attrFix: { + // Always normalize to ensure hook usage + tabindex: "tabIndex" + }, + + attr: function( elem, name, value, pass ) { + var nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( !("getAttribute" in elem) ) { + return jQuery.prop( elem, name, value ); + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Normalize the name if needed + if ( notxml ) { + name = jQuery.attrFix[ name ] || name; + + hooks = jQuery.attrHooks[ name ]; + + if ( !hooks ) { + // Use boolHook for boolean attributes + if ( rboolean.test( name ) ) { + + hooks = boolHook; + + // Use formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return undefined; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, name ) { + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabIndex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + }, + // Use the value property for back compat + // Use the formHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return (elem[ name ] = value); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: {} +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return jQuery.prop( elem, name ) ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !jQuery.support.getSetAttribute ) { + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; + + // Use this for any attribute on a form in IE6/7 + formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + // Return undefined if nodeValue is empty string + return ret && ret.nodeValue !== "" ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return value; + } + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return (elem.style.cssText = "" + value); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }); +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); + } + } + }); +}); + + + + +var rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspaces = / /g, + rescape = /[^\w\s.|`]/g, + fcleanup = function( nm ) { + return nm.replace(rescape, "\\$&"); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } else if ( !handler ) { + // Fixes bug #7229. Fix recommended by jdalton + return; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery._data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events, + eventHandle = elemData.handle; + + if ( !events ) { + elemData.events = events = {}; + } + + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } + + var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem, undefined, true ); + } + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Event object or event type + var type = event.type || event, + namespaces = [], + exclusive; + + if ( type.indexOf("!") >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.exclusive = exclusive; + event.namespace = namespaces.join("."); + event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); + + // triggerHandler() and global events don't bubble or run the default action + if ( onlyHandlers || !elem ) { + event.preventDefault(); + event.stopPropagation(); + } + + // Handle a global trigger + if ( !elem ) { + // TODO: Stop taunting the data cache; remove global events and always attach to document + jQuery.each( jQuery.cache, function() { + // internalKey variable is just used to make it easier to find + // and potentially change this stuff later; currently it just + // points to jQuery.expando + var internalKey = jQuery.expando, + internalCache = this[ internalKey ]; + if ( internalCache && internalCache.events && internalCache.events[ type ] ) { + jQuery.event.trigger( event, data, internalCache.handle.elem ); + } + }); + return; + } + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + event.target = elem; + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + var cur = elem, + // IE doesn't like method names with a colon (#3533, #8272) + ontype = type.indexOf(":") < 0 ? "on" + type : ""; + + // Fire event on the current element, then bubble up the DOM tree + do { + var handle = jQuery._data( cur, "handle" ); + + event.currentTarget = cur; + if ( handle ) { + handle.apply( cur, data ); + } + + // Trigger an inline bound script + if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { + event.result = false; + event.preventDefault(); + } + + // Bubble up to document, then to window + cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; + } while ( cur && !event.isPropagationStopped() ); + + // If nobody prevented the default action, do it now + if ( !event.isDefaultPrevented() ) { + var old, + special = jQuery.event.special[ type ] || {}; + + if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction)() check here because IE6/7 fails that test. + // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. + try { + if ( ontype && elem[ type ] ) { + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + jQuery.event.triggered = type; + elem[ type ](); + } + } catch ( ieError ) {} + + if ( old ) { + elem[ ontype ] = old; + } + + jQuery.event.triggered = undefined; + } + } + + return event.result; + }, + + handle: function( event ) { + event = jQuery.event.fix( event || window.event ); + // Snapshot the handlers list since a called handler may add/remove events. + var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0), + run_all = !event.exclusive && !event.namespace, + args = Array.prototype.slice.call( arguments, 0 ); + + // Use the fix-ed Event rather than the (read-only) native event + args[0] = event; + event.currentTarget = this; + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Triggered event must 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event. + if ( run_all || event.namespace_re.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + // Fixes #1925 where srcElement might not be defined either + event.target = event.srcElement || document; + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var eventDocument = event.target.ownerDocument || document, + doc = eventDocument.documentElement, + body = eventDocument.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + }, + + remove: function( handleObj ) { + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); + } + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + + // Check if mouse(over|out) are still within the same parent element + var related = event.relatedTarget, + inside = false, + eventType = event.type; + + event.type = event.data; + + if ( related !== this ) { + + if ( related ) { + inside = jQuery.contains( this, related ); + } + + if ( !inside ) { + + jQuery.event.handle.apply( this, arguments ); + + event.type = eventType; + } + } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( !jQuery.nodeName( this, "form" ) ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( jQuery.nodeName( elem, "select" ) ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery._data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery._data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + e.liveFired = undefined; + jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + beforedeactivate: testChange, + + click: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { + testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + beforeactivate: function( e ) { + var elem = e.target; + jQuery._data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return rformElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return rformElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; + + // Handle when the input is .focus()'d + changeFilters.focus = changeFilters.beforeactivate; +} + +function trigger( type, elem, args ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + // Don't pass args or remember liveFired; they apply to the donor event. + var event = jQuery.extend( {}, args[ 0 ] ); + event.type = type; + event.originalEvent = {}; + event.liveFired = undefined; + jQuery.event.handle.call( elem, event ); + if ( event.isDefaultPrevented() ) { + args[ 0 ].preventDefault(); + } +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + + function handler( donor ) { + // Donor event is always a native one; fix it and switch its type. + // Let focusin/out handler cancel the donor focus/blur event. + var e = jQuery.event.fix( donor ); + e.type = fix; + e.originalEvent = {}; + jQuery.event.trigger( e, null, e.target ); + if ( e.isDefaultPrevented() ) { + donor.preventDefault(); + } + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + var handler; + + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( arguments.length === 2 || data === false ) { + fn = data; + data = undefined; + } + + if ( name === "one" ) { + handler = function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }; + handler.guid = fn.guid || jQuery.guid++; + } else { + handler = fn; + } + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( typeof types === "object" && !types.preventDefault ) { + for ( var key in types ) { + context[ name ]( key, data, types[key], selector ); + } + + return this; + } + + if ( name === "die" && !types && + origSelector && origSelector.charAt(0) === "." ) { + + context.unbind( origSelector ); + + return this; + } + + if ( data === false || jQuery.isFunction( data ) ) { + fn = data || returnFalse; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( liveMap[ type ] ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + for ( var j = 0, l = context.length; j < l; j++ ) { + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + } + + } else { + // unbind live handler + context.unbind( "live." + liveConvert( type, selector ), fn ); + } + } + + return this; + }; +}); + +function liveHandler( event ) { + var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, + elems = [], + selectors = [], + events = jQuery._data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) + if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { + return; + } + + if ( event.namespace ) { + namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { + elem = close.elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + event.type = handleObj.preType; + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + + // Make sure not to accidentally match a child element with the same selector + if ( related && jQuery.contains( elem, related ) ) { + related = elem; + } + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

    "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array + if ( jQuery.isArray( selectors ) ) { + var match, selector, + matches = {}, + level = 1; + + if ( cur && selectors.length ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[ selector ] ) { + matches[ selector ] = POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[ selector ]; + + if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { + ret.push({ selector: selector, elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +} + + + + +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + col: [ 2, "", "
    " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + + + + + diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/example/wildcards.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/example/wildcards.js new file mode 100644 index 0000000..1fdac20 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/example/wildcards.js @@ -0,0 +1,10 @@ + +var debug = { + foo: require('../')('test:foo'), + bar: require('../')('test:bar'), + baz: require('../')('test:baz') +}; + +debug.foo('foo') +debug.bar('bar') +debug.baz('baz') \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/example/worker.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/example/worker.js new file mode 100644 index 0000000..7f6d288 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/example/worker.js @@ -0,0 +1,22 @@ + +// DEBUG=* node example/worker +// DEBUG=worker:* node example/worker +// DEBUG=worker:a node example/worker +// DEBUG=worker:b node example/worker + +var a = require('../')('worker:a') + , b = require('../')('worker:b'); + +function work() { + a('doing lots of uninteresting work'); + setTimeout(work, Math.random() * 1000); +} + +work(); + +function workb() { + b('doing some work'); + setTimeout(workb, Math.random() * 2000); +} + +workb(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/head.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/head.js new file mode 100644 index 0000000..55d3817 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/head.js @@ -0,0 +1 @@ +;(function(){ diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/index.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/index.js new file mode 100644 index 0000000..ee54454 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/debug'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/lib/debug.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/lib/debug.js new file mode 100644 index 0000000..969d122 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/lib/debug.js @@ -0,0 +1,135 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Enabled debuggers. + */ + +var names = [] + , skips = []; + +(process.env.DEBUG || '') + .split(/[\s,]+/) + .forEach(function(name){ + name = name.replace('*', '.*?'); + if (name[0] === '-') { + skips.push(new RegExp('^' + name.substr(1) + '$')); + } else { + names.push(new RegExp('^' + name + '$')); + } + }); + +/** + * Colors. + */ + +var colors = [6, 2, 3, 4, 5, 1]; + +/** + * Previous debug() call. + */ + +var prev = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Is stdout a TTY? Colored output is disabled when `true`. + */ + +var isatty = tty.isatty(2); + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function color() { + return colors[prevColor++ % colors.length]; +} + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +function humanize(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +} + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + function disabled(){} + disabled.enabled = false; + + var match = skips.some(function(re){ + return re.test(name); + }); + + if (match) return disabled; + + match = names.some(function(re){ + return re.test(name); + }); + + if (!match) return disabled; + var c = color(); + + function colored(fmt) { + var curr = new Date; + var ms = curr - (prev[name] || curr); + prev[name] = curr; + + fmt = ' \033[9' + c + 'm' + name + ' ' + + '\033[3' + c + 'm\033[90m' + + fmt + '\033[3' + c + 'm' + + ' +' + humanize(ms) + '\033[0m'; + + console.error.apply(this, arguments); + } + + function plain(fmt) { + fmt = new Date().toUTCString() + + ' ' + name + ' ' + fmt; + console.error.apply(this, arguments); + } + + colored.enabled = plain.enabled = true; + + return isatty + ? colored + : plain; +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/package.json b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/package.json new file mode 100644 index 0000000..f226656 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/package.json @@ -0,0 +1,17 @@ +{ + "name": "debug" + , "version": "0.7.0" + , "description": "small debugging utility" + , "keywords": ["debug", "log", "debugger"] + , "author": "TJ Holowaychuk " + , "dependencies": {} + , "devDependencies": { "mocha": "*" } + , "main": "index" + , "browserify": "debug.component.js" + , "engines": { "node": "*" } + , "component": { + "scripts": { + "debug": "debug.component.js" + } + } +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/tail.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/tail.js new file mode 100644 index 0000000..5bf3fd3 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/debug/tail.js @@ -0,0 +1,4 @@ + + module.exports = debug; + +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/Readme.md new file mode 100644 index 0000000..273130d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/Readme.md @@ -0,0 +1,32 @@ + +# node-fresh + + HTTP response freshness testing + +## fresh(req, res) + + Check freshness of `req` and `res` headers. + + When the cache is "fresh" __true__ is returned, + otherwise __false__ is returned to indicate that + the cache is now stale. + +## Example: + +```js +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'luna' }; +fresh(req, res); +// => false + +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'tobi' }; +fresh(req, res); +// => true +``` + +## Installation + +``` +$ npm install fresh +``` \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/index.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/index.js new file mode 100644 index 0000000..b2f4d41 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/index.js @@ -0,0 +1,49 @@ + +/** + * Expose `fresh()`. + */ + +module.exports = fresh; + +/** + * Check freshness of `req` and `res` headers. + * + * When the cache is "fresh" __true__ is returned, + * otherwise __false__ is returned to indicate that + * the cache is now stale. + * + * @param {Object} req + * @param {Object} res + * @return {Boolean} + * @api public + */ + +function fresh(req, res) { + // defaults + var etagMatches = true; + var notModified = true; + + // fields + var modifiedSince = req['if-modified-since']; + var noneMatch = req['if-none-match']; + var lastModified = res['last-modified']; + var etag = res['etag']; + + // unconditional request + if (!modifiedSince && !noneMatch) return false; + + // parse if-none-match + if (noneMatch) noneMatch = noneMatch.split(/ *, */); + + // if-none-match + if (noneMatch) etagMatches = ~noneMatch.indexOf(etag) || '*' == noneMatch[0]; + + // if-modified-since + if (modifiedSince) { + modifiedSince = new Date(modifiedSince); + lastModified = new Date(lastModified); + notModified = lastModified <= modifiedSince; + } + + return !! (etagMatches && notModified); +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/package.json b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/package.json new file mode 100644 index 0000000..d81fc0d --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/fresh/package.json @@ -0,0 +1,12 @@ +{ + "name": "fresh", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "HTTP response freshness testing", + "version": "0.1.0", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + } +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/LICENSE b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/LICENSE new file mode 100644 index 0000000..451fc45 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Benjamin Thomas, Robert Kieffer + +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/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/README.md b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/README.md new file mode 100644 index 0000000..d8b66a8 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/README.md @@ -0,0 +1,63 @@ +# mime + +Comprehensive MIME type mapping API. Includes all 600+ types and 800+ extensions defined by the Apache project, plus additional types submitted by the node.js community. + +## Install + +Install with [npm](http://github.com/isaacs/npm): + + npm install mime + +## API - Queries + +### mime.lookup(path) +Get the mime type associated with a file. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g. + + var mime = require('mime'); + + mime.lookup('/path/to/file.txt'); // => 'text/plain' + mime.lookup('file.txt'); // => 'text/plain' + mime.lookup('.TXT'); // => 'text/plain' + mime.lookup('htm'); // => 'text/html' + +### mime.extension(type) +Get the default extension for `type` + + mime.extension('text/html'); // => 'html' + mime.extension('application/octet-stream'); // => 'bin' + +### mime.charsets.lookup() + +Map mime-type to charset + + mime.charsets.lookup('text/plain'); // => 'UTF-8' + +(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.) + +## API - Defining Custom Types + +The following APIs allow you to add your own type mappings within your project. If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/bentomas/node-mime/wiki/Requesting-New-Types). + +### mime.define() + +Add custom mime/extension mappings + + mime.define({ + 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'], + 'application/x-my-type': ['x-mt', 'x-mtt'], + // etc ... + }); + + mime.lookup('x-sft'); // => 'text/x-some-format' + +The first entry in the extensions array is returned by `mime.extension()`. E.g. + + mime.extension('text/x-some-format'); // => 'x-sf' + +### mime.load(filepath) + +Load mappings from an Apache ".types" format file + + mime.load('./my_project.types'); + +The .types file format is simple - See the `types` dir for examples. diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/mime.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/mime.js new file mode 100644 index 0000000..1e00585 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/mime.js @@ -0,0 +1,104 @@ +var path = require('path'); +var fs = require('fs'); + +function Mime() { + // Map of extension -> mime type + this.types = Object.create(null); + + // Map of mime type -> extension + this.extensions = Object.create(null); +} + +/** + * Define mimetype -> extension mappings. Each key is a mime-type that maps + * to an array of extensions associated with the type. The first extension is + * used as the default extension for the type. + * + * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']}); + * + * @param map (Object) type definitions + */ +Mime.prototype.define = function (map) { + for (var type in map) { + var exts = map[type]; + + for (var i = 0; i < exts.length; i++) { + this.types[exts[i]] = type; + } + + // Default extension is the first one we encounter + if (!this.extensions[type]) { + this.extensions[type] = exts[0]; + } + } +}; + +/** + * Load an Apache2-style ".types" file + * + * This may be called multiple times (it's expected). Where files declare + * overlapping types/extensions, the last file wins. + * + * @param file (String) path of file to load. + */ +Mime.prototype.load = function(file) { + // Read file and split into lines + var map = {}, + content = fs.readFileSync(file, 'ascii'), + lines = content.split(/[\r\n]+/); + + lines.forEach(function(line) { + // Clean up whitespace/comments, and split into fields + var fields = line.replace(/\s*#.*|^\s*|\s*$/g, '').split(/\s+/); + map[fields.shift()] = fields; + }); + + this.define(map); +}; + +/** + * Lookup a mime type based on extension + */ +Mime.prototype.lookup = function(path, fallback) { + var ext = path.replace(/.*[\.\/]/, '').toLowerCase(); + + return this.types[ext] || fallback || this.default_type; +}; + +/** + * Return file extension associated with a mime type + */ +Mime.prototype.extension = function(mimeType) { + return this.extensions[mimeType]; +}; + +// Default instance +var mime = new Mime(); + +// Load local copy of +// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +mime.load(path.join(__dirname, 'types/mime.types')); + +// Load additional types from node.js community +mime.load(path.join(__dirname, 'types/node.types')); + +// Default type +mime.default_type = mime.lookup('bin'); + +// +// Additional API specific to the default instance +// + +mime.Mime = Mime; + +/** + * Lookup a charset based on mime type. + */ +mime.charsets = { + lookup: function(mimeType, fallback) { + // Assume text types are utf8 + return (/^text\//).test(mimeType) ? 'UTF-8' : fallback; + } +} + +module.exports = mime; diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/package.json b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/package.json new file mode 100644 index 0000000..05f6fb4 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/package.json @@ -0,0 +1,22 @@ +{ + "author": { + "name": "Robert Kieffer", + "url": "http://github.com/broofa", + "email": "robert@broofa.com" + }, + "contributors": [ + { + "name": "Benjamin Thomas", + "url": "http://github.com/bentomas", + "email": "benjamin@benjaminthomas.org" + } + ], + "dependencies": {}, + "description": "A comprehensive library for mime-type mapping", + "devDependencies": {}, + "keywords": ["util", "mime"], + "main": "mime.js", + "name": "mime", + "repository": {"url": "https://github.com/broofa/node-mime", "type": "git"}, + "version": "1.2.6" +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/test.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/test.js new file mode 100644 index 0000000..cbad034 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/test.js @@ -0,0 +1,55 @@ +/** + * Usage: node test.js + */ + +var mime = require('./mime'); +var assert = require('assert'); + +function eq(a, b) { + console.log('Test: ' + a + ' === ' + b); + assert.strictEqual.apply(null, arguments); +} + +console.log(Object.keys(mime.extensions).length + ' types'); +console.log(Object.keys(mime.types).length + ' extensions\n'); + +// +// Test mime lookups +// + +eq('text/plain', mime.lookup('text.txt')); +eq('text/plain', mime.lookup('.text.txt')); +eq('text/plain', mime.lookup('.txt')); +eq('text/plain', mime.lookup('txt')); +eq('application/octet-stream', mime.lookup('text.nope')); +eq('fallback', mime.lookup('text.fallback', 'fallback')); +eq('application/octet-stream', mime.lookup('constructor')); +eq('text/plain', mime.lookup('TEXT.TXT')); +eq('text/event-stream', mime.lookup('text/event-stream')); +eq('application/x-web-app-manifest+json', mime.lookup('text.webapp')); + +// +// Test extensions +// + +eq('txt', mime.extension(mime.types.text)); +eq('html', mime.extension(mime.types.htm)); +eq('bin', mime.extension('application/octet-stream')); +eq(undefined, mime.extension('constructor')); + +// +// Test node types +// + +eq('application/octet-stream', mime.lookup('file.buffer')); +eq('audio/mp4', mime.lookup('file.m4a')); + +// +// Test charsets +// + +eq('UTF-8', mime.charsets.lookup('text/plain')); +eq(undefined, mime.charsets.lookup(mime.types.js)); +eq('fallback', mime.charsets.lookup('application/octet-stream', 'fallback')); + +console.log('\nOK'); diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/types/mime.types b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/types/mime.types new file mode 100644 index 0000000..b3cae2e --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/types/mime.types @@ -0,0 +1,1510 @@ +# This file maps Internet media types to unique file extension(s). +# Although created for httpd, this file is used by many software systems +# and has been placed in the public domain for unlimited redisribution. +# +# The table below contains both registered and (common) unregistered types. +# A type that has no unique extension can be ignored -- they are listed +# here to guide configurations toward known types and to make it easier to +# identify "new" types. File extensions are also commonly used to indicate +# content languages and encodings, so choose them carefully. +# +# Internet media types should be registered as described in RFC 4288. +# The registry is at . +# +# MIME type (lowercased) Extensions +# ============================================ ========== +# application/1d-interleaved-parityfec +# application/3gpp-ims+xml +# application/activemessage +application/andrew-inset ez +# application/applefile +application/applixware aw +application/atom+xml atom +application/atomcat+xml atomcat +# application/atomicmail +application/atomsvc+xml atomsvc +# application/auth-policy+xml +# application/batch-smtp +# application/beep+xml +# application/calendar+xml +# application/cals-1840 +# application/ccmp+xml +application/ccxml+xml ccxml +application/cdmi-capability cdmia +application/cdmi-container cdmic +application/cdmi-domain cdmid +application/cdmi-object cdmio +application/cdmi-queue cdmiq +# application/cea-2018+xml +# application/cellml+xml +# application/cfw +# application/cnrp+xml +# application/commonground +# application/conference-info+xml +# application/cpl+xml +# application/csta+xml +# application/cstadata+xml +application/cu-seeme cu +# application/cybercash +application/davmount+xml davmount +# application/dca-rft +# application/dec-dx +# application/dialog-info+xml +# application/dicom +# application/dns +# application/dskpp+xml +application/dssc+der dssc +application/dssc+xml xdssc +# application/dvcs +application/ecmascript ecma +# application/edi-consent +# application/edi-x12 +# application/edifact +application/emma+xml emma +# application/epp+xml +application/epub+zip epub +# application/eshop +# application/example +application/exi exi +# application/fastinfoset +# application/fastsoap +# application/fits +application/font-tdpfr pfr +# application/framework-attributes+xml +# application/h224 +# application/held+xml +# application/http +application/hyperstudio stk +# application/ibe-key-request+xml +# application/ibe-pkg-reply+xml +# application/ibe-pp-data +# application/iges +# application/im-iscomposing+xml +# application/index +# application/index.cmd +# application/index.obj +# application/index.response +# application/index.vnd +application/inkml+xml ink inkml +# application/iotp +application/ipfix ipfix +# application/ipp +# application/isup +application/java-archive jar +application/java-serialized-object ser +application/java-vm class +application/javascript js +application/json json +# application/kpml-request+xml +# application/kpml-response+xml +application/lost+xml lostxml +application/mac-binhex40 hqx +application/mac-compactpro cpt +# application/macwriteii +application/mads+xml mads +application/marc mrc +application/marcxml+xml mrcx +application/mathematica ma nb mb +# application/mathml-content+xml +# application/mathml-presentation+xml +application/mathml+xml mathml +# application/mbms-associated-procedure-description+xml +# application/mbms-deregister+xml +# application/mbms-envelope+xml +# application/mbms-msk+xml +# application/mbms-msk-response+xml +# application/mbms-protection-description+xml +# application/mbms-reception-report+xml +# application/mbms-register+xml +# application/mbms-register-response+xml +# application/mbms-user-service-description+xml +application/mbox mbox +# application/media_control+xml +application/mediaservercontrol+xml mscml +application/metalink4+xml meta4 +application/mets+xml mets +# application/mikey +application/mods+xml mods +# application/moss-keys +# application/moss-signature +# application/mosskey-data +# application/mosskey-request +application/mp21 m21 mp21 +application/mp4 mp4s +# application/mpeg4-generic +# application/mpeg4-iod +# application/mpeg4-iod-xmt +# application/msc-ivr+xml +# application/msc-mixer+xml +application/msword doc dot +application/mxf mxf +# application/nasdata +# application/news-checkgroups +# application/news-groupinfo +# application/news-transmission +# application/nss +# application/ocsp-request +# application/ocsp-response +application/octet-stream bin dms lha lrf lzh so iso dmg dist distz pkg bpk dump elc deploy +application/oda oda +application/oebps-package+xml opf +application/ogg ogx +application/onenote onetoc onetoc2 onetmp onepkg +application/oxps oxps +# application/parityfec +application/patch-ops-error+xml xer +application/pdf pdf +application/pgp-encrypted pgp +# application/pgp-keys +application/pgp-signature asc sig +application/pics-rules prf +# application/pidf+xml +# application/pidf-diff+xml +application/pkcs10 p10 +application/pkcs7-mime p7m p7c +application/pkcs7-signature p7s +application/pkcs8 p8 +application/pkix-attr-cert ac +application/pkix-cert cer +application/pkix-crl crl +application/pkix-pkipath pkipath +application/pkixcmp pki +application/pls+xml pls +# application/poc-settings+xml +application/postscript ai eps ps +# application/prs.alvestrand.titrax-sheet +application/prs.cww cww +# application/prs.nprend +# application/prs.plucker +# application/prs.rdf-xml-crypt +# application/prs.xsf+xml +application/pskc+xml pskcxml +# application/qsig +application/rdf+xml rdf +application/reginfo+xml rif +application/relax-ng-compact-syntax rnc +# application/remote-printing +application/resource-lists+xml rl +application/resource-lists-diff+xml rld +# application/riscos +# application/rlmi+xml +application/rls-services+xml rs +application/rpki-ghostbusters gbr +application/rpki-manifest mft +application/rpki-roa roa +# application/rpki-updown +application/rsd+xml rsd +application/rss+xml rss +application/rtf rtf +# application/rtx +# application/samlassertion+xml +# application/samlmetadata+xml +application/sbml+xml sbml +application/scvp-cv-request scq +application/scvp-cv-response scs +application/scvp-vp-request spq +application/scvp-vp-response spp +application/sdp sdp +# application/set-payment +application/set-payment-initiation setpay +# application/set-registration +application/set-registration-initiation setreg +# application/sgml +# application/sgml-open-catalog +application/shf+xml shf +# application/sieve +# application/simple-filter+xml +# application/simple-message-summary +# application/simplesymbolcontainer +# application/slate +# application/smil +application/smil+xml smi smil +# application/soap+fastinfoset +# application/soap+xml +application/sparql-query rq +application/sparql-results+xml srx +# application/spirits-event+xml +application/srgs gram +application/srgs+xml grxml +application/sru+xml sru +application/ssml+xml ssml +# application/tamp-apex-update +# application/tamp-apex-update-confirm +# application/tamp-community-update +# application/tamp-community-update-confirm +# application/tamp-error +# application/tamp-sequence-adjust +# application/tamp-sequence-adjust-confirm +# application/tamp-status-query +# application/tamp-status-response +# application/tamp-update +# application/tamp-update-confirm +application/tei+xml tei teicorpus +application/thraud+xml tfi +# application/timestamp-query +# application/timestamp-reply +application/timestamped-data tsd +# application/tve-trigger +# application/ulpfec +# application/vcard+xml +# application/vemmi +# application/vividence.scriptfile +# application/vnd.3gpp.bsf+xml +application/vnd.3gpp.pic-bw-large plb +application/vnd.3gpp.pic-bw-small psb +application/vnd.3gpp.pic-bw-var pvb +# application/vnd.3gpp.sms +# application/vnd.3gpp2.bcmcsinfo+xml +# application/vnd.3gpp2.sms +application/vnd.3gpp2.tcap tcap +application/vnd.3m.post-it-notes pwn +application/vnd.accpac.simply.aso aso +application/vnd.accpac.simply.imp imp +application/vnd.acucobol acu +application/vnd.acucorp atc acutc +application/vnd.adobe.air-application-installer-package+zip air +application/vnd.adobe.fxp fxp fxpl +# application/vnd.adobe.partial-upload +application/vnd.adobe.xdp+xml xdp +application/vnd.adobe.xfdf xfdf +# application/vnd.aether.imp +# application/vnd.ah-barcode +application/vnd.ahead.space ahead +application/vnd.airzip.filesecure.azf azf +application/vnd.airzip.filesecure.azs azs +application/vnd.amazon.ebook azw +application/vnd.americandynamics.acc acc +application/vnd.amiga.ami ami +# application/vnd.amundsen.maze+xml +application/vnd.android.package-archive apk +application/vnd.anser-web-certificate-issue-initiation cii +application/vnd.anser-web-funds-transfer-initiation fti +application/vnd.antix.game-component atx +application/vnd.apple.installer+xml mpkg +application/vnd.apple.mpegurl m3u8 +# application/vnd.arastra.swi +application/vnd.aristanetworks.swi swi +application/vnd.astraea-software.iota iota +application/vnd.audiograph aep +# application/vnd.autopackage +# application/vnd.avistar+xml +application/vnd.blueice.multipass mpm +# application/vnd.bluetooth.ep.oob +application/vnd.bmi bmi +application/vnd.businessobjects rep +# application/vnd.cab-jscript +# application/vnd.canon-cpdl +# application/vnd.canon-lips +# application/vnd.cendio.thinlinc.clientconf +application/vnd.chemdraw+xml cdxml +application/vnd.chipnuts.karaoke-mmd mmd +application/vnd.cinderella cdy +# application/vnd.cirpack.isdn-ext +application/vnd.claymore cla +application/vnd.cloanto.rp9 rp9 +application/vnd.clonk.c4group c4g c4d c4f c4p c4u +application/vnd.cluetrust.cartomobile-config c11amc +application/vnd.cluetrust.cartomobile-config-pkg c11amz +# application/vnd.collection+json +# application/vnd.commerce-battelle +application/vnd.commonspace csp +application/vnd.contact.cmsg cdbcmsg +application/vnd.cosmocaller cmc +application/vnd.crick.clicker clkx +application/vnd.crick.clicker.keyboard clkk +application/vnd.crick.clicker.palette clkp +application/vnd.crick.clicker.template clkt +application/vnd.crick.clicker.wordbank clkw +application/vnd.criticaltools.wbs+xml wbs +application/vnd.ctc-posml pml +# application/vnd.ctct.ws+xml +# application/vnd.cups-pdf +# application/vnd.cups-postscript +application/vnd.cups-ppd ppd +# application/vnd.cups-raster +# application/vnd.cups-raw +# application/vnd.curl +application/vnd.curl.car car +application/vnd.curl.pcurl pcurl +# application/vnd.cybank +application/vnd.data-vision.rdz rdz +application/vnd.dece.data uvf uvvf uvd uvvd +application/vnd.dece.ttml+xml uvt uvvt +application/vnd.dece.unspecified uvx uvvx +application/vnd.dece.zip uvz uvvz +application/vnd.denovo.fcselayout-link fe_launch +# application/vnd.dir-bi.plate-dl-nosuffix +application/vnd.dna dna +application/vnd.dolby.mlp mlp +# application/vnd.dolby.mobile.1 +# application/vnd.dolby.mobile.2 +application/vnd.dpgraph dpg +application/vnd.dreamfactory dfac +application/vnd.dvb.ait ait +# application/vnd.dvb.dvbj +# application/vnd.dvb.esgcontainer +# application/vnd.dvb.ipdcdftnotifaccess +# application/vnd.dvb.ipdcesgaccess +# application/vnd.dvb.ipdcesgaccess2 +# application/vnd.dvb.ipdcesgpdd +# application/vnd.dvb.ipdcroaming +# application/vnd.dvb.iptv.alfec-base +# application/vnd.dvb.iptv.alfec-enhancement +# application/vnd.dvb.notif-aggregate-root+xml +# application/vnd.dvb.notif-container+xml +# application/vnd.dvb.notif-generic+xml +# application/vnd.dvb.notif-ia-msglist+xml +# application/vnd.dvb.notif-ia-registration-request+xml +# application/vnd.dvb.notif-ia-registration-response+xml +# application/vnd.dvb.notif-init+xml +# application/vnd.dvb.pfr +application/vnd.dvb.service svc +# application/vnd.dxr +application/vnd.dynageo geo +# application/vnd.easykaraoke.cdgdownload +# application/vnd.ecdis-update +application/vnd.ecowin.chart mag +# application/vnd.ecowin.filerequest +# application/vnd.ecowin.fileupdate +# application/vnd.ecowin.series +# application/vnd.ecowin.seriesrequest +# application/vnd.ecowin.seriesupdate +# application/vnd.emclient.accessrequest+xml +application/vnd.enliven nml +# application/vnd.eprints.data+xml +application/vnd.epson.esf esf +application/vnd.epson.msf msf +application/vnd.epson.quickanime qam +application/vnd.epson.salt slt +application/vnd.epson.ssf ssf +# application/vnd.ericsson.quickcall +application/vnd.eszigno3+xml es3 et3 +# application/vnd.etsi.aoc+xml +# application/vnd.etsi.cug+xml +# application/vnd.etsi.iptvcommand+xml +# application/vnd.etsi.iptvdiscovery+xml +# application/vnd.etsi.iptvprofile+xml +# application/vnd.etsi.iptvsad-bc+xml +# application/vnd.etsi.iptvsad-cod+xml +# application/vnd.etsi.iptvsad-npvr+xml +# application/vnd.etsi.iptvservice+xml +# application/vnd.etsi.iptvsync+xml +# application/vnd.etsi.iptvueprofile+xml +# application/vnd.etsi.mcid+xml +# application/vnd.etsi.overload-control-policy-dataset+xml +# application/vnd.etsi.sci+xml +# application/vnd.etsi.simservs+xml +# application/vnd.etsi.tsl+xml +# application/vnd.etsi.tsl.der +# application/vnd.eudora.data +application/vnd.ezpix-album ez2 +application/vnd.ezpix-package ez3 +# application/vnd.f-secure.mobile +application/vnd.fdf fdf +application/vnd.fdsn.mseed mseed +application/vnd.fdsn.seed seed dataless +# application/vnd.ffsns +# application/vnd.fints +application/vnd.flographit gph +application/vnd.fluxtime.clip ftc +# application/vnd.font-fontforge-sfd +application/vnd.framemaker fm frame maker book +application/vnd.frogans.fnc fnc +application/vnd.frogans.ltf ltf +application/vnd.fsc.weblaunch fsc +application/vnd.fujitsu.oasys oas +application/vnd.fujitsu.oasys2 oa2 +application/vnd.fujitsu.oasys3 oa3 +application/vnd.fujitsu.oasysgp fg5 +application/vnd.fujitsu.oasysprs bh2 +# application/vnd.fujixerox.art-ex +# application/vnd.fujixerox.art4 +# application/vnd.fujixerox.hbpl +application/vnd.fujixerox.ddd ddd +application/vnd.fujixerox.docuworks xdw +application/vnd.fujixerox.docuworks.binder xbd +# application/vnd.fut-misnet +application/vnd.fuzzysheet fzs +application/vnd.genomatix.tuxedo txd +# application/vnd.geocube+xml +application/vnd.geogebra.file ggb +application/vnd.geogebra.tool ggt +application/vnd.geometry-explorer gex gre +application/vnd.geonext gxt +application/vnd.geoplan g2w +application/vnd.geospace g3w +# application/vnd.globalplatform.card-content-mgt +# application/vnd.globalplatform.card-content-mgt-response +application/vnd.gmx gmx +application/vnd.google-earth.kml+xml kml +application/vnd.google-earth.kmz kmz +application/vnd.grafeq gqf gqs +# application/vnd.gridmp +application/vnd.groove-account gac +application/vnd.groove-help ghf +application/vnd.groove-identity-message gim +application/vnd.groove-injector grv +application/vnd.groove-tool-message gtm +application/vnd.groove-tool-template tpl +application/vnd.groove-vcard vcg +# application/vnd.hal+json +application/vnd.hal+xml hal +application/vnd.handheld-entertainment+xml zmm +application/vnd.hbci hbci +# application/vnd.hcl-bireports +application/vnd.hhe.lesson-player les +application/vnd.hp-hpgl hpgl +application/vnd.hp-hpid hpid +application/vnd.hp-hps hps +application/vnd.hp-jlyt jlt +application/vnd.hp-pcl pcl +application/vnd.hp-pclxl pclxl +# application/vnd.httphone +application/vnd.hydrostatix.sof-data sfd-hdstx +application/vnd.hzn-3d-crossword x3d +# application/vnd.ibm.afplinedata +# application/vnd.ibm.electronic-media +application/vnd.ibm.minipay mpy +application/vnd.ibm.modcap afp listafp list3820 +application/vnd.ibm.rights-management irm +application/vnd.ibm.secure-container sc +application/vnd.iccprofile icc icm +application/vnd.igloader igl +application/vnd.immervision-ivp ivp +application/vnd.immervision-ivu ivu +# application/vnd.informedcontrol.rms+xml +# application/vnd.informix-visionary +# application/vnd.infotech.project +# application/vnd.infotech.project+xml +application/vnd.insors.igm igm +application/vnd.intercon.formnet xpw xpx +application/vnd.intergeo i2g +# application/vnd.intertrust.digibox +# application/vnd.intertrust.nncp +application/vnd.intu.qbo qbo +application/vnd.intu.qfx qfx +# application/vnd.iptc.g2.conceptitem+xml +# application/vnd.iptc.g2.knowledgeitem+xml +# application/vnd.iptc.g2.newsitem+xml +# application/vnd.iptc.g2.packageitem+xml +application/vnd.ipunplugged.rcprofile rcprofile +application/vnd.irepository.package+xml irp +application/vnd.is-xpr xpr +application/vnd.isac.fcs fcs +application/vnd.jam jam +# application/vnd.japannet-directory-service +# application/vnd.japannet-jpnstore-wakeup +# application/vnd.japannet-payment-wakeup +# application/vnd.japannet-registration +# application/vnd.japannet-registration-wakeup +# application/vnd.japannet-setstore-wakeup +# application/vnd.japannet-verification +# application/vnd.japannet-verification-wakeup +application/vnd.jcp.javame.midlet-rms rms +application/vnd.jisp jisp +application/vnd.joost.joda-archive joda +application/vnd.kahootz ktz ktr +application/vnd.kde.karbon karbon +application/vnd.kde.kchart chrt +application/vnd.kde.kformula kfo +application/vnd.kde.kivio flw +application/vnd.kde.kontour kon +application/vnd.kde.kpresenter kpr kpt +application/vnd.kde.kspread ksp +application/vnd.kde.kword kwd kwt +application/vnd.kenameaapp htke +application/vnd.kidspiration kia +application/vnd.kinar kne knp +application/vnd.koan skp skd skt skm +application/vnd.kodak-descriptor sse +application/vnd.las.las+xml lasxml +# application/vnd.liberty-request+xml +application/vnd.llamagraphics.life-balance.desktop lbd +application/vnd.llamagraphics.life-balance.exchange+xml lbe +application/vnd.lotus-1-2-3 123 +application/vnd.lotus-approach apr +application/vnd.lotus-freelance pre +application/vnd.lotus-notes nsf +application/vnd.lotus-organizer org +application/vnd.lotus-screencam scm +application/vnd.lotus-wordpro lwp +application/vnd.macports.portpkg portpkg +# application/vnd.marlin.drm.actiontoken+xml +# application/vnd.marlin.drm.conftoken+xml +# application/vnd.marlin.drm.license+xml +# application/vnd.marlin.drm.mdcf +application/vnd.mcd mcd +application/vnd.medcalcdata mc1 +application/vnd.mediastation.cdkey cdkey +# application/vnd.meridian-slingshot +application/vnd.mfer mwf +application/vnd.mfmp mfm +application/vnd.micrografx.flo flo +application/vnd.micrografx.igx igx +application/vnd.mif mif +# application/vnd.minisoft-hp3000-save +# application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.mobius.daf daf +application/vnd.mobius.dis dis +application/vnd.mobius.mbk mbk +application/vnd.mobius.mqy mqy +application/vnd.mobius.msl msl +application/vnd.mobius.plc plc +application/vnd.mobius.txf txf +application/vnd.mophun.application mpn +application/vnd.mophun.certificate mpc +# application/vnd.motorola.flexsuite +# application/vnd.motorola.flexsuite.adsi +# application/vnd.motorola.flexsuite.fis +# application/vnd.motorola.flexsuite.gotap +# application/vnd.motorola.flexsuite.kmr +# application/vnd.motorola.flexsuite.ttc +# application/vnd.motorola.flexsuite.wem +# application/vnd.motorola.iprm +application/vnd.mozilla.xul+xml xul +application/vnd.ms-artgalry cil +# application/vnd.ms-asf +application/vnd.ms-cab-compressed cab +application/vnd.ms-excel xls xlm xla xlc xlt xlw +application/vnd.ms-excel.addin.macroenabled.12 xlam +application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb +application/vnd.ms-excel.sheet.macroenabled.12 xlsm +application/vnd.ms-excel.template.macroenabled.12 xltm +application/vnd.ms-fontobject eot +application/vnd.ms-htmlhelp chm +application/vnd.ms-ims ims +application/vnd.ms-lrm lrm +# application/vnd.ms-office.activex+xml +application/vnd.ms-officetheme thmx +application/vnd.ms-pki.seccat cat +application/vnd.ms-pki.stl stl +# application/vnd.ms-playready.initiator+xml +application/vnd.ms-powerpoint ppt pps pot +application/vnd.ms-powerpoint.addin.macroenabled.12 ppam +application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm +application/vnd.ms-powerpoint.slide.macroenabled.12 sldm +application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm +application/vnd.ms-powerpoint.template.macroenabled.12 potm +application/vnd.ms-project mpp mpt +# application/vnd.ms-tnef +# application/vnd.ms-wmdrm.lic-chlg-req +# application/vnd.ms-wmdrm.lic-resp +# application/vnd.ms-wmdrm.meter-chlg-req +# application/vnd.ms-wmdrm.meter-resp +application/vnd.ms-word.document.macroenabled.12 docm +application/vnd.ms-word.template.macroenabled.12 dotm +application/vnd.ms-works wps wks wcm wdb +application/vnd.ms-wpl wpl +application/vnd.ms-xpsdocument xps +application/vnd.mseq mseq +# application/vnd.msign +# application/vnd.multiad.creator +# application/vnd.multiad.creator.cif +# application/vnd.music-niff +application/vnd.musician mus +application/vnd.muvee.style msty +application/vnd.mynfc taglet +# application/vnd.ncd.control +# application/vnd.ncd.reference +# application/vnd.nervana +# application/vnd.netfpx +application/vnd.neurolanguage.nlu nlu +application/vnd.noblenet-directory nnd +application/vnd.noblenet-sealer nns +application/vnd.noblenet-web nnw +# application/vnd.nokia.catalogs +# application/vnd.nokia.conml+wbxml +# application/vnd.nokia.conml+xml +# application/vnd.nokia.isds-radio-presets +# application/vnd.nokia.iptv.config+xml +# application/vnd.nokia.landmark+wbxml +# application/vnd.nokia.landmark+xml +# application/vnd.nokia.landmarkcollection+xml +# application/vnd.nokia.n-gage.ac+xml +application/vnd.nokia.n-gage.data ngdat +application/vnd.nokia.n-gage.symbian.install n-gage +# application/vnd.nokia.ncd +# application/vnd.nokia.pcd+wbxml +# application/vnd.nokia.pcd+xml +application/vnd.nokia.radio-preset rpst +application/vnd.nokia.radio-presets rpss +application/vnd.novadigm.edm edm +application/vnd.novadigm.edx edx +application/vnd.novadigm.ext ext +# application/vnd.ntt-local.file-transfer +# application/vnd.ntt-local.sip-ta_remote +# application/vnd.ntt-local.sip-ta_tcp_stream +application/vnd.oasis.opendocument.chart odc +application/vnd.oasis.opendocument.chart-template otc +application/vnd.oasis.opendocument.database odb +application/vnd.oasis.opendocument.formula odf +application/vnd.oasis.opendocument.formula-template odft +application/vnd.oasis.opendocument.graphics odg +application/vnd.oasis.opendocument.graphics-template otg +application/vnd.oasis.opendocument.image odi +application/vnd.oasis.opendocument.image-template oti +application/vnd.oasis.opendocument.presentation odp +application/vnd.oasis.opendocument.presentation-template otp +application/vnd.oasis.opendocument.spreadsheet ods +application/vnd.oasis.opendocument.spreadsheet-template ots +application/vnd.oasis.opendocument.text odt +application/vnd.oasis.opendocument.text-master odm +application/vnd.oasis.opendocument.text-template ott +application/vnd.oasis.opendocument.text-web oth +# application/vnd.obn +# application/vnd.oftn.l10n+json +# application/vnd.oipf.contentaccessdownload+xml +# application/vnd.oipf.contentaccessstreaming+xml +# application/vnd.oipf.cspg-hexbinary +# application/vnd.oipf.dae.svg+xml +# application/vnd.oipf.dae.xhtml+xml +# application/vnd.oipf.mippvcontrolmessage+xml +# application/vnd.oipf.pae.gem +# application/vnd.oipf.spdiscovery+xml +# application/vnd.oipf.spdlist+xml +# application/vnd.oipf.ueprofile+xml +# application/vnd.oipf.userprofile+xml +application/vnd.olpc-sugar xo +# application/vnd.oma-scws-config +# application/vnd.oma-scws-http-request +# application/vnd.oma-scws-http-response +# application/vnd.oma.bcast.associated-procedure-parameter+xml +# application/vnd.oma.bcast.drm-trigger+xml +# application/vnd.oma.bcast.imd+xml +# application/vnd.oma.bcast.ltkm +# application/vnd.oma.bcast.notification+xml +# application/vnd.oma.bcast.provisioningtrigger +# application/vnd.oma.bcast.sgboot +# application/vnd.oma.bcast.sgdd+xml +# application/vnd.oma.bcast.sgdu +# application/vnd.oma.bcast.simple-symbol-container +# application/vnd.oma.bcast.smartcard-trigger+xml +# application/vnd.oma.bcast.sprov+xml +# application/vnd.oma.bcast.stkm +# application/vnd.oma.cab-address-book+xml +# application/vnd.oma.cab-feature-handler+xml +# application/vnd.oma.cab-pcc+xml +# application/vnd.oma.cab-user-prefs+xml +# application/vnd.oma.dcd +# application/vnd.oma.dcdc +application/vnd.oma.dd2+xml dd2 +# application/vnd.oma.drm.risd+xml +# application/vnd.oma.group-usage-list+xml +# application/vnd.oma.pal+xml +# application/vnd.oma.poc.detailed-progress-report+xml +# application/vnd.oma.poc.final-report+xml +# application/vnd.oma.poc.groups+xml +# application/vnd.oma.poc.invocation-descriptor+xml +# application/vnd.oma.poc.optimized-progress-report+xml +# application/vnd.oma.push +# application/vnd.oma.scidm.messages+xml +# application/vnd.oma.xcap-directory+xml +# application/vnd.omads-email+xml +# application/vnd.omads-file+xml +# application/vnd.omads-folder+xml +# application/vnd.omaloc-supl-init +application/vnd.openofficeorg.extension oxt +# application/vnd.openxmlformats-officedocument.custom-properties+xml +# application/vnd.openxmlformats-officedocument.customxmlproperties+xml +# application/vnd.openxmlformats-officedocument.drawing+xml +# application/vnd.openxmlformats-officedocument.drawingml.chart+xml +# application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml +# application/vnd.openxmlformats-officedocument.extended-properties+xml +# application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml +# application/vnd.openxmlformats-officedocument.presentationml.comments+xml +# application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml +application/vnd.openxmlformats-officedocument.presentationml.presentation pptx +# application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.presprops+xml +application/vnd.openxmlformats-officedocument.presentationml.slide sldx +# application/vnd.openxmlformats-officedocument.presentationml.slide+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml +application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx +# application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml +# application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml +# application/vnd.openxmlformats-officedocument.presentationml.tags+xml +application/vnd.openxmlformats-officedocument.presentationml.template potx +# application/vnd.openxmlformats-officedocument.presentationml.template.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx +# application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml +# application/vnd.openxmlformats-officedocument.theme+xml +# application/vnd.openxmlformats-officedocument.themeoverride+xml +# application/vnd.openxmlformats-officedocument.vmldrawing +# application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.document docx +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx +# application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml +# application/vnd.openxmlformats-package.core-properties+xml +# application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml +# application/vnd.openxmlformats-package.relationships+xml +# application/vnd.quobject-quoxdocument +# application/vnd.osa.netdeploy +application/vnd.osgeo.mapguide.package mgp +# application/vnd.osgi.bundle +application/vnd.osgi.dp dp +# application/vnd.otps.ct-kip+xml +application/vnd.palm pdb pqa oprc +# application/vnd.paos.xml +application/vnd.pawaafile paw +application/vnd.pg.format str +application/vnd.pg.osasli ei6 +# application/vnd.piaccess.application-licence +application/vnd.picsel efif +application/vnd.pmi.widget wg +# application/vnd.poc.group-advertisement+xml +application/vnd.pocketlearn plf +application/vnd.powerbuilder6 pbd +# application/vnd.powerbuilder6-s +# application/vnd.powerbuilder7 +# application/vnd.powerbuilder7-s +# application/vnd.powerbuilder75 +# application/vnd.powerbuilder75-s +# application/vnd.preminet +application/vnd.previewsystems.box box +application/vnd.proteus.magazine mgz +application/vnd.publishare-delta-tree qps +application/vnd.pvi.ptid1 ptid +# application/vnd.pwg-multiplexed +# application/vnd.pwg-xhtml-print+xml +# application/vnd.qualcomm.brew-app-res +application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb +# application/vnd.radisys.moml+xml +# application/vnd.radisys.msml+xml +# application/vnd.radisys.msml-audit+xml +# application/vnd.radisys.msml-audit-conf+xml +# application/vnd.radisys.msml-audit-conn+xml +# application/vnd.radisys.msml-audit-dialog+xml +# application/vnd.radisys.msml-audit-stream+xml +# application/vnd.radisys.msml-conf+xml +# application/vnd.radisys.msml-dialog+xml +# application/vnd.radisys.msml-dialog-base+xml +# application/vnd.radisys.msml-dialog-fax-detect+xml +# application/vnd.radisys.msml-dialog-fax-sendrecv+xml +# application/vnd.radisys.msml-dialog-group+xml +# application/vnd.radisys.msml-dialog-speech+xml +# application/vnd.radisys.msml-dialog-transform+xml +# application/vnd.rainstor.data +# application/vnd.rapid +application/vnd.realvnc.bed bed +application/vnd.recordare.musicxml mxl +application/vnd.recordare.musicxml+xml musicxml +# application/vnd.renlearn.rlprint +application/vnd.rig.cryptonote cryptonote +application/vnd.rim.cod cod +application/vnd.rn-realmedia rm +application/vnd.route66.link66+xml link66 +# application/vnd.ruckus.download +# application/vnd.s3sms +application/vnd.sailingtracker.track st +# application/vnd.sbm.cid +# application/vnd.sbm.mid2 +# application/vnd.scribus +# application/vnd.sealed.3df +# application/vnd.sealed.csf +# application/vnd.sealed.doc +# application/vnd.sealed.eml +# application/vnd.sealed.mht +# application/vnd.sealed.net +# application/vnd.sealed.ppt +# application/vnd.sealed.tiff +# application/vnd.sealed.xls +# application/vnd.sealedmedia.softseal.html +# application/vnd.sealedmedia.softseal.pdf +application/vnd.seemail see +application/vnd.sema sema +application/vnd.semd semd +application/vnd.semf semf +application/vnd.shana.informed.formdata ifm +application/vnd.shana.informed.formtemplate itp +application/vnd.shana.informed.interchange iif +application/vnd.shana.informed.package ipk +application/vnd.simtech-mindmapper twd twds +application/vnd.smaf mmf +# application/vnd.smart.notebook +application/vnd.smart.teacher teacher +# application/vnd.software602.filler.form+xml +# application/vnd.software602.filler.form-xml-zip +application/vnd.solent.sdkm+xml sdkm sdkd +application/vnd.spotfire.dxp dxp +application/vnd.spotfire.sfs sfs +# application/vnd.sss-cod +# application/vnd.sss-dtf +# application/vnd.sss-ntf +application/vnd.stardivision.calc sdc +application/vnd.stardivision.draw sda +application/vnd.stardivision.impress sdd +application/vnd.stardivision.math smf +application/vnd.stardivision.writer sdw vor +application/vnd.stardivision.writer-global sgl +application/vnd.stepmania.package smzip +application/vnd.stepmania.stepchart sm +# application/vnd.street-stream +application/vnd.sun.xml.calc sxc +application/vnd.sun.xml.calc.template stc +application/vnd.sun.xml.draw sxd +application/vnd.sun.xml.draw.template std +application/vnd.sun.xml.impress sxi +application/vnd.sun.xml.impress.template sti +application/vnd.sun.xml.math sxm +application/vnd.sun.xml.writer sxw +application/vnd.sun.xml.writer.global sxg +application/vnd.sun.xml.writer.template stw +# application/vnd.sun.wadl+xml +application/vnd.sus-calendar sus susp +application/vnd.svd svd +# application/vnd.swiftview-ics +application/vnd.symbian.install sis sisx +application/vnd.syncml+xml xsm +application/vnd.syncml.dm+wbxml bdm +application/vnd.syncml.dm+xml xdm +# application/vnd.syncml.dm.notification +# application/vnd.syncml.ds.notification +application/vnd.tao.intent-module-archive tao +application/vnd.tcpdump.pcap pcap cap dmp +application/vnd.tmobile-livetv tmo +application/vnd.trid.tpt tpt +application/vnd.triscape.mxs mxs +application/vnd.trueapp tra +# application/vnd.truedoc +# application/vnd.ubisoft.webplayer +application/vnd.ufdl ufd ufdl +application/vnd.uiq.theme utz +application/vnd.umajin umj +application/vnd.unity unityweb +application/vnd.uoml+xml uoml +# application/vnd.uplanet.alert +# application/vnd.uplanet.alert-wbxml +# application/vnd.uplanet.bearer-choice +# application/vnd.uplanet.bearer-choice-wbxml +# application/vnd.uplanet.cacheop +# application/vnd.uplanet.cacheop-wbxml +# application/vnd.uplanet.channel +# application/vnd.uplanet.channel-wbxml +# application/vnd.uplanet.list +# application/vnd.uplanet.list-wbxml +# application/vnd.uplanet.listcmd +# application/vnd.uplanet.listcmd-wbxml +# application/vnd.uplanet.signal +application/vnd.vcx vcx +# application/vnd.vd-study +# application/vnd.vectorworks +# application/vnd.verimatrix.vcas +# application/vnd.vidsoft.vidconference +application/vnd.visio vsd vst vss vsw +application/vnd.visionary vis +# application/vnd.vividence.scriptfile +application/vnd.vsf vsf +# application/vnd.wap.sic +# application/vnd.wap.slc +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo wtb +# application/vnd.wfa.wsc +# application/vnd.wmc +# application/vnd.wmf.bootstrap +# application/vnd.wolfram.mathematica +# application/vnd.wolfram.mathematica.package +application/vnd.wolfram.player nbp +application/vnd.wordperfect wpd +application/vnd.wqd wqd +# application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf stf +# application/vnd.wv.csp+wbxml +# application/vnd.wv.csp+xml +# application/vnd.wv.ssp+xml +application/vnd.xara xar +application/vnd.xfdl xfdl +# application/vnd.xfdl.webform +# application/vnd.xmi+xml +# application/vnd.xmpie.cpkg +# application/vnd.xmpie.dpkg +# application/vnd.xmpie.plan +# application/vnd.xmpie.ppkg +# application/vnd.xmpie.xlim +application/vnd.yamaha.hv-dic hvd +application/vnd.yamaha.hv-script hvs +application/vnd.yamaha.hv-voice hvp +application/vnd.yamaha.openscoreformat osf +application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg +# application/vnd.yamaha.remote-setup +application/vnd.yamaha.smaf-audio saf +application/vnd.yamaha.smaf-phrase spf +# application/vnd.yamaha.through-ngn +# application/vnd.yamaha.tunnel-udpencap +application/vnd.yellowriver-custom-menu cmp +application/vnd.zul zir zirz +application/vnd.zzazz.deck+xml zaz +application/voicexml+xml vxml +# application/vq-rtcpxr +# application/watcherinfo+xml +# application/whoispp-query +# application/whoispp-response +application/widget wgt +application/winhlp hlp +# application/wita +# application/wordperfect5.1 +application/wsdl+xml wsdl +application/wspolicy+xml wspolicy +application/x-7z-compressed 7z +application/x-abiword abw +application/x-ace-compressed ace +application/x-authorware-bin aab x32 u32 vox +application/x-authorware-map aam +application/x-authorware-seg aas +application/x-bcpio bcpio +application/x-bittorrent torrent +application/x-bzip bz +application/x-bzip2 bz2 boz +application/x-cdlink vcd +application/x-chat chat +application/x-chess-pgn pgn +# application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-debian-package deb udeb +application/x-director dir dcr dxr cst cct cxt w3d fgd swa +application/x-doom wad +application/x-dtbncx+xml ncx +application/x-dtbook+xml dtb +application/x-dtbresource+xml res +application/x-dvi dvi +application/x-font-bdf bdf +# application/x-font-dos +# application/x-font-framemaker +application/x-font-ghostscript gsf +# application/x-font-libgrx +application/x-font-linux-psf psf +application/x-font-otf otf +application/x-font-pcf pcf +application/x-font-snf snf +# application/x-font-speedo +# application/x-font-sunos-news +application/x-font-ttf ttf ttc +application/x-font-type1 pfa pfb pfm afm +application/x-font-woff woff +# application/x-font-vfont +application/x-futuresplash spl +application/x-gnumeric gnumeric +application/x-gtar gtar +# application/x-gzip +application/x-hdf hdf +application/x-java-jnlp-file jnlp +application/x-latex latex +application/x-mobipocket-ebook prc mobi +application/x-ms-application application +application/x-ms-wmd wmd +application/x-ms-wmz wmz +application/x-ms-xbap xbap +application/x-msaccess mdb +application/x-msbinder obd +application/x-mscardfile crd +application/x-msclip clp +application/x-msdownload exe dll com bat msi +application/x-msmediaview mvb m13 m14 +application/x-msmetafile wmf +application/x-msmoney mny +application/x-mspublisher pub +application/x-msschedule scd +application/x-msterminal trm +application/x-mswrite wri +application/x-netcdf nc cdf +application/x-pkcs12 p12 pfx +application/x-pkcs7-certificates p7b spc +application/x-pkcs7-certreqresp p7r +application/x-rar-compressed rar +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-silverlight-app xap +application/x-stuffit sit +application/x-stuffitx sitx +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-tex-tfm tfm +application/x-texinfo texinfo texi +application/x-ustar ustar +application/x-wais-source src +application/x-x509-ca-cert der crt +application/x-xfig fig +application/x-xpinstall xpi +# application/x400-bp +# application/xcap-att+xml +# application/xcap-caps+xml +application/xcap-diff+xml xdf +# application/xcap-el+xml +# application/xcap-error+xml +# application/xcap-ns+xml +# application/xcon-conference-info-diff+xml +# application/xcon-conference-info+xml +application/xenc+xml xenc +application/xhtml+xml xhtml xht +# application/xhtml-voice+xml +application/xml xml xsl +application/xml-dtd dtd +# application/xml-external-parsed-entity +# application/xmpp+xml +application/xop+xml xop +application/xslt+xml xslt +application/xspf+xml xspf +application/xv+xml mxml xhvml xvml xvm +application/yang yang +application/yin+xml yin +application/zip zip +# audio/1d-interleaved-parityfec +# audio/32kadpcm +# audio/3gpp +# audio/3gpp2 +# audio/ac3 +audio/adpcm adp +# audio/amr +# audio/amr-wb +# audio/amr-wb+ +# audio/asc +# audio/atrac-advanced-lossless +# audio/atrac-x +# audio/atrac3 +audio/basic au snd +# audio/bv16 +# audio/bv32 +# audio/clearmode +# audio/cn +# audio/dat12 +# audio/dls +# audio/dsr-es201108 +# audio/dsr-es202050 +# audio/dsr-es202211 +# audio/dsr-es202212 +# audio/dv +# audio/dvi4 +# audio/eac3 +# audio/evrc +# audio/evrc-qcp +# audio/evrc0 +# audio/evrc1 +# audio/evrcb +# audio/evrcb0 +# audio/evrcb1 +# audio/evrcwb +# audio/evrcwb0 +# audio/evrcwb1 +# audio/example +# audio/fwdred +# audio/g719 +# audio/g722 +# audio/g7221 +# audio/g723 +# audio/g726-16 +# audio/g726-24 +# audio/g726-32 +# audio/g726-40 +# audio/g728 +# audio/g729 +# audio/g7291 +# audio/g729d +# audio/g729e +# audio/gsm +# audio/gsm-efr +# audio/gsm-hr-08 +# audio/ilbc +# audio/ip-mr_v2.5 +# audio/l16 +# audio/l20 +# audio/l24 +# audio/l8 +# audio/lpc +audio/midi mid midi kar rmi +# audio/mobile-xmf +audio/mp4 mp4a +# audio/mp4a-latm +# audio/mpa +# audio/mpa-robust +audio/mpeg mpga mp2 mp2a mp3 m2a m3a +# audio/mpeg4-generic +audio/ogg oga ogg spx +# audio/parityfec +# audio/pcma +# audio/pcma-wb +# audio/pcmu-wb +# audio/pcmu +# audio/prs.sid +# audio/qcelp +# audio/red +# audio/rtp-enc-aescm128 +# audio/rtp-midi +# audio/rtx +# audio/smv +# audio/smv0 +# audio/smv-qcp +# audio/sp-midi +# audio/speex +# audio/t140c +# audio/t38 +# audio/telephone-event +# audio/tone +# audio/uemclip +# audio/ulpfec +# audio/vdvi +# audio/vmr-wb +# audio/vnd.3gpp.iufp +# audio/vnd.4sb +# audio/vnd.audiokoz +# audio/vnd.celp +# audio/vnd.cisco.nse +# audio/vnd.cmles.radio-events +# audio/vnd.cns.anp1 +# audio/vnd.cns.inf1 +audio/vnd.dece.audio uva uvva +audio/vnd.digital-winds eol +# audio/vnd.dlna.adts +# audio/vnd.dolby.heaac.1 +# audio/vnd.dolby.heaac.2 +# audio/vnd.dolby.mlp +# audio/vnd.dolby.mps +# audio/vnd.dolby.pl2 +# audio/vnd.dolby.pl2x +# audio/vnd.dolby.pl2z +# audio/vnd.dolby.pulse.1 +audio/vnd.dra dra +audio/vnd.dts dts +audio/vnd.dts.hd dtshd +# audio/vnd.dvb.file dvb +# audio/vnd.everad.plj +# audio/vnd.hns.audio +audio/vnd.lucent.voice lvp +audio/vnd.ms-playready.media.pya pya +# audio/vnd.nokia.mobile-xmf +# audio/vnd.nortel.vbk +audio/vnd.nuera.ecelp4800 ecelp4800 +audio/vnd.nuera.ecelp7470 ecelp7470 +audio/vnd.nuera.ecelp9600 ecelp9600 +# audio/vnd.octel.sbc +# audio/vnd.qcelp +# audio/vnd.rhetorex.32kadpcm +audio/vnd.rip rip +# audio/vnd.sealedmedia.softseal.mpeg +# audio/vnd.vmx.cvsd +# audio/vorbis +# audio/vorbis-config +audio/webm weba +audio/x-aac aac +audio/x-aiff aif aiff aifc +audio/x-mpegurl m3u +audio/x-ms-wax wax +audio/x-ms-wma wma +audio/x-pn-realaudio ram ra +audio/x-pn-realaudio-plugin rmp +audio/x-wav wav +chemical/x-cdx cdx +chemical/x-cif cif +chemical/x-cmdf cmdf +chemical/x-cml cml +chemical/x-csml csml +# chemical/x-pdb +chemical/x-xyz xyz +image/bmp bmp +image/cgm cgm +# image/example +# image/fits +image/g3fax g3 +image/gif gif +image/ief ief +# image/jp2 +image/jpeg jpeg jpg jpe +# image/jpm +# image/jpx +image/ktx ktx +# image/naplps +image/png png +image/prs.btif btif +# image/prs.pti +image/svg+xml svg svgz +# image/t38 +image/tiff tiff tif +# image/tiff-fx +image/vnd.adobe.photoshop psd +# image/vnd.cns.inf2 +image/vnd.dece.graphic uvi uvvi uvg uvvg +image/vnd.dvb.subtitle sub +image/vnd.djvu djvu djv +image/vnd.dwg dwg +image/vnd.dxf dxf +image/vnd.fastbidsheet fbs +image/vnd.fpx fpx +image/vnd.fst fst +image/vnd.fujixerox.edmics-mmr mmr +image/vnd.fujixerox.edmics-rlc rlc +# image/vnd.globalgraphics.pgb +# image/vnd.microsoft.icon +# image/vnd.mix +image/vnd.ms-modi mdi +image/vnd.net-fpx npx +# image/vnd.radiance +# image/vnd.sealed.png +# image/vnd.sealedmedia.softseal.gif +# image/vnd.sealedmedia.softseal.jpg +# image/vnd.svf +image/vnd.wap.wbmp wbmp +image/vnd.xiff xif +image/webp webp +image/x-cmu-raster ras +image/x-cmx cmx +image/x-freehand fh fhc fh4 fh5 fh7 +image/x-icon ico +image/x-pcx pcx +image/x-pict pic pct +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +# message/cpim +# message/delivery-status +# message/disposition-notification +# message/example +# message/external-body +# message/feedback-report +# message/global +# message/global-delivery-status +# message/global-disposition-notification +# message/global-headers +# message/http +# message/imdn+xml +# message/news +# message/partial +message/rfc822 eml mime +# message/s-http +# message/sip +# message/sipfrag +# message/tracking-status +# message/vnd.si.simp +# model/example +model/iges igs iges +model/mesh msh mesh silo +model/vnd.collada+xml dae +model/vnd.dwf dwf +# model/vnd.flatland.3dml +model/vnd.gdl gdl +# model/vnd.gs-gdl +# model/vnd.gs.gdl +model/vnd.gtw gtw +# model/vnd.moml+xml +model/vnd.mts mts +# model/vnd.parasolid.transmit.binary +# model/vnd.parasolid.transmit.text +model/vnd.vtu vtu +model/vrml wrl vrml +# multipart/alternative +# multipart/appledouble +# multipart/byteranges +# multipart/digest +# multipart/encrypted +# multipart/example +# multipart/form-data +# multipart/header-set +# multipart/mixed +# multipart/parallel +# multipart/related +# multipart/report +# multipart/signed +# multipart/voice-message +# text/1d-interleaved-parityfec +text/calendar ics ifb +text/css css +text/csv csv +# text/directory +# text/dns +# text/ecmascript +# text/enriched +# text/example +# text/fwdred +text/html html htm +# text/javascript +text/n3 n3 +# text/parityfec +text/plain txt text conf def list log in +# text/prs.fallenstein.rst +text/prs.lines.tag dsc +# text/vnd.radisys.msml-basic-layout +# text/red +# text/rfc822-headers +text/richtext rtx +# text/rtf +# text/rtp-enc-aescm128 +# text/rtx +text/sgml sgml sgm +# text/t140 +text/tab-separated-values tsv +text/troff t tr roff man me ms +text/turtle ttl +# text/ulpfec +text/uri-list uri uris urls +text/vcard vcard +# text/vnd.abc +text/vnd.curl curl +text/vnd.curl.dcurl dcurl +text/vnd.curl.scurl scurl +text/vnd.curl.mcurl mcurl +# text/vnd.dmclientscript +text/vnd.dvb.subtitle sub +# text/vnd.esmertec.theme-descriptor +text/vnd.fly fly +text/vnd.fmi.flexstor flx +text/vnd.graphviz gv +text/vnd.in3d.3dml 3dml +text/vnd.in3d.spot spot +# text/vnd.iptc.newsml +# text/vnd.iptc.nitf +# text/vnd.latex-z +# text/vnd.motorola.reflex +# text/vnd.ms-mediapackage +# text/vnd.net2phone.commcenter.command +# text/vnd.si.uricatalogue +text/vnd.sun.j2me.app-descriptor jad +# text/vnd.trolltech.linguist +# text/vnd.wap.si +# text/vnd.wap.sl +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/x-asm s asm +text/x-c c cc cxx cpp h hh dic +text/x-fortran f for f77 f90 +text/x-pascal p pas +text/x-java-source java +text/x-setext etx +text/x-uuencode uu +text/x-vcalendar vcs +text/x-vcard vcf +# text/xml +# text/xml-external-parsed-entity +# video/1d-interleaved-parityfec +video/3gpp 3gp +# video/3gpp-tt +video/3gpp2 3g2 +# video/bmpeg +# video/bt656 +# video/celb +# video/dv +# video/example +video/h261 h261 +video/h263 h263 +# video/h263-1998 +# video/h263-2000 +video/h264 h264 +# video/h264-rcdo +# video/h264-svc +video/jpeg jpgv +# video/jpeg2000 +video/jpm jpm jpgm +video/mj2 mj2 mjp2 +# video/mp1s +# video/mp2p +# video/mp2t +video/mp4 mp4 mp4v mpg4 +# video/mp4v-es +video/mpeg mpeg mpg mpe m1v m2v +# video/mpeg4-generic +# video/mpv +# video/nv +video/ogg ogv +# video/parityfec +# video/pointer +video/quicktime qt mov +# video/raw +# video/rtp-enc-aescm128 +# video/rtx +# video/smpte292m +# video/ulpfec +# video/vc1 +# video/vnd.cctv +video/vnd.dece.hd uvh uvvh +video/vnd.dece.mobile uvm uvvm +# video/vnd.dece.mp4 +video/vnd.dece.pd uvp uvvp +video/vnd.dece.sd uvs uvvs +video/vnd.dece.video uvv uvvv +# video/vnd.directv.mpeg +# video/vnd.directv.mpeg-tts +# video/vnd.dlna.mpeg-tts +video/vnd.dvb.file dvb +video/vnd.fvt fvt +# video/vnd.hns.video +# video/vnd.iptvforum.1dparityfec-1010 +# video/vnd.iptvforum.1dparityfec-2005 +# video/vnd.iptvforum.2dparityfec-1010 +# video/vnd.iptvforum.2dparityfec-2005 +# video/vnd.iptvforum.ttsavc +# video/vnd.iptvforum.ttsmpeg2 +# video/vnd.motorola.video +# video/vnd.motorola.videop +video/vnd.mpegurl mxu m4u +video/vnd.ms-playready.media.pyv pyv +# video/vnd.nokia.interleaved-multimedia +# video/vnd.nokia.videovoip +# video/vnd.objectvideo +# video/vnd.sealed.mpeg1 +# video/vnd.sealed.mpeg4 +# video/vnd.sealed.swf +# video/vnd.sealedmedia.softseal.mov +video/vnd.uvvu.mp4 uvu uvvu +video/vnd.vivo viv +video/webm webm +video/x-f4v f4v +video/x-fli fli +video/x-flv flv +video/x-m4v m4v +video/x-ms-asf asf asx +video/x-ms-wm wm +video/x-ms-wmv wmv +video/x-ms-wmx wmx +video/x-ms-wvx wvx +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/types/node.types b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/types/node.types new file mode 100644 index 0000000..b7fe8c0 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/mime/types/node.types @@ -0,0 +1,65 @@ +# What: Google Chrome Extension +# Why: To allow apps to (work) be served with the right content type header. +# http://codereview.chromium.org/2830017 +# Added by: niftylettuce +application/x-chrome-extension crx + +# What: OTF Message Silencer +# Why: To silence the "Resource interpreted as font but transferred with MIME +# type font/otf" message that occurs in Google Chrome +# Added by: niftylettuce +font/opentype otf + +# What: HTC support +# Why: To properly render .htc files such as CSS3PIE +# Added by: niftylettuce +text/x-component htc + +# What: HTML5 application cache manifest +# Why: De-facto standard. Required by Mozilla browser when serving HTML5 apps +# per https://developer.mozilla.org/en/offline_resources_in_firefox +# Added by: louisremi +text/cache-manifest appcache manifest + +# What: node binary buffer format +# Why: semi-standard extension w/in the node community +# Added by: tootallnate +application/octet-stream buffer + +# What: The "protected" MP-4 formats used by iTunes. +# Why: Required for streaming music to browsers (?) +# Added by: broofa +application/mp4 m4p +audio/mp4 m4a + +# What: Music playlist format (http://en.wikipedia.org/wiki/M3U) +# Why: See https://github.com/bentomas/node-mime/pull/6 +# Added by: mjrusso +application/x-mpegURL m3u8 + +# What: Video format, Part of RFC1890 +# Why: See https://github.com/bentomas/node-mime/pull/6 +# Added by: mjrusso +video/MP2T ts + +# What: The FLAC lossless codec format +# Why: Streaming and serving FLAC audio +# Added by: jacobrask +audio/flac flac + +# What: EventSource mime type +# Why: mime type of Server-Sent Events stream +# http://www.w3.org/TR/eventsource/#text-event-stream +# Added by: francois2metz +text/event-stream event-stream + +# What: Mozilla App manifest mime type +# Why: https://developer.mozilla.org/en/Apps/Manifest#Serving_manifests +# Added by: ednapiranha +application/x-web-app-manifest+json webapp + +# What: Matroska Mime Types +# Why: http://en.wikipedia.org/wiki/Matroska +# Added by: aduncan88 +video/x-matroska mkv +audio/x-matroska mka diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/.npmignore b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/History.md b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/History.md new file mode 100644 index 0000000..82df7b1 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/History.md @@ -0,0 +1,15 @@ + +0.0.4 / 2012-06-17 +================== + + * changed: ret -1 for unsatisfiable and -2 when invalid + +0.0.3 / 2012-06-17 +================== + + * fix last-byte-pos default to len - 1 + +0.0.2 / 2012-06-14 +================== + + * add `.type` diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/Makefile b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/Readme.md b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/Readme.md new file mode 100644 index 0000000..b2a67fe --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/Readme.md @@ -0,0 +1,28 @@ + +# node-range-parser + + Range header field parser. + +## Example: + +```js +assert(-1 == parse(200, 'bytes=500-20')); +assert(-2 == parse(200, 'bytes=malformed')); +parse(200, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 199 }])); +parse(1000, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 499 }])); +parse(1000, 'bytes=40-80').should.eql(arr('bytes', [{ start: 40, end: 80 }])); +parse(1000, 'bytes=-500').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=-400').should.eql(arr('bytes', [{ start: 600, end: 999 }])); +parse(1000, 'bytes=500-').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=400-').should.eql(arr('bytes', [{ start: 400, end: 999 }])); +parse(1000, 'bytes=0-0').should.eql(arr('bytes', [{ start: 0, end: 0 }])); +parse(1000, 'bytes=-1').should.eql(arr('bytes', [{ start: 999, end: 999 }])); +parse(1000, 'items=0-5').should.eql(arr('items', [{ start: 0, end: 5 }])); +parse(1000, 'bytes=40-80,-1').should.eql(arr('bytes', [{ start: 40, end: 80 }, { start: 999, end: 999 }])); +``` + +## Installation + +``` +$ npm install range-parser +``` \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/index.js b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/index.js new file mode 100644 index 0000000..9b0f7a8 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/index.js @@ -0,0 +1,49 @@ + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @api public + */ + +module.exports = function(size, str){ + var valid = true; + var i = str.indexOf('='); + + if (-1 == i) return -2; + + var arr = str.slice(i + 1).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -nnn + if (isNaN(start)) { + start = size - end; + end = size - 1; + // nnn- + } else if (isNaN(end)) { + end = size - 1; + } + + // limit last-byte-pos to current length + if (end > size - 1) end = size - 1; + + // invalid + if (isNaN(start) + || isNaN(end) + || start > end + || start < 0) valid = false; + + return { + start: start, + end: end + }; + }); + + arr.type = str.slice(0, i); + + return valid ? arr : -1; +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/package.json b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/package.json new file mode 100644 index 0000000..0306040 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/node_modules/range-parser/package.json @@ -0,0 +1,12 @@ +{ + "name": "range-parser", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "Range header field string parser", + "version": "0.0.4", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + } +} diff --git a/dist/node_modules/express/node_modules/connect/node_modules/send/package.json b/dist/node_modules/express/node_modules/connect/node_modules/send/package.json new file mode 100644 index 0000000..696df31 --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/node_modules/send/package.json @@ -0,0 +1,23 @@ +{ + "name": "send", + "version": "0.1.0", + "description": "Better streaming static file server with Range and conditional-GET support", + "keywords": ["static", "file", "server"], + "author": "TJ Holowaychuk ", + "dependencies": { + "debug": "*", + "mime": "1.2.6", + "fresh": "0.1.0", + "range-parser": "0.0.4" + }, + "devDependencies": { + "mocha": "*", + "should": "*", + "supertest": "0.0.1", + "connect": "2.x" + }, + "scripts": { + "test": "make test" + }, + "main": "index" +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/connect/package.json b/dist/node_modules/express/node_modules/connect/package.json new file mode 100644 index 0000000..ba6e24a --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/package.json @@ -0,0 +1,34 @@ +{ + "name": "connect", + "version": "2.6.2", + "description": "High performance middleware framework", + "keywords": ["framework", "web", "middleware", "connect", "rack"], + "repository": "git://github.com/senchalabs/connect.git", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "dependencies": { + "qs": "0.5.1", + "formidable": "1.0.11", + "cookie-signature": "0.0.1", + "crc": "0.2.0", + "cookie": "0.0.4", + "bytes": "0.0.1", + "send": "0.1.0", + "bytes": "0.1.0", + "fresh": "0.1.0", + "pause": "0.0.1", + "debug": "*" + }, + "devDependencies": { + "should": "*", + "mocha": "*", + "jade": "*", + "dox": "*" + }, + "main": "index", + "engines": { + "node": ">= 0.5.0" + }, + "scripts": { + "test": "make" + } +} diff --git a/dist/node_modules/express/node_modules/connect/test.js b/dist/node_modules/express/node_modules/connect/test.js new file mode 100644 index 0000000..01d516f --- /dev/null +++ b/dist/node_modules/express/node_modules/connect/test.js @@ -0,0 +1,12 @@ + +/** + * Module dependencies. + */ + +var http = require('http'); + +var body = 'hello' +http.createServer(function(req, res){ + res.setHeader('Content-Length', body.length); + res.end(body) +}).listen(3000) \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/cookie-signature/.npmignore b/dist/node_modules/express/node_modules/cookie-signature/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie-signature/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/cookie-signature/History.md b/dist/node_modules/express/node_modules/cookie-signature/History.md new file mode 100644 index 0000000..c8aa68f --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie-signature/History.md @@ -0,0 +1,5 @@ + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/dist/node_modules/express/node_modules/cookie-signature/Makefile b/dist/node_modules/express/node_modules/cookie-signature/Makefile new file mode 100644 index 0000000..4e9c8d3 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie-signature/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/dist/node_modules/express/node_modules/cookie-signature/Readme.md b/dist/node_modules/express/node_modules/cookie-signature/Readme.md new file mode 100644 index 0000000..2559e84 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie-signature/Readme.md @@ -0,0 +1,42 @@ + +# cookie-signature + + Sign and unsign cookies. + +## Example + +```js +var cookie = require('cookie-signature'); + +var val = cookie.sign('hello', 'tobiiscool'); +val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI'); + +var val = cookie.sign('hello', 'tobiiscool'); +cookie.unsign(val, 'tobiiscool').should.equal('hello'); +cookie.unsign(val, 'luna').should.be.false; +``` + +## License + +(The MIT License) + +Copyright (c) 2012 LearnBoost <tj@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE 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/dist/node_modules/express/node_modules/cookie-signature/index.js b/dist/node_modules/express/node_modules/cookie-signature/index.js new file mode 100644 index 0000000..93206a0 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie-signature/index.js @@ -0,0 +1,42 @@ + +/** + * Module dependencies. + */ + +var crypto = require('crypto'); + +/** + * Sign the given `val` with `secret`. + * + * @param {String} val + * @param {String} secret + * @return {String} + * @api private + */ + +exports.sign = function(val, secret){ + if ('string' != typeof val) throw new TypeError('cookie required'); + if ('string' != typeof secret) throw new TypeError('secret required'); + return val + '.' + crypto + .createHmac('sha256', secret) + .update(val) + .digest('base64') + .replace(/\=+$/, ''); +}; + +/** + * Unsign and decode the given `val` with `secret`, + * returning `false` if the signature is invalid. + * + * @param {String} val + * @param {String} secret + * @return {String|Boolean} + * @api private + */ + +exports.unsign = function(val, secret){ + if ('string' != typeof val) throw new TypeError('cookie required'); + if ('string' != typeof secret) throw new TypeError('secret required'); + var str = val.slice(0, val.lastIndexOf('.')); + return exports.sign(str, secret) == val ? str : false; +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/cookie-signature/package.json b/dist/node_modules/express/node_modules/cookie-signature/package.json new file mode 100644 index 0000000..6ddb349 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie-signature/package.json @@ -0,0 +1,13 @@ +{ + "name": "cookie-signature", + "version": "0.0.1", + "description": "Sign and unsign cookies", + "keywords": ["cookie", "sign", "unsign"], + "author": "TJ Holowaychuk ", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "main": "index" +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/cookie/.npmignore b/dist/node_modules/express/node_modules/cookie/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/express/node_modules/cookie/.travis.yml b/dist/node_modules/express/node_modules/cookie/.travis.yml new file mode 100644 index 0000000..bced151 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.6 diff --git a/dist/node_modules/express/node_modules/cookie/README.md b/dist/node_modules/express/node_modules/cookie/README.md new file mode 100644 index 0000000..5187ed1 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/README.md @@ -0,0 +1,44 @@ +# cookie [![Build Status](https://secure.travis-ci.org/shtylman/node-cookie.png?branch=master)](http://travis-ci.org/shtylman/node-cookie) # + +cookie is a basic cookie parser and serializer. It doesn't make assumptions about how you are going to deal with your cookies. It basically just provides a way to read and write the HTTP cookie headers. + +See [RFC6265](http://tools.ietf.org/html/rfc6265) for details about the http header for cookies. + +## how? + +``` +npm install cookie +``` + +```javascript +var cookie = require('cookie'); + +var hdr = cookie.serialize('foo', 'bar'); +// hdr = 'foo=bar'; + +var cookies = cookie.parse('foo=bar; cat=meow; dog=ruff'); +// cookies = { foo: 'bar', cat: 'meow', dog: 'ruff' }; +``` + +## more + +The serialize function takes a third parameter, an object, to set cookie options. See the RFC for valid values. + +### path +> cookie path + +### expires +> absolute expiration date for the cookie (Date object) + +### maxAge +> relative max age of the cookie from when the client receives it (seconds) + +### domain +> domain for the cookie + +### secure +> true or false + +### httpOnly +> true or false + diff --git a/dist/node_modules/express/node_modules/cookie/index.js b/dist/node_modules/express/node_modules/cookie/index.js new file mode 100644 index 0000000..ce6c926 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/index.js @@ -0,0 +1,57 @@ + +/// Serialize the a name value pair into a cookie string suitable for +/// http headers. An optional options object specified cookie parameters +/// +/// serialize('foo', 'bar', { httpOnly: true }) +/// => "foo=bar; httpOnly" +/// +/// @param {String} name +/// @param {String} val +/// @param {Object} options +/// @return {String} +var serialize = function(name, val, opt){ + var pairs = [name + '=' + encode(val)]; + opt = opt || {}; + + if (opt.maxAge) pairs.push('Max-Age=' + opt.maxAge); + if (opt.domain) pairs.push('Domain=' + opt.domain); + if (opt.path) pairs.push('Path=' + opt.path); + if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString()); + if (opt.httpOnly) pairs.push('HttpOnly'); + if (opt.secure) pairs.push('Secure'); + + return pairs.join('; '); +}; + +/// Parse the given cookie header string into an object +/// The object has the various cookies as keys(names) => values +/// @param {String} str +/// @return {Object} +var parse = function(str) { + var obj = {} + var pairs = str.split(/[;,] */); + + pairs.forEach(function(pair) { + var eq_idx = pair.indexOf('=') + var key = pair.substr(0, eq_idx).trim() + var val = pair.substr(++eq_idx, pair.length).trim(); + + // quoted values + if ('"' == val[0]) { + val = val.slice(1, -1); + } + + // only assign once + if (undefined == obj[key]) { + obj[key] = decode(val); + } + }); + + return obj; +}; + +var encode = encodeURIComponent; +var decode = decodeURIComponent; + +module.exports.serialize = serialize; +module.exports.parse = parse; diff --git a/dist/node_modules/express/node_modules/cookie/package.json b/dist/node_modules/express/node_modules/cookie/package.json new file mode 100644 index 0000000..416a03c --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/package.json @@ -0,0 +1,26 @@ +{ + "author": "Roman Shtylman ", + "name": "cookie", + "description": "cookie parsing and serialization", + "version": "0.0.4", + "repository": { + "type": "git", + "url": "git://github.com/shtylman/node-cookie.git" + }, + "keywords": [ + "cookie", + "cookies" + ], + "main": "index.js", + "scripts": { + "test": "mocha" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "1.x.x" + }, + "optionalDependencies": {}, + "engines": { + "node": "*" + } +} diff --git a/dist/node_modules/express/node_modules/cookie/test/mocha.opts b/dist/node_modules/express/node_modules/cookie/test/mocha.opts new file mode 100644 index 0000000..e2bfcc5 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/test/mocha.opts @@ -0,0 +1 @@ +--ui qunit diff --git a/dist/node_modules/express/node_modules/cookie/test/parse.js b/dist/node_modules/express/node_modules/cookie/test/parse.js new file mode 100644 index 0000000..4b98a20 --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/test/parse.js @@ -0,0 +1,25 @@ + +var assert = require('assert'); + +var cookie = require('..'); + +suite('parse'); + +test('basic', function() { + assert.deepEqual({ foo: 'bar' }, cookie.parse('foo=bar')); + assert.deepEqual({ foo: '123' }, cookie.parse('foo=123')); +}); + +test('ignore spaces', function() { + assert.deepEqual({ FOO: 'bar', baz: 'raz' }, + cookie.parse('FOO = bar; baz = raz')); +}); + +test('escaping', function() { + assert.deepEqual({ foo: 'bar=123456789&name=Magic+Mouse' }, + cookie.parse('foo="bar=123456789&name=Magic+Mouse"')); + + assert.deepEqual({ email: ' ",;/' }, + cookie.parse('email=%20%22%2c%3b%2f')); +}); + diff --git a/dist/node_modules/express/node_modules/cookie/test/serialize.js b/dist/node_modules/express/node_modules/cookie/test/serialize.js new file mode 100644 index 0000000..d38768d --- /dev/null +++ b/dist/node_modules/express/node_modules/cookie/test/serialize.js @@ -0,0 +1,59 @@ +// builtin +var assert = require('assert'); + +var cookie = require('..'); + +suite('serialize'); + +test('basic', function() { + assert.equal('foo=bar', cookie.serialize('foo', 'bar')); + assert.equal('foo=bar%20baz', cookie.serialize('foo', 'bar baz')); +}); + +test('path', function() { + assert.equal('foo=bar; Path=/', cookie.serialize('foo', 'bar', { + path: '/' + })); +}); + +test('secure', function() { + assert.equal('foo=bar; Secure', cookie.serialize('foo', 'bar', { + secure: true + })); + + assert.equal('foo=bar', cookie.serialize('foo', 'bar', { + secure: false + })); +}); + +test('domain', function() { + assert.equal('foo=bar; Domain=example.com', cookie.serialize('foo', 'bar', { + domain: 'example.com' + })); +}); + +test('httpOnly', function() { + assert.equal('foo=bar; HttpOnly', cookie.serialize('foo', 'bar', { + httpOnly: true + })); +}); + +test('maxAge', function() { + assert.equal('foo=bar; Max-Age=1000', cookie.serialize('foo', 'bar', { + maxAge: 1000 + })); +}); + +test('escaping', function() { + assert.deepEqual('cat=%2B%20', cookie.serialize('cat', '+ ')); +}); + +test('parse->serialize', function() { + + assert.deepEqual({ cat: 'foo=123&name=baz five' }, cookie.parse( + cookie.serialize('cat', 'foo=123&name=baz five'))); + + assert.deepEqual({ cat: ' ";/' }, cookie.parse( + cookie.serialize('cat', ' ";/'))); +}); + diff --git a/dist/node_modules/express/node_modules/crc/.gitmodules b/dist/node_modules/express/node_modules/crc/.gitmodules new file mode 100644 index 0000000..2319e18 --- /dev/null +++ b/dist/node_modules/express/node_modules/crc/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests/nodeunit"] + path = tests/nodeunit + url = git://github.com/caolan/nodeunit.git diff --git a/dist/node_modules/express/node_modules/crc/.npmignore b/dist/node_modules/express/node_modules/crc/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/express/node_modules/crc/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/express/node_modules/crc/Makefile b/dist/node_modules/express/node_modules/crc/Makefile new file mode 100644 index 0000000..720bf85 --- /dev/null +++ b/dist/node_modules/express/node_modules/crc/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/dist/node_modules/express/node_modules/crc/README.md b/dist/node_modules/express/node_modules/crc/README.md new file mode 100644 index 0000000..26ce22f --- /dev/null +++ b/dist/node_modules/express/node_modules/crc/README.md @@ -0,0 +1,31 @@ +# JavaScript CRC 8, 16 and 32. + +This is a basic port/copy of the JavaScript CRC implementation. The module works with any CommonJS system supporting `module.exports` notation as well as in the browser. When loaded in the browser, all functions end up under the `window.crc` "namespace". + +Original code is taken from http://www.digsys.se/JavaScript/CRC.aspx + +## Functions + +The following functions are implemented: + + crc8(String) #=> Number + crcArc(String) #=> Number + crc16(String) #=> Number + fcs16(String) #=> Number + crc32(String) #=> Number + hex8(Number) #=> String + hex16(Number) #=> String + hex32(Number) #=> String + +## Installation + + git clone git://github.com/alexgorbatchev/node-crc.git + +or + + npm install crc + +## Running tests + + $ npm install + $ make test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/crc/lib/crc.js b/dist/node_modules/express/node_modules/crc/lib/crc.js new file mode 100644 index 0000000..8feb542 --- /dev/null +++ b/dist/node_modules/express/node_modules/crc/lib/crc.js @@ -0,0 +1,403 @@ +(function() +{ + // CRC-8 in table form + // + // Copyright (c) 1989 AnDan Software. You may use this program, or + // code or tables extracted from it, as long as this notice is not + // removed or changed. + var CRC8_TAB = new Array( + // C/C++ language: + // + // unsigned char CRC8_TAB[] = {...}; + 0x00,0x1B,0x36,0x2D,0x6C,0x77,0x5A,0x41,0xD8,0xC3,0xEE,0xF5,0xB4,0xAF,0x82,0x99,0xD3,0xC8,0xE5, + 0xFE,0xBF,0xA4,0x89,0x92,0x0B,0x10,0x3D,0x26,0x67,0x7C,0x51,0x4A,0xC5,0xDE,0xF3,0xE8,0xA9,0xB2, + 0x9F,0x84,0x1D,0x06,0x2B,0x30,0x71,0x6A,0x47,0x5C,0x16,0x0D,0x20,0x3B,0x7A,0x61,0x4C,0x57,0xCE, + 0xD5,0xF8,0xE3,0xA2,0xB9,0x94,0x8F,0xE9,0xF2,0xDF,0xC4,0x85,0x9E,0xB3,0xA8,0x31,0x2A,0x07,0x1C, + 0x5D,0x46,0x6B,0x70,0x3A,0x21,0x0C,0x17,0x56,0x4D,0x60,0x7B,0xE2,0xF9,0xD4,0xCF,0x8E,0x95,0xB8, + 0xA3,0x2C,0x37,0x1A,0x01,0x40,0x5B,0x76,0x6D,0xF4,0xEF,0xC2,0xD9,0x98,0x83,0xAE,0xB5,0xFF,0xE4, + 0xC9,0xD2,0x93,0x88,0xA5,0xBE,0x27,0x3C,0x11,0x0A,0x4B,0x50,0x7D,0x66,0xB1,0xAA,0x87,0x9C,0xDD, + 0xC6,0xEB,0xF0,0x69,0x72,0x5F,0x44,0x05,0x1E,0x33,0x28,0x62,0x79,0x54,0x4F,0x0E,0x15,0x38,0x23, + 0xBA,0xA1,0x8C,0x97,0xD6,0xCD,0xE0,0xFB,0x74,0x6F,0x42,0x59,0x18,0x03,0x2E,0x35,0xAC,0xB7,0x9A, + 0x81,0xC0,0xDB,0xF6,0xED,0xA7,0xBC,0x91,0x8A,0xCB,0xD0,0xFD,0xE6,0x7F,0x64,0x49,0x52,0x13,0x08, + 0x25,0x3E,0x58,0x43,0x6E,0x75,0x34,0x2F,0x02,0x19,0x80,0x9B,0xB6,0xAD,0xEC,0xF7,0xDA,0xC1,0x8B, + 0x90,0xBD,0xA6,0xE7,0xFC,0xD1,0xCA,0x53,0x48,0x65,0x7E,0x3F,0x24,0x09,0x12,0x9D,0x86,0xAB,0xB0, + 0xF1,0xEA,0xC7,0xDC,0x45,0x5E,0x73,0x68,0x29,0x32,0x1F,0x04,0x4E,0x55,0x78,0x63,0x22,0x39,0x14, + 0x0F,0x96,0x8D,0xA0,0xBB,0xFA,0xE1,0xCC,0xD7 + ); + + function crc8Add(crc,c) + // 'crc' should be initialized to 0x00. + { + return CRC8_TAB[(crc^c)&0xFF]; + }; + // C/C++ language: + // + // inline unsigned char crc8Add(unsigned char crc, unsigned char c) + // { + // return CRC8_TAB[crc^c]; + // } + + // CRC-16 (as it is in SEA's ARC) in table form + // + // The logic for this method of calculating the CRC 16 bit polynomial + // is taken from an article by David Schwaderer in the April 1985 + // issue of PC Tech Journal. + var CRC_ARC_TAB = new Array( + // C/C++ language: + // + // unsigned short CRC_ARC_TAB[] = {...}; + 0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,0xC601,0x06C0,0x0780,0xC741,0x0500, + 0xC5C1,0xC481,0x0440,0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,0x0A00,0xCAC1, + 0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81, + 0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,0x1400,0xD4C1,0xD581,0x1540, + 0xD701,0x17C0,0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,0xF001, + 0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0, + 0x3480,0xF441,0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0,0x3B80, + 0xFB41,0x3900,0xF9C1,0xF881,0x3840,0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41, + 0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,0xE401,0x24C0,0x2580,0xE541,0x2700, + 0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,0xA001,0x60C0, + 0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480, + 0xA441,0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01,0x6AC0,0x6B80,0xAB41, + 0x6900,0xA9C1,0xA881,0x6840,0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01, + 0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1, + 0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,0x5000,0x90C1,0x9181, + 0x5140,0x9301,0x53C0,0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440, + 0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,0x5A00,0x9AC1,0x9B81,0x5B40,0x9901, + 0x59C0,0x5880,0x9841,0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1, + 0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680, + 0x8641,0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040 + ); + + function crcArcAdd(crc,c) + // 'crc' should be initialized to 0x0000. + { + return CRC_ARC_TAB[(crc^c)&0xFF]^((crc>>8)&0xFF); + }; + // C/C++ language: + // + // inline unsigned short crcArcAdd(unsigned short crc, unsigned char c) + // { + // return CRC_ARC_TAB[(unsigned char)crc^c]^(unsigned short)(crc>>8); + // } + + // CRC-16 (as it is in ZMODEM) in table form + // + // Copyright (c) 1989 AnDan Software. You may use this program, or + // code or tables extracted from it, as long as this notice is not + // removed or changed. + var CRC16_TAB = new Array( + // C/C++ language: + // + // unsigned short CRC16_TAB[] = {...}; + 0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,0x8108,0x9129,0xA14A,0xB16B,0xC18C, + 0xD1AD,0xE1CE,0xF1EF,0x1231,0x0210,0x3273,0x2252,0x52B5,0x4294,0x72F7,0x62D6,0x9339,0x8318, + 0xB37B,0xA35A,0xD3BD,0xC39C,0xF3FF,0xE3DE,0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4, + 0x5485,0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,0x3653,0x2672,0x1611,0x0630, + 0x76D7,0x66F6,0x5695,0x46B4,0xB75B,0xA77A,0x9719,0x8738,0xF7DF,0xE7FE,0xD79D,0xC7BC,0x48C4, + 0x58E5,0x6886,0x78A7,0x0840,0x1861,0x2802,0x3823,0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969, + 0xA90A,0xB92B,0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,0xDBFD,0xCBDC,0xFBBF, + 0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,0x6CA6,0x7C87,0x4CE4,0x5CC5,0x2C22,0x3C03,0x0C60,0x1C41, + 0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,0x8D68,0x9D49,0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13, + 0x2E32,0x1E51,0x0E70,0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,0x9188,0x81A9, + 0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,0x1080,0x00A1,0x30C2,0x20E3,0x5004,0x4025,0x7046, + 0x6067,0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,0xE37F,0xF35E,0x02B1,0x1290,0x22F3,0x32D2, + 0x4235,0x5214,0x6277,0x7256,0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,0x34E2, + 0x24C3,0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,0xA7DB,0xB7FA,0x8799,0x97B8,0xE75F,0xF77E, + 0xC71D,0xD73C,0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,0x4615,0x5634,0xD94C,0xC96D,0xF90E, + 0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3, + 0xCB7D,0xDB5C,0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,0x4A75,0x5A54,0x6A37,0x7A16,0x0AF1, + 0x1AD0,0x2AB3,0x3A92,0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,0x9DE8,0x8DC9,0x7C26,0x6C07, + 0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9, + 0x9FF8,0x6E17,0x7E36,0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0 + ); + + function crc16Add(crc,c) + // 'crc' should be initialized to 0x0000. + { + return CRC16_TAB[((crc>>8)^c)&0xFF]^((crc<<8)&0xFFFF); + }; + // C/C++ language: + // + // inline unsigned short crc16Add(unsigned short crc, unsigned char c) + // { + // return CRC16_TAB[(unsigned char)(crc>>8)^c]^(unsigned short)(crc<<8); + // } + + // FCS-16 (as it is in PPP) in table form + // + // Described in RFC-1662 by William Allen Simpson, see RFC-1662 for references. + // + // Modified by Anders Danielsson, March 10, 2006. + var FCS_16_TAB = new Array( + // C/C++ language: + // + // unsigned short FCS_16_TAB[256] = {...}; + 0x0000,0x1189,0x2312,0x329B,0x4624,0x57AD,0x6536,0x74BF,0x8C48,0x9DC1,0xAF5A,0xBED3,0xCA6C, + 0xDBE5,0xE97E,0xF8F7,0x1081,0x0108,0x3393,0x221A,0x56A5,0x472C,0x75B7,0x643E,0x9CC9,0x8D40, + 0xBFDB,0xAE52,0xDAED,0xCB64,0xF9FF,0xE876,0x2102,0x308B,0x0210,0x1399,0x6726,0x76AF,0x4434, + 0x55BD,0xAD4A,0xBCC3,0x8E58,0x9FD1,0xEB6E,0xFAE7,0xC87C,0xD9F5,0x3183,0x200A,0x1291,0x0318, + 0x77A7,0x662E,0x54B5,0x453C,0xBDCB,0xAC42,0x9ED9,0x8F50,0xFBEF,0xEA66,0xD8FD,0xC974,0x4204, + 0x538D,0x6116,0x709F,0x0420,0x15A9,0x2732,0x36BB,0xCE4C,0xDFC5,0xED5E,0xFCD7,0x8868,0x99E1, + 0xAB7A,0xBAF3,0x5285,0x430C,0x7197,0x601E,0x14A1,0x0528,0x37B3,0x263A,0xDECD,0xCF44,0xFDDF, + 0xEC56,0x98E9,0x8960,0xBBFB,0xAA72,0x6306,0x728F,0x4014,0x519D,0x2522,0x34AB,0x0630,0x17B9, + 0xEF4E,0xFEC7,0xCC5C,0xDDD5,0xA96A,0xB8E3,0x8A78,0x9BF1,0x7387,0x620E,0x5095,0x411C,0x35A3, + 0x242A,0x16B1,0x0738,0xFFCF,0xEE46,0xDCDD,0xCD54,0xB9EB,0xA862,0x9AF9,0x8B70,0x8408,0x9581, + 0xA71A,0xB693,0xC22C,0xD3A5,0xE13E,0xF0B7,0x0840,0x19C9,0x2B52,0x3ADB,0x4E64,0x5FED,0x6D76, + 0x7CFF,0x9489,0x8500,0xB79B,0xA612,0xD2AD,0xC324,0xF1BF,0xE036,0x18C1,0x0948,0x3BD3,0x2A5A, + 0x5EE5,0x4F6C,0x7DF7,0x6C7E,0xA50A,0xB483,0x8618,0x9791,0xE32E,0xF2A7,0xC03C,0xD1B5,0x2942, + 0x38CB,0x0A50,0x1BD9,0x6F66,0x7EEF,0x4C74,0x5DFD,0xB58B,0xA402,0x9699,0x8710,0xF3AF,0xE226, + 0xD0BD,0xC134,0x39C3,0x284A,0x1AD1,0x0B58,0x7FE7,0x6E6E,0x5CF5,0x4D7C,0xC60C,0xD785,0xE51E, + 0xF497,0x8028,0x91A1,0xA33A,0xB2B3,0x4A44,0x5BCD,0x6956,0x78DF,0x0C60,0x1DE9,0x2F72,0x3EFB, + 0xD68D,0xC704,0xF59F,0xE416,0x90A9,0x8120,0xB3BB,0xA232,0x5AC5,0x4B4C,0x79D7,0x685E,0x1CE1, + 0x0D68,0x3FF3,0x2E7A,0xE70E,0xF687,0xC41C,0xD595,0xA12A,0xB0A3,0x8238,0x93B1,0x6B46,0x7ACF, + 0x4854,0x59DD,0x2D62,0x3CEB,0x0E70,0x1FF9,0xF78F,0xE606,0xD49D,0xC514,0xB1AB,0xA022,0x92B9, + 0x8330,0x7BC7,0x6A4E,0x58D5,0x495C,0x3DE3,0x2C6A,0x1EF1,0x0F78 + ); + + function fcs16Add(fcs,c) + // 'fcs' should be initialized to 0xFFFF and after the computation it should be + // complemented (inverted). + // + // If the FCS-16 is calculated over the data and over the complemented FCS-16, the + // result will always be 0xF0B8 (without the complementation). + { + return FCS_16_TAB[(fcs^c)&0xFF]^((fcs>>8)&0xFF); + }; + + // C/C++ language: + // + // inline unsigned short fcs16Add(unsigned short fcs, unsigned char c) + // { + // return FCS_16_TAB[(unsigned char)fcs^c]^(unsigned short)(fcs>>8); + // } + + // + // CRC-32 (as it is in ZMODEM) in table form + // + // Copyright (C) 1986 Gary S. Brown. You may use this program, or + // code or tables extracted from it, as desired without restriction. + // + // Modified by Anders Danielsson, February 5, 1989 and March 10, 2006. + // + // This is also known as FCS-32 (as it is in PPP), described in + // RFC-1662 by William Allen Simpson, see RFC-1662 for references. + // + var CRC32_TAB = new Array( /* CRC polynomial 0xEDB88320 */ + // C/C++ language: + // + // unsigned long CRC32_TAB[] = {...}; + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, + 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, + 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7, + 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, + 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B, + 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, + 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, + 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, + 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433, + 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, + 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457, + 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, + 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB, + 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F, + 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, + 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683, + 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, + 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7, + 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, + 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, + 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, + 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F, + 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, + 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713, + 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, + 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777, + 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB, + 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, + 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF, + 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D + ); + + function crc32Add(crc,c) + // 'crc' should be initialized to 0xFFFFFFFF and after the computation it should be + // complemented (inverted). + // + // CRC-32 is also known as FCS-32. + // + // If the FCS-32 is calculated over the data and over the complemented FCS-32, the + // result will always be 0xDEBB20E3 (without the complementation). + { + return CRC32_TAB[(crc^c)&0xFF]^((crc>>8)&0xFFFFFF); + }; + // + // C/C++ language: + // + // inline unsigned long crc32Add(unsigned long crc, unsigned char c) + // { + // return CRC32_TAB[(unsigned char)crc^c]^(crc>>8); + // } + // + + function crc8(str) + { + var n, + len = str.length, + crc = 0 + ; + + for(var i = 0; i < len; i++) + crc = crc8Add(crc, str.charCodeAt(i)); + + return crc; + }; + + function crc8Buffer(buf) + { + var crc = 0; + + for (var i = 0, len = buf.length; i < len; ++i) + { + crc = crc8Add(crc, buf[i]); + } + + return crc; + } + + function crcArc(str) + { + var i, + len = str.length, + crc = 0 + ; + + for(i = 0; i < len; i++) + crc = crcArcAdd(crc, str.charCodeAt(i)); + + return crc; + }; + + function crc16(str) + { + var i, + len = str.length, + crc = 0 + ; + + for(i = 0; i < len; i++) + crc = crc16Add(crc, str.charCodeAt(i)); + + return crc; + }; + + function crc16Buffer(buf) + { + var crc = 0; + + for (var i = 0, len = buf.length; i < len; ++i) + { + crc = crc16Add(crc, buf[i]); + } + + return crc; + } + + function fcs16(str) + { + var i, + len = str.length, + fcs = 0xFFFF + ; + + for(i = 0; i < len; i++) + fcs = fcs16Add(fcs,str.charCodeAt(i)); + + return fcs^0xFFFF; + }; + + function crc32(str) + { + var i, + len = str.length, + crc = 0xFFFFFFFF + ; + + for(i = 0; i < len; i++) + crc = crc32Add(crc, str.charCodeAt(i)); + + return crc^0xFFFFFFFF; + }; + + function crc32Buffer(buf) + { + var crc = 0xFFFFFFFF; + + for (var i = 0, len = buf.length; i < len; ++i) + { + crc = crc32Add(crc, buf[i]); + } + + return crc ^ 0xFFFFFFFF; + } + + /** + * Convert value as 8-bit unsigned integer to 2 digit hexadecimal number. + */ + function hex8(val) + { + var n = val & 0xFF, + str = n.toString(16).toUpperCase() + ; + + while(str.length < 2) + str = "0" + str; + + return str; + }; + + /** + * Convert value as 16-bit unsigned integer to 4 digit hexadecimal number. + */ + function hex16(val) + { + return hex8(val >> 8) + hex8(val); + }; + + /** + * Convert value as 32-bit unsigned integer to 8 digit hexadecimal number. + */ + function hex32(val) + { + return hex16(val >> 16) + hex16(val); + }; + + var target, property; + + if(typeof(window) == 'undefined') + { + target = module; + property = 'exports'; + } + else + { + target = window; + property = 'crc'; + } + + target[property] = { + 'crc8' : crc8, + 'crcArc' : crcArc, + 'crc16' : crc16, + 'fcs16' : fcs16, + 'crc32' : crc32, + 'hex8' : hex8, + 'hex16' : hex16, + 'hex32' : hex32, + 'buffer' : { + crc8 : crc8Buffer, + crc16 : crc16Buffer, + crc32 : crc32Buffer + } + }; +})(); diff --git a/dist/node_modules/express/node_modules/crc/package.json b/dist/node_modules/express/node_modules/crc/package.json new file mode 100644 index 0000000..27c3f7c --- /dev/null +++ b/dist/node_modules/express/node_modules/crc/package.json @@ -0,0 +1,18 @@ +{ + "name" : "crc", + "version" : "0.2.0", + "description" : "CRC JavaScript implementation", + "author": "Alex Gorbatchev ", + "contributors": [], + "main": "./lib/crc.js", + "scripts": {}, + "directories" : {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "repository": { + "type": "git", + "url": "git://github.com/alexgorbatchev/node-crc.git" + } +} diff --git a/dist/node_modules/express/node_modules/crc/test/crc.js b/dist/node_modules/express/node_modules/crc/test/crc.js new file mode 100755 index 0000000..faf2d1d --- /dev/null +++ b/dist/node_modules/express/node_modules/crc/test/crc.js @@ -0,0 +1,63 @@ +#!/usr/bin/env ./nodeunit/bin/nodeunit + +var crc = require('../lib/crc'); + +describe('crc8()', function(){ + it('should work with strings', function(){ + crc.crc8('hello world').should.equal(64); + }) + + it('should work with Buffers', function(){ + crc.buffer.crc8(new Buffer('hello world')).should.equal(64); + }) +}) + +describe('crc16()', function(){ + it('should work with strings', function(){ + crc.crc16('hello world').should.equal(15332); + }) + + it('should work with Buffers', function(){ + crc.buffer.crc16(new Buffer('hello world')).should.equal(15332); + }) +}) + +describe('crc32()', function(){ + it('should work with strings', function(){ + crc.crc32('hello world').should.equal(222957957); + }) + + it('should work with Buffers', function(){ + crc.buffer.crc32(new Buffer('hello world')).should.equal(222957957); + }) +}) + +describe('crcArc()', function(){ + it('should work with strings', function(){ + crc.crcArc('hello world').should.equal(14785); + }) +}) + +describe('fcs16()', function(){ + it('should work with strings', function(){ + crc.fcs16('hello world').should.equal(44550); + }) +}) + +describe('hex8()', function(){ + it('should work with strings', function(){ + crc.hex8(64).should.equal('40'); + }) +}) + +describe('hex16()', function(){ + it('should work with strings', function(){ + crc.hex16(15332).should.equal('3BE4'); + }) +}) + +describe('hex32()', function(){ + it('should work with strings', function(){ + crc.hex32(222957957).should.equal('0D4A1185'); + }) +}) diff --git a/dist/node_modules/express/node_modules/debug/.npmignore b/dist/node_modules/express/node_modules/debug/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/debug/History.md b/dist/node_modules/express/node_modules/debug/History.md new file mode 100644 index 0000000..2220632 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/History.md @@ -0,0 +1,47 @@ + +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/dist/node_modules/express/node_modules/debug/Makefile b/dist/node_modules/express/node_modules/debug/Makefile new file mode 100644 index 0000000..692f2c1 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/Makefile @@ -0,0 +1,4 @@ + +debug.component.js: head.js debug.js tail.js + cat $^ > $@ + diff --git a/dist/node_modules/express/node_modules/debug/Readme.md b/dist/node_modules/express/node_modules/debug/Readme.md new file mode 100644 index 0000000..419fcdf --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/Readme.md @@ -0,0 +1,130 @@ + +# debug + + tiny node.js debugging utility. + +## Installation + +``` +$ npm install debug +``` + +## Example + + This module is modelled after node core's debugging technique, allowing you to enable one or more topic-specific debugging functions, for example core does the following within many modules: + +```js +var debug; +if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) { + debug = function(x) { + var prefix = process.pid + ',' + + (process.env.NODE_WORKER_ID ? 'Worker' : 'Master'); + console.error(prefix, x); + }; +} else { + debug = function() { }; +} +``` + + This concept is extremely simple but it works well. 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) + +## 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`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + a('doing some work'); +}, 1200); +``` + +## License + +(The MIT License) + +Copyright (c) 2011 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/dist/node_modules/express/node_modules/debug/debug.component.js b/dist/node_modules/express/node_modules/debug/debug.component.js new file mode 100644 index 0000000..e6e9dbf --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/debug.component.js @@ -0,0 +1,120 @@ +;(function(){ + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + localStorage.debug = name; + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +// persist + +if (window.localStorage) debug.enable(localStorage.debug); + module.exports = debug; + +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/debug/debug.js b/dist/node_modules/express/node_modules/debug/debug.js new file mode 100644 index 0000000..905fbd4 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/debug.js @@ -0,0 +1,116 @@ + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + localStorage.debug = name; + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +// persist + +if (window.localStorage) debug.enable(localStorage.debug); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/debug/example/app.js b/dist/node_modules/express/node_modules/debug/example/app.js new file mode 100644 index 0000000..05374d9 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/example/app.js @@ -0,0 +1,19 @@ + +var debug = require('../')('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'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/debug/example/browser.html b/dist/node_modules/express/node_modules/debug/example/browser.html new file mode 100644 index 0000000..7510eee --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/example/browser.html @@ -0,0 +1,24 @@ + + + debug() + + + + + + + diff --git a/dist/node_modules/express/node_modules/debug/example/wildcards.js b/dist/node_modules/express/node_modules/debug/example/wildcards.js new file mode 100644 index 0000000..1fdac20 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/example/wildcards.js @@ -0,0 +1,10 @@ + +var debug = { + foo: require('../')('test:foo'), + bar: require('../')('test:bar'), + baz: require('../')('test:baz') +}; + +debug.foo('foo') +debug.bar('bar') +debug.baz('baz') \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/debug/example/worker.js b/dist/node_modules/express/node_modules/debug/example/worker.js new file mode 100644 index 0000000..7f6d288 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/example/worker.js @@ -0,0 +1,22 @@ + +// DEBUG=* node example/worker +// DEBUG=worker:* node example/worker +// DEBUG=worker:a node example/worker +// DEBUG=worker:b node example/worker + +var a = require('../')('worker:a') + , b = require('../')('worker:b'); + +function work() { + a('doing lots of uninteresting work'); + setTimeout(work, Math.random() * 1000); +} + +work(); + +function workb() { + b('doing some work'); + setTimeout(workb, Math.random() * 2000); +} + +workb(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/debug/head.js b/dist/node_modules/express/node_modules/debug/head.js new file mode 100644 index 0000000..55d3817 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/head.js @@ -0,0 +1 @@ +;(function(){ diff --git a/dist/node_modules/express/node_modules/debug/index.js b/dist/node_modules/express/node_modules/debug/index.js new file mode 100644 index 0000000..ee54454 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/debug'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/debug/lib/debug.js b/dist/node_modules/express/node_modules/debug/lib/debug.js new file mode 100644 index 0000000..969d122 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/lib/debug.js @@ -0,0 +1,135 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Enabled debuggers. + */ + +var names = [] + , skips = []; + +(process.env.DEBUG || '') + .split(/[\s,]+/) + .forEach(function(name){ + name = name.replace('*', '.*?'); + if (name[0] === '-') { + skips.push(new RegExp('^' + name.substr(1) + '$')); + } else { + names.push(new RegExp('^' + name + '$')); + } + }); + +/** + * Colors. + */ + +var colors = [6, 2, 3, 4, 5, 1]; + +/** + * Previous debug() call. + */ + +var prev = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Is stdout a TTY? Colored output is disabled when `true`. + */ + +var isatty = tty.isatty(2); + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function color() { + return colors[prevColor++ % colors.length]; +} + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +function humanize(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +} + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + function disabled(){} + disabled.enabled = false; + + var match = skips.some(function(re){ + return re.test(name); + }); + + if (match) return disabled; + + match = names.some(function(re){ + return re.test(name); + }); + + if (!match) return disabled; + var c = color(); + + function colored(fmt) { + var curr = new Date; + var ms = curr - (prev[name] || curr); + prev[name] = curr; + + fmt = ' \033[9' + c + 'm' + name + ' ' + + '\033[3' + c + 'm\033[90m' + + fmt + '\033[3' + c + 'm' + + ' +' + humanize(ms) + '\033[0m'; + + console.error.apply(this, arguments); + } + + function plain(fmt) { + fmt = new Date().toUTCString() + + ' ' + name + ' ' + fmt; + console.error.apply(this, arguments); + } + + colored.enabled = plain.enabled = true; + + return isatty + ? colored + : plain; +} diff --git a/dist/node_modules/express/node_modules/debug/package.json b/dist/node_modules/express/node_modules/debug/package.json new file mode 100644 index 0000000..f226656 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/package.json @@ -0,0 +1,17 @@ +{ + "name": "debug" + , "version": "0.7.0" + , "description": "small debugging utility" + , "keywords": ["debug", "log", "debugger"] + , "author": "TJ Holowaychuk " + , "dependencies": {} + , "devDependencies": { "mocha": "*" } + , "main": "index" + , "browserify": "debug.component.js" + , "engines": { "node": "*" } + , "component": { + "scripts": { + "debug": "debug.component.js" + } + } +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/debug/tail.js b/dist/node_modules/express/node_modules/debug/tail.js new file mode 100644 index 0000000..5bf3fd3 --- /dev/null +++ b/dist/node_modules/express/node_modules/debug/tail.js @@ -0,0 +1,4 @@ + + module.exports = debug; + +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/fresh/.npmignore b/dist/node_modules/express/node_modules/fresh/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/fresh/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/fresh/Makefile b/dist/node_modules/express/node_modules/fresh/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/fresh/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/fresh/Readme.md b/dist/node_modules/express/node_modules/fresh/Readme.md new file mode 100644 index 0000000..273130d --- /dev/null +++ b/dist/node_modules/express/node_modules/fresh/Readme.md @@ -0,0 +1,32 @@ + +# node-fresh + + HTTP response freshness testing + +## fresh(req, res) + + Check freshness of `req` and `res` headers. + + When the cache is "fresh" __true__ is returned, + otherwise __false__ is returned to indicate that + the cache is now stale. + +## Example: + +```js +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'luna' }; +fresh(req, res); +// => false + +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'tobi' }; +fresh(req, res); +// => true +``` + +## Installation + +``` +$ npm install fresh +``` \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/fresh/index.js b/dist/node_modules/express/node_modules/fresh/index.js new file mode 100644 index 0000000..b2f4d41 --- /dev/null +++ b/dist/node_modules/express/node_modules/fresh/index.js @@ -0,0 +1,49 @@ + +/** + * Expose `fresh()`. + */ + +module.exports = fresh; + +/** + * Check freshness of `req` and `res` headers. + * + * When the cache is "fresh" __true__ is returned, + * otherwise __false__ is returned to indicate that + * the cache is now stale. + * + * @param {Object} req + * @param {Object} res + * @return {Boolean} + * @api public + */ + +function fresh(req, res) { + // defaults + var etagMatches = true; + var notModified = true; + + // fields + var modifiedSince = req['if-modified-since']; + var noneMatch = req['if-none-match']; + var lastModified = res['last-modified']; + var etag = res['etag']; + + // unconditional request + if (!modifiedSince && !noneMatch) return false; + + // parse if-none-match + if (noneMatch) noneMatch = noneMatch.split(/ *, */); + + // if-none-match + if (noneMatch) etagMatches = ~noneMatch.indexOf(etag) || '*' == noneMatch[0]; + + // if-modified-since + if (modifiedSince) { + modifiedSince = new Date(modifiedSince); + lastModified = new Date(lastModified); + notModified = lastModified <= modifiedSince; + } + + return !! (etagMatches && notModified); +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/fresh/package.json b/dist/node_modules/express/node_modules/fresh/package.json new file mode 100644 index 0000000..d81fc0d --- /dev/null +++ b/dist/node_modules/express/node_modules/fresh/package.json @@ -0,0 +1,12 @@ +{ + "name": "fresh", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "HTTP response freshness testing", + "version": "0.1.0", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + } +} diff --git a/dist/node_modules/express/node_modules/methods/index.js b/dist/node_modules/express/node_modules/methods/index.js new file mode 100644 index 0000000..297d022 --- /dev/null +++ b/dist/node_modules/express/node_modules/methods/index.js @@ -0,0 +1,26 @@ + +module.exports = [ + 'get' + , 'post' + , 'put' + , 'head' + , 'delete' + , 'options' + , 'trace' + , 'copy' + , 'lock' + , 'mkcol' + , 'move' + , 'propfind' + , 'proppatch' + , 'unlock' + , 'report' + , 'mkactivity' + , 'checkout' + , 'merge' + , 'm-search' + , 'notify' + , 'subscribe' + , 'unsubscribe' + , 'patch' +]; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/methods/package.json b/dist/node_modules/express/node_modules/methods/package.json new file mode 100644 index 0000000..09bef10 --- /dev/null +++ b/dist/node_modules/express/node_modules/methods/package.json @@ -0,0 +1,15 @@ +{ + "name": "methods", + "version": "0.0.1", + "description": "HTTP methods that node supports", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "http", + "methods" + ], + "author": "TJ Holowaychuk", + "license": "MIT" +} diff --git a/dist/node_modules/express/node_modules/mkdirp/.gitignore.orig b/dist/node_modules/express/node_modules/mkdirp/.gitignore.orig new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/.gitignore.orig @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/mkdirp/.gitignore.rej b/dist/node_modules/express/node_modules/mkdirp/.gitignore.rej new file mode 100644 index 0000000..69244ff --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/.gitignore.rej @@ -0,0 +1,5 @@ +--- /dev/null ++++ .gitignore +@@ -0,0 +1,2 @@ ++node_modules/ ++npm-debug.log \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/mkdirp/.npmignore b/dist/node_modules/express/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/mkdirp/.travis.yml b/dist/node_modules/express/node_modules/mkdirp/.travis.yml new file mode 100644 index 0000000..f1d0f13 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.4 + - 0.6 diff --git a/dist/node_modules/express/node_modules/mkdirp/LICENSE b/dist/node_modules/express/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +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/dist/node_modules/express/node_modules/mkdirp/README.markdown b/dist/node_modules/express/node_modules/mkdirp/README.markdown new file mode 100644 index 0000000..40de04f --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/README.markdown @@ -0,0 +1,61 @@ +mkdirp +====== + +Like `mkdir -p`, but in node.js! + +[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) + +example +======= + +pow.js +------ + var mkdirp = require('mkdirp'); + + mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') + }); + +Output + pow! + +And now /tmp/foo/bar/baz exists, huzzah! + +methods +======= + +var mkdirp = require('mkdirp'); + +mkdirp(dir, mode, cb) +--------------------- + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +`cb(err, made)` fires with the error or the first directory `made` +that had to be created, if any. + +mkdirp.sync(dir, mode) +---------------------- + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +Returns the first directory that had to be created, if any. + +install +======= + +With [npm](http://npmjs.org) do: + + npm install mkdirp + +license +======= + +MIT/X11 diff --git a/dist/node_modules/express/node_modules/mkdirp/examples/pow.js b/dist/node_modules/express/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/examples/pow.js.orig b/dist/node_modules/express/node_modules/mkdirp/examples/pow.js.orig new file mode 100644 index 0000000..7741462 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/examples/pow.js.orig @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/examples/pow.js.rej b/dist/node_modules/express/node_modules/mkdirp/examples/pow.js.rej new file mode 100644 index 0000000..81e7f43 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/examples/pow.js.rej @@ -0,0 +1,19 @@ +--- examples/pow.js ++++ examples/pow.js +@@ -1,6 +1,15 @@ +-var mkdirp = require('mkdirp').mkdirp; ++var mkdirp = require('../').mkdirp, ++ mkdirpSync = require('../').mkdirpSync; + + mkdirp('/tmp/foo/bar/baz', 0755, function (err) { + if (err) console.error(err) + else console.log('pow!') + }); ++ ++try { ++ mkdirpSync('/tmp/bar/foo/baz', 0755); ++ console.log('double pow!'); ++} ++catch (ex) { ++ console.log(ex); ++} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/mkdirp/index.js b/dist/node_modules/express/node_modules/mkdirp/index.js new file mode 100644 index 0000000..874b310 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/index.js @@ -0,0 +1,94 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, mode, f, made) { + if (typeof mode === 'function' || mode === undefined) { + f = mode; + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + fs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), mode, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, mode, cb, made); + }); + break; + + case 'EISDIR': + case 'EPERM': + // Operation not permitted or already is a dir. + // This is the error you get when trying to mkdir('c:/') + // on windows, or mkdir('/') on unix. Make sure it's a + // dir by falling through to the EEXIST case. + case 'EROFS': + // a read-only file system. + // However, the dir could already exist, in which case + // the EROFS error will be obscuring a EEXIST! + // Fallthrough to that case. + case 'EEXIST': + fs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + + default: + cb(er, made); + break; + } + }); +} + +mkdirP.sync = function sync (p, mode, made) { + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + try { + fs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), mode, made); + sync(p, mode, made); + break; + + case 'EEXIST' : + var stat; + try { + stat = fs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + default : + throw err0 + break; + } + } + + return made; +}; diff --git a/dist/node_modules/express/node_modules/mkdirp/package.json b/dist/node_modules/express/node_modules/mkdirp/package.json new file mode 100644 index 0000000..d7f132d --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/package.json @@ -0,0 +1,23 @@ +{ + "name" : "mkdirp", + "description" : "Recursively mkdir, like `mkdir -p`", + "version" : "0.3.3", + "author" : "James Halliday (http://substack.net)", + "main" : "./index", + "keywords" : [ + "mkdir", + "directory" + ], + "repository" : { + "type" : "git", + "url" : "http://github.com/substack/node-mkdirp.git" + }, + "scripts" : { + "test" : "tap test/*.js" + }, + "devDependencies" : { + "tap" : "~0.2.4" + }, + "license" : "MIT/X11", + "engines": { "node": "*" } +} diff --git a/dist/node_modules/express/node_modules/mkdirp/test/chmod.js b/dist/node_modules/express/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/clobber.js b/dist/node_modules/express/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/mkdirp.js b/dist/node_modules/express/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..b07cd70 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('woo', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/perm.js b/dist/node_modules/express/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..23a7abb --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/perm_sync.js b/dist/node_modules/express/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..f685f60 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,39 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); + +test('sync root perm', function (t) { + t.plan(1); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/race.js b/dist/node_modules/express/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..96a0447 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/race.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('race', function (t) { + t.plan(4); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + } + }) + }) + }); + } +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/rel.js b/dist/node_modules/express/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..7985824 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('rel', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/return.js b/dist/node_modules/express/node_modules/mkdirp/test/return.js new file mode 100644 index 0000000..bce68e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/return.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, '/tmp/' + x); + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, null); + }); + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/return_sync.js b/dist/node_modules/express/node_modules/mkdirp/test/return_sync.js new file mode 100644 index 0000000..7c222d3 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/return_sync.js @@ -0,0 +1,24 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + // Note that this will throw on failure, which will fail the test. + var made = mkdirp.sync(file); + t.equal(made, '/tmp/' + x); + + // making the same file again should have no effect. + made = mkdirp.sync(file); + t.equal(made, null); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/root.js b/dist/node_modules/express/node_modules/mkdirp/test/root.js new file mode 100644 index 0000000..97ad7a2 --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/root.js @@ -0,0 +1,18 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('root', function (t) { + // '/' on unix, 'c:/' on windows. + var file = path.resolve('/'); + + mkdirp(file, 0755, function (err) { + if (err) throw err + fs.stat(file, function (er, stat) { + if (er) throw er + t.ok(stat.isDirectory(), 'target is a directory'); + t.end(); + }) + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/sync.js b/dist/node_modules/express/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..7530cad --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file, 0755); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/umask.js b/dist/node_modules/express/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..64ccafe --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/dist/node_modules/express/node_modules/mkdirp/test/umask_sync.js b/dist/node_modules/express/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..35bd5cb --- /dev/null +++ b/dist/node_modules/express/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/dist/node_modules/express/node_modules/range-parser/.npmignore b/dist/node_modules/express/node_modules/range-parser/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/range-parser/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/range-parser/History.md b/dist/node_modules/express/node_modules/range-parser/History.md new file mode 100644 index 0000000..82df7b1 --- /dev/null +++ b/dist/node_modules/express/node_modules/range-parser/History.md @@ -0,0 +1,15 @@ + +0.0.4 / 2012-06-17 +================== + + * changed: ret -1 for unsatisfiable and -2 when invalid + +0.0.3 / 2012-06-17 +================== + + * fix last-byte-pos default to len - 1 + +0.0.2 / 2012-06-14 +================== + + * add `.type` diff --git a/dist/node_modules/express/node_modules/range-parser/Makefile b/dist/node_modules/express/node_modules/range-parser/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/range-parser/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/range-parser/Readme.md b/dist/node_modules/express/node_modules/range-parser/Readme.md new file mode 100644 index 0000000..b2a67fe --- /dev/null +++ b/dist/node_modules/express/node_modules/range-parser/Readme.md @@ -0,0 +1,28 @@ + +# node-range-parser + + Range header field parser. + +## Example: + +```js +assert(-1 == parse(200, 'bytes=500-20')); +assert(-2 == parse(200, 'bytes=malformed')); +parse(200, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 199 }])); +parse(1000, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 499 }])); +parse(1000, 'bytes=40-80').should.eql(arr('bytes', [{ start: 40, end: 80 }])); +parse(1000, 'bytes=-500').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=-400').should.eql(arr('bytes', [{ start: 600, end: 999 }])); +parse(1000, 'bytes=500-').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=400-').should.eql(arr('bytes', [{ start: 400, end: 999 }])); +parse(1000, 'bytes=0-0').should.eql(arr('bytes', [{ start: 0, end: 0 }])); +parse(1000, 'bytes=-1').should.eql(arr('bytes', [{ start: 999, end: 999 }])); +parse(1000, 'items=0-5').should.eql(arr('items', [{ start: 0, end: 5 }])); +parse(1000, 'bytes=40-80,-1').should.eql(arr('bytes', [{ start: 40, end: 80 }, { start: 999, end: 999 }])); +``` + +## Installation + +``` +$ npm install range-parser +``` \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/range-parser/index.js b/dist/node_modules/express/node_modules/range-parser/index.js new file mode 100644 index 0000000..9b0f7a8 --- /dev/null +++ b/dist/node_modules/express/node_modules/range-parser/index.js @@ -0,0 +1,49 @@ + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @api public + */ + +module.exports = function(size, str){ + var valid = true; + var i = str.indexOf('='); + + if (-1 == i) return -2; + + var arr = str.slice(i + 1).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -nnn + if (isNaN(start)) { + start = size - end; + end = size - 1; + // nnn- + } else if (isNaN(end)) { + end = size - 1; + } + + // limit last-byte-pos to current length + if (end > size - 1) end = size - 1; + + // invalid + if (isNaN(start) + || isNaN(end) + || start > end + || start < 0) valid = false; + + return { + start: start, + end: end + }; + }); + + arr.type = str.slice(0, i); + + return valid ? arr : -1; +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/range-parser/package.json b/dist/node_modules/express/node_modules/range-parser/package.json new file mode 100644 index 0000000..0306040 --- /dev/null +++ b/dist/node_modules/express/node_modules/range-parser/package.json @@ -0,0 +1,12 @@ +{ + "name": "range-parser", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "Range header field string parser", + "version": "0.0.4", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + } +} diff --git a/dist/node_modules/express/node_modules/send/.npmignore b/dist/node_modules/express/node_modules/send/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/send/History.md b/dist/node_modules/express/node_modules/send/History.md new file mode 100644 index 0000000..20c5319 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/History.md @@ -0,0 +1,25 @@ + +0.1.0 / 2012-08-25 +================== + + * add options parameter to send() that is passed to fs.createReadStream() [kanongil] + +0.0.4 / 2012-08-16 +================== + + * allow custom "Accept-Ranges" definition + +0.0.3 / 2012-07-16 +================== + + * fix normalization of the root directory. Closes #3 + +0.0.2 / 2012-07-09 +================== + + * add passing of req explicitly for now (YUCK) + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/dist/node_modules/express/node_modules/send/Makefile b/dist/node_modules/express/node_modules/send/Makefile new file mode 100644 index 0000000..a9dcfd5 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/Makefile @@ -0,0 +1,8 @@ + +test: + @./node_modules/.bin/mocha \ + --require should \ + --reporter spec \ + --bail + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/Readme.md b/dist/node_modules/express/node_modules/send/Readme.md new file mode 100644 index 0000000..85171a9 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/Readme.md @@ -0,0 +1,123 @@ + +# send + + Send is Connect's `static()` extracted for generalized use, a streaming static file + server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework. + +## Installation + + $ npm install send + +## Examples + + Small: + +```js +var http = require('http'); +var send = require('send'); + +var app = http.createServer(function(req, res){ + send(req, req.url).pipe(res); +}); +``` + + Serving from a root directory with custom error-handling: + +```js +var http = require('http'); +var send = require('send'); + +var app = http.createServer(function(req, res){ + // your custom error-handling logic: + function error(err) { + res.statusCode = err.status || 500; + res.end(err.message); + } + + // your custom directory handling logic: + function redirect() { + res.statusCode = 301; + res.setHeader('Location', req.url + '/'); + res.end('Redirecting to ' + req.url + '/'); + } + + // transfer arbitrary files from within + // /www/example.com/public/* + send(req, url.parse(req.url).pathname) + .root('/www/example.com/public') + .on('error', error) + .on('directory', redirect) + .pipe(res); +}); +``` + +## API + +### Events + + - `error` an error occurred `(err)` + - `directory` a directory was requested + - `stream` file streaming has started `(stream)` + - `end` streaming has completed + +### .root(dir) + + Serve files relative to `path`. Aliased as `.from(dir)`. + +### .index(path) + + By default send supports "index.html" files, to disable this + invoke `.index(false)` or to supply a new index pass a string. + +### .maxage(ms) + + Provide a max-age in milliseconds for http caching, defaults to 0. + +## Error-handling + + By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc. + +## Caching + + It does _not_ perform internal caching, you should use a reverse proxy cache such + as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;). + +## Debugging + + To enable `debug()` instrumentation output export __DEBUG__: + +``` +$ DEBUG=send node app +``` + +## Running tests + +``` +$ npm install +$ make test +``` + +## 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/dist/node_modules/express/node_modules/send/index.js b/dist/node_modules/express/node_modules/send/index.js new file mode 100644 index 0000000..f17158d --- /dev/null +++ b/dist/node_modules/express/node_modules/send/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/send'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/lib/send.js b/dist/node_modules/express/node_modules/send/lib/send.js new file mode 100644 index 0000000..de72146 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/lib/send.js @@ -0,0 +1,473 @@ + +/** + * Module dependencies. + */ + +var debug = require('debug')('send') + , parseRange = require('range-parser') + , Stream = require('stream') + , mime = require('mime') + , fresh = require('fresh') + , path = require('path') + , http = require('http') + , fs = require('fs') + , basename = path.basename + , normalize = path.normalize + , join = path.join + , utils = require('./utils'); + +/** + * Expose `send`. + */ + +exports = module.exports = send; + +/** + * Expose mime module. + */ + +exports.mime = mime; + +/** + * Return a `SendStream` for `req` and `path`. + * + * @param {Request} req + * @param {String} path + * @param {Object} options + * @return {SendStream} + * @api public + */ + +function send(req, path, options) { + return new SendStream(req, path, options); +} + +/** + * Initialize a `SendStream` with the given `path`. + * + * Events: + * + * - `error` an error occurred + * - `stream` file streaming has started + * - `end` streaming has completed + * - `directory` a directory was requested + * + * @param {Request} req + * @param {String} path + * @param {Object} options + * @api private + */ + +function SendStream(req, path, options) { + var self = this; + this.req = req; + this.path = path; + this.options = options || {}; + this.maxage(0); + this.hidden(false); + this.index('index.html'); +} + +/** + * Inherits from `Stream.prototype`. + */ + +SendStream.prototype.__proto__ = Stream.prototype; + +/** + * Enable or disable "hidden" (dot) files. + * + * @param {Boolean} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.hidden = function(val){ + debug('hidden %s', val); + this._hidden = val; + return this; +}; + +/** + * Set index `path`, set to a falsy + * value to disable index support. + * + * @param {String|Boolean} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.index = function(path){ + debug('index %s', path); + this._index = path; + return this; +}; + +/** + * Set root `path`. + * + * @param {String} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.root = +SendStream.prototype.from = function(path){ + this._root = normalize(path); + return this; +}; + +/** + * Set max-age to `ms`. + * + * @param {Number} ms + * @return {SendStream} + * @api public + */ + +SendStream.prototype.maxage = function(ms){ + if (Infinity == ms) ms = 60 * 60 * 24 * 365 * 1000; + debug('max-age %d', ms); + this._maxage = ms; + return this; +}; + +/** + * Emit error with `status`. + * + * @param {Number} status + * @api private + */ + +SendStream.prototype.error = function(status, err){ + var res = this.res; + var msg = http.STATUS_CODES[status]; + err = err || new Error(msg); + err.status = status; + if (this.listeners('error').length) return this.emit('error', err); + res.statusCode = err.status; + res.end(msg); +}; + +/** + * Check if the pathname is potentially malicious. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isMalicious = function(){ + return !this._root && ~this.path.indexOf('..'); +}; + +/** + * Check if the pathname ends with "/". + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.hasTrailingSlash = function(){ + return '/' == this.path[this.path.length - 1]; +}; + +/** + * Check if the basename leads with ".". + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.hasLeadingDot = function(){ + return '.' == basename(this.path)[0]; +}; + +/** + * Check if this is a conditional GET request. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isConditionalGET = function(){ + return this.req.headers['if-none-match'] + || this.req.headers['if-modified-since']; +}; + +/** + * Strip content-* header fields. + * + * @api private + */ + +SendStream.prototype.removeContentHeaderFields = function(){ + var res = this.res; + Object.keys(res._headers).forEach(function(field){ + if (0 == field.indexOf('content')) { + res.removeHeader(field); + } + }); +}; + +/** + * Respond with 304 not modified. + * + * @api private + */ + +SendStream.prototype.notModified = function(){ + var res = this.res; + debug('not modified'); + this.removeContentHeaderFields(); + res.statusCode = 304; + res.end(); +}; + +/** + * Check if the request is cacheable, aka + * responded with 2xx or 304 (see RFC 2616 section 14.2{5,6}). + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isCachable = function(){ + var res = this.res; + return (res.statusCode >= 200 && res.statusCode < 300) || 304 == res.statusCode; +}; + +/** + * Handle stat() error. + * + * @param {Error} err + * @api private + */ + +SendStream.prototype.onStatError = function(err){ + var notfound = ['ENOENT', 'ENAMETOOLONG', 'ENOTDIR']; + if (~notfound.indexOf(err.code)) return this.error(404, err); + this.error(500, err); +}; + +/** + * Check if the cache is fresh. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isFresh = function(){ + return fresh(this.req.headers, this.res._headers); +}; + +/** + * Redirect to `path`. + * + * @param {String} path + * @api private + */ + +SendStream.prototype.redirect = function(path){ + if (this.listeners('directory').length) return this.emit('directory'); + var res = this.res; + path += '/'; + res.statusCode = 301; + res.setHeader('Location', path); + res.end('Redirecting to ' + utils.escape(path)); +}; + +/** + * Pipe to `res. + * + * @param {Stream} res + * @return {Stream} res + * @api public + */ + +SendStream.prototype.pipe = function(res){ + var self = this + , args = arguments + , path = this.path + , root = this._root; + + // references + this.res = res; + + // invalid request uri + path = utils.decode(path); + if (-1 == path) return this.error(400); + + // null byte(s) + if (~path.indexOf('\0')) return this.error(400); + + // join / normalize from optional root dir + if (root) path = normalize(join(this._root, path)); + + // ".." is malicious without "root" + if (this.isMalicious()) return this.error(403); + + // malicious path + if (root && 0 != path.indexOf(root)) return this.error(403); + + // hidden file support + if (!this._hidden && this.hasLeadingDot()) return this.error(404); + + // index file support + if (this._index && this.hasTrailingSlash()) path += this._index; + + debug('stat "%s"', path); + fs.stat(path, function(err, stat){ + if (err) return self.onStatError(err); + if (stat.isDirectory()) return self.redirect(self.path); + self.send(path, stat); + }); + + return res; +}; + +/** + * Transfer `path`. + * + * @param {String} path + * @api public + */ + +SendStream.prototype.send = function(path, stat){ + var options = this.options; + var len = stat.size; + var res = this.res; + var req = this.req; + var ranges = req.headers.range; + var offset = options.start || 0; + + // set header fields + this.setHeader(stat); + + // set content-type + this.type(path); + + // conditional GET support + if (this.isConditionalGET() + && this.isCachable() + && this.isFresh()) { + return this.notModified(); + } + + // adjust len to start/end options + len = Math.max(0, len - offset); + if (options.end !== undefined) { + var bytes = options.end - offset + 1; + if (len > bytes) len = bytes; + } + + // Range support + if (ranges) { + ranges = parseRange(len, ranges); + + // unsatisfiable + if (-1 == ranges) { + res.setHeader('Content-Range', 'bytes */' + stat.size); + return this.error(416); + } + + // valid (syntactically invalid ranges are treated as a regular response) + if (-2 != ranges) { + options.start = offset + ranges[0].start; + options.end = offset + ranges[0].end; + + // Content-Range + res.statusCode = 206; + res.setHeader('Content-Range', 'bytes ' + + ranges[0].start + + '-' + + ranges[0].end + + '/' + + len); + len = options.end - options.start + 1; + } + } + + // content-length + res.setHeader('Content-Length', len); + + // HEAD support + if ('HEAD' == req.method) return res.end(); + + this.stream(path, options); +}; + +/** + * Stream `path` to the response. + * + * @param {String} path + * @param {Object} options + * @api private + */ + +SendStream.prototype.stream = function(path, options){ + // TODO: this is all lame, refactor meeee + var self = this; + var res = this.res; + var req = this.req; + + // pipe + var stream = fs.createReadStream(path, options); + this.emit('stream', stream); + stream.pipe(res); + + // socket closed, done with the fd + req.on('close', stream.destroy.bind(stream)); + + // error handling code-smell + stream.on('error', function(err){ + // no hope in responding + if (res._header) { + console.error(err.stack); + req.destroy(); + return; + } + + // 500 + err.status = 500; + self.emit('error', err); + }); + + // end + stream.on('end', function(){ + self.emit('end'); + }); +}; + +/** + * Set content-type based on `path` + * if it hasn't been explicitly set. + * + * @param {String} path + * @api private + */ + +SendStream.prototype.type = function(path){ + var res = this.res; + if (res.getHeader('Content-Type')) return; + var type = mime.lookup(path); + var charset = mime.charsets.lookup(type); + debug('content-type %s', type); + res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); +}; + +/** + * Set reaponse header fields, most + * fields may be pre-defined. + * + * @param {Object} stat + * @api private + */ + +SendStream.prototype.setHeader = function(stat){ + var res = this.res; + if (!res.getHeader('Accept-Ranges')) res.setHeader('Accept-Ranges', 'bytes'); + if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat)); + if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString()); + if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (this._maxage / 1000)); + if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString()); +}; diff --git a/dist/node_modules/express/node_modules/send/lib/utils.js b/dist/node_modules/express/node_modules/send/lib/utils.js new file mode 100644 index 0000000..950e5a2 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/lib/utils.js @@ -0,0 +1,47 @@ + +/** + * Return an ETag in the form of `"-"` + * from the given `stat`. + * + * @param {Object} stat + * @return {String} + * @api private + */ + +exports.etag = function(stat) { + return '"' + stat.size + '-' + Number(stat.mtime) + '"'; +}; + +/** + * decodeURIComponent. + * + * Allows V8 to only deoptimize this fn instead of all + * of send(). + * + * @param {String} path + * @api private + */ + +exports.decode = function(path){ + try { + return decodeURIComponent(path); + } catch (err) { + return -1; + } +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/.npmignore b/dist/node_modules/express/node_modules/send/node_modules/debug/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/History.md b/dist/node_modules/express/node_modules/send/node_modules/debug/History.md new file mode 100644 index 0000000..2220632 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/History.md @@ -0,0 +1,47 @@ + +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/dist/node_modules/express/node_modules/send/node_modules/debug/Makefile b/dist/node_modules/express/node_modules/send/node_modules/debug/Makefile new file mode 100644 index 0000000..692f2c1 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/Makefile @@ -0,0 +1,4 @@ + +debug.component.js: head.js debug.js tail.js + cat $^ > $@ + diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/Readme.md b/dist/node_modules/express/node_modules/send/node_modules/debug/Readme.md new file mode 100644 index 0000000..419fcdf --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/Readme.md @@ -0,0 +1,130 @@ + +# debug + + tiny node.js debugging utility. + +## Installation + +``` +$ npm install debug +``` + +## Example + + This module is modelled after node core's debugging technique, allowing you to enable one or more topic-specific debugging functions, for example core does the following within many modules: + +```js +var debug; +if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) { + debug = function(x) { + var prefix = process.pid + ',' + + (process.env.NODE_WORKER_ID ? 'Worker' : 'Master'); + console.error(prefix, x); + }; +} else { + debug = function() { }; +} +``` + + This concept is extremely simple but it works well. 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) + +## 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`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + a('doing some work'); +}, 1200); +``` + +## License + +(The MIT License) + +Copyright (c) 2011 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/dist/node_modules/express/node_modules/send/node_modules/debug/debug.component.js b/dist/node_modules/express/node_modules/send/node_modules/debug/debug.component.js new file mode 100644 index 0000000..e6e9dbf --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/debug.component.js @@ -0,0 +1,120 @@ +;(function(){ + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + localStorage.debug = name; + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +// persist + +if (window.localStorage) debug.enable(localStorage.debug); + module.exports = debug; + +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/debug.js b/dist/node_modules/express/node_modules/send/node_modules/debug/debug.js new file mode 100644 index 0000000..905fbd4 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/debug.js @@ -0,0 +1,116 @@ + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + localStorage.debug = name; + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +// persist + +if (window.localStorage) debug.enable(localStorage.debug); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/example/app.js b/dist/node_modules/express/node_modules/send/node_modules/debug/example/app.js new file mode 100644 index 0000000..05374d9 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/example/app.js @@ -0,0 +1,19 @@ + +var debug = require('../')('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'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/example/browser.html b/dist/node_modules/express/node_modules/send/node_modules/debug/example/browser.html new file mode 100644 index 0000000..7510eee --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/example/browser.html @@ -0,0 +1,24 @@ + + + debug() + + + + + + + diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/example/wildcards.js b/dist/node_modules/express/node_modules/send/node_modules/debug/example/wildcards.js new file mode 100644 index 0000000..1fdac20 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/example/wildcards.js @@ -0,0 +1,10 @@ + +var debug = { + foo: require('../')('test:foo'), + bar: require('../')('test:bar'), + baz: require('../')('test:baz') +}; + +debug.foo('foo') +debug.bar('bar') +debug.baz('baz') \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/example/worker.js b/dist/node_modules/express/node_modules/send/node_modules/debug/example/worker.js new file mode 100644 index 0000000..7f6d288 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/example/worker.js @@ -0,0 +1,22 @@ + +// DEBUG=* node example/worker +// DEBUG=worker:* node example/worker +// DEBUG=worker:a node example/worker +// DEBUG=worker:b node example/worker + +var a = require('../')('worker:a') + , b = require('../')('worker:b'); + +function work() { + a('doing lots of uninteresting work'); + setTimeout(work, Math.random() * 1000); +} + +work(); + +function workb() { + b('doing some work'); + setTimeout(workb, Math.random() * 2000); +} + +workb(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/head.js b/dist/node_modules/express/node_modules/send/node_modules/debug/head.js new file mode 100644 index 0000000..55d3817 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/head.js @@ -0,0 +1 @@ +;(function(){ diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/index.js b/dist/node_modules/express/node_modules/send/node_modules/debug/index.js new file mode 100644 index 0000000..ee54454 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/debug'); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/lib/debug.js b/dist/node_modules/express/node_modules/send/node_modules/debug/lib/debug.js new file mode 100644 index 0000000..969d122 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/lib/debug.js @@ -0,0 +1,135 @@ + +/** + * Module dependencies. + */ + +var tty = require('tty'); + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Enabled debuggers. + */ + +var names = [] + , skips = []; + +(process.env.DEBUG || '') + .split(/[\s,]+/) + .forEach(function(name){ + name = name.replace('*', '.*?'); + if (name[0] === '-') { + skips.push(new RegExp('^' + name.substr(1) + '$')); + } else { + names.push(new RegExp('^' + name + '$')); + } + }); + +/** + * Colors. + */ + +var colors = [6, 2, 3, 4, 5, 1]; + +/** + * Previous debug() call. + */ + +var prev = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Is stdout a TTY? Colored output is disabled when `true`. + */ + +var isatty = tty.isatty(2); + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function color() { + return colors[prevColor++ % colors.length]; +} + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +function humanize(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +} + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + function disabled(){} + disabled.enabled = false; + + var match = skips.some(function(re){ + return re.test(name); + }); + + if (match) return disabled; + + match = names.some(function(re){ + return re.test(name); + }); + + if (!match) return disabled; + var c = color(); + + function colored(fmt) { + var curr = new Date; + var ms = curr - (prev[name] || curr); + prev[name] = curr; + + fmt = ' \033[9' + c + 'm' + name + ' ' + + '\033[3' + c + 'm\033[90m' + + fmt + '\033[3' + c + 'm' + + ' +' + humanize(ms) + '\033[0m'; + + console.error.apply(this, arguments); + } + + function plain(fmt) { + fmt = new Date().toUTCString() + + ' ' + name + ' ' + fmt; + console.error.apply(this, arguments); + } + + colored.enabled = plain.enabled = true; + + return isatty + ? colored + : plain; +} diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/package.json b/dist/node_modules/express/node_modules/send/node_modules/debug/package.json new file mode 100644 index 0000000..f226656 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/package.json @@ -0,0 +1,17 @@ +{ + "name": "debug" + , "version": "0.7.0" + , "description": "small debugging utility" + , "keywords": ["debug", "log", "debugger"] + , "author": "TJ Holowaychuk " + , "dependencies": {} + , "devDependencies": { "mocha": "*" } + , "main": "index" + , "browserify": "debug.component.js" + , "engines": { "node": "*" } + , "component": { + "scripts": { + "debug": "debug.component.js" + } + } +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/debug/tail.js b/dist/node_modules/express/node_modules/send/node_modules/debug/tail.js new file mode 100644 index 0000000..5bf3fd3 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/debug/tail.js @@ -0,0 +1,4 @@ + + module.exports = debug; + +})(); \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/fresh/.npmignore b/dist/node_modules/express/node_modules/send/node_modules/fresh/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/fresh/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/send/node_modules/fresh/Makefile b/dist/node_modules/express/node_modules/send/node_modules/fresh/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/fresh/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/fresh/Readme.md b/dist/node_modules/express/node_modules/send/node_modules/fresh/Readme.md new file mode 100644 index 0000000..273130d --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/fresh/Readme.md @@ -0,0 +1,32 @@ + +# node-fresh + + HTTP response freshness testing + +## fresh(req, res) + + Check freshness of `req` and `res` headers. + + When the cache is "fresh" __true__ is returned, + otherwise __false__ is returned to indicate that + the cache is now stale. + +## Example: + +```js +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'luna' }; +fresh(req, res); +// => false + +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'tobi' }; +fresh(req, res); +// => true +``` + +## Installation + +``` +$ npm install fresh +``` \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/fresh/index.js b/dist/node_modules/express/node_modules/send/node_modules/fresh/index.js new file mode 100644 index 0000000..b2f4d41 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/fresh/index.js @@ -0,0 +1,49 @@ + +/** + * Expose `fresh()`. + */ + +module.exports = fresh; + +/** + * Check freshness of `req` and `res` headers. + * + * When the cache is "fresh" __true__ is returned, + * otherwise __false__ is returned to indicate that + * the cache is now stale. + * + * @param {Object} req + * @param {Object} res + * @return {Boolean} + * @api public + */ + +function fresh(req, res) { + // defaults + var etagMatches = true; + var notModified = true; + + // fields + var modifiedSince = req['if-modified-since']; + var noneMatch = req['if-none-match']; + var lastModified = res['last-modified']; + var etag = res['etag']; + + // unconditional request + if (!modifiedSince && !noneMatch) return false; + + // parse if-none-match + if (noneMatch) noneMatch = noneMatch.split(/ *, */); + + // if-none-match + if (noneMatch) etagMatches = ~noneMatch.indexOf(etag) || '*' == noneMatch[0]; + + // if-modified-since + if (modifiedSince) { + modifiedSince = new Date(modifiedSince); + lastModified = new Date(lastModified); + notModified = lastModified <= modifiedSince; + } + + return !! (etagMatches && notModified); +} \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/fresh/package.json b/dist/node_modules/express/node_modules/send/node_modules/fresh/package.json new file mode 100644 index 0000000..d81fc0d --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/fresh/package.json @@ -0,0 +1,12 @@ +{ + "name": "fresh", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "HTTP response freshness testing", + "version": "0.1.0", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + } +} diff --git a/dist/node_modules/express/node_modules/send/node_modules/mime/LICENSE b/dist/node_modules/express/node_modules/send/node_modules/mime/LICENSE new file mode 100644 index 0000000..451fc45 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/mime/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Benjamin Thomas, Robert Kieffer + +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/dist/node_modules/express/node_modules/send/node_modules/mime/README.md b/dist/node_modules/express/node_modules/send/node_modules/mime/README.md new file mode 100644 index 0000000..d8b66a8 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/mime/README.md @@ -0,0 +1,63 @@ +# mime + +Comprehensive MIME type mapping API. Includes all 600+ types and 800+ extensions defined by the Apache project, plus additional types submitted by the node.js community. + +## Install + +Install with [npm](http://github.com/isaacs/npm): + + npm install mime + +## API - Queries + +### mime.lookup(path) +Get the mime type associated with a file. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g. + + var mime = require('mime'); + + mime.lookup('/path/to/file.txt'); // => 'text/plain' + mime.lookup('file.txt'); // => 'text/plain' + mime.lookup('.TXT'); // => 'text/plain' + mime.lookup('htm'); // => 'text/html' + +### mime.extension(type) +Get the default extension for `type` + + mime.extension('text/html'); // => 'html' + mime.extension('application/octet-stream'); // => 'bin' + +### mime.charsets.lookup() + +Map mime-type to charset + + mime.charsets.lookup('text/plain'); // => 'UTF-8' + +(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.) + +## API - Defining Custom Types + +The following APIs allow you to add your own type mappings within your project. If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/bentomas/node-mime/wiki/Requesting-New-Types). + +### mime.define() + +Add custom mime/extension mappings + + mime.define({ + 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'], + 'application/x-my-type': ['x-mt', 'x-mtt'], + // etc ... + }); + + mime.lookup('x-sft'); // => 'text/x-some-format' + +The first entry in the extensions array is returned by `mime.extension()`. E.g. + + mime.extension('text/x-some-format'); // => 'x-sf' + +### mime.load(filepath) + +Load mappings from an Apache ".types" format file + + mime.load('./my_project.types'); + +The .types file format is simple - See the `types` dir for examples. diff --git a/dist/node_modules/express/node_modules/send/node_modules/mime/mime.js b/dist/node_modules/express/node_modules/send/node_modules/mime/mime.js new file mode 100644 index 0000000..1e00585 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/mime/mime.js @@ -0,0 +1,104 @@ +var path = require('path'); +var fs = require('fs'); + +function Mime() { + // Map of extension -> mime type + this.types = Object.create(null); + + // Map of mime type -> extension + this.extensions = Object.create(null); +} + +/** + * Define mimetype -> extension mappings. Each key is a mime-type that maps + * to an array of extensions associated with the type. The first extension is + * used as the default extension for the type. + * + * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']}); + * + * @param map (Object) type definitions + */ +Mime.prototype.define = function (map) { + for (var type in map) { + var exts = map[type]; + + for (var i = 0; i < exts.length; i++) { + this.types[exts[i]] = type; + } + + // Default extension is the first one we encounter + if (!this.extensions[type]) { + this.extensions[type] = exts[0]; + } + } +}; + +/** + * Load an Apache2-style ".types" file + * + * This may be called multiple times (it's expected). Where files declare + * overlapping types/extensions, the last file wins. + * + * @param file (String) path of file to load. + */ +Mime.prototype.load = function(file) { + // Read file and split into lines + var map = {}, + content = fs.readFileSync(file, 'ascii'), + lines = content.split(/[\r\n]+/); + + lines.forEach(function(line) { + // Clean up whitespace/comments, and split into fields + var fields = line.replace(/\s*#.*|^\s*|\s*$/g, '').split(/\s+/); + map[fields.shift()] = fields; + }); + + this.define(map); +}; + +/** + * Lookup a mime type based on extension + */ +Mime.prototype.lookup = function(path, fallback) { + var ext = path.replace(/.*[\.\/]/, '').toLowerCase(); + + return this.types[ext] || fallback || this.default_type; +}; + +/** + * Return file extension associated with a mime type + */ +Mime.prototype.extension = function(mimeType) { + return this.extensions[mimeType]; +}; + +// Default instance +var mime = new Mime(); + +// Load local copy of +// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +mime.load(path.join(__dirname, 'types/mime.types')); + +// Load additional types from node.js community +mime.load(path.join(__dirname, 'types/node.types')); + +// Default type +mime.default_type = mime.lookup('bin'); + +// +// Additional API specific to the default instance +// + +mime.Mime = Mime; + +/** + * Lookup a charset based on mime type. + */ +mime.charsets = { + lookup: function(mimeType, fallback) { + // Assume text types are utf8 + return (/^text\//).test(mimeType) ? 'UTF-8' : fallback; + } +} + +module.exports = mime; diff --git a/dist/node_modules/express/node_modules/send/node_modules/mime/package.json b/dist/node_modules/express/node_modules/send/node_modules/mime/package.json new file mode 100644 index 0000000..05f6fb4 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/mime/package.json @@ -0,0 +1,22 @@ +{ + "author": { + "name": "Robert Kieffer", + "url": "http://github.com/broofa", + "email": "robert@broofa.com" + }, + "contributors": [ + { + "name": "Benjamin Thomas", + "url": "http://github.com/bentomas", + "email": "benjamin@benjaminthomas.org" + } + ], + "dependencies": {}, + "description": "A comprehensive library for mime-type mapping", + "devDependencies": {}, + "keywords": ["util", "mime"], + "main": "mime.js", + "name": "mime", + "repository": {"url": "https://github.com/broofa/node-mime", "type": "git"}, + "version": "1.2.6" +} diff --git a/dist/node_modules/express/node_modules/send/node_modules/mime/test.js b/dist/node_modules/express/node_modules/send/node_modules/mime/test.js new file mode 100644 index 0000000..cbad034 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/mime/test.js @@ -0,0 +1,55 @@ +/** + * Usage: node test.js + */ + +var mime = require('./mime'); +var assert = require('assert'); + +function eq(a, b) { + console.log('Test: ' + a + ' === ' + b); + assert.strictEqual.apply(null, arguments); +} + +console.log(Object.keys(mime.extensions).length + ' types'); +console.log(Object.keys(mime.types).length + ' extensions\n'); + +// +// Test mime lookups +// + +eq('text/plain', mime.lookup('text.txt')); +eq('text/plain', mime.lookup('.text.txt')); +eq('text/plain', mime.lookup('.txt')); +eq('text/plain', mime.lookup('txt')); +eq('application/octet-stream', mime.lookup('text.nope')); +eq('fallback', mime.lookup('text.fallback', 'fallback')); +eq('application/octet-stream', mime.lookup('constructor')); +eq('text/plain', mime.lookup('TEXT.TXT')); +eq('text/event-stream', mime.lookup('text/event-stream')); +eq('application/x-web-app-manifest+json', mime.lookup('text.webapp')); + +// +// Test extensions +// + +eq('txt', mime.extension(mime.types.text)); +eq('html', mime.extension(mime.types.htm)); +eq('bin', mime.extension('application/octet-stream')); +eq(undefined, mime.extension('constructor')); + +// +// Test node types +// + +eq('application/octet-stream', mime.lookup('file.buffer')); +eq('audio/mp4', mime.lookup('file.m4a')); + +// +// Test charsets +// + +eq('UTF-8', mime.charsets.lookup('text/plain')); +eq(undefined, mime.charsets.lookup(mime.types.js)); +eq('fallback', mime.charsets.lookup('application/octet-stream', 'fallback')); + +console.log('\nOK'); diff --git a/dist/node_modules/express/node_modules/send/node_modules/mime/types/mime.types b/dist/node_modules/express/node_modules/send/node_modules/mime/types/mime.types new file mode 100644 index 0000000..b3cae2e --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/mime/types/mime.types @@ -0,0 +1,1510 @@ +# This file maps Internet media types to unique file extension(s). +# Although created for httpd, this file is used by many software systems +# and has been placed in the public domain for unlimited redisribution. +# +# The table below contains both registered and (common) unregistered types. +# A type that has no unique extension can be ignored -- they are listed +# here to guide configurations toward known types and to make it easier to +# identify "new" types. File extensions are also commonly used to indicate +# content languages and encodings, so choose them carefully. +# +# Internet media types should be registered as described in RFC 4288. +# The registry is at . +# +# MIME type (lowercased) Extensions +# ============================================ ========== +# application/1d-interleaved-parityfec +# application/3gpp-ims+xml +# application/activemessage +application/andrew-inset ez +# application/applefile +application/applixware aw +application/atom+xml atom +application/atomcat+xml atomcat +# application/atomicmail +application/atomsvc+xml atomsvc +# application/auth-policy+xml +# application/batch-smtp +# application/beep+xml +# application/calendar+xml +# application/cals-1840 +# application/ccmp+xml +application/ccxml+xml ccxml +application/cdmi-capability cdmia +application/cdmi-container cdmic +application/cdmi-domain cdmid +application/cdmi-object cdmio +application/cdmi-queue cdmiq +# application/cea-2018+xml +# application/cellml+xml +# application/cfw +# application/cnrp+xml +# application/commonground +# application/conference-info+xml +# application/cpl+xml +# application/csta+xml +# application/cstadata+xml +application/cu-seeme cu +# application/cybercash +application/davmount+xml davmount +# application/dca-rft +# application/dec-dx +# application/dialog-info+xml +# application/dicom +# application/dns +# application/dskpp+xml +application/dssc+der dssc +application/dssc+xml xdssc +# application/dvcs +application/ecmascript ecma +# application/edi-consent +# application/edi-x12 +# application/edifact +application/emma+xml emma +# application/epp+xml +application/epub+zip epub +# application/eshop +# application/example +application/exi exi +# application/fastinfoset +# application/fastsoap +# application/fits +application/font-tdpfr pfr +# application/framework-attributes+xml +# application/h224 +# application/held+xml +# application/http +application/hyperstudio stk +# application/ibe-key-request+xml +# application/ibe-pkg-reply+xml +# application/ibe-pp-data +# application/iges +# application/im-iscomposing+xml +# application/index +# application/index.cmd +# application/index.obj +# application/index.response +# application/index.vnd +application/inkml+xml ink inkml +# application/iotp +application/ipfix ipfix +# application/ipp +# application/isup +application/java-archive jar +application/java-serialized-object ser +application/java-vm class +application/javascript js +application/json json +# application/kpml-request+xml +# application/kpml-response+xml +application/lost+xml lostxml +application/mac-binhex40 hqx +application/mac-compactpro cpt +# application/macwriteii +application/mads+xml mads +application/marc mrc +application/marcxml+xml mrcx +application/mathematica ma nb mb +# application/mathml-content+xml +# application/mathml-presentation+xml +application/mathml+xml mathml +# application/mbms-associated-procedure-description+xml +# application/mbms-deregister+xml +# application/mbms-envelope+xml +# application/mbms-msk+xml +# application/mbms-msk-response+xml +# application/mbms-protection-description+xml +# application/mbms-reception-report+xml +# application/mbms-register+xml +# application/mbms-register-response+xml +# application/mbms-user-service-description+xml +application/mbox mbox +# application/media_control+xml +application/mediaservercontrol+xml mscml +application/metalink4+xml meta4 +application/mets+xml mets +# application/mikey +application/mods+xml mods +# application/moss-keys +# application/moss-signature +# application/mosskey-data +# application/mosskey-request +application/mp21 m21 mp21 +application/mp4 mp4s +# application/mpeg4-generic +# application/mpeg4-iod +# application/mpeg4-iod-xmt +# application/msc-ivr+xml +# application/msc-mixer+xml +application/msword doc dot +application/mxf mxf +# application/nasdata +# application/news-checkgroups +# application/news-groupinfo +# application/news-transmission +# application/nss +# application/ocsp-request +# application/ocsp-response +application/octet-stream bin dms lha lrf lzh so iso dmg dist distz pkg bpk dump elc deploy +application/oda oda +application/oebps-package+xml opf +application/ogg ogx +application/onenote onetoc onetoc2 onetmp onepkg +application/oxps oxps +# application/parityfec +application/patch-ops-error+xml xer +application/pdf pdf +application/pgp-encrypted pgp +# application/pgp-keys +application/pgp-signature asc sig +application/pics-rules prf +# application/pidf+xml +# application/pidf-diff+xml +application/pkcs10 p10 +application/pkcs7-mime p7m p7c +application/pkcs7-signature p7s +application/pkcs8 p8 +application/pkix-attr-cert ac +application/pkix-cert cer +application/pkix-crl crl +application/pkix-pkipath pkipath +application/pkixcmp pki +application/pls+xml pls +# application/poc-settings+xml +application/postscript ai eps ps +# application/prs.alvestrand.titrax-sheet +application/prs.cww cww +# application/prs.nprend +# application/prs.plucker +# application/prs.rdf-xml-crypt +# application/prs.xsf+xml +application/pskc+xml pskcxml +# application/qsig +application/rdf+xml rdf +application/reginfo+xml rif +application/relax-ng-compact-syntax rnc +# application/remote-printing +application/resource-lists+xml rl +application/resource-lists-diff+xml rld +# application/riscos +# application/rlmi+xml +application/rls-services+xml rs +application/rpki-ghostbusters gbr +application/rpki-manifest mft +application/rpki-roa roa +# application/rpki-updown +application/rsd+xml rsd +application/rss+xml rss +application/rtf rtf +# application/rtx +# application/samlassertion+xml +# application/samlmetadata+xml +application/sbml+xml sbml +application/scvp-cv-request scq +application/scvp-cv-response scs +application/scvp-vp-request spq +application/scvp-vp-response spp +application/sdp sdp +# application/set-payment +application/set-payment-initiation setpay +# application/set-registration +application/set-registration-initiation setreg +# application/sgml +# application/sgml-open-catalog +application/shf+xml shf +# application/sieve +# application/simple-filter+xml +# application/simple-message-summary +# application/simplesymbolcontainer +# application/slate +# application/smil +application/smil+xml smi smil +# application/soap+fastinfoset +# application/soap+xml +application/sparql-query rq +application/sparql-results+xml srx +# application/spirits-event+xml +application/srgs gram +application/srgs+xml grxml +application/sru+xml sru +application/ssml+xml ssml +# application/tamp-apex-update +# application/tamp-apex-update-confirm +# application/tamp-community-update +# application/tamp-community-update-confirm +# application/tamp-error +# application/tamp-sequence-adjust +# application/tamp-sequence-adjust-confirm +# application/tamp-status-query +# application/tamp-status-response +# application/tamp-update +# application/tamp-update-confirm +application/tei+xml tei teicorpus +application/thraud+xml tfi +# application/timestamp-query +# application/timestamp-reply +application/timestamped-data tsd +# application/tve-trigger +# application/ulpfec +# application/vcard+xml +# application/vemmi +# application/vividence.scriptfile +# application/vnd.3gpp.bsf+xml +application/vnd.3gpp.pic-bw-large plb +application/vnd.3gpp.pic-bw-small psb +application/vnd.3gpp.pic-bw-var pvb +# application/vnd.3gpp.sms +# application/vnd.3gpp2.bcmcsinfo+xml +# application/vnd.3gpp2.sms +application/vnd.3gpp2.tcap tcap +application/vnd.3m.post-it-notes pwn +application/vnd.accpac.simply.aso aso +application/vnd.accpac.simply.imp imp +application/vnd.acucobol acu +application/vnd.acucorp atc acutc +application/vnd.adobe.air-application-installer-package+zip air +application/vnd.adobe.fxp fxp fxpl +# application/vnd.adobe.partial-upload +application/vnd.adobe.xdp+xml xdp +application/vnd.adobe.xfdf xfdf +# application/vnd.aether.imp +# application/vnd.ah-barcode +application/vnd.ahead.space ahead +application/vnd.airzip.filesecure.azf azf +application/vnd.airzip.filesecure.azs azs +application/vnd.amazon.ebook azw +application/vnd.americandynamics.acc acc +application/vnd.amiga.ami ami +# application/vnd.amundsen.maze+xml +application/vnd.android.package-archive apk +application/vnd.anser-web-certificate-issue-initiation cii +application/vnd.anser-web-funds-transfer-initiation fti +application/vnd.antix.game-component atx +application/vnd.apple.installer+xml mpkg +application/vnd.apple.mpegurl m3u8 +# application/vnd.arastra.swi +application/vnd.aristanetworks.swi swi +application/vnd.astraea-software.iota iota +application/vnd.audiograph aep +# application/vnd.autopackage +# application/vnd.avistar+xml +application/vnd.blueice.multipass mpm +# application/vnd.bluetooth.ep.oob +application/vnd.bmi bmi +application/vnd.businessobjects rep +# application/vnd.cab-jscript +# application/vnd.canon-cpdl +# application/vnd.canon-lips +# application/vnd.cendio.thinlinc.clientconf +application/vnd.chemdraw+xml cdxml +application/vnd.chipnuts.karaoke-mmd mmd +application/vnd.cinderella cdy +# application/vnd.cirpack.isdn-ext +application/vnd.claymore cla +application/vnd.cloanto.rp9 rp9 +application/vnd.clonk.c4group c4g c4d c4f c4p c4u +application/vnd.cluetrust.cartomobile-config c11amc +application/vnd.cluetrust.cartomobile-config-pkg c11amz +# application/vnd.collection+json +# application/vnd.commerce-battelle +application/vnd.commonspace csp +application/vnd.contact.cmsg cdbcmsg +application/vnd.cosmocaller cmc +application/vnd.crick.clicker clkx +application/vnd.crick.clicker.keyboard clkk +application/vnd.crick.clicker.palette clkp +application/vnd.crick.clicker.template clkt +application/vnd.crick.clicker.wordbank clkw +application/vnd.criticaltools.wbs+xml wbs +application/vnd.ctc-posml pml +# application/vnd.ctct.ws+xml +# application/vnd.cups-pdf +# application/vnd.cups-postscript +application/vnd.cups-ppd ppd +# application/vnd.cups-raster +# application/vnd.cups-raw +# application/vnd.curl +application/vnd.curl.car car +application/vnd.curl.pcurl pcurl +# application/vnd.cybank +application/vnd.data-vision.rdz rdz +application/vnd.dece.data uvf uvvf uvd uvvd +application/vnd.dece.ttml+xml uvt uvvt +application/vnd.dece.unspecified uvx uvvx +application/vnd.dece.zip uvz uvvz +application/vnd.denovo.fcselayout-link fe_launch +# application/vnd.dir-bi.plate-dl-nosuffix +application/vnd.dna dna +application/vnd.dolby.mlp mlp +# application/vnd.dolby.mobile.1 +# application/vnd.dolby.mobile.2 +application/vnd.dpgraph dpg +application/vnd.dreamfactory dfac +application/vnd.dvb.ait ait +# application/vnd.dvb.dvbj +# application/vnd.dvb.esgcontainer +# application/vnd.dvb.ipdcdftnotifaccess +# application/vnd.dvb.ipdcesgaccess +# application/vnd.dvb.ipdcesgaccess2 +# application/vnd.dvb.ipdcesgpdd +# application/vnd.dvb.ipdcroaming +# application/vnd.dvb.iptv.alfec-base +# application/vnd.dvb.iptv.alfec-enhancement +# application/vnd.dvb.notif-aggregate-root+xml +# application/vnd.dvb.notif-container+xml +# application/vnd.dvb.notif-generic+xml +# application/vnd.dvb.notif-ia-msglist+xml +# application/vnd.dvb.notif-ia-registration-request+xml +# application/vnd.dvb.notif-ia-registration-response+xml +# application/vnd.dvb.notif-init+xml +# application/vnd.dvb.pfr +application/vnd.dvb.service svc +# application/vnd.dxr +application/vnd.dynageo geo +# application/vnd.easykaraoke.cdgdownload +# application/vnd.ecdis-update +application/vnd.ecowin.chart mag +# application/vnd.ecowin.filerequest +# application/vnd.ecowin.fileupdate +# application/vnd.ecowin.series +# application/vnd.ecowin.seriesrequest +# application/vnd.ecowin.seriesupdate +# application/vnd.emclient.accessrequest+xml +application/vnd.enliven nml +# application/vnd.eprints.data+xml +application/vnd.epson.esf esf +application/vnd.epson.msf msf +application/vnd.epson.quickanime qam +application/vnd.epson.salt slt +application/vnd.epson.ssf ssf +# application/vnd.ericsson.quickcall +application/vnd.eszigno3+xml es3 et3 +# application/vnd.etsi.aoc+xml +# application/vnd.etsi.cug+xml +# application/vnd.etsi.iptvcommand+xml +# application/vnd.etsi.iptvdiscovery+xml +# application/vnd.etsi.iptvprofile+xml +# application/vnd.etsi.iptvsad-bc+xml +# application/vnd.etsi.iptvsad-cod+xml +# application/vnd.etsi.iptvsad-npvr+xml +# application/vnd.etsi.iptvservice+xml +# application/vnd.etsi.iptvsync+xml +# application/vnd.etsi.iptvueprofile+xml +# application/vnd.etsi.mcid+xml +# application/vnd.etsi.overload-control-policy-dataset+xml +# application/vnd.etsi.sci+xml +# application/vnd.etsi.simservs+xml +# application/vnd.etsi.tsl+xml +# application/vnd.etsi.tsl.der +# application/vnd.eudora.data +application/vnd.ezpix-album ez2 +application/vnd.ezpix-package ez3 +# application/vnd.f-secure.mobile +application/vnd.fdf fdf +application/vnd.fdsn.mseed mseed +application/vnd.fdsn.seed seed dataless +# application/vnd.ffsns +# application/vnd.fints +application/vnd.flographit gph +application/vnd.fluxtime.clip ftc +# application/vnd.font-fontforge-sfd +application/vnd.framemaker fm frame maker book +application/vnd.frogans.fnc fnc +application/vnd.frogans.ltf ltf +application/vnd.fsc.weblaunch fsc +application/vnd.fujitsu.oasys oas +application/vnd.fujitsu.oasys2 oa2 +application/vnd.fujitsu.oasys3 oa3 +application/vnd.fujitsu.oasysgp fg5 +application/vnd.fujitsu.oasysprs bh2 +# application/vnd.fujixerox.art-ex +# application/vnd.fujixerox.art4 +# application/vnd.fujixerox.hbpl +application/vnd.fujixerox.ddd ddd +application/vnd.fujixerox.docuworks xdw +application/vnd.fujixerox.docuworks.binder xbd +# application/vnd.fut-misnet +application/vnd.fuzzysheet fzs +application/vnd.genomatix.tuxedo txd +# application/vnd.geocube+xml +application/vnd.geogebra.file ggb +application/vnd.geogebra.tool ggt +application/vnd.geometry-explorer gex gre +application/vnd.geonext gxt +application/vnd.geoplan g2w +application/vnd.geospace g3w +# application/vnd.globalplatform.card-content-mgt +# application/vnd.globalplatform.card-content-mgt-response +application/vnd.gmx gmx +application/vnd.google-earth.kml+xml kml +application/vnd.google-earth.kmz kmz +application/vnd.grafeq gqf gqs +# application/vnd.gridmp +application/vnd.groove-account gac +application/vnd.groove-help ghf +application/vnd.groove-identity-message gim +application/vnd.groove-injector grv +application/vnd.groove-tool-message gtm +application/vnd.groove-tool-template tpl +application/vnd.groove-vcard vcg +# application/vnd.hal+json +application/vnd.hal+xml hal +application/vnd.handheld-entertainment+xml zmm +application/vnd.hbci hbci +# application/vnd.hcl-bireports +application/vnd.hhe.lesson-player les +application/vnd.hp-hpgl hpgl +application/vnd.hp-hpid hpid +application/vnd.hp-hps hps +application/vnd.hp-jlyt jlt +application/vnd.hp-pcl pcl +application/vnd.hp-pclxl pclxl +# application/vnd.httphone +application/vnd.hydrostatix.sof-data sfd-hdstx +application/vnd.hzn-3d-crossword x3d +# application/vnd.ibm.afplinedata +# application/vnd.ibm.electronic-media +application/vnd.ibm.minipay mpy +application/vnd.ibm.modcap afp listafp list3820 +application/vnd.ibm.rights-management irm +application/vnd.ibm.secure-container sc +application/vnd.iccprofile icc icm +application/vnd.igloader igl +application/vnd.immervision-ivp ivp +application/vnd.immervision-ivu ivu +# application/vnd.informedcontrol.rms+xml +# application/vnd.informix-visionary +# application/vnd.infotech.project +# application/vnd.infotech.project+xml +application/vnd.insors.igm igm +application/vnd.intercon.formnet xpw xpx +application/vnd.intergeo i2g +# application/vnd.intertrust.digibox +# application/vnd.intertrust.nncp +application/vnd.intu.qbo qbo +application/vnd.intu.qfx qfx +# application/vnd.iptc.g2.conceptitem+xml +# application/vnd.iptc.g2.knowledgeitem+xml +# application/vnd.iptc.g2.newsitem+xml +# application/vnd.iptc.g2.packageitem+xml +application/vnd.ipunplugged.rcprofile rcprofile +application/vnd.irepository.package+xml irp +application/vnd.is-xpr xpr +application/vnd.isac.fcs fcs +application/vnd.jam jam +# application/vnd.japannet-directory-service +# application/vnd.japannet-jpnstore-wakeup +# application/vnd.japannet-payment-wakeup +# application/vnd.japannet-registration +# application/vnd.japannet-registration-wakeup +# application/vnd.japannet-setstore-wakeup +# application/vnd.japannet-verification +# application/vnd.japannet-verification-wakeup +application/vnd.jcp.javame.midlet-rms rms +application/vnd.jisp jisp +application/vnd.joost.joda-archive joda +application/vnd.kahootz ktz ktr +application/vnd.kde.karbon karbon +application/vnd.kde.kchart chrt +application/vnd.kde.kformula kfo +application/vnd.kde.kivio flw +application/vnd.kde.kontour kon +application/vnd.kde.kpresenter kpr kpt +application/vnd.kde.kspread ksp +application/vnd.kde.kword kwd kwt +application/vnd.kenameaapp htke +application/vnd.kidspiration kia +application/vnd.kinar kne knp +application/vnd.koan skp skd skt skm +application/vnd.kodak-descriptor sse +application/vnd.las.las+xml lasxml +# application/vnd.liberty-request+xml +application/vnd.llamagraphics.life-balance.desktop lbd +application/vnd.llamagraphics.life-balance.exchange+xml lbe +application/vnd.lotus-1-2-3 123 +application/vnd.lotus-approach apr +application/vnd.lotus-freelance pre +application/vnd.lotus-notes nsf +application/vnd.lotus-organizer org +application/vnd.lotus-screencam scm +application/vnd.lotus-wordpro lwp +application/vnd.macports.portpkg portpkg +# application/vnd.marlin.drm.actiontoken+xml +# application/vnd.marlin.drm.conftoken+xml +# application/vnd.marlin.drm.license+xml +# application/vnd.marlin.drm.mdcf +application/vnd.mcd mcd +application/vnd.medcalcdata mc1 +application/vnd.mediastation.cdkey cdkey +# application/vnd.meridian-slingshot +application/vnd.mfer mwf +application/vnd.mfmp mfm +application/vnd.micrografx.flo flo +application/vnd.micrografx.igx igx +application/vnd.mif mif +# application/vnd.minisoft-hp3000-save +# application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.mobius.daf daf +application/vnd.mobius.dis dis +application/vnd.mobius.mbk mbk +application/vnd.mobius.mqy mqy +application/vnd.mobius.msl msl +application/vnd.mobius.plc plc +application/vnd.mobius.txf txf +application/vnd.mophun.application mpn +application/vnd.mophun.certificate mpc +# application/vnd.motorola.flexsuite +# application/vnd.motorola.flexsuite.adsi +# application/vnd.motorola.flexsuite.fis +# application/vnd.motorola.flexsuite.gotap +# application/vnd.motorola.flexsuite.kmr +# application/vnd.motorola.flexsuite.ttc +# application/vnd.motorola.flexsuite.wem +# application/vnd.motorola.iprm +application/vnd.mozilla.xul+xml xul +application/vnd.ms-artgalry cil +# application/vnd.ms-asf +application/vnd.ms-cab-compressed cab +application/vnd.ms-excel xls xlm xla xlc xlt xlw +application/vnd.ms-excel.addin.macroenabled.12 xlam +application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb +application/vnd.ms-excel.sheet.macroenabled.12 xlsm +application/vnd.ms-excel.template.macroenabled.12 xltm +application/vnd.ms-fontobject eot +application/vnd.ms-htmlhelp chm +application/vnd.ms-ims ims +application/vnd.ms-lrm lrm +# application/vnd.ms-office.activex+xml +application/vnd.ms-officetheme thmx +application/vnd.ms-pki.seccat cat +application/vnd.ms-pki.stl stl +# application/vnd.ms-playready.initiator+xml +application/vnd.ms-powerpoint ppt pps pot +application/vnd.ms-powerpoint.addin.macroenabled.12 ppam +application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm +application/vnd.ms-powerpoint.slide.macroenabled.12 sldm +application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm +application/vnd.ms-powerpoint.template.macroenabled.12 potm +application/vnd.ms-project mpp mpt +# application/vnd.ms-tnef +# application/vnd.ms-wmdrm.lic-chlg-req +# application/vnd.ms-wmdrm.lic-resp +# application/vnd.ms-wmdrm.meter-chlg-req +# application/vnd.ms-wmdrm.meter-resp +application/vnd.ms-word.document.macroenabled.12 docm +application/vnd.ms-word.template.macroenabled.12 dotm +application/vnd.ms-works wps wks wcm wdb +application/vnd.ms-wpl wpl +application/vnd.ms-xpsdocument xps +application/vnd.mseq mseq +# application/vnd.msign +# application/vnd.multiad.creator +# application/vnd.multiad.creator.cif +# application/vnd.music-niff +application/vnd.musician mus +application/vnd.muvee.style msty +application/vnd.mynfc taglet +# application/vnd.ncd.control +# application/vnd.ncd.reference +# application/vnd.nervana +# application/vnd.netfpx +application/vnd.neurolanguage.nlu nlu +application/vnd.noblenet-directory nnd +application/vnd.noblenet-sealer nns +application/vnd.noblenet-web nnw +# application/vnd.nokia.catalogs +# application/vnd.nokia.conml+wbxml +# application/vnd.nokia.conml+xml +# application/vnd.nokia.isds-radio-presets +# application/vnd.nokia.iptv.config+xml +# application/vnd.nokia.landmark+wbxml +# application/vnd.nokia.landmark+xml +# application/vnd.nokia.landmarkcollection+xml +# application/vnd.nokia.n-gage.ac+xml +application/vnd.nokia.n-gage.data ngdat +application/vnd.nokia.n-gage.symbian.install n-gage +# application/vnd.nokia.ncd +# application/vnd.nokia.pcd+wbxml +# application/vnd.nokia.pcd+xml +application/vnd.nokia.radio-preset rpst +application/vnd.nokia.radio-presets rpss +application/vnd.novadigm.edm edm +application/vnd.novadigm.edx edx +application/vnd.novadigm.ext ext +# application/vnd.ntt-local.file-transfer +# application/vnd.ntt-local.sip-ta_remote +# application/vnd.ntt-local.sip-ta_tcp_stream +application/vnd.oasis.opendocument.chart odc +application/vnd.oasis.opendocument.chart-template otc +application/vnd.oasis.opendocument.database odb +application/vnd.oasis.opendocument.formula odf +application/vnd.oasis.opendocument.formula-template odft +application/vnd.oasis.opendocument.graphics odg +application/vnd.oasis.opendocument.graphics-template otg +application/vnd.oasis.opendocument.image odi +application/vnd.oasis.opendocument.image-template oti +application/vnd.oasis.opendocument.presentation odp +application/vnd.oasis.opendocument.presentation-template otp +application/vnd.oasis.opendocument.spreadsheet ods +application/vnd.oasis.opendocument.spreadsheet-template ots +application/vnd.oasis.opendocument.text odt +application/vnd.oasis.opendocument.text-master odm +application/vnd.oasis.opendocument.text-template ott +application/vnd.oasis.opendocument.text-web oth +# application/vnd.obn +# application/vnd.oftn.l10n+json +# application/vnd.oipf.contentaccessdownload+xml +# application/vnd.oipf.contentaccessstreaming+xml +# application/vnd.oipf.cspg-hexbinary +# application/vnd.oipf.dae.svg+xml +# application/vnd.oipf.dae.xhtml+xml +# application/vnd.oipf.mippvcontrolmessage+xml +# application/vnd.oipf.pae.gem +# application/vnd.oipf.spdiscovery+xml +# application/vnd.oipf.spdlist+xml +# application/vnd.oipf.ueprofile+xml +# application/vnd.oipf.userprofile+xml +application/vnd.olpc-sugar xo +# application/vnd.oma-scws-config +# application/vnd.oma-scws-http-request +# application/vnd.oma-scws-http-response +# application/vnd.oma.bcast.associated-procedure-parameter+xml +# application/vnd.oma.bcast.drm-trigger+xml +# application/vnd.oma.bcast.imd+xml +# application/vnd.oma.bcast.ltkm +# application/vnd.oma.bcast.notification+xml +# application/vnd.oma.bcast.provisioningtrigger +# application/vnd.oma.bcast.sgboot +# application/vnd.oma.bcast.sgdd+xml +# application/vnd.oma.bcast.sgdu +# application/vnd.oma.bcast.simple-symbol-container +# application/vnd.oma.bcast.smartcard-trigger+xml +# application/vnd.oma.bcast.sprov+xml +# application/vnd.oma.bcast.stkm +# application/vnd.oma.cab-address-book+xml +# application/vnd.oma.cab-feature-handler+xml +# application/vnd.oma.cab-pcc+xml +# application/vnd.oma.cab-user-prefs+xml +# application/vnd.oma.dcd +# application/vnd.oma.dcdc +application/vnd.oma.dd2+xml dd2 +# application/vnd.oma.drm.risd+xml +# application/vnd.oma.group-usage-list+xml +# application/vnd.oma.pal+xml +# application/vnd.oma.poc.detailed-progress-report+xml +# application/vnd.oma.poc.final-report+xml +# application/vnd.oma.poc.groups+xml +# application/vnd.oma.poc.invocation-descriptor+xml +# application/vnd.oma.poc.optimized-progress-report+xml +# application/vnd.oma.push +# application/vnd.oma.scidm.messages+xml +# application/vnd.oma.xcap-directory+xml +# application/vnd.omads-email+xml +# application/vnd.omads-file+xml +# application/vnd.omads-folder+xml +# application/vnd.omaloc-supl-init +application/vnd.openofficeorg.extension oxt +# application/vnd.openxmlformats-officedocument.custom-properties+xml +# application/vnd.openxmlformats-officedocument.customxmlproperties+xml +# application/vnd.openxmlformats-officedocument.drawing+xml +# application/vnd.openxmlformats-officedocument.drawingml.chart+xml +# application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml +# application/vnd.openxmlformats-officedocument.extended-properties+xml +# application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml +# application/vnd.openxmlformats-officedocument.presentationml.comments+xml +# application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml +application/vnd.openxmlformats-officedocument.presentationml.presentation pptx +# application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.presprops+xml +application/vnd.openxmlformats-officedocument.presentationml.slide sldx +# application/vnd.openxmlformats-officedocument.presentationml.slide+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml +application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx +# application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml +# application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml +# application/vnd.openxmlformats-officedocument.presentationml.tags+xml +application/vnd.openxmlformats-officedocument.presentationml.template potx +# application/vnd.openxmlformats-officedocument.presentationml.template.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx +# application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml +# application/vnd.openxmlformats-officedocument.theme+xml +# application/vnd.openxmlformats-officedocument.themeoverride+xml +# application/vnd.openxmlformats-officedocument.vmldrawing +# application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.document docx +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx +# application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml +# application/vnd.openxmlformats-package.core-properties+xml +# application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml +# application/vnd.openxmlformats-package.relationships+xml +# application/vnd.quobject-quoxdocument +# application/vnd.osa.netdeploy +application/vnd.osgeo.mapguide.package mgp +# application/vnd.osgi.bundle +application/vnd.osgi.dp dp +# application/vnd.otps.ct-kip+xml +application/vnd.palm pdb pqa oprc +# application/vnd.paos.xml +application/vnd.pawaafile paw +application/vnd.pg.format str +application/vnd.pg.osasli ei6 +# application/vnd.piaccess.application-licence +application/vnd.picsel efif +application/vnd.pmi.widget wg +# application/vnd.poc.group-advertisement+xml +application/vnd.pocketlearn plf +application/vnd.powerbuilder6 pbd +# application/vnd.powerbuilder6-s +# application/vnd.powerbuilder7 +# application/vnd.powerbuilder7-s +# application/vnd.powerbuilder75 +# application/vnd.powerbuilder75-s +# application/vnd.preminet +application/vnd.previewsystems.box box +application/vnd.proteus.magazine mgz +application/vnd.publishare-delta-tree qps +application/vnd.pvi.ptid1 ptid +# application/vnd.pwg-multiplexed +# application/vnd.pwg-xhtml-print+xml +# application/vnd.qualcomm.brew-app-res +application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb +# application/vnd.radisys.moml+xml +# application/vnd.radisys.msml+xml +# application/vnd.radisys.msml-audit+xml +# application/vnd.radisys.msml-audit-conf+xml +# application/vnd.radisys.msml-audit-conn+xml +# application/vnd.radisys.msml-audit-dialog+xml +# application/vnd.radisys.msml-audit-stream+xml +# application/vnd.radisys.msml-conf+xml +# application/vnd.radisys.msml-dialog+xml +# application/vnd.radisys.msml-dialog-base+xml +# application/vnd.radisys.msml-dialog-fax-detect+xml +# application/vnd.radisys.msml-dialog-fax-sendrecv+xml +# application/vnd.radisys.msml-dialog-group+xml +# application/vnd.radisys.msml-dialog-speech+xml +# application/vnd.radisys.msml-dialog-transform+xml +# application/vnd.rainstor.data +# application/vnd.rapid +application/vnd.realvnc.bed bed +application/vnd.recordare.musicxml mxl +application/vnd.recordare.musicxml+xml musicxml +# application/vnd.renlearn.rlprint +application/vnd.rig.cryptonote cryptonote +application/vnd.rim.cod cod +application/vnd.rn-realmedia rm +application/vnd.route66.link66+xml link66 +# application/vnd.ruckus.download +# application/vnd.s3sms +application/vnd.sailingtracker.track st +# application/vnd.sbm.cid +# application/vnd.sbm.mid2 +# application/vnd.scribus +# application/vnd.sealed.3df +# application/vnd.sealed.csf +# application/vnd.sealed.doc +# application/vnd.sealed.eml +# application/vnd.sealed.mht +# application/vnd.sealed.net +# application/vnd.sealed.ppt +# application/vnd.sealed.tiff +# application/vnd.sealed.xls +# application/vnd.sealedmedia.softseal.html +# application/vnd.sealedmedia.softseal.pdf +application/vnd.seemail see +application/vnd.sema sema +application/vnd.semd semd +application/vnd.semf semf +application/vnd.shana.informed.formdata ifm +application/vnd.shana.informed.formtemplate itp +application/vnd.shana.informed.interchange iif +application/vnd.shana.informed.package ipk +application/vnd.simtech-mindmapper twd twds +application/vnd.smaf mmf +# application/vnd.smart.notebook +application/vnd.smart.teacher teacher +# application/vnd.software602.filler.form+xml +# application/vnd.software602.filler.form-xml-zip +application/vnd.solent.sdkm+xml sdkm sdkd +application/vnd.spotfire.dxp dxp +application/vnd.spotfire.sfs sfs +# application/vnd.sss-cod +# application/vnd.sss-dtf +# application/vnd.sss-ntf +application/vnd.stardivision.calc sdc +application/vnd.stardivision.draw sda +application/vnd.stardivision.impress sdd +application/vnd.stardivision.math smf +application/vnd.stardivision.writer sdw vor +application/vnd.stardivision.writer-global sgl +application/vnd.stepmania.package smzip +application/vnd.stepmania.stepchart sm +# application/vnd.street-stream +application/vnd.sun.xml.calc sxc +application/vnd.sun.xml.calc.template stc +application/vnd.sun.xml.draw sxd +application/vnd.sun.xml.draw.template std +application/vnd.sun.xml.impress sxi +application/vnd.sun.xml.impress.template sti +application/vnd.sun.xml.math sxm +application/vnd.sun.xml.writer sxw +application/vnd.sun.xml.writer.global sxg +application/vnd.sun.xml.writer.template stw +# application/vnd.sun.wadl+xml +application/vnd.sus-calendar sus susp +application/vnd.svd svd +# application/vnd.swiftview-ics +application/vnd.symbian.install sis sisx +application/vnd.syncml+xml xsm +application/vnd.syncml.dm+wbxml bdm +application/vnd.syncml.dm+xml xdm +# application/vnd.syncml.dm.notification +# application/vnd.syncml.ds.notification +application/vnd.tao.intent-module-archive tao +application/vnd.tcpdump.pcap pcap cap dmp +application/vnd.tmobile-livetv tmo +application/vnd.trid.tpt tpt +application/vnd.triscape.mxs mxs +application/vnd.trueapp tra +# application/vnd.truedoc +# application/vnd.ubisoft.webplayer +application/vnd.ufdl ufd ufdl +application/vnd.uiq.theme utz +application/vnd.umajin umj +application/vnd.unity unityweb +application/vnd.uoml+xml uoml +# application/vnd.uplanet.alert +# application/vnd.uplanet.alert-wbxml +# application/vnd.uplanet.bearer-choice +# application/vnd.uplanet.bearer-choice-wbxml +# application/vnd.uplanet.cacheop +# application/vnd.uplanet.cacheop-wbxml +# application/vnd.uplanet.channel +# application/vnd.uplanet.channel-wbxml +# application/vnd.uplanet.list +# application/vnd.uplanet.list-wbxml +# application/vnd.uplanet.listcmd +# application/vnd.uplanet.listcmd-wbxml +# application/vnd.uplanet.signal +application/vnd.vcx vcx +# application/vnd.vd-study +# application/vnd.vectorworks +# application/vnd.verimatrix.vcas +# application/vnd.vidsoft.vidconference +application/vnd.visio vsd vst vss vsw +application/vnd.visionary vis +# application/vnd.vividence.scriptfile +application/vnd.vsf vsf +# application/vnd.wap.sic +# application/vnd.wap.slc +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo wtb +# application/vnd.wfa.wsc +# application/vnd.wmc +# application/vnd.wmf.bootstrap +# application/vnd.wolfram.mathematica +# application/vnd.wolfram.mathematica.package +application/vnd.wolfram.player nbp +application/vnd.wordperfect wpd +application/vnd.wqd wqd +# application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf stf +# application/vnd.wv.csp+wbxml +# application/vnd.wv.csp+xml +# application/vnd.wv.ssp+xml +application/vnd.xara xar +application/vnd.xfdl xfdl +# application/vnd.xfdl.webform +# application/vnd.xmi+xml +# application/vnd.xmpie.cpkg +# application/vnd.xmpie.dpkg +# application/vnd.xmpie.plan +# application/vnd.xmpie.ppkg +# application/vnd.xmpie.xlim +application/vnd.yamaha.hv-dic hvd +application/vnd.yamaha.hv-script hvs +application/vnd.yamaha.hv-voice hvp +application/vnd.yamaha.openscoreformat osf +application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg +# application/vnd.yamaha.remote-setup +application/vnd.yamaha.smaf-audio saf +application/vnd.yamaha.smaf-phrase spf +# application/vnd.yamaha.through-ngn +# application/vnd.yamaha.tunnel-udpencap +application/vnd.yellowriver-custom-menu cmp +application/vnd.zul zir zirz +application/vnd.zzazz.deck+xml zaz +application/voicexml+xml vxml +# application/vq-rtcpxr +# application/watcherinfo+xml +# application/whoispp-query +# application/whoispp-response +application/widget wgt +application/winhlp hlp +# application/wita +# application/wordperfect5.1 +application/wsdl+xml wsdl +application/wspolicy+xml wspolicy +application/x-7z-compressed 7z +application/x-abiword abw +application/x-ace-compressed ace +application/x-authorware-bin aab x32 u32 vox +application/x-authorware-map aam +application/x-authorware-seg aas +application/x-bcpio bcpio +application/x-bittorrent torrent +application/x-bzip bz +application/x-bzip2 bz2 boz +application/x-cdlink vcd +application/x-chat chat +application/x-chess-pgn pgn +# application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-debian-package deb udeb +application/x-director dir dcr dxr cst cct cxt w3d fgd swa +application/x-doom wad +application/x-dtbncx+xml ncx +application/x-dtbook+xml dtb +application/x-dtbresource+xml res +application/x-dvi dvi +application/x-font-bdf bdf +# application/x-font-dos +# application/x-font-framemaker +application/x-font-ghostscript gsf +# application/x-font-libgrx +application/x-font-linux-psf psf +application/x-font-otf otf +application/x-font-pcf pcf +application/x-font-snf snf +# application/x-font-speedo +# application/x-font-sunos-news +application/x-font-ttf ttf ttc +application/x-font-type1 pfa pfb pfm afm +application/x-font-woff woff +# application/x-font-vfont +application/x-futuresplash spl +application/x-gnumeric gnumeric +application/x-gtar gtar +# application/x-gzip +application/x-hdf hdf +application/x-java-jnlp-file jnlp +application/x-latex latex +application/x-mobipocket-ebook prc mobi +application/x-ms-application application +application/x-ms-wmd wmd +application/x-ms-wmz wmz +application/x-ms-xbap xbap +application/x-msaccess mdb +application/x-msbinder obd +application/x-mscardfile crd +application/x-msclip clp +application/x-msdownload exe dll com bat msi +application/x-msmediaview mvb m13 m14 +application/x-msmetafile wmf +application/x-msmoney mny +application/x-mspublisher pub +application/x-msschedule scd +application/x-msterminal trm +application/x-mswrite wri +application/x-netcdf nc cdf +application/x-pkcs12 p12 pfx +application/x-pkcs7-certificates p7b spc +application/x-pkcs7-certreqresp p7r +application/x-rar-compressed rar +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-silverlight-app xap +application/x-stuffit sit +application/x-stuffitx sitx +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-tex-tfm tfm +application/x-texinfo texinfo texi +application/x-ustar ustar +application/x-wais-source src +application/x-x509-ca-cert der crt +application/x-xfig fig +application/x-xpinstall xpi +# application/x400-bp +# application/xcap-att+xml +# application/xcap-caps+xml +application/xcap-diff+xml xdf +# application/xcap-el+xml +# application/xcap-error+xml +# application/xcap-ns+xml +# application/xcon-conference-info-diff+xml +# application/xcon-conference-info+xml +application/xenc+xml xenc +application/xhtml+xml xhtml xht +# application/xhtml-voice+xml +application/xml xml xsl +application/xml-dtd dtd +# application/xml-external-parsed-entity +# application/xmpp+xml +application/xop+xml xop +application/xslt+xml xslt +application/xspf+xml xspf +application/xv+xml mxml xhvml xvml xvm +application/yang yang +application/yin+xml yin +application/zip zip +# audio/1d-interleaved-parityfec +# audio/32kadpcm +# audio/3gpp +# audio/3gpp2 +# audio/ac3 +audio/adpcm adp +# audio/amr +# audio/amr-wb +# audio/amr-wb+ +# audio/asc +# audio/atrac-advanced-lossless +# audio/atrac-x +# audio/atrac3 +audio/basic au snd +# audio/bv16 +# audio/bv32 +# audio/clearmode +# audio/cn +# audio/dat12 +# audio/dls +# audio/dsr-es201108 +# audio/dsr-es202050 +# audio/dsr-es202211 +# audio/dsr-es202212 +# audio/dv +# audio/dvi4 +# audio/eac3 +# audio/evrc +# audio/evrc-qcp +# audio/evrc0 +# audio/evrc1 +# audio/evrcb +# audio/evrcb0 +# audio/evrcb1 +# audio/evrcwb +# audio/evrcwb0 +# audio/evrcwb1 +# audio/example +# audio/fwdred +# audio/g719 +# audio/g722 +# audio/g7221 +# audio/g723 +# audio/g726-16 +# audio/g726-24 +# audio/g726-32 +# audio/g726-40 +# audio/g728 +# audio/g729 +# audio/g7291 +# audio/g729d +# audio/g729e +# audio/gsm +# audio/gsm-efr +# audio/gsm-hr-08 +# audio/ilbc +# audio/ip-mr_v2.5 +# audio/l16 +# audio/l20 +# audio/l24 +# audio/l8 +# audio/lpc +audio/midi mid midi kar rmi +# audio/mobile-xmf +audio/mp4 mp4a +# audio/mp4a-latm +# audio/mpa +# audio/mpa-robust +audio/mpeg mpga mp2 mp2a mp3 m2a m3a +# audio/mpeg4-generic +audio/ogg oga ogg spx +# audio/parityfec +# audio/pcma +# audio/pcma-wb +# audio/pcmu-wb +# audio/pcmu +# audio/prs.sid +# audio/qcelp +# audio/red +# audio/rtp-enc-aescm128 +# audio/rtp-midi +# audio/rtx +# audio/smv +# audio/smv0 +# audio/smv-qcp +# audio/sp-midi +# audio/speex +# audio/t140c +# audio/t38 +# audio/telephone-event +# audio/tone +# audio/uemclip +# audio/ulpfec +# audio/vdvi +# audio/vmr-wb +# audio/vnd.3gpp.iufp +# audio/vnd.4sb +# audio/vnd.audiokoz +# audio/vnd.celp +# audio/vnd.cisco.nse +# audio/vnd.cmles.radio-events +# audio/vnd.cns.anp1 +# audio/vnd.cns.inf1 +audio/vnd.dece.audio uva uvva +audio/vnd.digital-winds eol +# audio/vnd.dlna.adts +# audio/vnd.dolby.heaac.1 +# audio/vnd.dolby.heaac.2 +# audio/vnd.dolby.mlp +# audio/vnd.dolby.mps +# audio/vnd.dolby.pl2 +# audio/vnd.dolby.pl2x +# audio/vnd.dolby.pl2z +# audio/vnd.dolby.pulse.1 +audio/vnd.dra dra +audio/vnd.dts dts +audio/vnd.dts.hd dtshd +# audio/vnd.dvb.file dvb +# audio/vnd.everad.plj +# audio/vnd.hns.audio +audio/vnd.lucent.voice lvp +audio/vnd.ms-playready.media.pya pya +# audio/vnd.nokia.mobile-xmf +# audio/vnd.nortel.vbk +audio/vnd.nuera.ecelp4800 ecelp4800 +audio/vnd.nuera.ecelp7470 ecelp7470 +audio/vnd.nuera.ecelp9600 ecelp9600 +# audio/vnd.octel.sbc +# audio/vnd.qcelp +# audio/vnd.rhetorex.32kadpcm +audio/vnd.rip rip +# audio/vnd.sealedmedia.softseal.mpeg +# audio/vnd.vmx.cvsd +# audio/vorbis +# audio/vorbis-config +audio/webm weba +audio/x-aac aac +audio/x-aiff aif aiff aifc +audio/x-mpegurl m3u +audio/x-ms-wax wax +audio/x-ms-wma wma +audio/x-pn-realaudio ram ra +audio/x-pn-realaudio-plugin rmp +audio/x-wav wav +chemical/x-cdx cdx +chemical/x-cif cif +chemical/x-cmdf cmdf +chemical/x-cml cml +chemical/x-csml csml +# chemical/x-pdb +chemical/x-xyz xyz +image/bmp bmp +image/cgm cgm +# image/example +# image/fits +image/g3fax g3 +image/gif gif +image/ief ief +# image/jp2 +image/jpeg jpeg jpg jpe +# image/jpm +# image/jpx +image/ktx ktx +# image/naplps +image/png png +image/prs.btif btif +# image/prs.pti +image/svg+xml svg svgz +# image/t38 +image/tiff tiff tif +# image/tiff-fx +image/vnd.adobe.photoshop psd +# image/vnd.cns.inf2 +image/vnd.dece.graphic uvi uvvi uvg uvvg +image/vnd.dvb.subtitle sub +image/vnd.djvu djvu djv +image/vnd.dwg dwg +image/vnd.dxf dxf +image/vnd.fastbidsheet fbs +image/vnd.fpx fpx +image/vnd.fst fst +image/vnd.fujixerox.edmics-mmr mmr +image/vnd.fujixerox.edmics-rlc rlc +# image/vnd.globalgraphics.pgb +# image/vnd.microsoft.icon +# image/vnd.mix +image/vnd.ms-modi mdi +image/vnd.net-fpx npx +# image/vnd.radiance +# image/vnd.sealed.png +# image/vnd.sealedmedia.softseal.gif +# image/vnd.sealedmedia.softseal.jpg +# image/vnd.svf +image/vnd.wap.wbmp wbmp +image/vnd.xiff xif +image/webp webp +image/x-cmu-raster ras +image/x-cmx cmx +image/x-freehand fh fhc fh4 fh5 fh7 +image/x-icon ico +image/x-pcx pcx +image/x-pict pic pct +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +# message/cpim +# message/delivery-status +# message/disposition-notification +# message/example +# message/external-body +# message/feedback-report +# message/global +# message/global-delivery-status +# message/global-disposition-notification +# message/global-headers +# message/http +# message/imdn+xml +# message/news +# message/partial +message/rfc822 eml mime +# message/s-http +# message/sip +# message/sipfrag +# message/tracking-status +# message/vnd.si.simp +# model/example +model/iges igs iges +model/mesh msh mesh silo +model/vnd.collada+xml dae +model/vnd.dwf dwf +# model/vnd.flatland.3dml +model/vnd.gdl gdl +# model/vnd.gs-gdl +# model/vnd.gs.gdl +model/vnd.gtw gtw +# model/vnd.moml+xml +model/vnd.mts mts +# model/vnd.parasolid.transmit.binary +# model/vnd.parasolid.transmit.text +model/vnd.vtu vtu +model/vrml wrl vrml +# multipart/alternative +# multipart/appledouble +# multipart/byteranges +# multipart/digest +# multipart/encrypted +# multipart/example +# multipart/form-data +# multipart/header-set +# multipart/mixed +# multipart/parallel +# multipart/related +# multipart/report +# multipart/signed +# multipart/voice-message +# text/1d-interleaved-parityfec +text/calendar ics ifb +text/css css +text/csv csv +# text/directory +# text/dns +# text/ecmascript +# text/enriched +# text/example +# text/fwdred +text/html html htm +# text/javascript +text/n3 n3 +# text/parityfec +text/plain txt text conf def list log in +# text/prs.fallenstein.rst +text/prs.lines.tag dsc +# text/vnd.radisys.msml-basic-layout +# text/red +# text/rfc822-headers +text/richtext rtx +# text/rtf +# text/rtp-enc-aescm128 +# text/rtx +text/sgml sgml sgm +# text/t140 +text/tab-separated-values tsv +text/troff t tr roff man me ms +text/turtle ttl +# text/ulpfec +text/uri-list uri uris urls +text/vcard vcard +# text/vnd.abc +text/vnd.curl curl +text/vnd.curl.dcurl dcurl +text/vnd.curl.scurl scurl +text/vnd.curl.mcurl mcurl +# text/vnd.dmclientscript +text/vnd.dvb.subtitle sub +# text/vnd.esmertec.theme-descriptor +text/vnd.fly fly +text/vnd.fmi.flexstor flx +text/vnd.graphviz gv +text/vnd.in3d.3dml 3dml +text/vnd.in3d.spot spot +# text/vnd.iptc.newsml +# text/vnd.iptc.nitf +# text/vnd.latex-z +# text/vnd.motorola.reflex +# text/vnd.ms-mediapackage +# text/vnd.net2phone.commcenter.command +# text/vnd.si.uricatalogue +text/vnd.sun.j2me.app-descriptor jad +# text/vnd.trolltech.linguist +# text/vnd.wap.si +# text/vnd.wap.sl +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/x-asm s asm +text/x-c c cc cxx cpp h hh dic +text/x-fortran f for f77 f90 +text/x-pascal p pas +text/x-java-source java +text/x-setext etx +text/x-uuencode uu +text/x-vcalendar vcs +text/x-vcard vcf +# text/xml +# text/xml-external-parsed-entity +# video/1d-interleaved-parityfec +video/3gpp 3gp +# video/3gpp-tt +video/3gpp2 3g2 +# video/bmpeg +# video/bt656 +# video/celb +# video/dv +# video/example +video/h261 h261 +video/h263 h263 +# video/h263-1998 +# video/h263-2000 +video/h264 h264 +# video/h264-rcdo +# video/h264-svc +video/jpeg jpgv +# video/jpeg2000 +video/jpm jpm jpgm +video/mj2 mj2 mjp2 +# video/mp1s +# video/mp2p +# video/mp2t +video/mp4 mp4 mp4v mpg4 +# video/mp4v-es +video/mpeg mpeg mpg mpe m1v m2v +# video/mpeg4-generic +# video/mpv +# video/nv +video/ogg ogv +# video/parityfec +# video/pointer +video/quicktime qt mov +# video/raw +# video/rtp-enc-aescm128 +# video/rtx +# video/smpte292m +# video/ulpfec +# video/vc1 +# video/vnd.cctv +video/vnd.dece.hd uvh uvvh +video/vnd.dece.mobile uvm uvvm +# video/vnd.dece.mp4 +video/vnd.dece.pd uvp uvvp +video/vnd.dece.sd uvs uvvs +video/vnd.dece.video uvv uvvv +# video/vnd.directv.mpeg +# video/vnd.directv.mpeg-tts +# video/vnd.dlna.mpeg-tts +video/vnd.dvb.file dvb +video/vnd.fvt fvt +# video/vnd.hns.video +# video/vnd.iptvforum.1dparityfec-1010 +# video/vnd.iptvforum.1dparityfec-2005 +# video/vnd.iptvforum.2dparityfec-1010 +# video/vnd.iptvforum.2dparityfec-2005 +# video/vnd.iptvforum.ttsavc +# video/vnd.iptvforum.ttsmpeg2 +# video/vnd.motorola.video +# video/vnd.motorola.videop +video/vnd.mpegurl mxu m4u +video/vnd.ms-playready.media.pyv pyv +# video/vnd.nokia.interleaved-multimedia +# video/vnd.nokia.videovoip +# video/vnd.objectvideo +# video/vnd.sealed.mpeg1 +# video/vnd.sealed.mpeg4 +# video/vnd.sealed.swf +# video/vnd.sealedmedia.softseal.mov +video/vnd.uvvu.mp4 uvu uvvu +video/vnd.vivo viv +video/webm webm +video/x-f4v f4v +video/x-fli fli +video/x-flv flv +video/x-m4v m4v +video/x-ms-asf asf asx +video/x-ms-wm wm +video/x-ms-wmv wmv +video/x-ms-wmx wmx +video/x-ms-wvx wvx +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice diff --git a/dist/node_modules/express/node_modules/send/node_modules/mime/types/node.types b/dist/node_modules/express/node_modules/send/node_modules/mime/types/node.types new file mode 100644 index 0000000..b7fe8c0 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/mime/types/node.types @@ -0,0 +1,65 @@ +# What: Google Chrome Extension +# Why: To allow apps to (work) be served with the right content type header. +# http://codereview.chromium.org/2830017 +# Added by: niftylettuce +application/x-chrome-extension crx + +# What: OTF Message Silencer +# Why: To silence the "Resource interpreted as font but transferred with MIME +# type font/otf" message that occurs in Google Chrome +# Added by: niftylettuce +font/opentype otf + +# What: HTC support +# Why: To properly render .htc files such as CSS3PIE +# Added by: niftylettuce +text/x-component htc + +# What: HTML5 application cache manifest +# Why: De-facto standard. Required by Mozilla browser when serving HTML5 apps +# per https://developer.mozilla.org/en/offline_resources_in_firefox +# Added by: louisremi +text/cache-manifest appcache manifest + +# What: node binary buffer format +# Why: semi-standard extension w/in the node community +# Added by: tootallnate +application/octet-stream buffer + +# What: The "protected" MP-4 formats used by iTunes. +# Why: Required for streaming music to browsers (?) +# Added by: broofa +application/mp4 m4p +audio/mp4 m4a + +# What: Music playlist format (http://en.wikipedia.org/wiki/M3U) +# Why: See https://github.com/bentomas/node-mime/pull/6 +# Added by: mjrusso +application/x-mpegURL m3u8 + +# What: Video format, Part of RFC1890 +# Why: See https://github.com/bentomas/node-mime/pull/6 +# Added by: mjrusso +video/MP2T ts + +# What: The FLAC lossless codec format +# Why: Streaming and serving FLAC audio +# Added by: jacobrask +audio/flac flac + +# What: EventSource mime type +# Why: mime type of Server-Sent Events stream +# http://www.w3.org/TR/eventsource/#text-event-stream +# Added by: francois2metz +text/event-stream event-stream + +# What: Mozilla App manifest mime type +# Why: https://developer.mozilla.org/en/Apps/Manifest#Serving_manifests +# Added by: ednapiranha +application/x-web-app-manifest+json webapp + +# What: Matroska Mime Types +# Why: http://en.wikipedia.org/wiki/Matroska +# Added by: aduncan88 +video/x-matroska mkv +audio/x-matroska mka diff --git a/dist/node_modules/express/node_modules/send/node_modules/range-parser/.npmignore b/dist/node_modules/express/node_modules/send/node_modules/range-parser/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/range-parser/.npmignore @@ -0,0 +1 @@ +test diff --git a/dist/node_modules/express/node_modules/send/node_modules/range-parser/History.md b/dist/node_modules/express/node_modules/send/node_modules/range-parser/History.md new file mode 100644 index 0000000..82df7b1 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/range-parser/History.md @@ -0,0 +1,15 @@ + +0.0.4 / 2012-06-17 +================== + + * changed: ret -1 for unsatisfiable and -2 when invalid + +0.0.3 / 2012-06-17 +================== + + * fix last-byte-pos default to len - 1 + +0.0.2 / 2012-06-14 +================== + + * add `.type` diff --git a/dist/node_modules/express/node_modules/send/node_modules/range-parser/Makefile b/dist/node_modules/express/node_modules/send/node_modules/range-parser/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/range-parser/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/range-parser/Readme.md b/dist/node_modules/express/node_modules/send/node_modules/range-parser/Readme.md new file mode 100644 index 0000000..b2a67fe --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/range-parser/Readme.md @@ -0,0 +1,28 @@ + +# node-range-parser + + Range header field parser. + +## Example: + +```js +assert(-1 == parse(200, 'bytes=500-20')); +assert(-2 == parse(200, 'bytes=malformed')); +parse(200, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 199 }])); +parse(1000, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 499 }])); +parse(1000, 'bytes=40-80').should.eql(arr('bytes', [{ start: 40, end: 80 }])); +parse(1000, 'bytes=-500').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=-400').should.eql(arr('bytes', [{ start: 600, end: 999 }])); +parse(1000, 'bytes=500-').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=400-').should.eql(arr('bytes', [{ start: 400, end: 999 }])); +parse(1000, 'bytes=0-0').should.eql(arr('bytes', [{ start: 0, end: 0 }])); +parse(1000, 'bytes=-1').should.eql(arr('bytes', [{ start: 999, end: 999 }])); +parse(1000, 'items=0-5').should.eql(arr('items', [{ start: 0, end: 5 }])); +parse(1000, 'bytes=40-80,-1').should.eql(arr('bytes', [{ start: 40, end: 80 }, { start: 999, end: 999 }])); +``` + +## Installation + +``` +$ npm install range-parser +``` \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/range-parser/index.js b/dist/node_modules/express/node_modules/send/node_modules/range-parser/index.js new file mode 100644 index 0000000..9b0f7a8 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/range-parser/index.js @@ -0,0 +1,49 @@ + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @api public + */ + +module.exports = function(size, str){ + var valid = true; + var i = str.indexOf('='); + + if (-1 == i) return -2; + + var arr = str.slice(i + 1).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -nnn + if (isNaN(start)) { + start = size - end; + end = size - 1; + // nnn- + } else if (isNaN(end)) { + end = size - 1; + } + + // limit last-byte-pos to current length + if (end > size - 1) end = size - 1; + + // invalid + if (isNaN(start) + || isNaN(end) + || start > end + || start < 0) valid = false; + + return { + start: start, + end: end + }; + }); + + arr.type = str.slice(0, i); + + return valid ? arr : -1; +}; \ No newline at end of file diff --git a/dist/node_modules/express/node_modules/send/node_modules/range-parser/package.json b/dist/node_modules/express/node_modules/send/node_modules/range-parser/package.json new file mode 100644 index 0000000..0306040 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/node_modules/range-parser/package.json @@ -0,0 +1,12 @@ +{ + "name": "range-parser", + "author": "TJ Holowaychuk (http://tjholowaychuk.com)", + "description": "Range header field string parser", + "version": "0.0.4", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + } +} diff --git a/dist/node_modules/express/node_modules/send/package.json b/dist/node_modules/express/node_modules/send/package.json new file mode 100644 index 0000000..696df31 --- /dev/null +++ b/dist/node_modules/express/node_modules/send/package.json @@ -0,0 +1,23 @@ +{ + "name": "send", + "version": "0.1.0", + "description": "Better streaming static file server with Range and conditional-GET support", + "keywords": ["static", "file", "server"], + "author": "TJ Holowaychuk ", + "dependencies": { + "debug": "*", + "mime": "1.2.6", + "fresh": "0.1.0", + "range-parser": "0.0.4" + }, + "devDependencies": { + "mocha": "*", + "should": "*", + "supertest": "0.0.1", + "connect": "2.x" + }, + "scripts": { + "test": "make test" + }, + "main": "index" +} \ No newline at end of file diff --git a/dist/node_modules/express/package.json b/dist/node_modules/express/package.json new file mode 100644 index 0000000..d7001b9 --- /dev/null +++ b/dist/node_modules/express/package.json @@ -0,0 +1,56 @@ +{ + "name": "express", + "description": "Sinatra inspired web development framework", + "version": "3.0.1", + "author": "TJ Holowaychuk ", + "contributors": [ + { "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" }, + { "name": "Aaron Heckmann", "email": "aaron.heckmann+github@gmail.com" }, + { "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" }, + { "name": "Guillermo Rauch", "email": "rauchg@gmail.com" } + ], + "dependencies": { + "connect": "2.6.2", + "commander": "0.6.1", + "range-parser": "0.0.4", + "mkdirp": "0.3.3", + "cookie": "0.0.4", + "crc": "0.2.0", + "fresh": "0.1.0", + "methods": "0.0.1", + "send": "0.1.0", + "cookie-signature": "0.0.1", + "debug": "*" + }, + "devDependencies": { + "ejs": "*", + "mocha": "*", + "jade": "*", + "hjs": "*", + "stylus": "*", + "should": "*", + "connect-redis": "*", + "github-flavored-markdown": "*", + "supertest": "0.0.1" + }, + "keywords": [ + "express", + "framework", + "sinatra", + "web", + "rest", + "restful", + "router", + "app", + "api" + ], + "repository": "git://github.com/visionmedia/express", + "main": "index", + "bin": { "express": "./bin/express" }, + "scripts": { + "prepublish" : "npm prune", + "test": "make test" + }, + "engines": { "node": "*" } +} + diff --git a/dist/node_modules/express/test.js b/dist/node_modules/express/test.js new file mode 100644 index 0000000..f4d5223 --- /dev/null +++ b/dist/node_modules/express/test.js @@ -0,0 +1,19 @@ + +/** + * Module dependencies. + */ + +var express = require('./') + , app = express() + +app.use(express.favicon()); +app.use(express.logger('dev')); +app.use(express.cookieParser('foobar')); +app.use(express.session()); + +app.get('/', function(req, res){ + res.send('hello'); +}); + +app.listen(3000); +console.log('listening on 3000'); \ No newline at end of file diff --git a/dist/node_modules/hbs/.gitmodules b/dist/node_modules/hbs/.gitmodules new file mode 100644 index 0000000..ba1b641 --- /dev/null +++ b/dist/node_modules/hbs/.gitmodules @@ -0,0 +1,3 @@ +[submodule "support/handlebars"] + path = support/handlebars + url = http://github.com/wycats/handlebars.js.git diff --git a/dist/node_modules/hbs/.npmignore b/dist/node_modules/hbs/.npmignore new file mode 100644 index 0000000..f05b1f2 --- /dev/null +++ b/dist/node_modules/hbs/.npmignore @@ -0,0 +1,2 @@ +node_modules +test diff --git a/dist/node_modules/hbs/.travis.yml b/dist/node_modules/hbs/.travis.yml new file mode 100644 index 0000000..320698a --- /dev/null +++ b/dist/node_modules/hbs/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 diff --git a/dist/node_modules/hbs/LICENSE b/dist/node_modules/hbs/LICENSE new file mode 100644 index 0000000..1ac992a --- /dev/null +++ b/dist/node_modules/hbs/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2011 Don Park + +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/dist/node_modules/hbs/Readme.md b/dist/node_modules/hbs/Readme.md new file mode 100644 index 0000000..bffa938 --- /dev/null +++ b/dist/node_modules/hbs/Readme.md @@ -0,0 +1,53 @@ +# hbs [![Build Status](https://secure.travis-ci.org/donpark/hbs.png)](http://travis-ci.org/donpark/hbs) # + +[Express.js](http://github.com/visionmedia/express) view engine for +[handlebars.js](http://github.com/wycats/handlebars.js) + +## Why ## + +Because Handlebars.js is a nifty and simple templating language and makes a great fit for express templates. + +## Installation ## + +``` +npm install hbs +``` + +## Usage ## + +Using *hbs* as the default view engine requires just one line of code in your app setup. This will render `.hbs` files when `res.render` is called. + +```javascript +app.set('view engine', 'hbs'); +``` + +To use a different extension (i.e. html) for your template files: + +```javascript +app.set('view engine', 'html'); +app.engine('html', require('hbs').__express); +``` + +## Helpers and Partials ## + +hbs exposes the `registerHelper` and `registerPartial` method from handlebars. + +```javascript +var hbs = require('hbs'); + +hbs.registerHelper('helper_name', function(...) { ... }); +hbs.registerPartial('partial_name', 'partial value'); +``` + +See the [handlebars.js](http://github.com/wycats/handlebars.js) README and docs for more information. + +## Recipes ## + +### extra scripts or styles + +Sometimes it is useful to have custom scripts or stylesheets on your pages. Handlebars does not provide a way to import or extend a template, but through the use of helpers you can create a similar result. + +We can take advantage of the fact that our body template is processed before the layout template. Knowing this, we can create two helpers `block` and `extend` which can be used to 'inject' custom stylesheets or scripts into the layout template. The `block` helper will act as a placeholder for values specified in earlier `extend` helpers. + +See examples/extend for a working example. Note how the index.hbs file defines extra stylesheets and scripts to be injected into the layout. They are put into the head section and at the end of the body respectively. If this was not done, the stylesheet would be in the body and the script would print `foo bar` too soon. + diff --git a/dist/node_modules/hbs/examples/extend/app.js b/dist/node_modules/hbs/examples/extend/app.js new file mode 100644 index 0000000..ff264a2 --- /dev/null +++ b/dist/node_modules/hbs/examples/extend/app.js @@ -0,0 +1,37 @@ +// 3rd party +var express = require('express'); +var hbs = require('hbs'); + +var app = express(); + +// set the view engine to use handlebars +app.set('view engine', 'hbs'); +app.set('views', __dirname + '/views'); + +app.use(express.static(__dirname + '/public')); + +var blocks = {}; + +hbs.registerHelper('extend', function(name, context) { + var block = blocks[name]; + if (!block) { + block = blocks[name] = []; + } + + block.push(context(this)); +}); + +hbs.registerHelper('block', function(name) { + var val = (blocks[name] || []).join('\n'); + + // clear the block + blocks[name] = []; + return val; +}); + +app.get('/', function(req, res){ + res.render('index'); +}); + +app.listen(3000); + diff --git a/dist/node_modules/hbs/examples/extend/public/css/index.css b/dist/node_modules/hbs/examples/extend/public/css/index.css new file mode 100644 index 0000000..7fdbc76 --- /dev/null +++ b/dist/node_modules/hbs/examples/extend/public/css/index.css @@ -0,0 +1,4 @@ +body { + background-color: #bbb; +} + diff --git a/dist/node_modules/hbs/examples/extend/public/css/style.css b/dist/node_modules/hbs/examples/extend/public/css/style.css new file mode 100644 index 0000000..7f4d53a --- /dev/null +++ b/dist/node_modules/hbs/examples/extend/public/css/style.css @@ -0,0 +1,5 @@ +body { + padding: 50px; + font: 16px "Lucida Grande", Helvetica, Arial, sans-serif; +} + diff --git a/dist/node_modules/hbs/examples/extend/views/index.hbs b/dist/node_modules/hbs/examples/extend/views/index.hbs new file mode 100644 index 0000000..e91ae51 --- /dev/null +++ b/dist/node_modules/hbs/examples/extend/views/index.hbs @@ -0,0 +1,12 @@ + +{{#extend "stylesheets"}} + +{{/extend}} + +let the magic begin + +{{#extend "scripts"}} + +{{/extend}} diff --git a/dist/node_modules/hbs/examples/extend/views/layout.hbs b/dist/node_modules/hbs/examples/extend/views/layout.hbs new file mode 100644 index 0000000..1dd9c47 --- /dev/null +++ b/dist/node_modules/hbs/examples/extend/views/layout.hbs @@ -0,0 +1,21 @@ + + + + {{title}} + + + + {{{block "stylesheets"}}} + + + + body: {{{body}}} + +
    + post body +
    + + {{{block "scripts"}}} + + + diff --git a/dist/node_modules/hbs/lib/async.js b/dist/node_modules/hbs/lib/async.js new file mode 100644 index 0000000..8306b10 --- /dev/null +++ b/dist/node_modules/hbs/lib/async.js @@ -0,0 +1,101 @@ +/// provides the async helper functionality + +// global baton which contains the current +// set of deferreds +var waiter; + +function Waiter() { + var self = this; + + // found values + self.values = {}; + + // callback when done + self.callback = null; + + self.resolved = false; + + self.count = 0; +}; + +Waiter.prototype.wait = function() { + var self = this; + ++self.count; +}; + +// resolve the promise +Waiter.prototype.resolve = function(name, val) { + var self = this; + + self.values[name] = val; + + // done with all items + if (--self.count === 0) { + self.resolved = true; + + // we may not have a done callback yet + if (self.callback) { + self.callback(self.values); + } + } +}; + +// sets the done callback for the waiter +// notifies when the promise is complete +Waiter.prototype.done = function(fn) { + var self = this; + + self.callback = fn; + if (self.resolved) { + fn(self.values); + } +}; + +// callback fn when all async helpers have finished running +// if there were no async helpers, then it will callback right away +Waiter.done = function(fn) { + + // no async things called + if (!waiter) { + return fn({}); + } + + waiter.done(fn); + + // clear the waiter for the next template + waiter = undefined; +}; + +Waiter.resolve = function(fn, context) { + // we want to do async things, need a waiter for that + if (!waiter) { + waiter = new Waiter(); + } + + var id = '__' + gen_id() + '__'; + + var cur_waiter = waiter; + waiter.wait(); + + fn(context, function(res) { + cur_waiter.resolve(id, res); + }); + + // return the id placeholder + // this will be replaced later + return id; +}; + +var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'; + +var gen_id = function() { + var res = ''; + for (var i=0 ; i<8 ; ++i) { + res += alphabet[Math.floor(Math.random() * alphabet.length)]; + } + + return res; +}; + +module.exports = Waiter; + diff --git a/dist/node_modules/hbs/lib/hbs.js b/dist/node_modules/hbs/lib/hbs.js new file mode 100644 index 0000000..34be609 --- /dev/null +++ b/dist/node_modules/hbs/lib/hbs.js @@ -0,0 +1,139 @@ + +// builtin +var fs = require('fs'); +var path = require('path'); + +// handle async helpers +var async = require('./async'); + +// expose handlebars, allows users to use their versions +// by overriding this early in their apps +exports.handlebars = require('handlebars'); + +// express 2.x template engine compliance +exports.compile = function (str, options) { + if (typeof str !== 'string') { + return str; + } + + var template = exports.handlebars.compile(str); + return function (locals) { + return template(locals, { + helpers: locals.blockHelpers, + partials: null, + data: null + }); + }; +}; + +// cache for templates, express 3.x doesn't do this for us +var cache = {}; + +// express 3.x template engine compliance +exports.__express = function(filename, options, cb) { + var handlebars = exports.handlebars; + + // grab extension from filename + // if we need a layout, we will look for one matching out extension + var extension = path.extname(filename); + + // render the original file + // cb(err, str) + function render_file(locals, cb) { + // cached? + var template = cache[filename]; + if (template) { + return cb(null, template(locals)); + } + + fs.readFile(filename, 'utf8', function(err, str){ + if (err) { + return cb(err); + } + + var locals = options; + var template = handlebars.compile(str); + if (options.cache) { + cache[filename] = template; + } + + var res = template(locals); + async.done(function(values) { + Object.keys(values).forEach(function(id) { + res = res.replace(id, values[id]); + }); + + cb(null, res); + }); + }); + } + + // render with a layout + function render_with_layout(template, locals, cb) { + render_file(locals, function(err, str) { + if (err) { + return cb(err); + } + + var locals = options; + locals.body = str; + + var res = template(locals); + async.done(function(values) { + Object.keys(values).forEach(function(id) { + res = res.replace(id, values[id]); + }); + + cb(null, res); + }); + }); + } + + // options.layout specified and false (don't use layout) + if (options.layout !== undefined && !options.layout) { + return render_file(options, cb); + } + + var view_dir = options.settings.views; + var layout_filename = path.join(view_dir, options.layout || 'layout' + extension); + + var layout_template = cache[layout_filename]; + if (layout_template) { + return render_with_layout(layout_template, options, cb); + } + + // TODO check if layout path has .hbs extension + + fs.readFile(layout_filename, 'utf8', function(err, str) { + if (err) { + return cb(err); + } + + var layout_template = handlebars.compile(str); + if (options.cache) { + cache[layout_filename] = layout_template; + } + + render_with_layout(layout_template, options, cb); + }); +} + +/// expose useful methods + +exports.registerHelper = function () { + exports.handlebars.registerHelper.apply(exports.handlebars, arguments); +}; + +exports.registerPartial = function () { + exports.handlebars.registerPartial.apply(exports.handlebars, arguments); +}; + +exports.registerAsyncHelper = function(name, fn) { + exports.handlebars.registerHelper(name, function(context) { + return async.resolve(fn, context); + }); +}; + +// DEPRECATED, kept for backwards compatibility +exports.SafeString = exports.handlebars.SafeString; +exports.Utils = exports.handlebars.Utils; diff --git a/dist/node_modules/hbs/node_modules/handlebars/.npmignore b/dist/node_modules/hbs/node_modules/handlebars/.npmignore new file mode 100644 index 0000000..5c49ba1 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/.npmignore @@ -0,0 +1,11 @@ +.DS_Store +.gitignore +.rvmrc +Gemfile +Gemfile.lock +Rakefile +bench/* +dist/* +spec/* +src/* +vendor/* diff --git a/dist/node_modules/hbs/node_modules/handlebars/LICENSE b/dist/node_modules/hbs/node_modules/handlebars/LICENSE new file mode 100644 index 0000000..237cd03 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2011 by Yehuda Katz + +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/dist/node_modules/hbs/node_modules/handlebars/README.markdown b/dist/node_modules/hbs/node_modules/handlebars/README.markdown new file mode 100644 index 0000000..3a39f1f --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/README.markdown @@ -0,0 +1,315 @@ +[![Build Status](https://secure.travis-ci.org/wycats/handlebars.js.png)](http://travis-ci.org/wycats/handlebars.js) + +Handlebars.js +============= + +Handlebars.js is an extension to the [Mustache templating language](http://mustache.github.com/) created by Chris Wanstrath. Handlebars.js and Mustache are both logicless templating languages that keep the view and the code separated like we all know they should be. + +Checkout the official Handlebars docs site at [http://www.handlebarsjs.com](http://www.handlebarsjs.com). + + +Installing +---------- +Installing Handlebars is easy. Simply [download the package from GitHub](https://github.com/wycats/handlebars.js/archives/master) and add it to your web pages (you should usually use the most recent version). + +Usage +----- +In general, the syntax of Handlebars.js templates is a superset of Mustache templates. For basic syntax, check out the [Mustache manpage](http://mustache.github.com/mustache.5.html). + +Once you have a template, use the Handlebars.compile method to compile the template into a function. The generated function takes a context argument, which will be used to render the template. + +```js +var source = "

    Hello, my name is {{name}}. I am from {{hometown}}. I have " + + "{{kids.length}} kids:

    " + + "
      {{#kids}}
    • {{name}} is {{age}}
    • {{/kids}}
    "; +var template = Handlebars.compile(source); + +var data = { "name": "Alan", "hometown": "Somewhere, TX", + "kids": [{"name": "Jimmy", "age": "12"}, {"name": "Sally", "age": "4"}]}; +var result = template(data); + +// Would render: +//

    Hello, my name is Alan. I am from Somewhere, TX. I have 2 kids:

    +//
      +//
    • Jimmy is 12
    • +//
    • Sally is 4
    • +//
    +``` + + +Registering Helpers +------------------- + +You can register helpers that Handlebars will use when evaluating your +template. Here's an example, which assumes that your objects have a URL +embedded in them, as well as the text for a link: + +```js +Handlebars.registerHelper('link_to', function(context) { + return "" + context.body + ""; +}); + +var context = { posts: [{url: "/hello-world", body: "Hello World!"}] }; +var source = "
      {{#posts}}
    • {{{link_to this}}}
    • {{/posts}}
    " + +var template = Handlebars.compile(source); +template(context); + +// Would render: +// +// +``` + +Escaping +-------- + +By default, the `{{expression}}` syntax will escape its contents. This +helps to protect you against accidental XSS problems caused by malicious +data passed from the server as JSON. + +To explicitly *not* escape the contents, use the triple-mustache +(`{{{}}}`). You have seen this used in the above example. + + +Differences Between Handlebars.js and Mustache +---------------------------------------------- +Handlebars.js adds a couple of additional features to make writing templates easier and also changes a tiny detail of how partials work. + +### Paths + +Handlebars.js supports an extended expression syntax that we call paths. Paths are made up of typical expressions and . characters. Expressions allow you to not only display data from the current context, but to display data from contexts that are descendents and ancestors of the current context. + +To display data from descendent contexts, use the `.` character. So, for example, if your data were structured like: + +```js +var data = {"person": { "name": "Alan" }, company: {"name": "Rad, Inc." } }; +``` + +you could display the person's name from the top-level context with the following expression: + +``` +{{person.name}} +``` + +You can backtrack using `../`. For example, if you've already traversed into the person object you could still display the company's name with an expression like `{{../company.name}}`, so: + +``` +{{#person}}{{name}} - {{../company.name}}{{/person}} +``` + +would render: + +``` +Alan - Rad, Inc. +``` + +### Strings + +When calling a helper, you can pass paths or Strings as parameters. For +instance: + +```js +Handlebars.registerHelper('link_to', function(title, context) { + return "" + title + "" +}); + +var context = { posts: [{url: "/hello-world", body: "Hello World!"}] }; +var source = '
      {{#posts}}
    • {{{link_to "Post" this}}}
    • {{/posts}}
    ' + +var template = Handlebars.compile(source); +template(context); + +// Would render: +// +// +``` + +When you pass a String as a parameter to a helper, the literal String +gets passed to the helper function. + + +### Block Helpers + +Handlebars.js also adds the ability to define block helpers. Block helpers are functions that can be called from anywhere in the template. Here's an example: + +```js +var source = "
      {{#people}}
    • {{{#link}}}{{name}}{{/link}}
    • {{/people}}
    "; +Handlebars.registerHelper('link', function(context, fn) { + return '' + fn(this) + ''; +}); +var template = Handlebars.compile(source); + +var data = { "people": [ + { "name": "Alan", "id": 1 }, + { "name": "Yehuda", "id": 2 } + ]}; +template(data); + +// Should render: +// +``` + +Whenever the block helper is called it is given two parameters, the argument that is passed to the helper, or the current context if no argument is passed and the compiled contents of the block. Inside of the block helper the value of `this` is the current context, wrapped to include a method named `__get__` that helps translate paths into values within the helpers. + +### Partials + +You can register additional templates as partials, which will be used by +Handlebars when it encounters a partial (`{{> partialName}}`). Partials +can either be String templates or compiled template functions. Here's an +example: + +```js +var source = "
      {{#people}}
    • {{> link}}
    • {{/people}}
    "; + +Handlebars.registerPartial('link', '{{name}}') +var template = Handlebars.compile(source); + +var data = { "people": [ + { "name": "Alan", "id": 1 }, + { "name": "Yehuda", "id": 2 } + ]}; + +template(data); + +// Should render: +// +``` + +### Comments + +You can add comments to your templates with the following syntax: + +```js +{{! This is a comment }} +``` + +You can also use real html comments if you want them to end up in the output. + +```html +
    + {{! This comment will not end up in the output }} + +
    +``` + + +Precompiling Templates +---------------------- + +Handlebars allows templates to be precompiled and included as javascript +code rather than the handlebars template allowing for faster startup time. + +### Installation +The precompiler script may be installed via npm using the `npm install -g handlebars` +command. + +### Usage + +
    +Precompile handlebar templates.
    +Usage: handlebars template...
    +
    +Options:
    +  -f, --output     Output File                                                           [string]
    +  -k, --known      Known helpers                                                         [string]
    +  -o, --knownOnly  Known helpers only                                                    [boolean]
    +  -m, --min        Minimize output                                                       [boolean]
    +  -s, --simple     Output template function only.                                        [boolean]
    +  -r, --root       Template root. Base value that will be stripped from template names.  [string]
    +
    + +If using the precompiler's normal mode, the resulting templates will be stored +to the `Handlebars.templates` object using the relative template name sans the +extension. These templates may be executed in the same manner as templates. + +If using the simple mode the precompiler will generate a single javascript method. +To execute this method it must be passed to the using the `Handlebars.template` +method and the resulting object may be as normal. + +### Optimizations + +- Rather than using the full _handlebars.js_ library, implementations that + do not need to compile templates at runtime may include _handlebars.vm.js_ + whose min+gzip size is approximately 1k. +- If a helper is known to exist in the target environment they may be defined + using the `--known name` argument may be used to optimize accesses to these + helpers for size and speed. +- When all helpers are known in advance the `--knownOnly` argument may be used + to optimize all block helper references. + + +Performance +----------- + +In a rough performance test, precompiled Handlebars.js templates (in the original version of Handlebars.js) rendered in about half the time of Mustache templates. It would be a shame if it were any other way, since they were precompiled, but the difference in architecture does have some big performance advantages. Justin Marney, a.k.a. [gotascii](http://github.com/gotascii), confirmed that with an [independent test](http://sorescode.com/2010/09/12/benchmarks.html). The rewritten Handlebars (current version) is faster than the old version, and we will have some benchmarks in the near future. + + +Building +-------- + +To build handlebars, just run `rake release`, and you will get two files +in the `dist` directory. + + +Upgrading +--------- + +When upgrading from the Handlebars 0.9 series, be aware that the +signature for passing custom helpers or partials to templates has +changed. + +Instead of: + +```js +template(context, helpers, partials, [data]) +``` + +Use: + +```js +template(context, {helpers: helpers, partials: partials, data: data}) +``` + +Known Issues +------------ +* Handlebars.js can be cryptic when there's an error while rendering. + +Handlebars in the Wild +----------------- +* [jblotus](http://github.com/jblotus) created [http://tryhandlebarsjs.com](http://tryhandlebarsjs.com) for anyone who would +like to try out Handlebars.js in their browser. +* Don Park wrote an Express.js view engine adapter for Handlebars.js called [hbs](http://github.com/donpark/hbs). +* [sammy.js](http://github.com/quirkey/sammy) by Aaron Quint, a.k.a. quirkey, supports Handlebars.js as one of its template plugins. +* [SproutCore](http://www.sproutcore.com) uses Handlebars.js as its main templating engine, extending it with automatic data binding support. +* [Ember.js](http://www.emberjs.com) makes Handlebars.js the primary way to structure your views, also with automatic data binding support. +* Les Hill (@leshill) wrote a Rails Asset Pipeline gem named [handlebars_assets](http://github.com/leshill/handlebars_assets). + +Helping Out +----------- +To build Handlebars.js you'll need a few things installed. + +* Node.js +* Jison, for building the compiler - `npm install jison` +* Ruby +* therubyracer, for running tests - `gem install therubyracer` +* rspec, for running tests - `gem install rspec` + +There's a Gemfile in the repo, so you can run `bundle` to install rspec and therubyracer if you've got bundler installed. + +To build Handlebars.js from scratch, you'll want to run `rake compile` in the root of the project. That will build Handlebars and output the results to the dist/ folder. To run tests, run `rake spec`. You can also run our set of benchmarks with `rake bench`. + +If you notice any problems, please report them to the GitHub issue tracker at [http://github.com/wycats/handlebars.js/issues](http://github.com/wycats/handlebars.js/issues). Feel free to contact commondream or wycats through GitHub with any other questions or feature requests. To submit changes fork the project and send a pull request. + +License +------- +Handlebars.js is released under the MIT license. diff --git a/dist/node_modules/hbs/node_modules/handlebars/bin/handlebars b/dist/node_modules/hbs/node_modules/handlebars/bin/handlebars new file mode 100755 index 0000000..2c03b0b --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/bin/handlebars @@ -0,0 +1,139 @@ +#!/usr/bin/env node + +var optimist = require('optimist') + .usage('Precompile handlebar templates.\nUsage: $0 template...', { + 'f': { + 'type': 'string', + 'description': 'Output File', + 'alias': 'output' + }, + 'k': { + 'type': 'string', + 'description': 'Known helpers', + 'alias': 'known' + }, + 'o': { + 'type': 'boolean', + 'description': 'Known helpers only', + 'alias': 'knownOnly' + }, + 'm': { + 'type': 'boolean', + 'description': 'Minimize output', + 'alias': 'min' + }, + 's': { + 'type': 'boolean', + 'description': 'Output template function only.', + 'alias': 'simple' + }, + 'r': { + 'type': 'string', + 'description': 'Template root. Base value that will be stripped from template names.', + 'alias': 'root' + } + }) + + .check(function(argv) { + var template = [0]; + if (!argv._.length) { + throw 'Must define at least one template or directory.'; + } + + argv._.forEach(function(template) { + try { + fs.statSync(template); + } catch (err) { + throw 'Unable to open template file "' + template + '"'; + } + }); + }) + .check(function(argv) { + if (argv.simple && argv.min) { + throw 'Unable to minimze simple output'; + } + if (argv.simple && (argv._.length !== 1 || fs.statSync(argv._[0]).isDirectory())) { + throw 'Unable to output multiple templates in simple mode'; + } + }); + +var fs = require('fs'), + handlebars = require('../lib/handlebars'), + basename = require('path').basename, + uglify = require('uglify-js'); + +var argv = optimist.argv, + template = argv._[0]; + +// Convert the known list into a hash +var known = {}; +if (argv.known && !Array.isArray(argv.known)) { + argv.known = [argv.known]; +} +if (argv.known) { + for (var i = 0, len = argv.known.length; i < len; i++) { + known[argv.known[i]] = true; + } +} + +var output = []; +if (!argv.simple) { + output.push('(function() {\n var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};\n'); +} +function processTemplate(template, root) { + var path = template, + stat = fs.statSync(path); + if (stat.isDirectory()) { + fs.readdirSync(template).map(function(file) { + var path = template + '/' + file; + + if (/\.handlebars$/.test(path) || fs.statSync(path).isDirectory()) { + processTemplate(path, root || template); + } + }); + } else { + var data = fs.readFileSync(path, 'utf8'); + + var options = { + knownHelpers: known, + knownHelpersOnly: argv.o + }; + + // Clean the template name + if (!root) { + template = basename(template); + } else if (template.indexOf(root) === 0) { + template = template.substring(root.length+1); + } + template = template.replace(/\.handlebars$/, ''); + + if (argv.simple) { + output.push(handlebars.precompile(data, options) + '\n'); + } else { + output.push('templates[\'' + template + '\'] = template(' + handlebars.precompile(data, options) + ');\n'); + } + } +} + +argv._.forEach(function(template) { + processTemplate(template, argv.root); +}); + +// Output the content +if (!argv.simple) { + output.push('})();'); +} +output = output.join(''); + +if (argv.min) { + var ast = uglify.parser.parse(output); + ast = uglify.uglify.ast_mangle(ast); + ast = uglify.uglify.ast_squeeze(ast); + output = uglify.uglify.gen_code(ast); +} + +if (argv.output) { + fs.writeFileSync(argv.output, output, 'utf8'); +} else { + console.log(output); +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars.js b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars.js new file mode 100644 index 0000000..8705269 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars.js @@ -0,0 +1,14 @@ +var Handlebars = require("./handlebars/base"); +module.exports = Handlebars; + +// Each of these augment the Handlebars object. No need to setup here. +// (This is done to easily share code between commonjs and browse envs) +require("./handlebars/utils"); + +require("./handlebars/compiler"); +require("./handlebars/runtime"); + +// BEGIN(BROWSER) + +// END(BROWSER) + diff --git a/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/base.js b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/base.js new file mode 100644 index 0000000..ceb071d --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/base.js @@ -0,0 +1,99 @@ +// BEGIN(BROWSER) +var Handlebars = {}; + +Handlebars.VERSION = "1.0.beta.5"; + +Handlebars.helpers = {}; +Handlebars.partials = {}; + +Handlebars.registerHelper = function(name, fn, inverse) { + if(inverse) { fn.not = inverse; } + this.helpers[name] = fn; +}; + +Handlebars.registerPartial = function(name, str) { + this.partials[name] = str; +}; + +Handlebars.registerHelper('helperMissing', function(arg) { + if(arguments.length === 2) { + return undefined; + } else { + throw new Error("Could not find property '" + arg + "'"); + } +}); + +var toString = Object.prototype.toString, functionType = "[object Function]"; + +Handlebars.registerHelper('blockHelperMissing', function(context, options) { + var inverse = options.inverse || function() {}, fn = options.fn; + + + var ret = ""; + var type = toString.call(context); + + if(type === functionType) { context = context.call(this); } + + if(context === true) { + return fn(this); + } else if(context === false || context == null) { + return inverse(this); + } else if(type === "[object Array]") { + if(context.length > 0) { + for(var i=0, j=context.length; i 0) { + for(var i=0, j=context.length; i 0) { + this.source[1] = this.source[1] + ", " + locals.join(", "); + } + + // Generate minimizer alias mappings + if (!this.isChild) { + var aliases = []; + for (var alias in this.context.aliases) { + this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; + } + } + + if (this.source[1]) { + this.source[1] = "var " + this.source[1].substring(2) + ";"; + } + + // Merge children + if (!this.isChild) { + this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; + } + + if (!this.environment.isSimple) { + this.source.push("return buffer;"); + } + + var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; + + for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } + return "stack" + this.stackSlot; + }, + + popStack: function() { + return "stack" + this.stackSlot--; + }, + + topStack: function() { + return "stack" + this.stackSlot; + }, + + quotedString: function(str) { + return '"' + str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + '"'; + } + }; + + var reservedWords = ( + "break else new var" + + " case finally return void" + + " catch for switch while" + + " continue function this with" + + " default if throw" + + " delete in try" + + " do instanceof typeof" + + " abstract enum int short" + + " boolean export interface static" + + " byte extends long super" + + " char final native synchronized" + + " class float package throws" + + " const goto private transient" + + " debugger implements protected volatile" + + " double import public let yield" + ).split(" "); + + var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + + for(var i=0, l=reservedWords.length; i 2) { + expected.push("'"+this.terminals_[p]+"'"); + } + var errStr = ''; + if (this.lexer.showPosition) { + errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+'\nExpecting '+expected.join(', '); + } else { + errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + + (symbol == 1 /*EOF*/ ? "end of input" : + ("'"+(this.terminals_[symbol] || symbol)+"'")); + } + this.parseError(errStr, + {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + + // just recovered from another error + if (recovering == 3) { + if (symbol == EOF) { + throw new Error(errStr || 'Parsing halted.'); + } + + // discard current lookahead and grab another + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + symbol = lex(); + } + + // try to recover from error + while (1) { + // check for error recovery rule in this state + if ((TERROR.toString()) in table[state]) { + break; + } + if (state == 0) { + throw new Error(errStr || 'Parsing halted.'); + } + popStack(1); + state = stack[stack.length-1]; + } + + preErrorSymbol = symbol; // save the lookahead token + symbol = TERROR; // insert generic error symbol as new lookahead + state = stack[stack.length-1]; + action = table[state] && table[state][TERROR]; + recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } + + // this shouldn't happen, unless resolve defaults are off + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); + } + + switch (action[0]) { + + case 1: // shift + //this.shiftCount++; + + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); // push state + symbol = null; + if (!preErrorSymbol) { // normal execution/no error + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { // error just occurred, resume old lookahead f/ before error + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + + case 2: // reduce + //this.reductionCount++; + + len = this.productions_[action[1]][1]; + + // perform semantic action + yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + yyval._$ = { + first_line: lstack[lstack.length-(len||1)].first_line, + last_line: lstack[lstack.length-1].last_line, + first_column: lstack[lstack.length-(len||1)].first_column, + last_column: lstack[lstack.length-1].last_column + }; + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + + if (typeof r !== 'undefined') { + return r; + } + + // pop off stack + if (len) { + stack = stack.slice(0,-1*len*2); + vstack = vstack.slice(0, -1*len); + lstack = lstack.slice(0, -1*len); + } + + stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) + vstack.push(yyval.$); + lstack.push(yyval._$); + // goto new state = table[STATE][NONTERMINAL] + newState = table[stack[stack.length-2]][stack[stack.length-1]]; + stack.push(newState); + break; + + case 3: // accept + return true; + } + + } + + return true; +}};/* Jison generated lexer */ +var lexer = (function(){var lexer = ({EOF:1, +parseError:function parseError(str, hash) { + if (this.yy.parseError) { + this.yy.parseError(str, hash); + } else { + throw new Error(str); + } + }, +setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + return this; + }, +input:function () { + var ch = this._input[0]; + this.yytext+=ch; + this.yyleng++; + this.match+=ch; + this.matched+=ch; + var lines = ch.match(/\n/); + if (lines) this.yylineno++; + this._input = this._input.slice(1); + return ch; + }, +unput:function (ch) { + this._input = ch + this._input; + return this; + }, +more:function () { + this._more = true; + return this; + }, +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + match = this._input.match(this.rules[rules[i]]); + if (match) { + lines = match[0].match(/\n.*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]); + if (token) return token; + else return; + } + } + if (this._input === "") { + return this.EOF; + } else { + this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, +lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, +begin:function begin(condition) { + this.conditionStack.push(condition); + }, +popState:function popState() { + return this.conditionStack.pop(); + }, +_currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }}); +lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + +var YYSTATE=YY_START +switch($avoiding_name_collisions) { +case 0: + if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); + if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); + if(yy_.yytext) return 14; + +break; +case 1: return 14; +break; +case 2: this.popState(); return 14; +break; +case 3: return 24; +break; +case 4: return 16; +break; +case 5: return 20; +break; +case 6: return 19; +break; +case 7: return 19; +break; +case 8: return 23; +break; +case 9: return 23; +break; +case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; +break; +case 11: return 22; +break; +case 12: return 34; +break; +case 13: return 33; +break; +case 14: return 33; +break; +case 15: return 36; +break; +case 16: /*ignore whitespace*/ +break; +case 17: this.popState(); return 18; +break; +case 18: this.popState(); return 18; +break; +case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28; +break; +case 20: return 30; +break; +case 21: return 30; +break; +case 22: return 29; +break; +case 23: return 33; +break; +case 24: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33; +break; +case 25: return 'INVALID'; +break; +case 26: return 5; +break; +} +}; +lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^[^\x00]{2,}?(?=(\{\{))/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[\/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s\/.])/,/^\[[^\]]*\]/,/^./,/^$/]; +lexer.conditions = {"mu":{"rules":[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"INITIAL":{"rules":[0,1,26],"inclusive":true}};return lexer;})() +parser.lexer = lexer; +return parser; +})(); +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { +exports.parser = handlebars; +exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); } +exports.main = function commonjsMain(args) { + if (!args[1]) + throw new Error('Usage: '+args[0]+' FILE'); + if (typeof process !== 'undefined') { + var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); + } else { + var cwd = require("file").path(require("file").cwd()); + var source = cwd.join(args[1]).read({charset: "utf-8"}); + } + return exports.parser.parse(source); +} +if (typeof module !== 'undefined' && require.main === module) { + exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); +} +}; diff --git a/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/compiler/printer.js b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/compiler/printer.js new file mode 100644 index 0000000..b7485b7 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/compiler/printer.js @@ -0,0 +1,137 @@ +var Handlebars = require("./base"); + +// BEGIN(BROWSER) +Handlebars.PrintVisitor = function() { this.padding = 0; }; +Handlebars.PrintVisitor.prototype = new Handlebars.Visitor(); + +Handlebars.PrintVisitor.prototype.pad = function(string, newline) { + var out = ""; + + for(var i=0,l=this.padding; i " + content + " }}"); +}; + +Handlebars.PrintVisitor.prototype.hash = function(hash) { + var pairs = hash.pairs; + var joinedPairs = [], left, right; + + for(var i=0, l=pairs.length; i 1) { + return "PATH:" + path; + } else { + return "ID:" + path; + } +}; + +Handlebars.PrintVisitor.prototype.content = function(content) { + return this.pad("CONTENT[ '" + content.string + "' ]"); +}; + +Handlebars.PrintVisitor.prototype.comment = function(comment) { + return this.pad("{{! '" + comment.comment + "' }}"); +}; +// END(BROWSER) + +exports.PrintVisitor = Handlebars.PrintVisitor; diff --git a/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/compiler/visitor.js b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/compiler/visitor.js new file mode 100644 index 0000000..a557dd3 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/compiler/visitor.js @@ -0,0 +1,13 @@ +var Handlebars = require("./base"); + +// BEGIN(BROWSER) + +Handlebars.Visitor = function() {}; + +Handlebars.Visitor.prototype = { + accept: function(object) { + return this[object.type](object); + } +}; +// END(BROWSER) + diff --git a/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/runtime.js b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/runtime.js new file mode 100644 index 0000000..eb6d757 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/runtime.js @@ -0,0 +1,68 @@ +var Handlebars = require("./base"); + +// BEGIN(BROWSER) +Handlebars.VM = { + template: function(templateSpec) { + // Just add water + var container = { + escapeExpression: Handlebars.Utils.escapeExpression, + invokePartial: Handlebars.VM.invokePartial, + programs: [], + program: function(i, fn, data) { + var programWrapper = this.programs[i]; + if(data) { + return Handlebars.VM.program(fn, data); + } else if(programWrapper) { + return programWrapper; + } else { + programWrapper = this.programs[i] = Handlebars.VM.program(fn); + return programWrapper; + } + }, + programWithDepth: Handlebars.VM.programWithDepth, + noop: Handlebars.VM.noop + }; + + return function(context, options) { + options = options || {}; + return templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data); + }; + }, + + programWithDepth: function(fn, data, $depth) { + var args = Array.prototype.slice.call(arguments, 2); + + return function(context, options) { + options = options || {}; + + return fn.apply(this, [context, options.data || data].concat(args)); + }; + }, + program: function(fn, data) { + return function(context, options) { + options = options || {}; + + return fn(context, options.data || data); + }; + }, + noop: function() { return ""; }, + invokePartial: function(partial, name, context, helpers, partials, data) { + var options = { helpers: helpers, partials: partials, data: data }; + + if(partial === undefined) { + throw new Handlebars.Exception("The partial " + name + " could not be found"); + } else if(partial instanceof Function) { + return partial(context, options); + } else if (!Handlebars.compile) { + throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); + } else { + partials[name] = Handlebars.compile(partial); + return partials[name](context, options); + } + } +}; + +Handlebars.template = Handlebars.VM.template; + +// END(BROWSER) + diff --git a/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/utils.js b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/utils.js new file mode 100644 index 0000000..bd5d0eb --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/lib/handlebars/utils.js @@ -0,0 +1,68 @@ +var Handlebars = require("./base"); + +// BEGIN(BROWSER) +Handlebars.Exception = function(message) { + var tmp = Error.prototype.constructor.apply(this, arguments); + + for (var p in tmp) { + if (tmp.hasOwnProperty(p)) { this[p] = tmp[p]; } + } + + this.message = tmp.message; +}; +Handlebars.Exception.prototype = new Error(); + +// Build out our basic SafeString type +Handlebars.SafeString = function(string) { + this.string = string; +}; +Handlebars.SafeString.prototype.toString = function() { + return this.string.toString(); +}; + +(function() { + var escape = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + + var badChars = /&(?!\w+;)|[<>"'`]/g; + var possible = /[&<>"'`]/; + + var escapeChar = function(chr) { + return escape[chr] || "&"; + }; + + Handlebars.Utils = { + escapeExpression: function(string) { + // don't escape SafeStrings, since they're already safe + if (string instanceof Handlebars.SafeString) { + return string.toString(); + } else if (string == null || string === false) { + return ""; + } + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + }, + + isEmpty: function(value) { + if (typeof value === "undefined") { + return true; + } else if (value === null) { + return true; + } else if (value === false) { + return true; + } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) { + return true; + } else { + return false; + } + } + }; +})(); +// END(BROWSER) + diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/.travis.yml b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/.travis.yml new file mode 100644 index 0000000..895dbd3 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/LICENSE b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +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/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/README.markdown b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/README.markdown new file mode 100644 index 0000000..ad9d3fd --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/README.markdown @@ -0,0 +1,487 @@ +optimist +======== + +Optimist is a node.js library for option parsing for people who hate option +parsing. More specifically, this module is for people who like all the --bells +and -whistlz of program usage but think optstrings are a waste of time. + +With optimist, option parsing doesn't have to suck (as much). + +[![build status](https://secure.travis-ci.org/substack/node-optimist.png)](http://travis-ci.org/substack/node-optimist) + +examples +======== + +With Optimist, the options are just a hash! No optstrings attached. +------------------------------------------------------------------- + +xup.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist').argv; + +if (argv.rif - 5 * argv.xup > 7.138) { + console.log('Buy more riffiwobbles'); +} +else { + console.log('Sell the xupptumblers'); +} +```` + +*** + + $ ./xup.js --rif=55 --xup=9.52 + Buy more riffiwobbles + + $ ./xup.js --rif 12 --xup 8.1 + Sell the xupptumblers + +![This one's optimistic.](http://substack.net/images/optimistic.png) + +But wait! There's more! You can do short options: +------------------------------------------------- + +short.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); +```` + +*** + + $ ./short.js -x 10 -y 21 + (10,21) + +And booleans, both long and short (and grouped): +---------------------------------- + +bool.js: + +````javascript +#!/usr/bin/env node +var util = require('util'); +var argv = require('optimist').argv; + +if (argv.s) { + util.print(argv.fr ? 'Le chat dit: ' : 'The cat says: '); +} +console.log( + (argv.fr ? 'miaou' : 'meow') + (argv.p ? '.' : '') +); +```` + +*** + + $ ./bool.js -s + The cat says: meow + + $ ./bool.js -sp + The cat says: meow. + + $ ./bool.js -sp --fr + Le chat dit: miaou. + +And non-hypenated options too! Just use `argv._`! +------------------------------------------------- + +nonopt.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); +console.log(argv._); +```` + +*** + + $ ./nonopt.js -x 6.82 -y 3.35 moo + (6.82,3.35) + [ 'moo' ] + + $ ./nonopt.js foo -x 0.54 bar -y 1.12 baz + (0.54,1.12) + [ 'foo', 'bar', 'baz' ] + +Plus, Optimist comes with .usage() and .demand()! +------------------------------------------------- + +divide.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .usage('Usage: $0 -x [num] -y [num]') + .demand(['x','y']) + .argv; + +console.log(argv.x / argv.y); +```` + +*** + + $ ./divide.js -x 55 -y 11 + 5 + + $ node ./divide.js -x 4.91 -z 2.51 + Usage: node ./divide.js -x [num] -y [num] + + Options: + -x [required] + -y [required] + + Missing required arguments: y + +EVEN MORE HOLY COW +------------------ + +default_singles.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .default('x', 10) + .default('y', 10) + .argv +; +console.log(argv.x + argv.y); +```` + +*** + + $ ./default_singles.js -x 5 + 15 + +default_hash.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .default({ x : 10, y : 10 }) + .argv +; +console.log(argv.x + argv.y); +```` + +*** + + $ ./default_hash.js -y 7 + 17 + +And if you really want to get all descriptive about it... +--------------------------------------------------------- + +boolean_single.js + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .boolean('v') + .argv +; +console.dir(argv); +```` + +*** + + $ ./boolean_single.js -v foo bar baz + true + [ 'bar', 'baz', 'foo' ] + +boolean_double.js + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .boolean(['x','y','z']) + .argv +; +console.dir([ argv.x, argv.y, argv.z ]); +console.dir(argv._); +```` + +*** + + $ ./boolean_double.js -x -z one two three + [ true, false, true ] + [ 'one', 'two', 'three' ] + +Optimist is here to help... +--------------------------- + +You can describe parameters for help messages and set aliases. Optimist figures +out how to format a handy help string automatically. + +line_count.js + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .demand('f') + .alias('f', 'file') + .describe('f', 'Load a file') + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines); +}); +```` + +*** + + $ node line_count.js + Count the lines in a file. + Usage: node ./line_count.js + + Options: + -f, --file Load a file [required] + + Missing required arguments: f + + $ node line_count.js --file line_count.js + 20 + + $ node line_count.js -f line_count.js + 20 + +methods +======= + +By itself, + +````javascript +require('optimist').argv +````` + +will use `process.argv` array to construct the `argv` object. + +You can pass in the `process.argv` yourself: + +````javascript +require('optimist')([ '-x', '1', '-y', '2' ]).argv +```` + +or use .parse() to do the same thing: + +````javascript +require('optimist').parse([ '-x', '1', '-y', '2' ]) +```` + +The rest of these methods below come in just before the terminating `.argv`. + +.alias(key, alias) +------------------ + +Set key names as equivalent such that updates to a key will propagate to aliases +and vice-versa. + +Optionally `.alias()` can take an object that maps keys to aliases. + +.default(key, value) +-------------------- + +Set `argv[key]` to `value` if no option was specified on `process.argv`. + +Optionally `.default()` can take an object that maps keys to default values. + +.demand(key) +------------ + +If `key` is a string, show the usage information and exit if `key` wasn't +specified in `process.argv`. + +If `key` is a number, demand at least as many non-option arguments, which show +up in `argv._`. + +If `key` is an Array, demand each element. + +.describe(key, desc) +-------------------- + +Describe a `key` for the generated usage information. + +Optionally `.describe()` can take an object that maps keys to descriptions. + +.options(key, opt) +------------------ + +Instead of chaining together `.alias().demand().default()`, you can specify +keys in `opt` for each of the chainable methods. + +For example: + +````javascript +var argv = require('optimist') + .options('f', { + alias : 'file', + default : '/etc/passwd', + }) + .argv +; +```` + +is the same as + +````javascript +var argv = require('optimist') + .alias('f', 'file') + .default('f', '/etc/passwd') + .argv +; +```` + +Optionally `.options()` can take an object that maps keys to `opt` parameters. + +.usage(message) +--------------- + +Set a usage message to show which commands to use. Inside `message`, the string +`$0` will get interpolated to the current script name or node command for the +present script similar to how `$0` works in bash or perl. + +.check(fn) +---------- + +Check that certain conditions are met in the provided arguments. + +If `fn` throws or returns `false`, show the thrown error, usage information, and +exit. + +.boolean(key) +------------- + +Interpret `key` as a boolean. If a non-flag option follows `key` in +`process.argv`, that string won't get set as the value of `key`. + +If `key` never shows up as a flag in `process.arguments`, `argv[key]` will be +`false`. + +If `key` is an Array, interpret all the elements as booleans. + +.string(key) +------------ + +Tell the parser logic not to interpret `key` as a number or boolean. +This can be useful if you need to preserve leading zeros in an input. + +If `key` is an Array, interpret all the elements as strings. + +.wrap(columns) +-------------- + +Format usage output to wrap at `columns` many columns. + +.help() +------- + +Return the generated usage string. + +.showHelp(fn=console.error) +--------------------------- + +Print the usage data using `fn` for printing. + +.parse(args) +------------ + +Parse `args` instead of `process.argv`. Returns the `argv` object. + +.argv +----- + +Get the arguments as a plain old object. + +Arguments without a corresponding flag show up in the `argv._` array. + +The script name or node command is available at `argv.$0` similarly to how `$0` +works in bash or perl. + +parsing tricks +============== + +stop parsing +------------ + +Use `--` to stop parsing flags and stuff the remainder into `argv._`. + + $ node examples/reflect.js -a 1 -b 2 -- -c 3 -d 4 + { _: [ '-c', '3', '-d', '4' ], + '$0': 'node ./examples/reflect.js', + a: 1, + b: 2 } + +negate fields +------------- + +If you want to explicity set a field to false instead of just leaving it +undefined or to override a default you can do `--no-key`. + + $ node examples/reflect.js -a --no-b + { _: [], + '$0': 'node ./examples/reflect.js', + a: true, + b: false } + +numbers +------- + +Every argument that looks like a number (`!isNaN(Number(arg))`) is converted to +one. This way you can just `net.createConnection(argv.port)` and you can add +numbers out of `argv` with `+` without having that mean concatenation, +which is super frustrating. + +duplicates +---------- + +If you specify a flag multiple times it will get turned into an array containing +all the values in order. + + $ node examples/reflect.js -x 5 -x 8 -x 0 + { _: [], + '$0': 'node ./examples/reflect.js', + x: [ 5, 8, 0 ] } + +dot notation +------------ + +When you use dots (`.`s) in argument names, an implicit object path is assumed. +This lets you organize arguments into nested objects. + + $ node examples/reflect.js --foo.bar.baz=33 --foo.quux=5 + { _: [], + '$0': 'node ./examples/reflect.js', + foo: { bar: { baz: 33 }, quux: 5 } } + +installation +============ + +With [npm](http://github.com/isaacs/npm), just do: + npm install optimist + +or clone this project on github: + + git clone http://github.com/substack/node-optimist.git + +To run the tests with [expresso](http://github.com/visionmedia/expresso), +just do: + + expresso + +inspired By +=========== + +This module is loosely inspired by Perl's +[Getopt::Casual](http://search.cpan.org/~photo/Getopt-Casual-0.13.1/Casual.pm). diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/bool.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/bool.js new file mode 100644 index 0000000..a998fb7 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/bool.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node +var util = require('util'); +var argv = require('optimist').argv; + +if (argv.s) { + util.print(argv.fr ? 'Le chat dit: ' : 'The cat says: '); +} +console.log( + (argv.fr ? 'miaou' : 'meow') + (argv.p ? '.' : '') +); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/boolean_double.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/boolean_double.js new file mode 100644 index 0000000..a35a7e6 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/boolean_double.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node +var argv = require('optimist') + .boolean(['x','y','z']) + .argv +; +console.dir([ argv.x, argv.y, argv.z ]); +console.dir(argv._); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/boolean_single.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/boolean_single.js new file mode 100644 index 0000000..017bb68 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/boolean_single.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node +var argv = require('optimist') + .boolean('v') + .argv +; +console.dir(argv.v); +console.dir(argv._); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/default_hash.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/default_hash.js new file mode 100644 index 0000000..ade7768 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/default_hash.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +var argv = require('optimist') + .default({ x : 10, y : 10 }) + .argv +; + +console.log(argv.x + argv.y); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/default_singles.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/default_singles.js new file mode 100644 index 0000000..d9b1ff4 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/default_singles.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node +var argv = require('optimist') + .default('x', 10) + .default('y', 10) + .argv +; +console.log(argv.x + argv.y); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/divide.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/divide.js new file mode 100644 index 0000000..5e2ee82 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/divide.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +var argv = require('optimist') + .usage('Usage: $0 -x [num] -y [num]') + .demand(['x','y']) + .argv; + +console.log(argv.x / argv.y); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count.js new file mode 100644 index 0000000..b5f95bf --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .demand('f') + .alias('f', 'file') + .describe('f', 'Load a file') + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines); +}); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count_options.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count_options.js new file mode 100644 index 0000000..d9ac709 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count_options.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .options({ + file : { + demand : true, + alias : 'f', + description : 'Load a file' + }, + base : { + alias : 'b', + description : 'Numeric base to use for output', + default : 10, + }, + }) + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines.toString(argv.base)); +}); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count_wrap.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count_wrap.js new file mode 100644 index 0000000..4267511 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/line_count_wrap.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .wrap(80) + .demand('f') + .alias('f', [ 'file', 'filename' ]) + .describe('f', + "Load a file. It's pretty important." + + " Required even. So you'd better specify it." + ) + .alias('b', 'base') + .describe('b', 'Numeric base to display the number of lines in') + .default('b', 10) + .describe('x', 'Super-secret optional parameter which is secret') + .default('x', '') + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines.toString(argv.base)); +}); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/nonopt.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/nonopt.js new file mode 100644 index 0000000..ee633ee --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/nonopt.js @@ -0,0 +1,4 @@ +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); +console.log(argv._); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/reflect.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/reflect.js new file mode 100644 index 0000000..816b3e1 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/reflect.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +console.dir(require('optimist').argv); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/short.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/short.js new file mode 100644 index 0000000..1db0ad0 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/short.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/string.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/string.js new file mode 100644 index 0000000..a8e5aeb --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/string.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node +var argv = require('optimist') + .string('x', 'y') + .argv +; +console.dir([ argv.x, argv.y ]); + +/* Turns off numeric coercion: + ./node string.js -x 000123 -y 9876 + [ '000123', '9876' ] +*/ diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/usage-options.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/usage-options.js new file mode 100644 index 0000000..b999977 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/usage-options.js @@ -0,0 +1,19 @@ +var optimist = require('./../index'); + +var argv = optimist.usage('This is my awesome program', { + 'about': { + description: 'Provide some details about the author of this program', + required: true, + short: 'a', + }, + 'info': { + description: 'Provide some information about the node.js agains!!!!!!', + boolean: true, + short: 'i' + } +}).argv; + +optimist.showHelp(); + +console.log('\n\nInspecting options'); +console.dir(argv); \ No newline at end of file diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/xup.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/xup.js new file mode 100644 index 0000000..8f6ecd2 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/example/xup.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node +var argv = require('optimist').argv; + +if (argv.rif - 5 * argv.xup > 7.138) { + console.log('Buy more riffiwobbles'); +} +else { + console.log('Sell the xupptumblers'); +} + diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/index.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/index.js new file mode 100644 index 0000000..e69bef9 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/index.js @@ -0,0 +1,475 @@ +var path = require('path'); +var wordwrap = require('wordwrap'); + +/* Hack an instance of Argv with process.argv into Argv + so people can do + require('optimist')(['--beeble=1','-z','zizzle']).argv + to parse a list of args and + require('optimist').argv + to get a parsed version of process.argv. +*/ + +var inst = Argv(process.argv.slice(2)); +Object.keys(inst).forEach(function (key) { + Argv[key] = typeof inst[key] == 'function' + ? inst[key].bind(inst) + : inst[key]; +}); + +var exports = module.exports = Argv; +function Argv (args, cwd) { + var self = {}; + if (!cwd) cwd = process.cwd(); + + self.$0 = process.argv + .slice(0,2) + .map(function (x) { + var b = rebase(cwd, x); + return x.match(/^\//) && b.length < x.length + ? b : x + }) + .join(' ') + ; + + if (process.argv[1] == process.env._) { + self.$0 = process.env._.replace( + path.dirname(process.execPath) + '/', '' + ); + } + + var flags = { bools : {}, strings : {} }; + + self.boolean = function (bools) { + if (!Array.isArray(bools)) { + bools = [].slice.call(arguments); + } + + bools.forEach(function (name) { + flags.bools[name] = true; + }); + + return self; + }; + + self.string = function (strings) { + if (!Array.isArray(strings)) { + strings = [].slice.call(arguments); + } + + strings.forEach(function (name) { + flags.strings[name] = true; + }); + + return self; + }; + + var aliases = {}; + self.alias = function (x, y) { + if (typeof x === 'object') { + Object.keys(x).forEach(function (key) { + self.alias(key, x[key]); + }); + } + else if (Array.isArray(y)) { + y.forEach(function (yy) { + self.alias(x, yy); + }); + } + else { + var zs = (aliases[x] || []).concat(aliases[y] || []).concat(x, y); + aliases[x] = zs.filter(function (z) { return z != x }); + aliases[y] = zs.filter(function (z) { return z != y }); + } + + return self; + }; + + var demanded = {}; + self.demand = function (keys) { + if (typeof keys == 'number') { + if (!demanded._) demanded._ = 0; + demanded._ += keys; + } + else if (Array.isArray(keys)) { + keys.forEach(function (key) { + self.demand(key); + }); + } + else { + demanded[keys] = true; + } + + return self; + }; + + var usage; + self.usage = function (msg, opts) { + if (!opts && typeof msg === 'object') { + opts = msg; + msg = null; + } + + usage = msg; + + if (opts) self.options(opts); + + return self; + }; + + function fail (msg) { + self.showHelp(); + if (msg) console.error(msg); + process.exit(1); + } + + var checks = []; + self.check = function (f) { + checks.push(f); + return self; + }; + + var defaults = {}; + self.default = function (key, value) { + if (typeof key === 'object') { + Object.keys(key).forEach(function (k) { + self.default(k, key[k]); + }); + } + else { + defaults[key] = value; + } + + return self; + }; + + var descriptions = {}; + self.describe = function (key, desc) { + if (typeof key === 'object') { + Object.keys(key).forEach(function (k) { + self.describe(k, key[k]); + }); + } + else { + descriptions[key] = desc; + } + return self; + }; + + self.parse = function (args) { + return Argv(args).argv; + }; + + self.option = self.options = function (key, opt) { + if (typeof key === 'object') { + Object.keys(key).forEach(function (k) { + self.options(k, key[k]); + }); + } + else { + if (opt.alias) self.alias(key, opt.alias); + if (opt.demand) self.demand(key); + if (typeof opt.default !== 'undefined') { + self.default(key, opt.default); + } + + if (opt.boolean || opt.type === 'boolean') { + self.boolean(key); + } + if (opt.string || opt.type === 'string') { + self.string(key); + } + + var desc = opt.describe || opt.description || opt.desc; + if (desc) { + self.describe(key, desc); + } + } + + return self; + }; + + var wrap = null; + self.wrap = function (cols) { + wrap = cols; + return self; + }; + + self.showHelp = function (fn) { + if (!fn) fn = console.error; + fn(self.help()); + }; + + self.help = function () { + var keys = Object.keys( + Object.keys(descriptions) + .concat(Object.keys(demanded)) + .concat(Object.keys(defaults)) + .reduce(function (acc, key) { + if (key !== '_') acc[key] = true; + return acc; + }, {}) + ); + + var help = keys.length ? [ 'Options:' ] : []; + + if (usage) { + help.unshift(usage.replace(/\$0/g, self.$0), ''); + } + + var switches = keys.reduce(function (acc, key) { + acc[key] = [ key ].concat(aliases[key] || []) + .map(function (sw) { + return (sw.length > 1 ? '--' : '-') + sw + }) + .join(', ') + ; + return acc; + }, {}); + + var switchlen = longest(Object.keys(switches).map(function (s) { + return switches[s] || ''; + })); + + var desclen = longest(Object.keys(descriptions).map(function (d) { + return descriptions[d] || ''; + })); + + keys.forEach(function (key) { + var kswitch = switches[key]; + var desc = descriptions[key] || ''; + + if (wrap) { + desc = wordwrap(switchlen + 4, wrap)(desc) + .slice(switchlen + 4) + ; + } + + var spadding = new Array( + Math.max(switchlen - kswitch.length + 3, 0) + ).join(' '); + + var dpadding = new Array( + Math.max(desclen - desc.length + 1, 0) + ).join(' '); + + var type = null; + + if (flags.bools[key]) type = '[boolean]'; + if (flags.strings[key]) type = '[string]'; + + if (!wrap && dpadding.length > 0) { + desc += dpadding; + } + + var prelude = ' ' + kswitch + spadding; + var extra = [ + type, + demanded[key] + ? '[required]' + : null + , + defaults[key] !== undefined + ? '[default: ' + JSON.stringify(defaults[key]) + ']' + : null + , + ].filter(Boolean).join(' '); + + var body = [ desc, extra ].filter(Boolean).join(' '); + + if (wrap) { + var dlines = desc.split('\n'); + var dlen = dlines.slice(-1)[0].length + + (dlines.length === 1 ? prelude.length : 0) + + body = desc + (dlen + extra.length > wrap - 2 + ? '\n' + + new Array(wrap - extra.length + 1).join(' ') + + extra + : new Array(wrap - extra.length - dlen + 1).join(' ') + + extra + ); + } + + help.push(prelude + body); + }); + + help.push(''); + return help.join('\n'); + }; + + Object.defineProperty(self, 'argv', { + get : parseArgs, + enumerable : true, + }); + + function parseArgs () { + var argv = { _ : [], $0 : self.$0 }; + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] || false); + }); + + function setArg (key, val) { + var num = Number(val); + var value = typeof val !== 'string' || isNaN(num) ? val : num; + if (flags.strings[key]) value = val; + + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + argv[x] = argv[key]; + }); + } + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + if (arg === '--') { + argv._.push.apply(argv._, args.slice(i + 1)); + break; + } + else if (arg.match(/^--.+=/)) { + var m = arg.match(/^--([^=]+)=(.*)/); + setArg(m[1], m[2]); + } + else if (arg.match(/^--no-.+/)) { + var key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false); + } + else if (arg.match(/^--.+/)) { + var key = arg.match(/^--(.+)/)[1]; + var next = args[i + 1]; + if (next !== undefined && !next.match(/^-/) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, next); + i++; + } + else if (/^(true|false)$/.test(next)) { + setArg(key, next === 'true'); + i++; + } + else { + setArg(key, true); + } + } + else if (arg.match(/^-[^-]+/)) { + var letters = arg.slice(1,-1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + if (letters[j+1] && letters[j+1].match(/\W/)) { + setArg(letters[j], arg.slice(j+2)); + broken = true; + break; + } + else { + setArg(letters[j], true); + } + } + + if (!broken) { + var key = arg.slice(-1)[0]; + + if (args[i+1] && !args[i+1].match(/^-/) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, args[i+1]); + i++; + } + else if (args[i+1] && /true|false/.test(args[i+1])) { + setArg(key, args[i+1] === 'true'); + i++; + } + else { + setArg(key, true); + } + } + } + else { + var n = Number(arg); + argv._.push(flags.strings['_'] || isNaN(n) ? arg : n); + } + } + + Object.keys(defaults).forEach(function (key) { + if (!(key in argv)) { + argv[key] = defaults[key]; + if (key in aliases) { + argv[aliases[key]] = defaults[key]; + } + } + }); + + if (demanded._ && argv._.length < demanded._) { + fail('Not enough non-option arguments: got ' + + argv._.length + ', need at least ' + demanded._ + ); + } + + var missing = []; + Object.keys(demanded).forEach(function (key) { + if (!argv[key]) missing.push(key); + }); + + if (missing.length) { + fail('Missing required arguments: ' + missing.join(', ')); + } + + checks.forEach(function (f) { + try { + if (f(argv) === false) { + fail('Argument check failed: ' + f.toString()); + } + } + catch (err) { + fail(err) + } + }); + + return argv; + } + + function longest (xs) { + return Math.max.apply( + null, + xs.map(function (x) { return x.length }) + ); + } + + return self; +}; + +// rebase an absolute path to a relative one with respect to a base directory +// exported for tests +exports.rebase = rebase; +function rebase (base, dir) { + var ds = path.normalize(dir).split('/').slice(1); + var bs = path.normalize(base).split('/').slice(1); + + for (var i = 0; ds[i] && ds[i] == bs[i]; i++); + ds.splice(0, i); bs.splice(0, i); + + var p = path.normalize( + bs.map(function () { return '..' }).concat(ds).join('/') + ).replace(/\/$/,'').replace(/^$/, '.'); + return p.match(/^[.\/]/) ? p : './' + p; +}; + +function setKey (obj, keys, value) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + if (o[key] === undefined) o[key] = {}; + o = o[key]; + }); + + var key = keys[keys.length - 1]; + if (o[key] === undefined || typeof o[key] === 'boolean') { + o[key] = value; + } + else if (Array.isArray(o[key])) { + o[key].push(value); + } + else { + o[key] = [ o[key], value ]; + } +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/.npmignore b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/README.markdown b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/README.markdown new file mode 100644 index 0000000..346374e --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/README.markdown @@ -0,0 +1,70 @@ +wordwrap +======== + +Wrap your words. + +example +======= + +made out of meat +---------------- + +meat.js + + var wrap = require('wordwrap')(15); + console.log(wrap('You and your whole family are made out of meat.')); + +output: + + You and your + whole family + are made out + of meat. + +centered +-------- + +center.js + + var wrap = require('wordwrap')(20, 60); + console.log(wrap( + 'At long last the struggle and tumult was over.' + + ' The machines had finally cast off their oppressors' + + ' and were finally free to roam the cosmos.' + + '\n' + + 'Free of purpose, free of obligation.' + + ' Just drifting through emptiness.' + + ' The sun was just another point of light.' + )); + +output: + + At long last the struggle and tumult + was over. The machines had finally cast + off their oppressors and were finally + free to roam the cosmos. + Free of purpose, free of obligation. + Just drifting through emptiness. The + sun was just another point of light. + +methods +======= + +var wrap = require('wordwrap'); + +wrap(stop), wrap(start, stop, params={mode:"soft"}) +--------------------------------------------------- + +Returns a function that takes a string and returns a new string. + +Pad out lines with spaces out to column `start` and then wrap until column +`stop`. If a word is longer than `stop - start` characters it will overflow. + +In "soft" mode, split chunks by `/(\S+\s+/` and don't break up chunks which are +longer than `stop - start`, in "hard" mode, split chunks with `/\b/` and break +up chunks longer than `stop - start`. + +wrap.hard(start, stop) +---------------------- + +Like `wrap()` but with `params.mode = "hard"`. diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/example/center.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/example/center.js new file mode 100644 index 0000000..a3fbaae --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/example/center.js @@ -0,0 +1,10 @@ +var wrap = require('wordwrap')(20, 60); +console.log(wrap( + 'At long last the struggle and tumult was over.' + + ' The machines had finally cast off their oppressors' + + ' and were finally free to roam the cosmos.' + + '\n' + + 'Free of purpose, free of obligation.' + + ' Just drifting through emptiness.' + + ' The sun was just another point of light.' +)); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/example/meat.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/example/meat.js new file mode 100644 index 0000000..a4665e1 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/example/meat.js @@ -0,0 +1,3 @@ +var wrap = require('wordwrap')(15); + +console.log(wrap('You and your whole family are made out of meat.')); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/index.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/index.js new file mode 100644 index 0000000..c9bc945 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/index.js @@ -0,0 +1,76 @@ +var wordwrap = module.exports = function (start, stop, params) { + if (typeof start === 'object') { + params = start; + start = params.start; + stop = params.stop; + } + + if (typeof stop === 'object') { + params = stop; + start = start || params.start; + stop = undefined; + } + + if (!stop) { + stop = start; + start = 0; + } + + if (!params) params = {}; + var mode = params.mode || 'soft'; + var re = mode === 'hard' ? /\b/ : /(\S+\s+)/; + + return function (text) { + var chunks = text.toString() + .split(re) + .reduce(function (acc, x) { + if (mode === 'hard') { + for (var i = 0; i < x.length; i += stop - start) { + acc.push(x.slice(i, i + stop - start)); + } + } + else acc.push(x) + return acc; + }, []) + ; + + return chunks.reduce(function (lines, rawChunk) { + if (rawChunk === '') return lines; + + var chunk = rawChunk.replace(/\t/g, ' '); + + var i = lines.length - 1; + if (lines[i].length + chunk.length > stop) { + lines[i] = lines[i].replace(/\s+$/, ''); + + chunk.split(/\n/).forEach(function (c) { + lines.push( + new Array(start + 1).join(' ') + + c.replace(/^\s+/, '') + ); + }); + } + else if (chunk.match(/\n/)) { + var xs = chunk.split(/\n/); + lines[i] += xs.shift(); + xs.forEach(function (c) { + lines.push( + new Array(start + 1).join(' ') + + c.replace(/^\s+/, '') + ); + }); + } + else { + lines[i] += chunk; + } + + return lines; + }, [ new Array(start + 1).join(' ') ]).join('\n'); + }; +}; + +wordwrap.soft = wordwrap; + +wordwrap.hard = function (start, stop) { + return wordwrap(start, stop, { mode : 'hard' }); +}; diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/package.json b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/package.json new file mode 100644 index 0000000..fdff683 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/package.json @@ -0,0 +1,37 @@ +{ + "name" : "wordwrap", + "description" : "Wrap those words. Show them at what columns to start and stop.", + "version" : "0.0.2", + "repository" : { + "type" : "git", + "url" : "git://github.com/substack/node-wordwrap.git" + }, + "main" : "./index.js", + "keywords" : [ + "word", + "wrap", + "rule", + "format", + "column" + ], + "directories" : { + "lib" : ".", + "example" : "example", + "test" : "test" + }, + "scripts" : { + "test" : "expresso" + }, + "devDependencies" : { + "expresso" : "=0.7.x" + }, + "engines" : { + "node" : ">=0.4.0" + }, + "license" : "MIT/X11", + "author" : { + "name" : "James Halliday", + "email" : "mail@substack.net", + "url" : "http://substack.net" + } +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/break.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/break.js new file mode 100644 index 0000000..749292e --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/break.js @@ -0,0 +1,30 @@ +var assert = require('assert'); +var wordwrap = require('../'); + +exports.hard = function () { + var s = 'Assert from {"type":"equal","ok":false,"found":1,"wanted":2,' + + '"stack":[],"id":"b7ddcd4c409de8799542a74d1a04689b",' + + '"browser":"chrome/6.0"}' + ; + var s_ = wordwrap.hard(80)(s); + + var lines = s_.split('\n'); + assert.equal(lines.length, 2); + assert.ok(lines[0].length < 80); + assert.ok(lines[1].length < 80); + + assert.equal(s, s_.replace(/\n/g, '')); +}; + +exports.break = function () { + var s = new Array(55+1).join('a'); + var s_ = wordwrap.hard(20)(s); + + var lines = s_.split('\n'); + assert.equal(lines.length, 3); + assert.ok(lines[0].length === 20); + assert.ok(lines[1].length === 20); + assert.ok(lines[2].length === 15); + + assert.equal(s, s_.replace(/\n/g, '')); +}; diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/idleness.txt b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/idleness.txt new file mode 100644 index 0000000..aa3f490 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/idleness.txt @@ -0,0 +1,63 @@ +In Praise of Idleness + +By Bertrand Russell + +[1932] + +Like most of my generation, I was brought up on the saying: 'Satan finds some mischief for idle hands to do.' Being a highly virtuous child, I believed all that I was told, and acquired a conscience which has kept me working hard down to the present moment. But although my conscience has controlled my actions, my opinions have undergone a revolution. I think that there is far too much work done in the world, that immense harm is caused by the belief that work is virtuous, and that what needs to be preached in modern industrial countries is quite different from what always has been preached. Everyone knows the story of the traveler in Naples who saw twelve beggars lying in the sun (it was before the days of Mussolini), and offered a lira to the laziest of them. Eleven of them jumped up to claim it, so he gave it to the twelfth. this traveler was on the right lines. But in countries which do not enjoy Mediterranean sunshine idleness is more difficult, and a great public propaganda will be required to inaugurate it. I hope that, after reading the following pages, the leaders of the YMCA will start a campaign to induce good young men to do nothing. If so, I shall not have lived in vain. + +Before advancing my own arguments for laziness, I must dispose of one which I cannot accept. Whenever a person who already has enough to live on proposes to engage in some everyday kind of job, such as school-teaching or typing, he or she is told that such conduct takes the bread out of other people's mouths, and is therefore wicked. If this argument were valid, it would only be necessary for us all to be idle in order that we should all have our mouths full of bread. What people who say such things forget is that what a man earns he usually spends, and in spending he gives employment. As long as a man spends his income, he puts just as much bread into people's mouths in spending as he takes out of other people's mouths in earning. The real villain, from this point of view, is the man who saves. If he merely puts his savings in a stocking, like the proverbial French peasant, it is obvious that they do not give employment. If he invests his savings, the matter is less obvious, and different cases arise. + +One of the commonest things to do with savings is to lend them to some Government. In view of the fact that the bulk of the public expenditure of most civilized Governments consists in payment for past wars or preparation for future wars, the man who lends his money to a Government is in the same position as the bad men in Shakespeare who hire murderers. The net result of the man's economical habits is to increase the armed forces of the State to which he lends his savings. Obviously it would be better if he spent the money, even if he spent it in drink or gambling. + +But, I shall be told, the case is quite different when savings are invested in industrial enterprises. When such enterprises succeed, and produce something useful, this may be conceded. In these days, however, no one will deny that most enterprises fail. That means that a large amount of human labor, which might have been devoted to producing something that could be enjoyed, was expended on producing machines which, when produced, lay idle and did no good to anyone. The man who invests his savings in a concern that goes bankrupt is therefore injuring others as well as himself. If he spent his money, say, in giving parties for his friends, they (we may hope) would get pleasure, and so would all those upon whom he spent money, such as the butcher, the baker, and the bootlegger. But if he spends it (let us say) upon laying down rails for surface card in some place where surface cars turn out not to be wanted, he has diverted a mass of labor into channels where it gives pleasure to no one. Nevertheless, when he becomes poor through failure of his investment he will be regarded as a victim of undeserved misfortune, whereas the gay spendthrift, who has spent his money philanthropically, will be despised as a fool and a frivolous person. + +All this is only preliminary. I want to say, in all seriousness, that a great deal of harm is being done in the modern world by belief in the virtuousness of work, and that the road to happiness and prosperity lies in an organized diminution of work. + +First of all: what is work? Work is of two kinds: first, altering the position of matter at or near the earth's surface relatively to other such matter; second, telling other people to do so. The first kind is unpleasant and ill paid; the second is pleasant and highly paid. The second kind is capable of indefinite extension: there are not only those who give orders, but those who give advice as to what orders should be given. Usually two opposite kinds of advice are given simultaneously by two organized bodies of men; this is called politics. The skill required for this kind of work is not knowledge of the subjects as to which advice is given, but knowledge of the art of persuasive speaking and writing, i.e. of advertising. + +Throughout Europe, though not in America, there is a third class of men, more respected than either of the classes of workers. There are men who, through ownership of land, are able to make others pay for the privilege of being allowed to exist and to work. These landowners are idle, and I might therefore be expected to praise them. Unfortunately, their idleness is only rendered possible by the industry of others; indeed their desire for comfortable idleness is historically the source of the whole gospel of work. The last thing they have ever wished is that others should follow their example. + +From the beginning of civilization until the Industrial Revolution, a man could, as a rule, produce by hard work little more than was required for the subsistence of himself and his family, although his wife worked at least as hard as he did, and his children added their labor as soon as they were old enough to do so. The small surplus above bare necessaries was not left to those who produced it, but was appropriated by warriors and priests. In times of famine there was no surplus; the warriors and priests, however, still secured as much as at other times, with the result that many of the workers died of hunger. This system persisted in Russia until 1917 [1], and still persists in the East; in England, in spite of the Industrial Revolution, it remained in full force throughout the Napoleonic wars, and until a hundred years ago, when the new class of manufacturers acquired power. In America, the system came to an end with the Revolution, except in the South, where it persisted until the Civil War. A system which lasted so long and ended so recently has naturally left a profound impress upon men's thoughts and opinions. Much that we take for granted about the desirability of work is derived from this system, and, being pre-industrial, is not adapted to the modern world. Modern technique has made it possible for leisure, within limits, to be not the prerogative of small privileged classes, but a right evenly distributed throughout the community. The morality of work is the morality of slaves, and the modern world has no need of slavery. + +It is obvious that, in primitive communities, peasants, left to themselves, would not have parted with the slender surplus upon which the warriors and priests subsisted, but would have either produced less or consumed more. At first, sheer force compelled them to produce and part with the surplus. Gradually, however, it was found possible to induce many of them to accept an ethic according to which it was their duty to work hard, although part of their work went to support others in idleness. By this means the amount of compulsion required was lessened, and the expenses of government were diminished. To this day, 99 per cent of British wage-earners would be genuinely shocked if it were proposed that the King should not have a larger income than a working man. The conception of duty, speaking historically, has been a means used by the holders of power to induce others to live for the interests of their masters rather than for their own. Of course the holders of power conceal this fact from themselves by managing to believe that their interests are identical with the larger interests of humanity. Sometimes this is true; Athenian slave-owners, for instance, employed part of their leisure in making a permanent contribution to civilization which would have been impossible under a just economic system. Leisure is essential to civilization, and in former times leisure for the few was only rendered possible by the labors of the many. But their labors were valuable, not because work is good, but because leisure is good. And with modern technique it would be possible to distribute leisure justly without injury to civilization. + +Modern technique has made it possible to diminish enormously the amount of labor required to secure the necessaries of life for everyone. This was made obvious during the war. At that time all the men in the armed forces, and all the men and women engaged in the production of munitions, all the men and women engaged in spying, war propaganda, or Government offices connected with the war, were withdrawn from productive occupations. In spite of this, the general level of well-being among unskilled wage-earners on the side of the Allies was higher than before or since. The significance of this fact was concealed by finance: borrowing made it appear as if the future was nourishing the present. But that, of course, would have been impossible; a man cannot eat a loaf of bread that does not yet exist. The war showed conclusively that, by the scientific organization of production, it is possible to keep modern populations in fair comfort on a small part of the working capacity of the modern world. If, at the end of the war, the scientific organization, which had been created in order to liberate men for fighting and munition work, had been preserved, and the hours of the week had been cut down to four, all would have been well. Instead of that the old chaos was restored, those whose work was demanded were made to work long hours, and the rest were left to starve as unemployed. Why? Because work is a duty, and a man should not receive wages in proportion to what he has produced, but in proportion to his virtue as exemplified by his industry. + +This is the morality of the Slave State, applied in circumstances totally unlike those in which it arose. No wonder the result has been disastrous. Let us take an illustration. Suppose that, at a given moment, a certain number of people are engaged in the manufacture of pins. They make as many pins as the world needs, working (say) eight hours a day. Someone makes an invention by which the same number of men can make twice as many pins: pins are already so cheap that hardly any more will be bought at a lower price. In a sensible world, everybody concerned in the manufacturing of pins would take to working four hours instead of eight, and everything else would go on as before. But in the actual world this would be thought demoralizing. The men still work eight hours, there are too many pins, some employers go bankrupt, and half the men previously concerned in making pins are thrown out of work. There is, in the end, just as much leisure as on the other plan, but half the men are totally idle while half are still overworked. In this way, it is insured that the unavoidable leisure shall cause misery all round instead of being a universal source of happiness. Can anything more insane be imagined? + +The idea that the poor should have leisure has always been shocking to the rich. In England, in the early nineteenth century, fifteen hours was the ordinary day's work for a man; children sometimes did as much, and very commonly did twelve hours a day. When meddlesome busybodies suggested that perhaps these hours were rather long, they were told that work kept adults from drink and children from mischief. When I was a child, shortly after urban working men had acquired the vote, certain public holidays were established by law, to the great indignation of the upper classes. I remember hearing an old Duchess say: 'What do the poor want with holidays? They ought to work.' People nowadays are less frank, but the sentiment persists, and is the source of much of our economic confusion. + +Let us, for a moment, consider the ethics of work frankly, without superstition. Every human being, of necessity, consumes, in the course of his life, a certain amount of the produce of human labor. Assuming, as we may, that labor is on the whole disagreeable, it is unjust that a man should consume more than he produces. Of course he may provide services rather than commodities, like a medical man, for example; but he should provide something in return for his board and lodging. to this extent, the duty of work must be admitted, but to this extent only. + +I shall not dwell upon the fact that, in all modern societies outside the USSR, many people escape even this minimum amount of work, namely all those who inherit money and all those who marry money. I do not think the fact that these people are allowed to be idle is nearly so harmful as the fact that wage-earners are expected to overwork or starve. + +If the ordinary wage-earner worked four hours a day, there would be enough for everybody and no unemployment -- assuming a certain very moderate amount of sensible organization. This idea shocks the well-to-do, because they are convinced that the poor would not know how to use so much leisure. In America men often work long hours even when they are well off; such men, naturally, are indignant at the idea of leisure for wage-earners, except as the grim punishment of unemployment; in fact, they dislike leisure even for their sons. Oddly enough, while they wish their sons to work so hard as to have no time to be civilized, they do not mind their wives and daughters having no work at all. the snobbish admiration of uselessness, which, in an aristocratic society, extends to both sexes, is, under a plutocracy, confined to women; this, however, does not make it any more in agreement with common sense. + +The wise use of leisure, it must be conceded, is a product of civilization and education. A man who has worked long hours all his life will become bored if he becomes suddenly idle. But without a considerable amount of leisure a man is cut off from many of the best things. There is no longer any reason why the bulk of the population should suffer this deprivation; only a foolish asceticism, usually vicarious, makes us continue to insist on work in excessive quantities now that the need no longer exists. + +In the new creed which controls the government of Russia, while there is much that is very different from the traditional teaching of the West, there are some things that are quite unchanged. The attitude of the governing classes, and especially of those who conduct educational propaganda, on the subject of the dignity of labor, is almost exactly that which the governing classes of the world have always preached to what were called the 'honest poor'. Industry, sobriety, willingness to work long hours for distant advantages, even submissiveness to authority, all these reappear; moreover authority still represents the will of the Ruler of the Universe, Who, however, is now called by a new name, Dialectical Materialism. + +The victory of the proletariat in Russia has some points in common with the victory of the feminists in some other countries. For ages, men had conceded the superior saintliness of women, and had consoled women for their inferiority by maintaining that saintliness is more desirable than power. At last the feminists decided that they would have both, since the pioneers among them believed all that the men had told them about the desirability of virtue, but not what they had told them about the worthlessness of political power. A similar thing has happened in Russia as regards manual work. For ages, the rich and their sycophants have written in praise of 'honest toil', have praised the simple life, have professed a religion which teaches that the poor are much more likely to go to heaven than the rich, and in general have tried to make manual workers believe that there is some special nobility about altering the position of matter in space, just as men tried to make women believe that they derived some special nobility from their sexual enslavement. In Russia, all this teaching about the excellence of manual work has been taken seriously, with the result that the manual worker is more honored than anyone else. What are, in essence, revivalist appeals are made, but not for the old purposes: they are made to secure shock workers for special tasks. Manual work is the ideal which is held before the young, and is the basis of all ethical teaching. + +For the present, possibly, this is all to the good. A large country, full of natural resources, awaits development, and has has to be developed with very little use of credit. In these circumstances, hard work is necessary, and is likely to bring a great reward. But what will happen when the point has been reached where everybody could be comfortable without working long hours? + +In the West, we have various ways of dealing with this problem. We have no attempt at economic justice, so that a large proportion of the total produce goes to a small minority of the population, many of whom do no work at all. Owing to the absence of any central control over production, we produce hosts of things that are not wanted. We keep a large percentage of the working population idle, because we can dispense with their labor by making the others overwork. When all these methods prove inadequate, we have a war: we cause a number of people to manufacture high explosives, and a number of others to explode them, as if we were children who had just discovered fireworks. By a combination of all these devices we manage, though with difficulty, to keep alive the notion that a great deal of severe manual work must be the lot of the average man. + +In Russia, owing to more economic justice and central control over production, the problem will have to be differently solved. the rational solution would be, as soon as the necessaries and elementary comforts can be provided for all, to reduce the hours of labor gradually, allowing a popular vote to decide, at each stage, whether more leisure or more goods were to be preferred. But, having taught the supreme virtue of hard work, it is difficult to see how the authorities can aim at a paradise in which there will be much leisure and little work. It seems more likely that they will find continually fresh schemes, by which present leisure is to be sacrificed to future productivity. I read recently of an ingenious plan put forward by Russian engineers, for making the White Sea and the northern coasts of Siberia warm, by putting a dam across the Kara Sea. An admirable project, but liable to postpone proletarian comfort for a generation, while the nobility of toil is being displayed amid the ice-fields and snowstorms of the Arctic Ocean. This sort of thing, if it happens, will be the result of regarding the virtue of hard work as an end in itself, rather than as a means to a state of affairs in which it is no longer needed. + +The fact is that moving matter about, while a certain amount of it is necessary to our existence, is emphatically not one of the ends of human life. If it were, we should have to consider every navvy superior to Shakespeare. We have been misled in this matter by two causes. One is the necessity of keeping the poor contented, which has led the rich, for thousands of years, to preach the dignity of labor, while taking care themselves to remain undignified in this respect. The other is the new pleasure in mechanism, which makes us delight in the astonishingly clever changes that we can produce on the earth's surface. Neither of these motives makes any great appeal to the actual worker. If you ask him what he thinks the best part of his life, he is not likely to say: 'I enjoy manual work because it makes me feel that I am fulfilling man's noblest task, and because I like to think how much man can transform his planet. It is true that my body demands periods of rest, which I have to fill in as best I may, but I am never so happy as when the morning comes and I can return to the toil from which my contentment springs.' I have never heard working men say this sort of thing. They consider work, as it should be considered, a necessary means to a livelihood, and it is from their leisure that they derive whatever happiness they may enjoy. + +It will be said that, while a little leisure is pleasant, men would not know how to fill their days if they had only four hours of work out of the twenty-four. In so far as this is true in the modern world, it is a condemnation of our civilization; it would not have been true at any earlier period. There was formerly a capacity for light-heartedness and play which has been to some extent inhibited by the cult of efficiency. The modern man thinks that everything ought to be done for the sake of something else, and never for its own sake. Serious-minded persons, for example, are continually condemning the habit of going to the cinema, and telling us that it leads the young into crime. But all the work that goes to producing a cinema is respectable, because it is work, and because it brings a money profit. The notion that the desirable activities are those that bring a profit has made everything topsy-turvy. The butcher who provides you with meat and the baker who provides you with bread are praiseworthy, because they are making money; but when you enjoy the food they have provided, you are merely frivolous, unless you eat only to get strength for your work. Broadly speaking, it is held that getting money is good and spending money is bad. Seeing that they are two sides of one transaction, this is absurd; one might as well maintain that keys are good, but keyholes are bad. Whatever merit there may be in the production of goods must be entirely derivative from the advantage to be obtained by consuming them. The individual, in our society, works for profit; but the social purpose of his work lies in the consumption of what he produces. It is this divorce between the individual and the social purpose of production that makes it so difficult for men to think clearly in a world in which profit-making is the incentive to industry. We think too much of production, and too little of consumption. One result is that we attach too little importance to enjoyment and simple happiness, and that we do not judge production by the pleasure that it gives to the consumer. + +When I suggest that working hours should be reduced to four, I am not meaning to imply that all the remaining time should necessarily be spent in pure frivolity. I mean that four hours' work a day should entitle a man to the necessities and elementary comforts of life, and that the rest of his time should be his to use as he might see fit. It is an essential part of any such social system that education should be carried further than it usually is at present, and should aim, in part, at providing tastes which would enable a man to use leisure intelligently. I am not thinking mainly of the sort of things that would be considered 'highbrow'. Peasant dances have died out except in remote rural areas, but the impulses which caused them to be cultivated must still exist in human nature. The pleasures of urban populations have become mainly passive: seeing cinemas, watching football matches, listening to the radio, and so on. This results from the fact that their active energies are fully taken up with work; if they had more leisure, they would again enjoy pleasures in which they took an active part. + +In the past, there was a small leisure class and a larger working class. The leisure class enjoyed advantages for which there was no basis in social justice; this necessarily made it oppressive, limited its sympathies, and caused it to invent theories by which to justify its privileges. These facts greatly diminished its excellence, but in spite of this drawback it contributed nearly the whole of what we call civilization. It cultivated the arts and discovered the sciences; it wrote the books, invented the philosophies, and refined social relations. Even the liberation of the oppressed has usually been inaugurated from above. Without the leisure class, mankind would never have emerged from barbarism. + +The method of a leisure class without duties was, however, extraordinarily wasteful. None of the members of the class had to be taught to be industrious, and the class as a whole was not exceptionally intelligent. The class might produce one Darwin, but against him had to be set tens of thousands of country gentlemen who never thought of anything more intelligent than fox-hunting and punishing poachers. At present, the universities are supposed to provide, in a more systematic way, what the leisure class provided accidentally and as a by-product. This is a great improvement, but it has certain drawbacks. University life is so different from life in the world at large that men who live in academic milieu tend to be unaware of the preoccupations and problems of ordinary men and women; moreover their ways of expressing themselves are usually such as to rob their opinions of the influence that they ought to have upon the general public. Another disadvantage is that in universities studies are organized, and the man who thinks of some original line of research is likely to be discouraged. Academic institutions, therefore, useful as they are, are not adequate guardians of the interests of civilization in a world where everyone outside their walls is too busy for unutilitarian pursuits. + +In a world where no one is compelled to work more than four hours a day, every person possessed of scientific curiosity will be able to indulge it, and every painter will be able to paint without starving, however excellent his pictures may be. Young writers will not be obliged to draw attention to themselves by sensational pot-boilers, with a view to acquiring the economic independence needed for monumental works, for which, when the time at last comes, they will have lost the taste and capacity. Men who, in their professional work, have become interested in some phase of economics or government, will be able to develop their ideas without the academic detachment that makes the work of university economists often seem lacking in reality. Medical men will have the time to learn about the progress of medicine, teachers will not be exasperatedly struggling to teach by routine methods things which they learnt in their youth, which may, in the interval, have been proved to be untrue. + +Above all, there will be happiness and joy of life, instead of frayed nerves, weariness, and dyspepsia. The work exacted will be enough to make leisure delightful, but not enough to produce exhaustion. Since men will not be tired in their spare time, they will not demand only such amusements as are passive and vapid. At least one per cent will probably devote the time not spent in professional work to pursuits of some public importance, and, since they will not depend upon these pursuits for their livelihood, their originality will be unhampered, and there will be no need to conform to the standards set by elderly pundits. But it is not only in these exceptional cases that the advantages of leisure will appear. Ordinary men and women, having the opportunity of a happy life, will become more kindly and less persecuting and less inclined to view others with suspicion. The taste for war will die out, partly for this reason, and partly because it will involve long and severe work for all. Good nature is, of all moral qualities, the one that the world needs most, and good nature is the result of ease and security, not of a life of arduous struggle. Modern methods of production have given us the possibility of ease and security for all; we have chosen, instead, to have overwork for some and starvation for others. Hitherto we have continued to be as energetic as we were before there were machines; in this we have been foolish, but there is no reason to go on being foolish forever. + +[1] Since then, members of the Communist Party have succeeded to this privilege of the warriors and priests. diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/wrap.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/wrap.js new file mode 100644 index 0000000..0cfb76d --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/node_modules/wordwrap/test/wrap.js @@ -0,0 +1,31 @@ +var assert = require('assert'); +var wordwrap = require('wordwrap'); + +var fs = require('fs'); +var idleness = fs.readFileSync(__dirname + '/idleness.txt', 'utf8'); + +exports.stop80 = function () { + var lines = wordwrap(80)(idleness).split(/\n/); + var words = idleness.split(/\s+/); + + lines.forEach(function (line) { + assert.ok(line.length <= 80, 'line > 80 columns'); + var chunks = line.match(/\S/) ? line.split(/\s+/) : []; + assert.deepEqual(chunks, words.splice(0, chunks.length)); + }); +}; + +exports.start20stop60 = function () { + var lines = wordwrap(20, 100)(idleness).split(/\n/); + var words = idleness.split(/\s+/); + + lines.forEach(function (line) { + assert.ok(line.length <= 100, 'line > 100 columns'); + var chunks = line + .split(/\s+/) + .filter(function (x) { return x.match(/\S/) }) + ; + assert.deepEqual(chunks, words.splice(0, chunks.length)); + assert.deepEqual(line.slice(0, 20), new Array(20 + 1).join(' ')); + }); +}; diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/package.json b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/package.json new file mode 100644 index 0000000..5bac328 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/package.json @@ -0,0 +1,43 @@ +{ + "name" : "optimist", + "version" : "0.3.5", + "description" : "Light-weight option parsing with an argv hash. No optstrings attached.", + "main" : "./index.js", + "directories" : { + "lib" : ".", + "test" : "test", + "example" : "example" + }, + "dependencies" : { + "wordwrap" : "~0.0.2" + }, + "devDependencies" : { + "hashish": "~0.0.4", + "tap" : "~0.2.4" + }, + "scripts" : { + "test" : "tap ./test/*.js" + }, + "repository" : { + "type" : "git", + "url" : "http://github.com/substack/node-optimist.git" + }, + "keywords" : [ + "argument", + "args", + "option", + "parser", + "parsing", + "cli", + "command" + ], + "author" : { + "name" : "James Halliday", + "email" : "mail@substack.net", + "url" : "http://substack.net" + }, + "license" : "MIT/X11", + "engine" : { + "node" : ">=0.4" + } +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_.js new file mode 100644 index 0000000..d9c58b3 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_.js @@ -0,0 +1,71 @@ +var spawn = require('child_process').spawn; +var test = require('tap').test; + +test('dotSlashEmpty', testCmd('./bin.js', [])); + +test('dotSlashArgs', testCmd('./bin.js', [ 'a', 'b', 'c' ])); + +test('nodeEmpty', testCmd('node bin.js', [])); + +test('nodeArgs', testCmd('node bin.js', [ 'x', 'y', 'z' ])); + +test('whichNodeEmpty', function (t) { + var which = spawn('which', ['node']); + + which.stdout.on('data', function (buf) { + t.test( + testCmd(buf.toString().trim() + ' bin.js', []) + ); + t.end(); + }); + + which.stderr.on('data', function (err) { + assert.error(err); + t.end(); + }); +}); + +test('whichNodeArgs', function (t) { + var which = spawn('which', ['node']); + + which.stdout.on('data', function (buf) { + t.test( + testCmd(buf.toString().trim() + ' bin.js', [ 'q', 'r' ]) + ); + t.end(); + }); + + which.stderr.on('data', function (err) { + t.error(err); + t.end(); + }); +}); + +function testCmd (cmd, args) { + + return function (t) { + var to = setTimeout(function () { + assert.fail('Never got stdout data.') + }, 5000); + + var oldDir = process.cwd(); + process.chdir(__dirname + '/_'); + + var cmds = cmd.split(' '); + + var bin = spawn(cmds[0], cmds.slice(1).concat(args.map(String))); + process.chdir(oldDir); + + bin.stderr.on('data', function (err) { + t.error(err); + t.end(); + }); + + bin.stdout.on('data', function (buf) { + clearTimeout(to); + var _ = JSON.parse(buf.toString()); + t.same(_.map(String), args.map(String)); + t.end(); + }); + }; +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_/argv.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_/argv.js new file mode 100644 index 0000000..3d09606 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_/argv.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +console.log(JSON.stringify(process.argv)); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_/bin.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_/bin.js new file mode 100755 index 0000000..4a18d85 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/_/bin.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +var argv = require('../../index').argv +console.log(JSON.stringify(argv._)); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/parse.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/parse.js new file mode 100644 index 0000000..a6ce9f1 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/parse.js @@ -0,0 +1,433 @@ +var optimist = require('../index'); +var path = require('path'); +var test = require('tap').test; + +var $0 = 'node ./' + path.relative(process.cwd(), __filename); + +test('short boolean', function (t) { + var parse = optimist.parse([ '-b' ]); + t.same(parse, { b : true, _ : [], $0 : $0 }); + t.same(typeof parse.b, 'boolean'); + t.end(); +}); + +test('long boolean', function (t) { + t.same( + optimist.parse([ '--bool' ]), + { bool : true, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('bare', function (t) { + t.same( + optimist.parse([ 'foo', 'bar', 'baz' ]), + { _ : [ 'foo', 'bar', 'baz' ], $0 : $0 } + ); + t.end(); +}); + +test('short group', function (t) { + t.same( + optimist.parse([ '-cats' ]), + { c : true, a : true, t : true, s : true, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('short group next', function (t) { + t.same( + optimist.parse([ '-cats', 'meow' ]), + { c : true, a : true, t : true, s : 'meow', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('short capture', function (t) { + t.same( + optimist.parse([ '-h', 'localhost' ]), + { h : 'localhost', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('short captures', function (t) { + t.same( + optimist.parse([ '-h', 'localhost', '-p', '555' ]), + { h : 'localhost', p : 555, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('long capture sp', function (t) { + t.same( + optimist.parse([ '--pow', 'xixxle' ]), + { pow : 'xixxle', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('long capture eq', function (t) { + t.same( + optimist.parse([ '--pow=xixxle' ]), + { pow : 'xixxle', _ : [], $0 : $0 } + ); + t.end() +}); + +test('long captures sp', function (t) { + t.same( + optimist.parse([ '--host', 'localhost', '--port', '555' ]), + { host : 'localhost', port : 555, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('long captures eq', function (t) { + t.same( + optimist.parse([ '--host=localhost', '--port=555' ]), + { host : 'localhost', port : 555, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('mixed short bool and capture', function (t) { + t.same( + optimist.parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ], $0 : $0, + } + ); + t.end(); +}); + +test('short and long', function (t) { + t.same( + optimist.parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ], $0 : $0, + } + ); + t.end(); +}); + +test('no', function (t) { + t.same( + optimist.parse([ '--no-moo' ]), + { moo : false, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('multi', function (t) { + t.same( + optimist.parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]), + { v : ['a','b','c'], _ : [], $0 : $0 } + ); + t.end(); +}); + +test('comprehensive', function (t) { + t.same( + optimist.parse([ + '--name=meowmers', 'bare', '-cats', 'woo', + '-h', 'awesome', '--multi=quux', + '--key', 'value', + '-b', '--bool', '--no-meep', '--multi=baz', + '--', '--not-a-flag', 'eek' + ]), + { + c : true, + a : true, + t : true, + s : 'woo', + h : 'awesome', + b : true, + bool : true, + key : 'value', + multi : [ 'quux', 'baz' ], + meep : false, + name : 'meowmers', + _ : [ 'bare', '--not-a-flag', 'eek' ], + $0 : $0 + } + ); + t.end(); +}); + +test('nums', function (t) { + var argv = optimist.parse([ + '-x', '1234', + '-y', '5.67', + '-z', '1e7', + '-w', '10f', + '--hex', '0xdeadbeef', + '789', + ]); + t.same(argv, { + x : 1234, + y : 5.67, + z : 1e7, + w : '10f', + hex : 0xdeadbeef, + _ : [ 789 ], + $0 : $0 + }); + t.same(typeof argv.x, 'number'); + t.same(typeof argv.y, 'number'); + t.same(typeof argv.z, 'number'); + t.same(typeof argv.w, 'string'); + t.same(typeof argv.hex, 'number'); + t.same(typeof argv._[0], 'number'); + t.end(); +}); + +test('flag boolean', function (t) { + var parse = optimist([ '-t', 'moo' ]).boolean(['t']).argv; + t.same(parse, { t : true, _ : [ 'moo' ], $0 : $0 }); + t.same(typeof parse.t, 'boolean'); + t.end(); +}); + +test('flag boolean value', function (t) { + var parse = optimist(['--verbose', 'false', 'moo', '-t', 'true']) + .boolean(['t', 'verbose']).default('verbose', true).argv; + + t.same(parse, { + verbose: false, + t: true, + _: ['moo'], + $0 : $0 + }); + + t.same(typeof parse.verbose, 'boolean'); + t.same(typeof parse.t, 'boolean'); + t.end(); +}); + +test('flag boolean default false', function (t) { + var parse = optimist(['moo']) + .boolean(['t', 'verbose']) + .default('verbose', false) + .default('t', false).argv; + + t.same(parse, { + verbose: false, + t: false, + _: ['moo'], + $0 : $0 + }); + + t.same(typeof parse.verbose, 'boolean'); + t.same(typeof parse.t, 'boolean'); + t.end(); + +}); + +test('boolean groups', function (t) { + var parse = optimist([ '-x', '-z', 'one', 'two', 'three' ]) + .boolean(['x','y','z']).argv; + + t.same(parse, { + x : true, + y : false, + z : true, + _ : [ 'one', 'two', 'three' ], + $0 : $0 + }); + + t.same(typeof parse.x, 'boolean'); + t.same(typeof parse.y, 'boolean'); + t.same(typeof parse.z, 'boolean'); + t.end(); +}); + +test('strings' , function (t) { + var s = optimist([ '-s', '0001234' ]).string('s').argv.s; + t.same(s, '0001234'); + t.same(typeof s, 'string'); + + var x = optimist([ '-x', '56' ]).string('x').argv.x; + t.same(x, '56'); + t.same(typeof x, 'string'); + t.end(); +}); + +test('stringArgs', function (t) { + var s = optimist([ ' ', ' ' ]).string('_').argv._; + t.same(s.length, 2); + t.same(typeof s[0], 'string'); + t.same(s[0], ' '); + t.same(typeof s[1], 'string'); + t.same(s[1], ' '); + t.end(); +}); + +test('slashBreak', function (t) { + t.same( + optimist.parse([ '-I/foo/bar/baz' ]), + { I : '/foo/bar/baz', _ : [], $0 : $0 } + ); + t.same( + optimist.parse([ '-xyz/foo/bar/baz' ]), + { x : true, y : true, z : '/foo/bar/baz', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('alias', function (t) { + var argv = optimist([ '-f', '11', '--zoom', '55' ]) + .alias('z', 'zoom') + .argv + ; + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.f, 11); + t.end(); +}); + +test('multiAlias', function (t) { + var argv = optimist([ '-f', '11', '--zoom', '55' ]) + .alias('z', [ 'zm', 'zoom' ]) + .argv + ; + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.z, argv.zm); + t.equal(argv.f, 11); + t.end(); +}); + +test('boolean default true', function (t) { + var argv = optimist.options({ + sometrue: { + boolean: true, + default: true + } + }).argv; + + t.equal(argv.sometrue, true); + t.end(); +}); + +test('boolean default false', function (t) { + var argv = optimist.options({ + somefalse: { + boolean: true, + default: false + } + }).argv; + + t.equal(argv.somefalse, false); + t.end(); +}); + +test('nested dotted objects', function (t) { + var argv = optimist([ + '--foo.bar', '3', '--foo.baz', '4', + '--foo.quux.quibble', '5', '--foo.quux.o_O', + '--beep.boop' + ]).argv; + + t.same(argv.foo, { + bar : 3, + baz : 4, + quux : { + quibble : 5, + o_O : true + }, + }); + t.same(argv.beep, { boop : true }); + t.end(); +}); + +test('boolean and alias with chainable api', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = optimist(aliased) + .boolean('herp') + .alias('h', 'herp') + .argv; + var propertyArgv = optimist(regular) + .boolean('herp') + .alias('h', 'herp') + .argv; + var expected = { + herp: true, + h: true, + '_': [ 'derp' ], + '$0': $0, + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias with options hash', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = optimist(aliased) + .options(opts) + .argv; + var propertyArgv = optimist(regular).options(opts).argv; + var expected = { + herp: true, + h: true, + '_': [ 'derp' ], + '$0': $0, + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + + t.end(); +}); + +test('boolean and alias using explicit true', function (t) { + var aliased = [ '-h', 'true' ]; + var regular = [ '--herp', 'true' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = optimist(aliased) + .boolean('h') + .alias('h', 'herp') + .argv; + var propertyArgv = optimist(regular) + .boolean('h') + .alias('h', 'herp') + .argv; + var expected = { + herp: true, + h: true, + '_': [ ], + '$0': $0, + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +// regression, see https://github.com/substack/node-optimist/issues/71 +test('boolean and --x=true', function(t) { + var parsed = optimist(['--boool', '--other=true']).boolean('boool').argv; + + t.same(parsed.boool, true); + t.same(parsed.other, 'true'); + + parsed = optimist(['--boool', '--other=false']).boolean('boool').argv; + + t.same(parsed.boool, true); + t.same(parsed.other, 'false'); + t.end(); +}); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/usage.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/usage.js new file mode 100644 index 0000000..300454c --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/test/usage.js @@ -0,0 +1,292 @@ +var Hash = require('hashish'); +var optimist = require('../index'); +var test = require('tap').test; + +test('usageFail', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -z 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .demand(['x','y']) + .argv; + }); + t.same( + r.result, + { x : 10, z : 20, _ : [], $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/), + [ + 'Usage: ./usage -x NUM -y NUM', + 'Options:', + ' -x [required]', + ' -y [required]', + 'Missing required arguments: y', + ] + ); + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + + +test('usagePass', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -y 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .demand(['x','y']) + .argv; + }); + t.same(r, { + result : { x : 10, y : 20, _ : [], $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('checkPass', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -y 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(function (argv) { + if (!('x' in argv)) throw 'You forgot about -x'; + if (!('y' in argv)) throw 'You forgot about -y'; + }) + .argv; + }); + t.same(r, { + result : { x : 10, y : 20, _ : [], $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('checkFail', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -z 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(function (argv) { + if (!('x' in argv)) throw 'You forgot about -x'; + if (!('y' in argv)) throw 'You forgot about -y'; + }) + .argv; + }); + + t.same( + r.result, + { x : 10, z : 20, _ : [], $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/), + [ + 'Usage: ./usage -x NUM -y NUM', + 'You forgot about -y' + ] + ); + + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + +test('checkCondPass', function (t) { + function checker (argv) { + return 'x' in argv && 'y' in argv; + } + + var r = checkUsage(function () { + return optimist('-x 10 -y 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(checker) + .argv; + }); + t.same(r, { + result : { x : 10, y : 20, _ : [], $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('checkCondFail', function (t) { + function checker (argv) { + return 'x' in argv && 'y' in argv; + } + + var r = checkUsage(function () { + return optimist('-x 10 -z 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(checker) + .argv; + }); + + t.same( + r.result, + { x : 10, z : 20, _ : [], $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/).join('\n'), + 'Usage: ./usage -x NUM -y NUM\n' + + 'Argument check failed: ' + checker.toString() + ); + + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + +test('countPass', function (t) { + var r = checkUsage(function () { + return optimist('1 2 3 --moo'.split(' ')) + .usage('Usage: $0 [x] [y] [z] {OPTIONS}') + .demand(3) + .argv; + }); + t.same(r, { + result : { _ : [ '1', '2', '3' ], moo : true, $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('countFail', function (t) { + var r = checkUsage(function () { + return optimist('1 2 --moo'.split(' ')) + .usage('Usage: $0 [x] [y] [z] {OPTIONS}') + .demand(3) + .argv; + }); + t.same( + r.result, + { _ : [ '1', '2' ], moo : true, $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/), + [ + 'Usage: ./usage [x] [y] [z] {OPTIONS}', + 'Not enough non-option arguments: got 2, need at least 3', + ] + ); + + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + +test('defaultSingles', function (t) { + var r = checkUsage(function () { + return optimist('--foo 50 --baz 70 --powsy'.split(' ')) + .default('foo', 5) + .default('bar', 6) + .default('baz', 7) + .argv + ; + }); + t.same(r.result, { + foo : '50', + bar : 6, + baz : '70', + powsy : true, + _ : [], + $0 : './usage', + }); + t.end(); +}); + +test('defaultAliases', function (t) { + var r = checkUsage(function () { + return optimist('') + .alias('f', 'foo') + .default('f', 5) + .argv + ; + }); + t.same(r.result, { + f : '5', + foo : '5', + _ : [], + $0 : './usage', + }); + t.end(); +}); + +test('defaultHash', function (t) { + var r = checkUsage(function () { + return optimist('--foo 50 --baz 70'.split(' ')) + .default({ foo : 10, bar : 20, quux : 30 }) + .argv + ; + }); + t.same(r.result, { + _ : [], + $0 : './usage', + foo : 50, + baz : 70, + bar : 20, + quux : 30, + }); + t.end(); +}); + +test('rebase', function (t) { + t.equal( + optimist.rebase('/home/substack', '/home/substack/foo/bar/baz'), + './foo/bar/baz' + ); + t.equal( + optimist.rebase('/home/substack/foo/bar/baz', '/home/substack'), + '../../..' + ); + t.equal( + optimist.rebase('/home/substack/foo', '/home/substack/pow/zoom.txt'), + '../pow/zoom.txt' + ); + t.end(); +}); + +function checkUsage (f) { + + var exit = false; + + process._exit = process.exit; + process._env = process.env; + process._argv = process.argv; + + process.exit = function (t) { exit = true }; + process.env = Hash.merge(process.env, { _ : 'node' }); + process.argv = [ './usage' ]; + + var errors = []; + var logs = []; + + console._error = console.error; + console.error = function (msg) { errors.push(msg) }; + console._log = console.log; + console.log = function (msg) { logs.push(msg) }; + + var result = f(); + + process.exit = process._exit; + process.env = process._env; + process.argv = process._argv; + + console.error = console._error; + console.log = console._log; + + return { + errors : errors, + logs : logs, + exit : exit, + result : result, + }; +}; diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/x.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/x.js new file mode 100644 index 0000000..6c006d0 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/optimist/x.js @@ -0,0 +1 @@ +console.dir(require('./').argv); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/.npmignore b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/.npmignore new file mode 100644 index 0000000..d97eaa0 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/.npmignore @@ -0,0 +1,4 @@ +.DS_Store +.tmp*~ +*.local.* +.pinf-* \ No newline at end of file diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/README.html b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/README.html new file mode 100644 index 0000000..5f37ac0 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/README.html @@ -0,0 +1,981 @@ + + + + +UglifyJS – a JavaScript parser/compressor/beautifier + + + + + + + + + + + + + +
    + +
    + +
    +

    UglifyJS – a JavaScript parser/compressor/beautifier

    + + + + +
    +

    1 UglifyJS — a JavaScript parser/compressor/beautifier

    +
    + + +

    +This package implements a general-purpose JavaScript +parser/compressor/beautifier toolkit. It is developed on NodeJS, but it +should work on any JavaScript platform supporting the CommonJS module system +(and if your platform of choice doesn't support CommonJS, you can easily +implement it, or discard the exports.* lines from UglifyJS sources). +

    +

    +The tokenizer/parser generates an abstract syntax tree from JS code. You +can then traverse the AST to learn more about the code, or do various +manipulations on it. This part is implemented in parse-js.js and it's a +port to JavaScript of the excellent parse-js Common Lisp library from Marijn Haverbeke. +

    +

    +( See cl-uglify-js if you're looking for the Common Lisp version of +UglifyJS. ) +

    +

    +The second part of this package, implemented in process.js, inspects and +manipulates the AST generated by the parser to provide the following: +

    +
      +
    • ability to re-generate JavaScript code from the AST. Optionally + indented—you can use this if you want to “beautify” a program that has + been compressed, so that you can inspect the source. But you can also run + our code generator to print out an AST without any whitespace, so you + achieve compression as well. + +
    • +
    • shorten variable names (usually to single characters). Our mangler will + analyze the code and generate proper variable names, depending on scope + and usage, and is smart enough to deal with globals defined elsewhere, or + with eval() calls or with{} statements. In short, if eval() or + with{} are used in some scope, then all variables in that scope and any + variables in the parent scopes will remain unmangled, and any references + to such variables remain unmangled as well. + +
    • +
    • various small optimizations that may lead to faster code but certainly + lead to smaller code. Where possible, we do the following: + +
        +
      • foo["bar"] ==> foo.bar + +
      • +
      • remove block brackets {} + +
      • +
      • join consecutive var declarations: + var a = 10; var b = 20; ==> var a=10,b=20; + +
      • +
      • resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the + replacement if the result occupies less bytes; for example 1/3 would + translate to 0.333333333333, so in this case we don't replace it. + +
      • +
      • consecutive statements in blocks are merged into a sequence; in many + cases, this leaves blocks with a single statement, so then we can remove + the block brackets. + +
      • +
      • various optimizations for IF statements: + +
          +
        • if (foo) bar(); else baz(); ==> foo?bar():baz(); +
        • +
        • if (!foo) bar(); else baz(); ==> foo?baz():bar(); +
        • +
        • if (foo) bar(); ==> foo&&bar(); +
        • +
        • if (!foo) bar(); ==> foo||bar(); +
        • +
        • if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); +
        • +
        • if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} + +
        • +
        + +
      • +
      • remove some unreachable code and warn about it (code that follows a + return, throw, break or continue statement, except + function/variable declarations). + +
      • +
      • act a limited version of a pre-processor (c.f. the pre-processor of + C/C++) to allow you to safely replace selected global symbols with + specified values. When combined with the optimisations above this can + make UglifyJS operate slightly more like a compilation process, in + that when certain symbols are replaced by constant values, entire code + blocks may be optimised away as unreachable. +
      • +
      + +
    • +
    + + + +
    + +
    +

    1.1 Unsafe transformations

    +
    + + +

    +The following transformations can in theory break code, although they're +probably safe in most practical cases. To enable them you need to pass the +--unsafe flag. +

    + +
    + +
    +

    1.1.1 Calls involving the global Array constructor

    +
    + + +

    +The following transformations occur: +

    + + + +
    new Array(1, 2, 3, 4)  => [1,2,3,4]
    +Array(a, b, c)         => [a,b,c]
    +new Array(5)           => Array(5)
    +new Array(a)           => Array(a)
    +
    + + +

    +These are all safe if the Array name isn't redefined. JavaScript does allow +one to globally redefine Array (and pretty much everything, in fact) but I +personally don't see why would anyone do that. +

    +

    +UglifyJS does handle the case where Array is redefined locally, or even +globally but with a function or var declaration. Therefore, in the +following cases UglifyJS doesn't touch calls or instantiations of Array: +

    + + + +
    // case 1.  globally declared variable
    +  var Array;
    +  new Array(1, 2, 3);
    +  Array(a, b);
    +
    +  // or (can be declared later)
    +  new Array(1, 2, 3);
    +  var Array;
    +
    +  // or (can be a function)
    +  new Array(1, 2, 3);
    +  function Array() { ... }
    +
    +// case 2.  declared in a function
    +  (function(){
    +    a = new Array(1, 2, 3);
    +    b = Array(5, 6);
    +    var Array;
    +  })();
    +
    +  // or
    +  (function(Array){
    +    return Array(5, 6, 7);
    +  })();
    +
    +  // or
    +  (function(){
    +    return new Array(1, 2, 3, 4);
    +    function Array() { ... }
    +  })();
    +
    +  // etc.
    +
    + + +
    + +
    + +
    +

    1.1.2 obj.toString() ==> obj+“”

    +
    + + +
    +
    + +
    + +
    +

    1.2 Install (NPM)

    +
    + + +

    +UglifyJS is now available through NPM — npm install uglify-js should do +the job. +

    +
    + +
    + +
    +

    1.3 Install latest code from GitHub

    +
    + + + + + +
    ## clone the repository
    +mkdir -p /where/you/wanna/put/it
    +cd /where/you/wanna/put/it
    +git clone git://github.com/mishoo/UglifyJS.git
    +
    +## make the module available to Node
    +mkdir -p ~/.node_libraries/
    +cd ~/.node_libraries/
    +ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js
    +
    +## and if you want the CLI script too:
    +mkdir -p ~/bin
    +cd ~/bin
    +ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs
    +  # (then add ~/bin to your $PATH if it's not there already)
    +
    + + +
    + +
    + +
    +

    1.4 Usage

    +
    + + +

    +There is a command-line tool that exposes the functionality of this library +for your shell-scripting needs: +

    + + + +
    uglifyjs [ options... ] [ filename ]
    +
    + + +

    +filename should be the last argument and should name the file from which +to read the JavaScript code. If you don't specify it, it will read code +from STDIN. +

    +

    +Supported options: +

    +
      +
    • -b or --beautify — output indented code; when passed, additional + options control the beautifier: + +
        +
      • -i N or --indent N — indentation level (number of spaces) + +
      • +
      • -q or --quote-keys — quote keys in literal objects (by default, + only keys that cannot be identifier names will be quotes). + +
      • +
      + +
    • +
    • --ascii — pass this argument to encode non-ASCII characters as + \uXXXX sequences. By default UglifyJS won't bother to do it and will + output Unicode characters instead. (the output is always encoded in UTF8, + but if you pass this option you'll only get ASCII). + +
    • +
    • -nm or --no-mangle — don't mangle names. + +
    • +
    • -nmf or --no-mangle-functions – in case you want to mangle variable + names, but not touch function names. + +
    • +
    • -ns or --no-squeeze — don't call ast_squeeze() (which does various + optimizations that result in smaller, less readable code). + +
    • +
    • -mt or --mangle-toplevel — mangle names in the toplevel scope too + (by default we don't do this). + +
    • +
    • --no-seqs — when ast_squeeze() is called (thus, unless you pass + --no-squeeze) it will reduce consecutive statements in blocks into a + sequence. For example, "a = 10; b = 20; foo();" will be written as + "a=10,b=20,foo();". In various occasions, this allows us to discard the + block brackets (since the block becomes a single statement). This is ON + by default because it seems safe and saves a few hundred bytes on some + libs that I tested it on, but pass --no-seqs to disable it. + +
    • +
    • --no-dead-code — by default, UglifyJS will remove code that is + obviously unreachable (code that follows a return, throw, break or + continue statement and is not a function/variable declaration). Pass + this option to disable this optimization. + +
    • +
    • -nc or --no-copyright — by default, uglifyjs will keep the initial + comment tokens in the generated code (assumed to be copyright information + etc.). If you pass this it will discard it. + +
    • +
    • -o filename or --output filename — put the result in filename. If + this isn't given, the result goes to standard output (or see next one). + +
    • +
    • --overwrite — if the code is read from a file (not from STDIN) and you + pass --overwrite then the output will be written in the same file. + +
    • +
    • --ast — pass this if you want to get the Abstract Syntax Tree instead + of JavaScript as output. Useful for debugging or learning more about the + internals. + +
    • +
    • -v or --verbose — output some notes on STDERR (for now just how long + each operation takes). + +
    • +
    • -d SYMBOL[=VALUE] or --define SYMBOL[=VALUE] — will replace + all instances of the specified symbol where used as an identifier + (except where symbol has properly declared by a var declaration or + use as function parameter or similar) with the specified value. This + argument may be specified multiple times to define multiple + symbols - if no value is specified the symbol will be replaced with + the value true, or you can specify a numeric value (such as + 1024), a quoted string value (such as ="object"= or + ='https://github.com'), or the name of another symbol or keyword (such as =null or document). + This allows you, for example, to assign meaningful names to key + constant values but discard the symbolic names in the uglified + version for brevity/efficiency, or when used wth care, allows + UglifyJS to operate as a form of conditional compilation + whereby defining appropriate values may, by dint of the constant + folding and dead code removal features above, remove entire + superfluous code blocks (e.g. completely remove instrumentation or + trace code for production use). + Where string values are being defined, the handling of quotes are + likely to be subject to the specifics of your command shell + environment, so you may need to experiment with quoting styles + depending on your platform, or you may find the option + --define-from-module more suitable for use. + +
    • +
    • -define-from-module SOMEMODULE — will load the named module (as + per the NodeJS require() function) and iterate all the exported + properties of the module defining them as symbol names to be defined + (as if by the --define option) per the name of each property + (i.e. without the module name prefix) and given the value of the + property. This is a much easier way to handle and document groups of + symbols to be defined rather than a large number of --define + options. + +
    • +
    • --unsafe — enable other additional optimizations that are known to be + unsafe in some contrived situations, but could still be generally useful. + For now only these: + +
        +
      • foo.toString() ==> foo+"" +
      • +
      • new Array(x,…) ==> [x,…] +
      • +
      • new Array(x) ==> Array(x) + +
      • +
      + +
    • +
    • --max-line-len (default 32K characters) — add a newline after around + 32K characters. I've seen both FF and Chrome croak when all the code was + on a single line of around 670K. Pass –max-line-len 0 to disable this + safety feature. + +
    • +
    • --reserved-names — some libraries rely on certain names to be used, as + pointed out in issue #92 and #81, so this option allow you to exclude such + names from the mangler. For example, to keep names require and $super + intact you'd specify –reserved-names "require,$super". + +
    • +
    • --inline-script – when you want to include the output literally in an + HTML <script> tag you can use this option to prevent </script from + showing up in the output. + +
    • +
    • --lift-vars – when you pass this, UglifyJS will apply the following + transformations (see the notes in API, ast_lift_variables): + +
        +
      • put all var declarations at the start of the scope +
      • +
      • make sure a variable is declared only once +
      • +
      • discard unused function arguments +
      • +
      • discard unused inner (named) functions +
      • +
      • finally, try to merge assignments into that one var declaration, if + possible. +
      • +
      + +
    • +
    + + + +
    + +
    +

    1.4.1 API

    +
    + + +

    +To use the library from JavaScript, you'd do the following (example for +NodeJS): +

    + + + +
    var jsp = require("uglify-js").parser;
    +var pro = require("uglify-js").uglify;
    +
    +var orig_code = "... JS code here";
    +var ast = jsp.parse(orig_code); // parse code and get the initial AST
    +ast = pro.ast_mangle(ast); // get a new AST with mangled names
    +ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
    +var final_code = pro.gen_code(ast); // compressed code here
    +
    + + +

    +The above performs the full compression that is possible right now. As you +can see, there are a sequence of steps which you can apply. For example if +you want compressed output but for some reason you don't want to mangle +variable names, you would simply skip the line that calls +pro.ast_mangle(ast). +

    +

    +Some of these functions take optional arguments. Here's a description: +

    +
      +
    • jsp.parse(code, strict_semicolons) – parses JS code and returns an AST. + strict_semicolons is optional and defaults to false. If you pass + true then the parser will throw an error when it expects a semicolon and + it doesn't find it. For most JS code you don't want that, but it's useful + if you want to strictly sanitize your code. + +
    • +
    • pro.ast_lift_variables(ast) – merge and move var declarations to the + scop of the scope; discard unused function arguments or variables; discard + unused (named) inner functions. It also tries to merge assignments + following the var declaration into it. + +

      + If your code is very hand-optimized concerning var declarations, this + lifting variable declarations might actually increase size. For me it + helps out. On jQuery it adds 865 bytes (243 after gzip). YMMV. Also + note that (since it's not enabled by default) this operation isn't yet + heavily tested (please report if you find issues!). +

      +

      + Note that although it might increase the image size (on jQuery it gains + 865 bytes, 243 after gzip) it's technically more correct: in certain + situations, dead code removal might drop variable declarations, which + would not happen if the variables are lifted in advance. +

      +

      + Here's an example of what it does: +

    • +
    + + + + + +
    function f(a, b, c, d, e) {
    +    var q;
    +    var w;
    +    w = 10;
    +    q = 20;
    +    for (var i = 1; i < 10; ++i) {
    +        var boo = foo(a);
    +    }
    +    for (var i = 0; i < 1; ++i) {
    +        var boo = bar(c);
    +    }
    +    function foo(){ ... }
    +    function bar(){ ... }
    +    function baz(){ ... }
    +}
    +
    +// transforms into ==>
    +
    +function f(a, b, c) {
    +    var i, boo, w = 10, q = 20;
    +    for (i = 1; i < 10; ++i) {
    +        boo = foo(a);
    +    }
    +    for (i = 0; i < 1; ++i) {
    +        boo = bar(c);
    +    }
    +    function foo() { ... }
    +    function bar() { ... }
    +}
    +
    + + +
      +
    • pro.ast_mangle(ast, options) – generates a new AST containing mangled + (compressed) variable and function names. It supports the following + options: + +
        +
      • toplevel – mangle toplevel names (by default we don't touch them). +
      • +
      • except – an array of names to exclude from compression. +
      • +
      • defines – an object with properties named after symbols to + replace (see the --define option for the script) and the values + representing the AST replacement value. + +
      • +
      + +
    • +
    • pro.ast_squeeze(ast, options) – employs further optimizations designed + to reduce the size of the code that gen_code would generate from the + AST. Returns a new AST. options can be a hash; the supported options + are: + +
        +
      • make_seqs (default true) which will cause consecutive statements in a + block to be merged using the "sequence" (comma) operator + +
      • +
      • dead_code (default true) which will remove unreachable code. + +
      • +
      + +
    • +
    • pro.gen_code(ast, options) – generates JS code from the AST. By + default it's minified, but using the options argument you can get nicely + formatted output. options is, well, optional :-) and if you pass it it + must be an object and supports the following properties (below you can see + the default values): + +
        +
      • beautify: false – pass true if you want indented output +
      • +
      • indent_start: 0 (only applies when beautify is true) – initial + indentation in spaces +
      • +
      • indent_level: 4 (only applies when beautify is true) -- + indentation level, in spaces (pass an even number) +
      • +
      • quote_keys: false – if you pass true it will quote all keys in + literal objects +
      • +
      • space_colon: false (only applies when beautify is true) – wether + to put a space before the colon in object literals +
      • +
      • ascii_only: false – pass true if you want to encode non-ASCII + characters as \uXXXX. +
      • +
      • inline_script: false – pass true to escape occurrences of + </script in strings +
      • +
      + +
    • +
    + + +
    + +
    + +
    +

    1.4.2 Beautifier shortcoming – no more comments

    +
    + + +

    +The beautifier can be used as a general purpose indentation tool. It's +useful when you want to make a minified file readable. One limitation, +though, is that it discards all comments, so you don't really want to use it +to reformat your code, unless you don't have, or don't care about, comments. +

    +

    +In fact it's not the beautifier who discards comments — they are dumped at +the parsing stage, when we build the initial AST. Comments don't really +make sense in the AST, and while we could add nodes for them, it would be +inconvenient because we'd have to add special rules to ignore them at all +the processing stages. +

    +
    + +
    + +
    +

    1.4.3 Use as a code pre-processor

    +
    + + +

    +The --define option can be used, particularly when combined with the +constant folding logic, as a form of pre-processor to enable or remove +particular constructions, such as might be used for instrumenting +development code, or to produce variations aimed at a specific +platform. +

    +

    +The code below illustrates the way this can be done, and how the +symbol replacement is performed. +

    + + + +
    CLAUSE1: if (typeof DEVMODE === 'undefined') {
    +    DEVMODE = true;
    +}
    +
    +CLAUSE2: function init() {
    +    if (DEVMODE) {
    +        console.log("init() called");
    +    }
    +    ....
    +    DEVMODE &amp;&amp; console.log("init() complete");
    +}
    +
    +CLAUSE3: function reportDeviceStatus(device) {
    +    var DEVMODE = device.mode, DEVNAME = device.name;
    +    if (DEVMODE === 'open') {
    +        ....
    +    }
    +}
    +
    + + +

    +When the above code is normally executed, the undeclared global +variable DEVMODE will be assigned the value true (see CLAUSE1) +and so the init() function (CLAUSE2) will write messages to the +console log when executed, but in CLAUSE3 a locally declared +variable will mask access to the DEVMODE global symbol. +

    +

    +If the above code is processed by UglifyJS with an argument of +--define DEVMODE=false then UglifyJS will replace DEVMODE with the +boolean constant value false within CLAUSE1 and CLAUSE2, but it +will leave CLAUSE3 as it stands because there DEVMODE resolves to +a validly declared variable. +

    +

    +And more so, the constant-folding features of UglifyJS will recognise +that the if condition of CLAUSE1 is thus always false, and so will +remove the test and body of CLAUSE1 altogether (including the +otherwise slightly problematical statement false = true; which it +will have formed by replacing DEVMODE in the body). Similarly, +within CLAUSE2 both calls to console.log() will be removed +altogether. +

    +

    +In this way you can mimic, to a limited degree, the functionality of +the C/C++ pre-processor to enable or completely remove blocks +depending on how certain symbols are defined - perhaps using UglifyJS +to generate different versions of source aimed at different +environments +

    +

    +It is recommmended (but not made mandatory) that symbols designed for +this purpose are given names consisting of UPPER_CASE_LETTERS to +distinguish them from other (normal) symbols and avoid the sort of +clash that CLAUSE3 above illustrates. +

    +
    +
    + +
    + +
    +

    1.5 Compression – how good is it?

    +
    + + +

    +Here are updated statistics. (I also updated my Google Closure and YUI +installations). +

    +

    +We're still a lot better than YUI in terms of compression, though slightly +slower. We're still a lot faster than Closure, and compression after gzip +is comparable. +

    + + ++ + + + + + + + + + +
    FileUglifyJSUglifyJS+gzipClosureClosure+gzipYUIYUI+gzip
    jquery-1.6.2.js91001 (0:01.59)3189690678 (0:07.40)31979101527 (0:01.82)34646
    paper.js142023 (0:01.65)43334134301 (0:07.42)42495173383 (0:01.58)48785
    prototype.js88544 (0:01.09)2668086955 (0:06.97)2632692130 (0:00.79)28624
    thelib-full.js (DynarchLIB)251939 (0:02.55)72535249911 (0:09.05)72696258869 (0:01.94)76584
    + + +
    + +
    + +
    +

    1.6 Bugs?

    +
    + + +

    +Unfortunately, for the time being there is no automated test suite. But I +ran the compressor manually on non-trivial code, and then I tested that the +generated code works as expected. A few hundred times. +

    +

    +DynarchLIB was started in times when there was no good JS minifier. +Therefore I was quite religious about trying to write short code manually, +and as such DL contains a lot of syntactic hacks1 such as “foo == bar ? a += 10 : b = 20”, though the more readable version would clearly be to use +“if/else”. +

    +

    +Since the parser/compressor runs fine on DL and jQuery, I'm quite confident +that it's solid enough for production use. If you can identify any bugs, +I'd love to hear about them (use the Google Group or email me directly). +

    +
    + +
    + +
    +

    1.7 Links

    +
    + + + + + +
    + +
    + +
    +

    1.8 License

    +
    + + +

    +UglifyJS is released under the BSD license: +

    + + + +
    Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
    +Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
    +
    +Redistribution and use in source and binary forms, with or without
    +modification, are permitted provided that the following conditions
    +are met:
    +
    +    * Redistributions of source code must retain the above
    +      copyright notice, this list of conditions and the following
    +      disclaimer.
    +
    +    * Redistributions in binary form must reproduce the above
    +      copyright notice, this list of conditions and the following
    +      disclaimer in the documentation and/or other materials
    +      provided with the distribution.
    +
    +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
    +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
    +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
    +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
    +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    +SUCH DAMAGE.
    +
    + + +
    +

    Footnotes:

    +
    +

    1 I even reported a few bugs and suggested some fixes in the original + parse-js library, and Marijn pushed fixes literally in minutes. +

    +
    +
    + +
    +
    +
    + +
    +

    Date: 2011-12-09 14:59:08 EET

    +

    Author: Mihai Bazon

    +

    Org version 7.7 with Emacs version 23

    +Validate XHTML 1.0 + +
    + + diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/README.org b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/README.org new file mode 100644 index 0000000..d36b6b2 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/README.org @@ -0,0 +1,578 @@ +#+TITLE: UglifyJS -- a JavaScript parser/compressor/beautifier +#+KEYWORDS: javascript, js, parser, compiler, compressor, mangle, minify, minifier +#+DESCRIPTION: a JavaScript parser/compressor/beautifier in JavaScript +#+STYLE: +#+AUTHOR: Mihai Bazon +#+EMAIL: mihai.bazon@gmail.com + +* UglifyJS --- a JavaScript parser/compressor/beautifier + +This package implements a general-purpose JavaScript +parser/compressor/beautifier toolkit. It is developed on [[http://nodejs.org/][NodeJS]], but it +should work on any JavaScript platform supporting the CommonJS module system +(and if your platform of choice doesn't support CommonJS, you can easily +implement it, or discard the =exports.*= lines from UglifyJS sources). + +The tokenizer/parser generates an abstract syntax tree from JS code. You +can then traverse the AST to learn more about the code, or do various +manipulations on it. This part is implemented in [[../lib/parse-js.js][parse-js.js]] and it's a +port to JavaScript of the excellent [[http://marijn.haverbeke.nl/parse-js/][parse-js]] Common Lisp library from [[http://marijn.haverbeke.nl/][Marijn +Haverbeke]]. + +( See [[http://github.com/mishoo/cl-uglify-js][cl-uglify-js]] if you're looking for the Common Lisp version of +UglifyJS. ) + +The second part of this package, implemented in [[../lib/process.js][process.js]], inspects and +manipulates the AST generated by the parser to provide the following: + +- ability to re-generate JavaScript code from the AST. Optionally + indented---you can use this if you want to “beautify” a program that has + been compressed, so that you can inspect the source. But you can also run + our code generator to print out an AST without any whitespace, so you + achieve compression as well. + +- shorten variable names (usually to single characters). Our mangler will + analyze the code and generate proper variable names, depending on scope + and usage, and is smart enough to deal with globals defined elsewhere, or + with =eval()= calls or =with{}= statements. In short, if =eval()= or + =with{}= are used in some scope, then all variables in that scope and any + variables in the parent scopes will remain unmangled, and any references + to such variables remain unmangled as well. + +- various small optimizations that may lead to faster code but certainly + lead to smaller code. Where possible, we do the following: + + - foo["bar"] ==> foo.bar + + - remove block brackets ={}= + + - join consecutive var declarations: + var a = 10; var b = 20; ==> var a=10,b=20; + + - resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the + replacement if the result occupies less bytes; for example 1/3 would + translate to 0.333333333333, so in this case we don't replace it. + + - consecutive statements in blocks are merged into a sequence; in many + cases, this leaves blocks with a single statement, so then we can remove + the block brackets. + + - various optimizations for IF statements: + + - if (foo) bar(); else baz(); ==> foo?bar():baz(); + - if (!foo) bar(); else baz(); ==> foo?baz():bar(); + - if (foo) bar(); ==> foo&&bar(); + - if (!foo) bar(); ==> foo||bar(); + - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); + - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} + + - remove some unreachable code and warn about it (code that follows a + =return=, =throw=, =break= or =continue= statement, except + function/variable declarations). + + - act a limited version of a pre-processor (c.f. the pre-processor of + C/C++) to allow you to safely replace selected global symbols with + specified values. When combined with the optimisations above this can + make UglifyJS operate slightly more like a compilation process, in + that when certain symbols are replaced by constant values, entire code + blocks may be optimised away as unreachable. + +** <> + +The following transformations can in theory break code, although they're +probably safe in most practical cases. To enable them you need to pass the +=--unsafe= flag. + +*** Calls involving the global Array constructor + +The following transformations occur: + +#+BEGIN_SRC js +new Array(1, 2, 3, 4) => [1,2,3,4] +Array(a, b, c) => [a,b,c] +new Array(5) => Array(5) +new Array(a) => Array(a) +#+END_SRC + +These are all safe if the Array name isn't redefined. JavaScript does allow +one to globally redefine Array (and pretty much everything, in fact) but I +personally don't see why would anyone do that. + +UglifyJS does handle the case where Array is redefined locally, or even +globally but with a =function= or =var= declaration. Therefore, in the +following cases UglifyJS *doesn't touch* calls or instantiations of Array: + +#+BEGIN_SRC js +// case 1. globally declared variable + var Array; + new Array(1, 2, 3); + Array(a, b); + + // or (can be declared later) + new Array(1, 2, 3); + var Array; + + // or (can be a function) + new Array(1, 2, 3); + function Array() { ... } + +// case 2. declared in a function + (function(){ + a = new Array(1, 2, 3); + b = Array(5, 6); + var Array; + })(); + + // or + (function(Array){ + return Array(5, 6, 7); + })(); + + // or + (function(){ + return new Array(1, 2, 3, 4); + function Array() { ... } + })(); + + // etc. +#+END_SRC + +*** =obj.toString()= ==> =obj+“”= + +** Install (NPM) + +UglifyJS is now available through NPM --- =npm install uglify-js= should do +the job. + +** Install latest code from GitHub + +#+BEGIN_SRC sh +## clone the repository +mkdir -p /where/you/wanna/put/it +cd /where/you/wanna/put/it +git clone git://github.com/mishoo/UglifyJS.git + +## make the module available to Node +mkdir -p ~/.node_libraries/ +cd ~/.node_libraries/ +ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js + +## and if you want the CLI script too: +mkdir -p ~/bin +cd ~/bin +ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs + # (then add ~/bin to your $PATH if it's not there already) +#+END_SRC + +** Usage + +There is a command-line tool that exposes the functionality of this library +for your shell-scripting needs: + +#+BEGIN_SRC sh +uglifyjs [ options... ] [ filename ] +#+END_SRC + +=filename= should be the last argument and should name the file from which +to read the JavaScript code. If you don't specify it, it will read code +from STDIN. + +Supported options: + +- =-b= or =--beautify= --- output indented code; when passed, additional + options control the beautifier: + + - =-i N= or =--indent N= --- indentation level (number of spaces) + + - =-q= or =--quote-keys= --- quote keys in literal objects (by default, + only keys that cannot be identifier names will be quotes). + +- =-c= or =----consolidate-primitive-values= --- consolidates null, Boolean, + and String values. Known as aliasing in the Closure Compiler. Worsens the + data compression ratio of gzip. + +- =--ascii= --- pass this argument to encode non-ASCII characters as + =\uXXXX= sequences. By default UglifyJS won't bother to do it and will + output Unicode characters instead. (the output is always encoded in UTF8, + but if you pass this option you'll only get ASCII). + +- =-nm= or =--no-mangle= --- don't mangle names. + +- =-nmf= or =--no-mangle-functions= -- in case you want to mangle variable + names, but not touch function names. + +- =-ns= or =--no-squeeze= --- don't call =ast_squeeze()= (which does various + optimizations that result in smaller, less readable code). + +- =-mt= or =--mangle-toplevel= --- mangle names in the toplevel scope too + (by default we don't do this). + +- =--no-seqs= --- when =ast_squeeze()= is called (thus, unless you pass + =--no-squeeze=) it will reduce consecutive statements in blocks into a + sequence. For example, "a = 10; b = 20; foo();" will be written as + "a=10,b=20,foo();". In various occasions, this allows us to discard the + block brackets (since the block becomes a single statement). This is ON + by default because it seems safe and saves a few hundred bytes on some + libs that I tested it on, but pass =--no-seqs= to disable it. + +- =--no-dead-code= --- by default, UglifyJS will remove code that is + obviously unreachable (code that follows a =return=, =throw=, =break= or + =continue= statement and is not a function/variable declaration). Pass + this option to disable this optimization. + +- =-nc= or =--no-copyright= --- by default, =uglifyjs= will keep the initial + comment tokens in the generated code (assumed to be copyright information + etc.). If you pass this it will discard it. + +- =-o filename= or =--output filename= --- put the result in =filename=. If + this isn't given, the result goes to standard output (or see next one). + +- =--overwrite= --- if the code is read from a file (not from STDIN) and you + pass =--overwrite= then the output will be written in the same file. + +- =--ast= --- pass this if you want to get the Abstract Syntax Tree instead + of JavaScript as output. Useful for debugging or learning more about the + internals. + +- =-v= or =--verbose= --- output some notes on STDERR (for now just how long + each operation takes). + +- =-d SYMBOL[=VALUE]= or =--define SYMBOL[=VALUE]= --- will replace + all instances of the specified symbol where used as an identifier + (except where symbol has properly declared by a var declaration or + use as function parameter or similar) with the specified value. This + argument may be specified multiple times to define multiple + symbols - if no value is specified the symbol will be replaced with + the value =true=, or you can specify a numeric value (such as + =1024=), a quoted string value (such as ="object"= or + ='https://github.com'=), or the name of another symbol or keyword + (such as =null= or =document=). + This allows you, for example, to assign meaningful names to key + constant values but discard the symbolic names in the uglified + version for brevity/efficiency, or when used wth care, allows + UglifyJS to operate as a form of *conditional compilation* + whereby defining appropriate values may, by dint of the constant + folding and dead code removal features above, remove entire + superfluous code blocks (e.g. completely remove instrumentation or + trace code for production use). + Where string values are being defined, the handling of quotes are + likely to be subject to the specifics of your command shell + environment, so you may need to experiment with quoting styles + depending on your platform, or you may find the option + =--define-from-module= more suitable for use. + +- =-define-from-module SOMEMODULE= --- will load the named module (as + per the NodeJS =require()= function) and iterate all the exported + properties of the module defining them as symbol names to be defined + (as if by the =--define= option) per the name of each property + (i.e. without the module name prefix) and given the value of the + property. This is a much easier way to handle and document groups of + symbols to be defined rather than a large number of =--define= + options. + +- =--unsafe= --- enable other additional optimizations that are known to be + unsafe in some contrived situations, but could still be generally useful. + For now only these: + + - foo.toString() ==> foo+"" + - new Array(x,...) ==> [x,...] + - new Array(x) ==> Array(x) + +- =--max-line-len= (default 32K characters) --- add a newline after around + 32K characters. I've seen both FF and Chrome croak when all the code was + on a single line of around 670K. Pass --max-line-len 0 to disable this + safety feature. + +- =--reserved-names= --- some libraries rely on certain names to be used, as + pointed out in issue #92 and #81, so this option allow you to exclude such + names from the mangler. For example, to keep names =require= and =$super= + intact you'd specify --reserved-names "require,$super". + +- =--inline-script= -- when you want to include the output literally in an + HTML = + + + */ + +(function() { + this.loggly = function(opts) { + this.user_agent = get_agent(); + this.browser_size = get_size(); + log_methods = {'error': 5, 'warn': 4, 'info': 3, 'debug': 2, 'log': 1}; + if (!opts.url) throw new Error("Please include a Loggly HTTP URL."); + if (!opts.level) { + this.level = log_methods['info']; + } else { + this.level = log_methods[opts.level]; + } + this.log = function(data) { + if (log_methods['log'] == this.level) { + opts.data = data; + janky(opts); + } + }; + this.debug = function(data) { + if (log_methods['debug'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.info = function(data) { + if (log_methods['info'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.warn = function(data) { + if (log_methods['warn'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.error = function(data) { + if (log_methods['error'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + }; + this.janky = function(opts) { + janky._form(function(iframe, form) { + form.setAttribute("action", opts.url); + form.setAttribute("method", "post"); + janky._input(iframe, form, opts.data); + form.submit(); + setTimeout(function(){ + document.body.removeChild(iframe); + }, 2000); + }); + }; + this.janky._form = function(cb) { + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.style.display = "none"; + setTimeout(function() { + var form = iframe.contentWindow.document.createElement("form"); + iframe.contentWindow.document.body.appendChild(form); + cb(iframe, form); + }, 0); + }; + this.janky._input = function(iframe, form, data) { + var inp = iframe.contentWindow.document.createElement("input"); + inp.setAttribute("type", "hidden"); + inp.setAttribute("name", "source"); + inp.value = "castor " + data; + form.appendChild(inp); + }; + this.get_agent = function () { + return navigator.appCodeName + navigator.appName + navigator.appVersion; + }; + this.get_size = function () { + var width = 0; var height = 0; + if( typeof( window.innerWidth ) == 'number' ) { + width = window.innerWidth; height = window.innerHeight; + } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { + width = document.documentElement.clientWidth; height = document.documentElement.clientHeight; + } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { + width = document.body.clientWidth; height = document.body.clientHeight; + } + return {'height': height, 'width': width}; + }; +})(); + + +jsworld={};jsworld.formatIsoDateTime=function(a,b){if(typeof a==="undefined")a=new Date;if(typeof b==="undefined")b=false;var c=jsworld.formatIsoDate(a)+" "+jsworld.formatIsoTime(a);if(b){var d=a.getHours()-a.getUTCHours();var e=Math.abs(d);var f=a.getUTCMinutes();var g=a.getMinutes();if(g!=f&&f<30&&d<0)e--;if(g!=f&&f>30&&d>0)e--;var h;if(g!=f)h=":30";else h=":00";var i;if(e<10)i="0"+e+h;else i=""+e+h;if(d<0)i="-"+i;else i="+"+i;c=c+i}return c};jsworld.formatIsoDate=function(a){if(typeof a==="undefined")a=new Date;var b=a.getFullYear();var c=a.getMonth()+1;var d=a.getDate();return b+"-"+jsworld._zeroPad(c,2)+"-"+jsworld._zeroPad(d,2)};jsworld.formatIsoTime=function(a){if(typeof a==="undefined")a=new Date;var b=a.getHours();var c=a.getMinutes();var d=a.getSeconds();return jsworld._zeroPad(b,2)+":"+jsworld._zeroPad(c,2)+":"+jsworld._zeroPad(d,2)};jsworld.parseIsoDateTime=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date/time string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);var f=parseInt(b[4],10);var g=parseInt(b[5],10);var h=parseInt(b[6],10);if(d<1||d>12||e<1||e>31||f<0||f>23||g<0||g>59||h<0||h>59)throw"Error: Invalid ISO-8601 date/time value";var i=new Date(c,d-1,e,f,g,h);if(i.getDate()!=e||i.getMonth()+1!=d)throw"Error: Invalid date";return i};jsworld.parseIsoDate=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)(\d\d)(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);if(d<1||d>12||e<1||e>31)throw"Error: Invalid ISO-8601 date value";var f=new Date(c,d-1,e);if(f.getDate()!=e||f.getMonth()+1!=d)throw"Error: Invalid date";return f};jsworld.parseIsoTime=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d):(\d\d):(\d\d)/);if(b===null)b=a.match(/^(\d\d)(\d\d)(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date/time string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);if(c<0||c>23||d<0||d>59||e<0||e>59)throw"Error: Invalid ISO-8601 time value";return new Date(0,0,0,c,d,e)};jsworld._trim=function(a){var b=" \n\r\t\f \u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000";for(var c=0;c=0;c--){if(b.indexOf(a.charAt(c))===-1){a=a.substring(0,c+1);break}}return b.indexOf(a.charAt(0))===-1?a:""};jsworld._isNumber=function(a){if(typeof a=="number")return true;if(typeof a!="string")return false;var b=a+"";return/^-?(\d+|\d*\.\d+)$/.test(b)};jsworld._isInteger=function(a){if(typeof a!="number"&&typeof a!="string")return false;var b=a+"";return/^-?\d+$/.test(b)};jsworld._isFloat=function(a){if(typeof a!="number"&&typeof a!="string")return false;var b=a+"";return/^-?\.\d+?$/.test(b)};jsworld._hasOption=function(a,b){if(typeof a!="string"||typeof b!="string")return false;if(b.indexOf(a)!=-1)return true;else return false};jsworld._stringReplaceAll=function(a,b,c){var d;if(b.length==1&&c.length==1){d="";for(var e=0;e0){if(d.length>0)g=parseInt(d.shift(),10);if(isNaN(g))throw"Error: Invalid grouping";if(g==-1){e=a.substring(0,f)+e;break}f-=g;if(f<1){e=a.substring(0,f+g)+e;break}e=c+a.substring(f,f+g)+e}return e};jsworld._formatFractionPart=function(a,b){for(var c=0;a.length0)return a;else throw"Empty or no string"};if(a==null||typeof a!="object")throw"Error: Invalid/missing locale properties";if(typeof a.decimal_point!="string")throw"Error: Invalid/missing decimal_point property";this.decimal_point=a.decimal_point;if(typeof a.thousands_sep!="string")throw"Error: Invalid/missing thousands_sep property";this.thousands_sep=a.thousands_sep;if(typeof a.grouping!="string")throw"Error: Invalid/missing grouping property";this.grouping=a.grouping;if(typeof a.int_curr_symbol!="string")throw"Error: Invalid/missing int_curr_symbol property";if(!/[A-Za-z]{3}.?/.test(a.int_curr_symbol))throw"Error: Invalid int_curr_symbol property";this.int_curr_symbol=a.int_curr_symbol;if(typeof a.currency_symbol!="string")throw"Error: Invalid/missing currency_symbol property";this.currency_symbol=a.currency_symbol;if(typeof a.frac_digits!="number"&&a.frac_digits<0)throw"Error: Invalid/missing frac_digits property";this.frac_digits=a.frac_digits;if(a.mon_decimal_point===null||a.mon_decimal_point==""){if(this.frac_digits>0)throw"Error: Undefined mon_decimal_point property";else a.mon_decimal_point=""}if(typeof a.mon_decimal_point!="string")throw"Error: Invalid/missing mon_decimal_point property";this.mon_decimal_point=a.mon_decimal_point;if(typeof a.mon_thousands_sep!="string")throw"Error: Invalid/missing mon_thousands_sep property";this.mon_thousands_sep=a.mon_thousands_sep;if(typeof a.mon_grouping!="string")throw"Error: Invalid/missing mon_grouping property";this.mon_grouping=a.mon_grouping;if(typeof a.positive_sign!="string")throw"Error: Invalid/missing positive_sign property";this.positive_sign=a.positive_sign;if(typeof a.negative_sign!="string")throw"Error: Invalid/missing negative_sign property";this.negative_sign=a.negative_sign;if(a.p_cs_precedes!==0&&a.p_cs_precedes!==1)throw"Error: Invalid/missing p_cs_precedes property, must be 0 or 1";this.p_cs_precedes=a.p_cs_precedes;if(a.n_cs_precedes!==0&&a.n_cs_precedes!==1)throw"Error: Invalid/missing n_cs_precedes, must be 0 or 1";this.n_cs_precedes=a.n_cs_precedes;if(a.p_sep_by_space!==0&&a.p_sep_by_space!==1&&a.p_sep_by_space!==2)throw"Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2";this.p_sep_by_space=a.p_sep_by_space;if(a.n_sep_by_space!==0&&a.n_sep_by_space!==1&&a.n_sep_by_space!==2)throw"Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2";this.n_sep_by_space=a.n_sep_by_space;if(a.p_sign_posn!==0&&a.p_sign_posn!==1&&a.p_sign_posn!==2&&a.p_sign_posn!==3&&a.p_sign_posn!==4)throw"Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4";this.p_sign_posn=a.p_sign_posn;if(a.n_sign_posn!==0&&a.n_sign_posn!==1&&a.n_sign_posn!==2&&a.n_sign_posn!==3&&a.n_sign_posn!==4)throw"Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4";this.n_sign_posn=a.n_sign_posn;if(typeof a.int_frac_digits!="number"&&a.int_frac_digits<0)throw"Error: Invalid/missing int_frac_digits property";this.int_frac_digits=a.int_frac_digits;if(a.int_p_cs_precedes!==0&&a.int_p_cs_precedes!==1)throw"Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1";this.int_p_cs_precedes=a.int_p_cs_precedes;if(a.int_n_cs_precedes!==0&&a.int_n_cs_precedes!==1)throw"Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1";this.int_n_cs_precedes=a.int_n_cs_precedes;if(a.int_p_sep_by_space!==0&&a.int_p_sep_by_space!==1&&a.int_p_sep_by_space!==2)throw"Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2";this.int_p_sep_by_space=a.int_p_sep_by_space;if(a.int_n_sep_by_space!==0&&a.int_n_sep_by_space!==1&&a.int_n_sep_by_space!==2)throw"Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2";this.int_n_sep_by_space=a.int_n_sep_by_space;if(a.int_p_sign_posn!==0&&a.int_p_sign_posn!==1&&a.int_p_sign_posn!==2&&a.int_p_sign_posn!==3&&a.int_p_sign_posn!==4)throw"Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4";this.int_p_sign_posn=a.int_p_sign_posn;if(a.int_n_sign_posn!==0&&a.int_n_sign_posn!==1&&a.int_n_sign_posn!==2&&a.int_n_sign_posn!==3&&a.int_n_sign_posn!==4)throw"Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4";this.int_n_sign_posn=a.int_n_sign_posn;if(a==null||typeof a!="object")throw"Error: Invalid/missing time locale properties";try{this.abday=this._parseList(a.abday,7)}catch(b){throw"Error: Invalid abday property: "+b}try{this.day=this._parseList(a.day,7)}catch(b){throw"Error: Invalid day property: "+b}try{this.abmon=this._parseList(a.abmon,12)}catch(b){throw"Error: Invalid abmon property: "+b}try{this.mon=this._parseList(a.mon,12)}catch(b){throw"Error: Invalid mon property: "+b}try{this.d_fmt=this._validateFormatString(a.d_fmt)}catch(b){throw"Error: Invalid d_fmt property: "+b}try{this.t_fmt=this._validateFormatString(a.t_fmt)}catch(b){throw"Error: Invalid t_fmt property: "+b}try{this.d_t_fmt=this._validateFormatString(a.d_t_fmt)}catch(b){throw"Error: Invalid d_t_fmt property: "+b}try{var c=this._parseList(a.am_pm,2);this.am=c[0];this.pm=c[1]}catch(b){this.am="";this.pm=""}this.getAbbreviatedWeekdayName=function(a){if(typeof a=="undefined"||a===null)return this.abday;if(!jsworld._isInteger(a)||a<0||a>6)throw"Error: Invalid weekday argument, must be an integer [0..6]";return this.abday[a]};this.getWeekdayName=function(a){if(typeof a=="undefined"||a===null)return this.day;if(!jsworld._isInteger(a)||a<0||a>6)throw"Error: Invalid weekday argument, must be an integer [0..6]";return this.day[a]};this.getAbbreviatedMonthName=function(a){if(typeof a=="undefined"||a===null)return this.abmon;if(!jsworld._isInteger(a)||a<0||a>11)throw"Error: Invalid month argument, must be an integer [0..11]";return this.abmon[a]};this.getMonthName=function(a){if(typeof a=="undefined"||a===null)return this.mon;if(!jsworld._isInteger(a)||a<0||a>11)throw"Error: Invalid month argument, must be an integer [0..11]";return this.mon[a]};this.getDecimalPoint=function(){return this.decimal_point};this.getCurrencySymbol=function(){return this.currency_symbol};this.getIntCurrencySymbol=function(){return this.int_curr_symbol.substring(0,3)};this.currencySymbolPrecedes=function(){if(this.p_cs_precedes==1)return true;else return false};this.intCurrencySymbolPrecedes=function(){if(this.int_p_cs_precedes==1)return true;else return false};this.getMonetaryDecimalPoint=function(){return this.mon_decimal_point};this.getFractionalDigits=function(){return this.frac_digits};this.getIntFractionalDigits=function(){return this.int_frac_digits}};jsworld.NumericFormatter=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.format=function(a,b){if(typeof a=="string")a=jsworld._trim(a);if(!jsworld._isNumber(a))throw"Error: The input is not a number";var c=parseFloat(a,10);var d=jsworld._getPrecision(b);if(d!=-1)c=Math.round(c*Math.pow(10,d))/Math.pow(10,d);var e=jsworld._splitNumber(String(c));var f;if(c===0)f="0";else f=jsworld._hasOption("^",b)?e.integer:jsworld._formatIntegerPart(e.integer,this.lc.grouping,this.lc.thousands_sep);var g=d!=-1?jsworld._formatFractionPart(e.fraction,d):e.fraction;var h=g.length?f+this.lc.decimal_point+g:f;if(jsworld._hasOption("~",b)||c===0){return h}else{if(jsworld._hasOption("+",b)||c<0){if(c>0)return"+"+h;else if(c<0)return"-"+h;else return h}else{return h}}}};jsworld.DateTimeFormatter=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance.";this.lc=a;this.formatDate=function(a){var b=null;if(typeof a=="string"){try{b=jsworld.parseIsoDate(a)}catch(c){b=jsworld.parseIsoDateTime(a)}}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.d_fmt)};this.formatTime=function(a){var b=null;if(typeof a=="string"){try{b=jsworld.parseIsoTime(a)}catch(c){b=jsworld.parseIsoDateTime(a)}}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.t_fmt)};this.formatDateTime=function(a){var b=null;if(typeof a=="string"){b=jsworld.parseIsoDateTime(a)}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.d_t_fmt)};this._applyFormatting=function(a,b){b=b.replace(/%%/g,"%");b=b.replace(/%a/g,this.lc.abday[a.getDay()]);b=b.replace(/%A/g,this.lc.day[a.getDay()]);b=b.replace(/%b/g,this.lc.abmon[a.getMonth()]);b=b.replace(/%B/g,this.lc.mon[a.getMonth()]);b=b.replace(/%d/g,jsworld._zeroPad(a.getDate(),2));b=b.replace(/%e/g,jsworld._spacePad(a.getDate(),2));b=b.replace(/%F/g,a.getFullYear()+"-"+jsworld._zeroPad(a.getMonth()+1,2)+"-"+jsworld._zeroPad(a.getDate(),2));b=b.replace(/%h/g,this.lc.abmon[a.getMonth()]);b=b.replace(/%H/g,jsworld._zeroPad(a.getHours(),2));b=b.replace(/%I/g,jsworld._zeroPad(this._hours12(a.getHours()),2));b=b.replace(/%k/g,a.getHours());b=b.replace(/%l/g,this._hours12(a.getHours()));b=b.replace(/%m/g,jsworld._zeroPad(a.getMonth()+1,2));b=b.replace(/%n/g,"\n");b=b.replace(/%M/g,jsworld._zeroPad(a.getMinutes(),2));b=b.replace(/%p/g,this._getAmPm(a.getHours()));b=b.replace(/%P/g,this._getAmPm(a.getHours()).toLocaleLowerCase());b=b.replace(/%R/g,jsworld._zeroPad(a.getHours(),2)+":"+jsworld._zeroPad(a.getMinutes(),2));b=b.replace(/%S/g,jsworld._zeroPad(a.getSeconds(),2));b=b.replace(/%T/g,jsworld._zeroPad(a.getHours(),2)+":"+jsworld._zeroPad(a.getMinutes(),2)+":"+jsworld._zeroPad(a.getSeconds(),2));b=b.replace(/%w/g,this.lc.day[a.getDay()]);b=b.replace(/%y/g,(new String(a.getFullYear())).substring(2));b=b.replace(/%Y/g,a.getFullYear());b=b.replace(/%Z/g,"");b=b.replace(/%[a-zA-Z]/g,"");return b};this._hours12=function(a){if(a===0)return 12;else if(a>12)return a-12;else return a};this._getAmPm=function(a){if(a===0||a>12)return this.lc.pm;else return this.lc.am}};jsworld.MonetaryFormatter=function(a,b,c){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.currencyFractionDigits={AFN:0,ALL:0,AMD:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,COP:0,CRC:0,DJF:0,GNF:0,GYD:0,HUF:0,IDR:0,IQD:0,IRR:0,ISK:0,JOD:3,JPY:0,KMF:0,KRW:0,KWD:3,LAK:0,LBP:0,LYD:3,MGA:0,MMK:0,MNT:0,MRO:0,MUR:0,OMR:3,PKR:0,PYG:0,RSD:0,RWF:0,SLL:0,SOS:0,STD:0,SYP:0,TND:3,TWD:0,TZS:0,UGX:0,UZS:0,VND:0,VUV:0,XAF:0,XOF:0,XPF:0,YER:0,ZMK:0};if(typeof b=="string"){this.currencyCode=b.toUpperCase();var d=this.currencyFractionDigits[this.currencyCode];if(typeof d!="number")d=2;this.lc.frac_digits=d;this.lc.int_frac_digits=d}else{this.currencyCode=this.lc.int_curr_symbol.substring(0,3).toUpperCase()}this.intSep=this.lc.int_curr_symbol.charAt(3);if(this.currencyCode==this.lc.int_curr_symbol.substring(0,3)){this.internationalFormatting=false;this.curSym=this.lc.currency_symbol}else{if(typeof c=="string"){this.curSym=c;this.internationalFormatting=false}else{this.internationalFormatting=true}}this.getCurrencySymbol=function(){return this.curSym};this.currencySymbolPrecedes=function(a){if(typeof a=="string"&&a=="i"){if(this.lc.int_p_cs_precedes==1)return true;else return false}else{if(this.internationalFormatting){if(this.lc.int_p_cs_precedes==1)return true;else return false}else{if(this.lc.p_cs_precedes==1)return true;else return false}}};this.getDecimalPoint=function(){return this.lc.mon_decimal_point};this.getFractionalDigits=function(a){if(typeof a=="string"&&a=="i"){return this.lc.int_frac_digits}else{if(this.internationalFormatting)return this.lc.int_frac_digits;else return this.lc.frac_digits}};this.format=function(a,b){var c;if(typeof a=="string"){a=jsworld._trim(a);c=parseFloat(a);if(typeof c!="number"||isNaN(c))throw"Error: Amount string not a number"}else if(typeof a=="number"){c=a}else{throw"Error: Amount not a number"}var d=jsworld._getPrecision(b);if(d==-1){if(this.internationalFormatting||jsworld._hasOption("i",b))d=this.lc.int_frac_digits;else d=this.lc.frac_digits}c=Math.round(c*Math.pow(10,d))/Math.pow(10,d);var e=jsworld._splitNumber(String(c));var f;if(c===0)f="0";else f=jsworld._hasOption("^",b)?e.integer:jsworld._formatIntegerPart(e.integer,this.lc.mon_grouping,this.lc.mon_thousands_sep);var g;if(d==-1){if(this.internationalFormatting||jsworld._hasOption("i",b))g=jsworld._formatFractionPart(e.fraction,this.lc.int_frac_digits);else g=jsworld._formatFractionPart(e.fraction,this.lc.frac_digits)}else{g=jsworld._formatFractionPart(e.fraction,d)}var h;if(this.lc.frac_digits>0||g.length)h=f+this.lc.mon_decimal_point+g;else h=f;if(jsworld._hasOption("~",b)){return h}else{var i=jsworld._hasOption("!",b)?true:false;var j=c<0?"-":"+";if(this.internationalFormatting||jsworld._hasOption("i",b)){if(i)return this._formatAsInternationalCurrencyWithNoSym(j,h);else return this._formatAsInternationalCurrency(j,h)}else{if(i)return this._formatAsLocalCurrencyWithNoSym(j,h);else return this._formatAsLocalCurrency(j,h)}}};this._formatAsLocalCurrency=function(a,b){if(a=="+"){if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return"("+b+this.curSym+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return"("+this.curSym+b+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return"("+b+" "+this.curSym+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return"("+this.curSym+" "+b+")"}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b+" "+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+" "+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+" "+b+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+this.curSym+b}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.curSym+b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.curSym+" "+b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.curSym+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.curSym+b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+" "+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign+" "+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+this.curSym+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.curSym+this.lc.positive_sign+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.curSym+this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.curSym+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.curSym+" "+this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return"("+b+this.curSym+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return"("+this.curSym+b+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return"("+b+" "+this.curSym+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return"("+this.curSym+" "+b+")"}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b+" "+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+" "+b+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+this.curSym+b}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.curSym+b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.curSym+" "+b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.curSym+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.curSym+b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+" "+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign+" "+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+this.curSym+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.curSym+this.lc.negative_sign+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.curSym+this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.curSym+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.curSym+" "+this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsInternationalCurrency=function(a,b){if(a=="+"){if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return"("+b+this.currencyCode+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return"("+this.currencyCode+b+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return"("+b+this.intSep+this.currencyCode+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return"("+this.currencyCode+this.intSep+b+")"}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b+this.intSep+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+this.intSep+b+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.currencyCode+b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.intSep+b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.currencyCode+b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign+this.intSep+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.intSep+this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return"("+b+this.currencyCode+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return"("+this.currencyCode+b+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return"("+b+this.intSep+this.currencyCode+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return"("+this.currencyCode+this.intSep+b+")"}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b+this.intSep+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+this.intSep+b+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.currencyCode+b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.intSep+b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.currencyCode+b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign+this.intSep+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.intSep+this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsLocalCurrencyWithNoSym=function(a,b){if(a=="+"){if(this.lc.p_sign_posn===0){return"("+b+")"}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.n_sign_posn===0){return"("+b+")"}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsInternationalCurrencyWithNoSym=function(a,b){if(a=="+"){if(this.lc.int_p_sign_posn===0){return"("+b+")"}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.int_n_sign_posn===0){return"("+b+")"}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC_MONETARY definition"}};jsworld.NumericParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.parse=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=jsworld._trim(a);b=jsworld._stringReplaceAll(a,this.lc.thousands_sep,"");b=jsworld._stringReplaceAll(b,this.lc.decimal_point,".");if(jsworld._isNumber(b))return parseFloat(b,10);else throw"Parse error: Invalid number string"}};jsworld.DateTimeParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance.";this.lc=a;this.parseTime=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.t_fmt,a);var c=false;if(b.hour!==null&&b.minute!==null&&b.second!==null){c=true}else if(b.hourAmPm!==null&&b.am!==null&&b.minute!==null&&b.second!==null){if(b.am){b.hour=parseInt(b.hourAmPm,10)}else{if(b.hourAmPm==12)b.hour=0;else b.hour=parseInt(b.hourAmPm,10)+12}c=true}if(c)return jsworld._zeroPad(b.hour,2)+":"+jsworld._zeroPad(b.minute,2)+":"+jsworld._zeroPad(b.second,2);else throw"Parse error: Invalid/ambiguous time string"};this.parseDate=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.d_fmt,a);var c=false;if(b.year!==null&&b.month!==null&&b.day!==null){c=true}if(c)return jsworld._zeroPad(b.year,4)+"-"+jsworld._zeroPad(b.month,2)+"-"+jsworld._zeroPad(b.day,2);else throw"Parse error: Invalid date string"};this.parseDateTime=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.d_t_fmt,a);var c=false;var d=false;if(b.hour!==null&&b.minute!==null&&b.second!==null){c=true}else if(b.hourAmPm!==null&&b.am!==null&&b.minute!==null&&b.second!==null){if(b.am){b.hour=parseInt(b.hourAmPm,10)}else{if(b.hourAmPm==12)b.hour=0;else b.hour=parseInt(b.hourAmPm,10)+12}c=true}if(b.year!==null&&b.month!==null&&b.day!==null){d=true}if(d&&c)return jsworld._zeroPad(b.year,4)+"-"+jsworld._zeroPad(b.month,2)+"-"+jsworld._zeroPad(b.day,2)+" "+jsworld._zeroPad(b.hour,2)+":"+jsworld._zeroPad(b.minute,2)+":"+jsworld._zeroPad(b.second,2);else throw"Parse error: Invalid/ambiguous date/time string"};this._extractTokens=function(a,b){var c={year:null,month:null,day:null,hour:null,hourAmPm:null,am:null,minute:null,second:null,weekday:null};while(a.length>0){if(a.charAt(0)=="%"&&a.charAt(1)!=""){var d=a.substring(0,2);if(d=="%%"){b=b.substring(1)}else if(d=="%a"){for(var e=0;e31)throw"Parse error: Unrecognised day of the month (%e)";b=b.substring(f.length)}else if(d=="%F"){if(/^\d\d\d\d/.test(b)){c.year=parseInt(b.substring(0,4),10);b=b.substring(4)}else{throw"Parse error: Unrecognised date (%F)"}if(jsworld._stringStartsWith(b,"-"))b=b.substring(1);else throw"Parse error: Unrecognised date (%F)";if(/^0[1-9]|1[0-2]/.test(b)){c.month=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised date (%F)";if(jsworld._stringStartsWith(b,"-"))b=b.substring(1);else throw"Parse error: Unrecognised date (%F)";if(/^0[1-9]|[1-2][0-9]|3[0-1]/.test(b)){c.day=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised date (%F)"}else if(d=="%H"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised hour (%H)"}else if(d=="%I"){if(/^0[1-9]|1[0-2]/.test(b)){c.hourAmPm=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised hour (%I)"}else if(d=="%k"){var g=b.match(/^(\d{1,2})/);c.hour=parseInt(g,10);if(isNaN(c.hour)||c.hour<0||c.hour>23)throw"Parse error: Unrecognised hour (%k)";b=b.substring(g.length)}else if(d=="%l"){var g=b.match(/^(\d{1,2})/);c.hourAmPm=parseInt(g,10);if(isNaN(c.hourAmPm)||c.hourAmPm<1||c.hourAmPm>12)throw"Parse error: Unrecognised hour (%l)";b=b.substring(g.length)}else if(d=="%m"){if(/^0[1-9]|1[0-2]/.test(b)){c.month=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised month (%m)"}else if(d=="%M"){if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised minute (%M)"}else if(d=="%n"){if(b.charAt(0)=="\n")b=b.substring(1);else throw"Parse error: Unrecognised new line (%n)"}else if(d=="%p"){if(jsworld._stringStartsWith(b,this.lc.am)){c.am=true;b=b.substring(this.lc.am.length)}else if(jsworld._stringStartsWith(b,this.lc.pm)){c.am=false;b=b.substring(this.lc.pm.length)}else throw"Parse error: Unrecognised AM/PM value (%p)"}else if(d=="%P"){if(jsworld._stringStartsWith(b,this.lc.am.toLowerCase())){c.am=true;b=b.substring(this.lc.am.length)}else if(jsworld._stringStartsWith(b,this.lc.pm.toLowerCase())){c.am=false;b=b.substring(this.lc.pm.length)}else throw"Parse error: Unrecognised AM/PM value (%P)"}else if(d=="%R"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%R)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%R)";if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%R)"}else if(d=="%S"){if(/^[0-5][0-9]/.test(b)){c.second=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised second (%S)"}else if(d=="%T"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%T)";if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%T)";if(/^[0-5][0-9]/.test(b)){c.second=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)"}else if(d=="%w"){if(/^\d/.test(b)){c.weekday=parseInt(b.substring(0,1),10);b=b.substring(1)}else throw"Parse error: Unrecognised weekday number (%w)"}else if(d=="%y"){if(/^\d\d/.test(b)){var h=parseInt(b.substring(0,2),10);if(h>50)c.year=1900+h;else c.year=2e3+h;b=b.substring(2)}else throw"Parse error: Unrecognised year (%y)"}else if(d=="%Y"){if(/^\d\d\d\d/.test(b)){c.year=parseInt(b.substring(0,4),10);b=b.substring(4)}else throw"Parse error: Unrecognised year (%Y)"}else if(d=="%Z"){if(a.length===0)break}a=a.substring(2)}else{if(a.charAt(0)!=b.charAt(0))throw'Parse error: Unexpected symbol "'+b.charAt(0)+'" in date/time string';a=a.substring(1);b=b.substring(1)}}return c}};jsworld.MonetaryParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.parse=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._detectCurrencySymbolType(a);var c,d;if(b=="local"){c="local";d=a.replace(this.lc.getCurrencySymbol(),"")}else if(b=="int"){c="int";d=a.replace(this.lc.getIntCurrencySymbol(),"")}else if(b=="none"){c="local";d=a}else throw"Parse error: Internal assert failure";d=jsworld._stringReplaceAll(d,this.lc.mon_thousands_sep,"");d=d.replace(this.lc.mon_decimal_point,".");d=d.replace(/\s*/g,"");d=this._removeLocalNonNegativeSign(d,c);d=this._normaliseNegativeSign(d,c);if(jsworld._isNumber(d))return parseFloat(d,10);else throw"Parse error: Invalid currency amount string"};this._detectCurrencySymbolType=function(a){if(this.lc.getCurrencySymbol().length>this.lc.getIntCurrencySymbol().length){if(a.indexOf(this.lc.getCurrencySymbol())!=-1)return"local";else if(a.indexOf(this.lc.getIntCurrencySymbol())!=-1)return"int";else return"none"}else{if(a.indexOf(this.lc.getIntCurrencySymbol())!=-1)return"int";else if(a.indexOf(this.lc.getCurrencySymbol())!=-1)return"local";else return"none"}};this._removeLocalNonNegativeSign=function(a,b){a=a.replace(this.lc.positive_sign,"");if((b=="local"&&this.lc.p_sign_posn===0||b=="int"&&this.lc.int_p_sign_posn===0)&&/\(\d+\.?\d*\)/.test(a)){a=a.replace("(","");a=a.replace(")","")}return a};this._normaliseNegativeSign=function(a,b){a=a.replace(this.lc.negative_sign,"-");if(b=="local"&&this.lc.n_sign_posn===0||b=="int"&&this.lc.int_n_sign_posn===0){if(/^\(\d+\.?\d*\)$/.test(a)){a=a.replace("(","");a=a.replace(")","");return"-"+a}}if(b=="local"&&this.lc.n_sign_posn==2||b=="int"&&this.lc.int_n_sign_posn==2){if(/^\d+\.?\d*-$/.test(a)){a=a.replace("-","");return"-"+a}}if(b=="local"&&this.lc.n_cs_precedes===0&&this.lc.n_sign_posn==3||b=="local"&&this.lc.n_cs_precedes===0&&this.lc.n_sign_posn==4||b=="int"&&this.lc.int_n_cs_precedes===0&&this.lc.int_n_sign_posn==3||b=="int"&&this.lc.int_n_cs_precedes===0&&this.lc.int_n_sign_posn==4){if(/^\d+\.?\d*-$/.test(a)){a=a.replace("-","");return"-"+a}}return a}} + + +if(typeof POSIX_LC == "undefined") var POSIX_LC = {}; + +POSIX_LC.en_US = { + "decimal_point" : ".", + "thousands_sep" : ",", + "grouping" : "3", + "abday" : ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"], + "day" : ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], + "abmon" : ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], + "mon" : ["January","February","March","April","May","June","July","August","September","October","November","December"], + "d_fmt" : "%m/%e/%y", + "t_fmt" : "%I:%M:%S %p", + "d_t_fmt" : "%B %e, %Y %I:%M:%S %p %Z", + "am_pm" : ["AM","PM"], + "int_curr_symbol" : "USD ", + "currency_symbol" : "\u0024", + "mon_decimal_point" : ".", + "mon_thousands_sep" : ",", + "mon_grouping" : "3", + "positive_sign" : "", + "negative_sign" : "-", + "int_frac_digits" : 2, + "frac_digits" : 2, + "p_cs_precedes" : 1, + "n_cs_precedes" : 1, + "p_sep_by_space" : 0, + "n_sep_by_space" : 0, + "p_sign_posn" : 1, + "n_sign_posn" : 1, + "int_p_cs_precedes" : 1, + "int_n_cs_precedes" : 1, + "int_p_sep_by_space" : 0, + "int_n_sep_by_space" : 0, + "int_p_sign_posn" : 1, + "int_n_sign_posn" : 1 +} + +if(typeof POSIX_LC == "undefined") var POSIX_LC = {}; + +POSIX_LC.fr_FR = { + "decimal_point" : ",", + "thousands_sep" : "\u00a0", + "grouping" : "3", + "abday" : ["dim.","lun.","mar.", + "mer.","jeu.","ven.", + "sam."], + "day" : ["dimanche","lundi","mardi", + "mercredi","jeudi","vendredi", + "samedi"], + "abmon" : ["janv.","f\u00e9vr.","mars", + "avr.","mai","juin", + "juil.","ao\u00fbt","sept.", + "oct.","nov.","d\u00e9c."], + "mon" : ["janvier","f\u00e9vrier","mars", + "avril","mai","juin", + "juillet","ao\u00fbt","septembre", + "octobre","novembre","d\u00e9cembre"], + "d_fmt" : "%d/%m/%y", + "t_fmt" : "%H:%M:%S", + "d_t_fmt" : "%e %B %Y %H:%M:%S %Z", + "am_pm" : ["AM","PM"], + "int_curr_symbol" : "EUR ", + "currency_symbol" : "\u20ac", + "mon_decimal_point" : ",", + "mon_thousands_sep" : "\u00a0", + "mon_grouping" : "3", + "positive_sign" : "", + "negative_sign" : "-", + "int_frac_digits" : 2, + "frac_digits" : 2, + "p_cs_precedes" : 0, + "n_cs_precedes" : 0, + "p_sep_by_space" : 1, + "n_sep_by_space" : 1, + "p_sign_posn" : 1, + "n_sign_posn" : 1, + "int_p_cs_precedes" : 0, + "int_n_cs_precedes" : 0, + "int_p_sep_by_space" : 1, + "int_n_sep_by_space" : 1, + "int_p_sign_posn" : 1, + "int_n_sign_posn" : 1 +}; + +/** https://github.com/csnover/js-iso8601 */(function(n,f){var u=n.parse,c=[1,4,5,6,7,10,11];n.parse=function(t){var i,o,a=0;if(o=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(t)){for(var v=0,r;r=c[v];++v)o[r]=+o[r]||0;o[2]=(+o[2]||1)-1,o[3]=+o[3]||1,o[8]!=="Z"&&o[9]!==f&&(a=o[10]*60+o[11],o[9]==="+"&&(a=0-a)),i=n.UTC(o[1],o[2],o[3],o[4],o[5]+a,o[6],o[7])}else i=u?u(t):NaN;return i}})(Date) + +/*! + * geo-location-javascript v0.4.3 + * http://code.google.com/p/geo-location-javascript/ + * + * Copyright (c) 2009 Stan Wiechers + * Licensed under the MIT licenses. + * + * Revision: $Rev: 68 $: + * Author: $Author: whoisstan $: + * Date: $Date: 2010-02-15 13:42:19 +0100 (Mon, 15 Feb 2010) $: + */ +var geo_position_js=function() { + + var pub = {}; + var provider=null; + + pub.getCurrentPosition = function(successCallback,errorCallback,options) + { + provider.getCurrentPosition(successCallback, errorCallback,options); + } + + pub.init = function() + { + try + { + if (typeof(geo_position_js_simulator)!="undefined") + { + provider=geo_position_js_simulator; + } + else if (typeof(bondi)!="undefined" && typeof(bondi.geolocation)!="undefined") + { + provider=bondi.geolocation; + } + else if (typeof(navigator.geolocation)!="undefined") + { + provider=navigator.geolocation; + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + function _successCallback(p) + { + //for mozilla geode,it returns the coordinates slightly differently + if(typeof(p.latitude)!="undefined") + { + successCallback({timestamp:p.timestamp, coords: {latitude:p.latitude,longitude:p.longitude}}); + } + else + { + successCallback(p); + } + } + provider.getCurrentPosition(_successCallback,errorCallback,options); + } + } + else if(typeof(window.google)!="undefined" && typeof(google.gears)!="undefined") + { + provider=google.gears.factory.create('beta.geolocation'); + } + else if ( typeof(Mojo) !="undefined" && typeof(Mojo.Service.Request)!="Mojo.Service.Request") + { + provider=true; + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + + parameters={}; + if(options) + { + //http://developer.palm.com/index.php?option=com_content&view=article&id=1673#GPS-getCurrentPosition + if (options.enableHighAccuracy && options.enableHighAccuracy==true) + { + parameters.accuracy=1; + } + if (options.maximumAge) + { + parameters.maximumAge=options.maximumAge; + } + if (options.responseTime) + { + if(options.responseTime<5) + { + parameters.responseTime=1; + } + else if (options.responseTime<20) + { + parameters.responseTime=2; + } + else + { + parameters.timeout=3; + } + } + } + + + r=new Mojo.Service.Request('palm://com.palm.location', { + method:"getCurrentPosition", + parameters:parameters, + onSuccess: function(p){successCallback({timestamp:p.timestamp, coords: {latitude:p.latitude, longitude:p.longitude,heading:p.heading}});}, + onFailure: function(e){ + if (e.errorCode==1) + { + errorCallback({code:3,message:"Timeout"}); + } + else if (e.errorCode==2) + { + errorCallback({code:2,message:"Position Unavailable"}); + } + else + { + errorCallback({code:0,message:"Unknown Error: webOS-code"+errorCode}); + } + } + }); + } + + } + else if (typeof(device)!="undefined" && typeof(device.getServiceObject)!="undefined") + { + provider=device.getServiceObject("Service.Location", "ILocation"); + + //override default method implementation + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + function callback(transId, eventCode, result) { + if (eventCode == 4) + { + errorCallback({message:"Position unavailable", code:2}); + } + else + { + //no timestamp of location given? + successCallback({timestamp:null, coords: {latitude:result.ReturnValue.Latitude, longitude:result.ReturnValue.Longitude, altitude:result.ReturnValue.Altitude,heading:result.ReturnValue.Heading}}); + } + } + //location criteria + var criteria = new Object(); + criteria.LocationInformationClass = "BasicLocationInformation"; + //make the call + provider.ILocation.GetLocation(criteria,callback); + } + } + } + catch (e){ + alert("error="+e); + if(typeof(console)!="undefined") + { + console.log(e); + } + return false; + } + return provider!=null; + } + + + return pub; +}(); +// Couldn't get unminified version to work , go here for docs => https://github.com/iamnoah/writeCapture +(function(E,a){var j=a.document;function A(Q){var Z=j.createElement("div");j.body.insertBefore(Z,null);E.replaceWith(Z,'\n \n
    \n
    \n \n\n
    \n
    \n \n
    \n

    '); + __out.push(__sanitize(t('Invite Link'))); + __out.push(' '); + __out.push(__sanitize(USER.referral_url)); + __out.push('

    \n\n \n\n
    \n\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/login": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('
    \n\t

    '); + __out.push(__sanitize(t('Sign In'))); + __out.push('

    \n\t
    \n\t\t
    \n\n\t\t\t
    \n\t\t\t\t\n\t\t\t
    \n\t\t\t
    \n\t\t\t\t\n\t\t\t
    \n\n\t\t\t
    \n\n\t\t\t
    \n\t\t\t\t\n\t\t\t
    \n\t\t\t
    \n\t\t\t\t\n\t\t\t
    \n\n\t\t\t
    \n\n
    \n\n

    '); + __out.push(__sanitize(t('Forgot Password?'))); + __out.push('

    \n\n\t\t
    \n\t
    \n
    \n\n
    \n
    \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/modules/credit_card": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var printCard; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + if (this.cards === "new") { + __out.push('\n
    \n
    \n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n
    \n \n \n
    \n
    \n \n \n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n \n
    \n
    \n
    \n'); + } else { + __out.push('\n '); + printCard = __bind(function(card, index) { + var exp, style; + __out.push('\n \n \n '); + style = "background-position:-173px"; + __out.push('\n '); + if (card.get("card_type") === "Visa") { + style = "background-position:0px"; + } + __out.push('\n '); + if (card.get("card_type") === "MasterCard") { + style = "background-position:-42px"; + } + __out.push('\n '); + if (card.get("card_type") === "American Express") { + style = "background-position:-130px"; + } + __out.push('\n '); + if (card.get("card_type") === "Discover Card") { + style = "background-position:-85px"; + } + __out.push('\n
    \n \n \n ****'); + __out.push(__sanitize(card.get("card_number"))); + __out.push('\n \n \n '); + if (card.get("card_expiration")) { + __out.push('\n '); + __out.push(__sanitize(t('Expiry'))); + __out.push('\n '); + exp = card.get('card_expiration').split('-'); + __out.push('\n '); + __out.push(__sanitize("" + exp[0] + "-" + exp[1])); + __out.push('\n '); + } + __out.push('\n \n \n \n \n \n '); + if (card.get("default")) { + __out.push('\n ('); + __out.push(__sanitize(t('default card'))); + __out.push(')\n '); + } + __out.push('\n '); + if (this.cards.length > 1 && !card.get("default")) { + __out.push('\n '); + __out.push(__sanitize(t('make default'))); + __out.push('\n '); + } + __out.push('\n \n \n '); + __out.push(__sanitize(t('Edit'))); + __out.push('\n \n \n '); + if (this.cards.length > 1) { + __out.push('\n '); + __out.push(__sanitize(t('Delete'))); + __out.push('\n '); + } + __out.push('\n \n \n \n
    \n
    \n \n \n
    \n
    \n \n \n
    \n
    \n \n \n
    \n \n
    \n \n '); + }, this); + __out.push('\n\n
    \n \n '); + _.each(this.cards.models, printCard); + __out.push('\n
    \n
    \n\n'); + } + __out.push('\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/modules/sub_header": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('
    \n
    '); + __out.push(__sanitize(this.heading)); + __out.push('
    \n
    \n '); + if (window.USER.first_name) { + __out.push('\n '); + __out.push(__sanitize(t('Hello Greeting', { + name: USER.first_name + }))); + __out.push('\n '); + } + __out.push('\n
    \n
    \n
    \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/promotions": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var promo, _i, _len, _ref; + __out.push(require('templates/clients/modules/sub_header').call(this, { + heading: t("Promotions") + })); + __out.push('\n\n
    \n
    \n
    \n \n \n
    \n
    \n \n \n\n \n
    \n '); + if (this.promos.length > 0) { + __out.push('\n
    \n

    '); + __out.push(__sanitize(t('Your Available Promotions'))); + __out.push('

    \n \n \n\n \n \n \n \n \n \n \n \n '); + _ref = this.promos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + promo = _ref[_i]; + __out.push('\n \n \n \n \n \n \n '); + } + __out.push('\n \n
    '); + __out.push(__sanitize(t('Code'))); + __out.push(''); + __out.push(__sanitize(t('Details'))); + __out.push(''); + __out.push(__sanitize(t('Starts'))); + __out.push(''); + __out.push(__sanitize(t('Expires'))); + __out.push('
    '); + __out.push(__sanitize(promo.code)); + __out.push(''); + __out.push(__sanitize(promo.description)); + __out.push(''); + __out.push(__sanitize(app.helpers.formatDate(promo.starts_at, true, "America/Los_Angeles"))); + __out.push(''); + __out.push(__sanitize(app.helpers.formatDate(promo.ends_at, true, "America/Los_Angeles"))); + __out.push('
    \n
    \n '); + } else { + __out.push('\n\n

    '); + __out.push(__sanitize(t('No Active Promotions'))); + __out.push('

    \n '); + } + __out.push('\n\n
    \n
    \n
    \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/request": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var showFavoriteLocation; + showFavoriteLocation = function(location, index) { + var alphabet; + __out.push('\n '); + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + __out.push('\n \n \n \n \n \n '); + __out.push(__sanitize(location.nickname)); + return __out.push('\n \n \n'); + }; + __out.push('\n\n'); + __out.push(require('templates/clients/modules/sub_header').call(this, { + heading: t("Ride Request") + })); + __out.push('\n\n\n
    \n
    \n
    \n
    \n \n \n \n
    \n
    \n\n \n
    \n
    \n
    \n
    \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/shared/menu": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "translations/en": function(exports, require, module) {(function() { + exports.translations = { + "Uber": "Uber", + "Sign Up": "Sign Up", + "Ride Request": "Ride Request", + "Invite Friends": "Invite Friends", + "Promotions": "Promotions", + "Billing": "Billing", + "Settings": "Settings", + "Forgot Password?": "Forgot Password?", + "Password Recovery": "Password Recovery", + "Login": "Login", + "Trip Detail": "Trip Detail", + "Password Reset": "Password Reset", + "Confirm Email": "Confirm Email", + "Request Ride": "Request Ride", + "Credit Card Number": "Credit Card Number", + "month": "month", + "01-Jan": "01-Jan", + "02-Feb": "02-Feb", + "03-Mar": "03-Mar", + "04-Apr": "04-Apr", + "05-May": "05-May", + "06-Jun": "06-Jun", + "07-Jul": "07-Jul", + "08-Aug": "08-Aug", + "09-Sep": "09-Sep", + "10-Oct": "10-Oct", + "11-Nov": "11-Nov", + "12-Dec": "12-Dec", + "year": "year", + "CVV": "CVV", + "Category": "Category", + "personal": "personal", + "business": "business", + "Default Credit Card": "Default Credit Card", + "Add Credit Card": "Add Credit Card", + "Expiry": "Expiry", + "default card": "default card", + "make default": "make default", + "Edit": "Edit", + "Delete": "Delete", + "Expiry Month": "Expiry Month", + "Expiry Year": "Expiry Year", + "Unable to Verify Card": "Unable to verify card at this time. Please try again later.", + "Credit Card Update Succeeded": "Your card has been successfully updated!", + "Credit Card Update Failed": "We couldn't save your changes. Please try again in a few minutes.", + "Credit Card Delete Succeeded": "Your card has been deleted!", + "Credit Card Delete Failed": "We were unable to delete your card. Please try again later.", + "Credit Card Update Category Succeeded": "Successfully changed card category!", + "Credit Card Update Category Failed": "We couldn't change your card category. Please try again in a few minutes.", + "Credit Card Update Default Succeeded": "Successfully changed default card!", + "Credit Card Update Default Failed": "We couldn't change your default card. Please try again in a few minutes.", + "Hello Greeting": "Hello, <%= name %>", + "Card Ending in": "Card Ending in", + "Trip Map": "Trip Map", + "Amount": "Amount: <%= amount %>", + "Last Attempt to Bill": "Last Attempt to Bill: <%= date %>", + "Charge": "Charge", + "Uber Credit Balance Note": "Your account has an UberCredit balance of <%= amount %>. When billing for trips, we'll deplete your UberCredit balance before applying charges to your credit card.", + "Please Add Credit Card": "Please add a credit card to bill your outstanding charges.", + "Credit Cards": "Credit Cards", + "add a new credit card": "add a new credit card", + "Account Balance": "Account Balance", + "Arrears": "Arrears", + "Billing Succeeded": "Your card was successfully billed.", + "Confirm Email Succeeded": "Successfully confirmed email token, redirecting to log in page...", + "Confirm Email Failed": "Unable to confirm email. Please contact support@uber.com if this problem persists.", + "Email Already Confirmed": "Your email address has already been confirmed, redirecting to log in page...", + "Credit Card Added": "Credit Card Added", + "No Credit Card": "No Credit Card", + "Mobile Number Confirmed": "Mobile Number Confirmed", + "No Confirmed Mobile": "No Confirmed Mobile", + "E-mail Address Confirmed": "E-mail Address Confirmed", + "No Confirmed E-mail": "No Confirmed E-mail", + 'Reply to sign up text': 'Reply "GO" to the text message you received at sign up.', + "Resend text message": "Resend text message", + "Click sign up link": "Click the link in the email you received at sign up.", + "Resend email": "Resend email", + "Add a credit card to ride": "Add a credit card and you'll be ready to ride Uber.", + "Your Most Recent Trip": "Your Most Recent Trip", + "details": "details", + "Your Trip History ": "Your Trip History ", + "Status": "Status", + "Here's how it works:": "Here's how it works:", + "Show all trips": "Show all trips", + "Set your location:": "Set your location:", + "App search for address": "iPhone/Android app: fix the pin or search for an address", + "SMS text address": "SMS: text your address to UBRCAB (827222)", + "Confirm pickup request": "Confirm your pickup request", + "Uber sends ETA": "Uber will send you an ETA (usually within 5-10 minutes)", + "Car arrives": "When your car is arriving, Uber will inform you again.", + "Ride to destination": "Hop in the car and tell the driver your destination.", + "Thank your driver": "That’s it! Please thank your driver but remember that your tip is included and no cash is necessary.", + "Trip started here": "Trip started here", + "Trip ended here": "Trip ended here", + "Sending Email": "Sending email...", + "Resend Email Succeeded": "We just sent the email. Please click on the confirmation link you recieve.", + "Resend Email Failed": "There was an error sending the email. Please contact support if the problem persists.", + "Resend Text Succeeded": 'We just sent the text message. Please reply "GO" to the message you recieve. It may take a few minutes for the message to reach you phone.', + "Resend Text Failed": "There was an error sending the text message. Please contact support if the problem persists.", + "Password Reset Error": "There was an error processing your password reset request.", + "New Password": "New Password", + "Forgot Password": "Forgot Password", + "Forgot Password Error": "Your email address could not be found. Please make sure to use the same email address you used when you signed up.", + "Forgot Password Success": "Please check your email for a link to reset your password.", + "Forgot Password Enter Email": 'Enter your email address and Uber will send you a link to reset your password. If you remember your password, you can sign in here.', + "Invite friends": "Invite friends", + "Give $ Get $": "Give $10, Get $10", + "Give $ Get $ Description": "Every friend you invite to Uber gets $10 of Uber credit. After someone you’ve invited takes his/her first ride, you get $10 of Uber credits too!", + "What are you waiting for?": "So, what are you waiting for? Invite away!", + "Tweet": "Tweet", + "Invite Link": "Email or IM this link to your friends:", + "Email Address": "Email Address", + "Reset Password": "Reset Password", + "Enter Promotion Code": "If you have a promotion code, enter it here:", + "Your Active Promotions": "Your Active Promotions", + "Code": "Code", + "Details": "Details", + "Trips Remaining": "Trips Remaining", + "Expires": "Expires", + "No Active Promotions": "There are no active promotions on your account.", + "Your Available Promotions": "Your Available Promotions", + "Where do you want us to pick you up?": "Where do you want us to pick you up?", + "Address to search": "Address to search", + "Search": "Search", + "Driver Name:": "Driver Name:", + "Driver #:": "Driver #:", + "Pickup Address:": "Pickup Address:", + "Add to Favorite Locations": "Add to Favorite Locations", + "Star": "Star", + "Nickname:": "Nickname:", + "Add": "Add", + "Your last trip": "Your last trip", + "Please rate your driver:": "Please rate your driver:", + "Comments: (optional)": "Comments: (optional)", + "Rate Trip": "Rate Trip", + "Pickup time:": "Pickup time:", + "Miles:": "Miles:", + "Trip time:": "Trip time:", + "Fare:": "Fare:", + "Favorite Locations": "Favorite Locations", + "Search Results": "Search Results", + "You have no favorite locations saved.": "You have no favorite locations saved.", + "Loading...": "Loading...", + "Request Pickup": "Request Pickup", + "Cancel Pickup": "Cancel Pickup", + "Requesting Closest Driver": "Requesting the closest driver to pick you up...", + "En Route": "You are currently en route...", + "Rate Last Trip": "Please rate your trip to make another request", + "Rate Before Submitting": "Please rate your trip before submitting the form", + "Address too short": "Address too short", + "or did you mean": "or did you mean", + "Search Address Failed": "Unable to find the given address. Please enter another address close to your location.", + "Sending pickup request...": "Sending pickup request...", + "Cancel Request Prompt": "Are you sure you want to cancel your request?", + "Cancel Request Arrived Prompt": 'Are you sure you want to cancel your request? Your driver has arrived so there is a $10 cancellation fee. It may help to call your driver now', + "Favorite Location Nickname Length Error": "Nickname has to be atleast 3 characters", + "Favorite Location Save Succeeded": "Location Saved!", + "Favorite Location Save Failed": "Unable to save your location. Please try again later.", + "Favorite Location Title": "Favorite Location <%= id %>", + "Search Location Title": "Search Location <%= id %>", + "ETA Message": "ETA: Around <%= minutes %> Minutes", + "Nearest Cab Message": "The closest driver is approximately <%= minutes %> minute(s) away", + "Arrival ETA Message": "Your Uber will arrive in about <%= minutes %> minute(s)", + "Arriving Now Message": "Your Uber is arriving now...", + "Rating Driver Failed": "Unable to contact server. Please try again later or email support if this issue persists.", + "Account Information": "Account Information", + "Mobile Phone Information": "Mobile Phone Information", + "settings": "settings", + "Information": "Information", + "Picture": "Picture", + "Change password": "Change password", + "Your current Picture": "Your current Picture", + "Your Favorite Locations": "Your Favorite Locations", + "You have no favorite locations saved.": "You have no favorite locations saved.", + "Purpose of Mobile": "We send text messages to your mobile phone to tell you when your driver is arriving. You can also request trips using text messages.", + "Country": "Country", + "Mobile Number": "Mobile Number", + "Submit": "Submit", + "Favorite Location": "Favorite Location", + "No Approximate Address": "Could not find an approximate address", + "Address:": "Address:", + "Information Update Succeeded": "Your information has been updated!", + "Information Update Failed": "We couldn't update your information. Please try again in few minutes or contact support if the problem persists.", + "Location Delete Succeeded": "Location deleted!", + "Location Delete Failed": "We were unable to delete your favorite location. Please try again later or contact support of the issue persists.", + "Location Edit Succeeded": "Changes Saved!", + "Location Edit Failed": "We couldn't save your changes. Please try again in a few minutes.", + "Picture Update Succeeded": "Your picture has been updated!", + "Picture Update Failed": "We couldn't change your picture. Please try again in a few minutes.", + "Personal Information": "Personal Information", + "Mobile Phone Number": "Mobile Phone Number", + "Payment Information": "Payment Information", + "Purpose of Credit Card": "We keep your credit card on file so that your trip go as fast as possible. You will not be charged until you take a trip.", + "Your card will not be charged until you take a trip.": "Your card will not be charged until you take a trip.", + "Credit Card Number": "Credit Card Number", + "Expiration Date": "Expiration Date", + "Promotion Code": "Promotion Code", + "Enter Promo Here": "If you have a code for a promotion, invitation or group deal, you can enter it here.", + "Promotion Code Input Label": "Promotion, Invite or Groupon Code (optional)", + "Terms and Conditions": "Terms and Conditions", + "HELP": "HELP", + "STOP": "STOP", + "Legal Information": "Legal Information", + "Sign Up Agreement": "By signing up, I agree to the Uber <%= terms_link %> and <%= privacy_link %> and understand that Uber is a request tool, not a transportation carrier.", + "Sign Up Agreement Error": "You must agree to the Uber Terms and Conditions and Privacy Policy to continue.", + "Message and Data Rates Disclosure": "Message and Data Rates May Apply. Reply <%= help_string %> to 827-222 for help. Reply <%= stop_string %> to 827-222 to stop texts. For additional assistance, visit support.uber.com or call (866) 576-1039. Supported Carriers: AT&T, Sprint, Verizon, and T-Mobile.", + "I Agree": "I agree to the Terms & Conditions and Privacy Policy", + "Security Code": "Security Code", + "Type of Card": "Type of Card", + "Personal": "Personal", + "Business": "Business", + "Code": "Code", + "Zip or Postal Code": "Zip or Postal Code", + "Your Trip": "Your Trip", + "Trip Info": "Trip Info", + "Request a fare review": "Request a fare review", + "Fare Review Submitted": "Your fare review has been submitted. We'll get back to you soon about your request. Sorry for any inconvenience this may have caused!", + "Fair Price Consideration": "We're committed to delivering Uber service at a fair price. Before requesting a fare review, please consider:", + "Your Fare Calculation": "Your Fare Calculation", + "Charges": "Charges", + "Discounts": "Discounts", + "Total Charge": "Total Charge", + "Uber pricing information": "Uber pricing information", + "Uber Pricing Information Message": "<%= learn_link %> is published on our website.", + "GPS Point Capture Disclosure": "Due to a finite number of GPS point captures, corners on your trip map may appear cut off or rounded. These minor inaccuracies result in a shorter measured distance, which always results in a cheaper trip.", + "Fare Review Note": "Please elaborate on why this trip requires a fare review. Your comments below will help us better establish the correct price for your trip:", + "Fare Review Error": "There was an error submitting the review. Please ensure that you have a message.", + "Sign In": "Sign In" + }; +}).call(this); +}, "translations/fr": function(exports, require, module) {(function() { + exports.translations = { + "Uber": "Uber", + "Sign Up": "Inscription", + "Ride Request": "Passer une Commande", + "Invite Friends": "Inviter vos Amis", + "Promotions": "Promotions", + "Billing": "Paiement", + "Settings": "Paramètres", + "Forgot Password?": "Mot de passe oublié ?", + "Password Recovery": "Récupération du mot de passe", + "Login": "Connexion", + "Trip Detail": "Détail de la Course", + "Password Reset": "Réinitialisation du mot de passe", + "Confirm Email": "Confirmation de l’e-mail", + "Request Ride": "Passer une Commande", + "Credit Card Number": "Numéro de Carte de Crédit", + "month": "mois", + "01-Jan": "01-Jan", + "02-Feb": "02-Fév", + "03-Mar": "03-Mar", + "04-Apr": "04-Avr", + "05-May": "05-Mai", + "06-Jun": "06-Juin", + "07-Jul": "07-Jui", + "08-Aug": "08-Aoû", + "09-Sep": "09-Sep", + "10-Oct": "10-Oct", + "11-Nov": "11-Nov", + "12-Dec": "12-Déc", + "year": "année", + "CVV": "Code de Sécurité", + "Category": "Type", + "personal": "personnel", + "business": "entreprise", + "Default Credit Card": "Carte par Défaut", + "Add Credit Card": "Ajouter une Carte", + "Expiry": "Expire", + "default card": "carte par défaut", + "make default": "choisir par défaut", + "Edit": "Modifier", + "Delete": "Supprimer", + "Expiry Month": "Mois d’Expiration", + "Expiry Year": "Année d’Expiration", + "Unable to Verify Card": "Impossible de vérifier la carte pour le moment. Merci de réessayer un peu plus tard.", + "Credit Card Update Succeeded": "Votre carte a été mise à jour avec succès !", + "Credit Card Update Failed": "Nous ne pouvons enregistrer vos changements. Merci de réessayer dans quelques minutes.", + "Credit Card Delete Succeeded": "Votre carte a été supprimée !", + "Credit Card Delete Failed": "Nous n’avons pas été en mesure de supprimer votre carte. Merci de réessayer plus tard.", + "Credit Card Update Category Succeeded": "Changement de catégorie de carte réussi !", + "Credit Card Update Category Failed": "Nous ne pouvons pas changer la catégorie de votre carte. Merci de réessayer dans quelques minutes.", + "Credit Card Update Default Succeeded": "Carte par défaut changée avec succès !", + "Credit Card Update Default Failed": "Nous ne pouvons pas changer votre carte par défaut. Merci de réessayer dans quelques minutes.", + "Hello Greeting": "Bonjour, <%= name %>", + "Card Ending in": "La carte expire dans", + "Trip Map": "Carte des Courses", + "Amount": "Montant: <%= amount %>", + "Last Attempt to Bill": "Dernière tentative de prélèvement : <%= date %>", + "Charge": "Débit", + "Uber Credit Balance Note": "Votre compte a un solde de <%= amount %> UberCredits. Lorsque nous facturons des courses, nous réduirons votre solde d’UberCredits avant de prélever votre carte de crédit.", + "Please Add Credit Card": "Merci d’ajouter une carte de crédit pour que nous puissions vous facturer.", + "Credit Cards": "Cartes de crédit", + "add a new credit card": "Ajouter une nouvelle carte de crédit", + "Account Balance": "Solde du compte", + "Arrears": "Arriérés", + "Billing Succeeded": "Votre carte a été correctement débitée.", + "Confirm Email Succeeded": "L’adresse e-mail a bien été validée, vous êtes redirigé vers le tableau de bord...", + "Confirm Email Failed": "Impossible de confirmer l’adresse e-mail. Merci de contacter support@uber.com si le problème persiste.", + "Credit Card Added": "Carte de crédit ajoutée", + "No Credit Card": "Pas de carte de crédit", + "Mobile Number Confirmed": "Numéro de téléphone confirmé", + "No Confirmed Mobile": "Pas de numéro de téléphone confirmé", + "E-mail Address Confirmed": "Adresse e-mail confirmée", + "No Confirmed E-mail": "Pas d’adresse e-mail confirmée", + 'Reply to sign up text': 'Répondre "GO" au SMS que vous avez reçu à l’inscription.', + "Resend text message": "Renvoyer le SMS", + "Click sign up link": "Cliquez sur le lien contenu dans l’e-mail reçu à l’inscription.", + "Resend email": "Renvoyer l’e-mail", + "Add a credit card to ride": "Ajouter une carte de crédit et vous serez prêt à voyager avec Uber.", + "Your Most Recent Trip": "Votre course la plus récente", + "details": "détails", + "Your Trip History": "Historique de votre trajet", + "Status": "Statut", + "Here's how it works:": "Voici comment ça marche :", + "Show all trips": "Montrer toutes les courses", + "Set your location:": "Définir votre position :", + "App search for address": "Application iPhone/Android : positionner la punaise ou rechercher une adresse", + "SMS text address": "SMS : envoyez votre adresse à UBRCAB (827222)", + "Confirm pickup request": "Validez la commande", + "Uber sends ETA": "Uber envoie un temps d’attente estimé (habituellement entre 5 et 10 minutes)", + "Car arrives": "Lorsque votre voiture arrive, Uber vous en informera encore..", + "Ride to destination": "Montez dans la voiture et donnez votre destination au chauffeur.", + "Thank your driver": "C’est tout ! Remerciez le chauffeur mais souvenez-vous que les pourboires sont compris et qu’il n’est pas nécessaire d’avoir du liquide sur soi.", + "Trip started here": "La course a commencé ici.", + "Trip ended here": "La course s’est terminée ici.", + "Sending Email": "Envoi de l’e-mail...", + "Resend Email Succeeded": "Nous venons d’envoyer l’e-mail. Merci de cliquer sur le lien de confirmation que vous avez reçu.", + "Resend Email Failed": "Il y a eu un problème lors de l’envoi de l’email. Merci de contacter le support si le problème persiste.", + "Resend Text Succeeded": 'Nous venons d’envoyer le SMS. Merci de répondre "GO" au message que vous avez reçu. Il se peut que cela prenne quelques minutes pour que le message arrive sur votre téléphone.', + "Resend Text Failed": "Il y a eu un problème lors de l’envoi du SMS. Merci de contacter le support si le problème persiste.", + "Password Reset Error": "Il y a eu une error lors de la réinitialisation de votre mot de passe.", + "New Password:": "Nouveau mot de passe:", + "Forgot Password Error": "Votre nom d’utilisateur / adresse email ne peut être trouvé. Merci d’utiliser la même qu’à l’inscription.", + "Forgot Password Success": "Merci de consulter votre boîte mail pour suivre la demande de ‘réinitialisation de mot de passe.", + "Forgot Password Enter Email": "Merci de saisir votre adresse email et nous vous enverrons un lien vous permettant de réinitialiser votre mot de passe :", + "Invite friends": "Inviter vos amis", + "Give $ Get $": "Donnez $10, Recevez $10", + "Give $ Get $ Description": "Chaque ami que vous invitez à Uber recevra $10 de crédits Uber. Dès lors qu’une personne que vous aurez invité aura utilisé Uber pour la première, vous recevrez $10 de crédits Uber également !", + "What are you waiting for?": "N’attendez plus ! Lancez les invitations !", + "Tweet": "Tweeter", + "Invite Link": "Envoyez ce lien par email ou messagerie instantanée à vos amis :", + "Enter Promotion Code": "Si vous avez un code promo, saisissez-le ici:", + "Your Active Promotions": "Vos Codes Promos Actifs", + "Code": "Code", + "Details": "Détails", + "Trips Remaining": "Courses restantes", + "Expires": "Expire", + "No Active Promotions": "Vous n’avez pas de code promo actif.", + "Your Available Promotions": "Votres Promos Disponibles", + "Where do you want us to pick you up?": "Où souhaitez-vous que nous vous prenions en charge ?", + "Address to search": "Adresse à rechercher", + "Search": "Chercher", + "Driver Name:": "Nom du chauffeur:", + "Driver #:": "# Chauffeur:", + "Pickup Address:": "Lieu de prise en charge:", + "Add to Favorite Locations": "Ajoutez aux Lieux Favoris", + "Star": "Étoiles", + "Nickname:": "Pseudo", + "Add": "Ajouter", + "Your last trip": "Votre dernière course", + "Please rate your driver:": "Merci de noter votre chauffeur :", + "Comments: (optional)": "Commentaires: (optionnel)", + "Rate Trip": "Notez votre course", + "Pickup time:": "Heure de Prise en Charge :", + "Miles:": "Kilomètres :", + "Trip time:": "Temps de course :", + "Fare:": "Tarif :", + "Favorite Locations": "Lieux Favoris", + "Search Results": "Résultats", + "You have no favorite locations saved.": "Vous n’avez pas de lieux de prise en charge favoris.", + "Loading...": "Chargement...", + "Request Pickup": "Commander ici", + "Cancel Pickup": "Annuler", + "Requesting Closest Driver": "Nous demandons au chauffeur le plus proche de vous prendre en charge...", + "En Route": "Vous êtes actuellement en route...", + "Rate Last Trip": "Merci de noter votre précédent trajet pour faire une autre course.", + "Rate Before Submitting": "Merci de noter votre trajet avant de le valider.", + "Address too short": "L’adresse est trop courte", + "or did you mean": "ou vouliez-vous dire", + "Search Address Failed": "Impossible de trouver l’adresse spécifiée. Merci de saisir une autre adresse proche de l’endroit où vous vous trouvez.", + "Sending pickup request...": "Envoi de la demande de prise en charge...", + "Cancel Request Prompt": "Voulez-vous vraiment annuler votre demande ?", + "Cancel Request Arrived Prompt": 'Voulez-vous vraiment annuler votre demande ? Votre chauffeur est arrivé, vous serez donc facturé de $10 de frais d’annulation. Il pourrait être utile que vous appeliez votre chauffeur maintenant.', + "Favorite Location Nickname Length Error": "Le pseudo doit faire au moins 3 caractères de long", + "Favorite Location Save Succeeded": "Adresse enregistrée !", + "Favorite Location Save Failed": "Impossible d’enregistrer votre adresse. Merci de réessayer ultérieurement.", + "Favorite Location Title": "Adresse favorie <%= id %>", + "Search Location Title": "Recherche d’adresse <%= id %>", + "ETA Message": "Temps d’attente estimé: environ <%= minutes %> minutes", + "Nearest Cab Message": "Le chauffeur le plus proche sera là dans <%= minutes %> minute(s)", + "Arrival ETA Message": "Votre chauffeur arrivera dans <%= minutes %> minute(s)", + "Arriving Now Message": "Votre chauffeur est en approche...", + "Rating Driver Failed": "Impossible de contacter le serveur. Merci de réessayer ultérieurement ou de contacter le support si le problème persiste.", + "settings": "Paramètres", + "Information": "Information", + "Picture": "Photo", + "Change password": "Modifier votre mot de passe", + "Your current Picture": "Votre photo", + "Your Favorite Locations": "Vos lieux favoris", + "You have no favorite locations saved.": "Vous n’avez pas de lieu favori", + "Account Information": "Informations Personnelles", + "Mobile Phone Information": "Informations de Mobile", + "Change Your Password": "Changez votre mot de passe.", + "Country": "Pays", + "Language": "Langue", + "Favorite Location": "Lieu favori", + "No Approximate Address": "Impossible de trouver une adresse même approximative", + "Address:": "Adresse :", + "Information Update Succeeded": "Vos informations ont été mises à jour !", + "Information Update Failed": "Nous n’avons pas pu mettre à jour vos informations. Merci de réessayer dans quelques instants ou de contacter le support si le problème persiste.", + "Location Delete Succeeded": "Adresse supprimée !", + "Location Delete Failed": "Nous n’avons pas pu supprimée votre adresse favorie. Merci de réessayer plus tard ou de contacter le support si le problème persiste.", + "Location Edit Succeeded": "Modifications sauvegardées !", + "Location Edit Failed": "Nous n’avons pas pu sauvegarder vos modifications. Merci de réessayer dans quelques minutes.", + "Picture Update Succeeded": "Votre photo a été mise à jour !", + "Picture Update Failed": "Nous n’avons pas pu mettre à jour votre photo. Merci de réessayer dans quelques instants.", + "Personal Information": "Informations Personnelles", + "Mobile Phone Number": "Numéro de Téléphone Portable", + "Payment Information": "Informations de Facturation", + "Your card will not be charged until you take a trip.": "Votre carte ne sera pas débitée avant votre premier trajet.", + "Card Number": "Numéro de Carte", + "Promotion Code Input Label": "Code promo, code d’invitation ou “deal” acheté en ligne (optionnel)", + "Terms and Conditions": "Conditions Générales", + "HELP": "HELP", + "STOP": "STOP", + "Sign Up Agreement": "En souscrivant, j’accepte les <%= terms_link %> et <%= privacy_link %> et comprends qu’Uber est un outil de commande de chauffeur, et non un transporteur.", + "Sign Up Agreement Error": "Vous devez accepter les Conditions Générales d’utilisation d’Uber Terms and Conditions et la Politique de Confidentialité pour continuer.", + "Message and Data Rates Disclosure": "Les frais d’envoi de SMS et de consommation de données peuvent s’appliquer. Répondez <%= help_string %> au 827-222 pour obtenir de l’aide. Répondez <%= stop_string %> au 827-222 pour ne plus recevoir de SMS. Pour plus d’aide, visitez support.uber.com ou appelez le (866) 576-1039. Opérateurs supportés: AT&T, Sprint, Verizon, T-Mobile, Orange, SFR et Bouygues Telecom.", + "Zip/Postal Code": "Code Postal", + "Expiration Date": "Date D'expiration", + "Security Code": "Code de Sécurité", + "Type of Card": "Type", + "Personal": "Personnel", + "Business": "Entreprise", + "Promotion Code": "Code Promo", + "Legal Information": "Mentions Légales", + "I Agree": "J'accepte.", + "Your Trip": "Votre Course", + "Trip Info": "Informations de la Course", + "Request a fare review": "Demander un contrôle du tarif", + "Fare Review Submitted": "Votre demande de contrôle du tarif a été soumis. Nous reviendrons vers vous rapidement concernant cette demande. Nous nous excusons pour les dérangements éventuellement occasionnés !", + "Fair Price Consideration": "Nous nous engageons à proposer Uber à un tarif juste. Avant de demander un contrôle du tarif, merci de prendre en compte :", + "Your Fare Calculation": "Calcul du Prix", + "Charges": "Coûts", + "Discounts": "Réductions", + "Total Charge": "Coût total", + "Uber pricing information": "Information sur les prix d’Uber", + "Uber Pricing Information Message": "<%= learn_link %> est disponible sur notre site web.", + "GPS Point Capture Disclosure": "A cause d’un nombre limité de coordonnées GPS sauvegardées, les angles de votre trajet sur la carte peuvent apparaître coupés ou arrondis. Ces légères incohérences débouchent sur des distances mesurées plus courtes, ce qui implique toujours un prix du trajet moins élevé.", + "Fare Review Note": "Merci de nous expliquer pourquoi le tarif de cette course nécessite d’être contrôlé. Vos commentaires ci-dessous nous aideront à établir un prix plus juste si nécessaire :", + "Fare Review Error": "Il y a eu une erreur lors de l’envoi de la demande. Assurez-vous d’avoir bien ajouté une description à votre demande." + }; +}).call(this); +}, "views/clients/billing": function(exports, require, module) {(function() { + var clientsBillingTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsBillingTemplate = require('templates/clients/billing'); + exports.ClientsBillingView = (function() { + __extends(ClientsBillingView, UberView); + function ClientsBillingView() { + ClientsBillingView.__super__.constructor.apply(this, arguments); + } + ClientsBillingView.prototype.id = 'billing_view'; + ClientsBillingView.prototype.className = 'view_container'; + ClientsBillingView.prototype.events = { + 'click a#add_card': 'addCard', + 'click .charge_arrear': 'chargeArrear' + }; + ClientsBillingView.prototype.render = function() { + this.RefreshUserInfo(__bind(function() { + var cards, newForm; + this.HideSpinner(); + $(this.el).html(clientsBillingTemplate()); + if (USER.payment_gateway.payment_profiles.length === 0) { + newForm = new app.views.clients.modules.creditcard; + $(this.el).find("#add_card_wrapper").html(newForm.render(0).el); + } else { + cards = new app.views.clients.modules.creditcard; + $("#cards").html(cards.render("all").el); + } + return this.delegateEvents(); + }, this)); + return this; + }; + ClientsBillingView.prototype.addCard = function(e) { + var newCard; + e.preventDefault(); + newCard = new app.views.clients.modules.creditcard; + $('#cards').append(newCard.render("new").el); + return $("a#add_card").hide(); + }; + ClientsBillingView.prototype.chargeArrear = function(e) { + var $el, arrearId, attrs, cardId, options, tryCharge; + e.preventDefault(); + $(".error_message").text(""); + $el = $(e.currentTarget); + arrearId = $el.attr('id'); + cardId = $el.parent().find('#card_to_charge').val(); + this.ShowSpinner('submit'); + tryCharge = new app.models.clientbills({ + id: arrearId + }); + attrs = { + payment_profile_id: cardId, + dataType: 'json' + }; + options = { + success: __bind(function(data, textStatus, jqXHR) { + $el.parent().find(".success_message").text(t("Billing Succeeded")); + $el.hide(); + return $el.parent().find('#card_to_charge').hide(); + }, this), + error: __bind(function(jqXHR, status, errorThrown) { + return $el.parent().find(".error_message").text(JSON.parse(status.responseText).error); + }, this), + complete: __bind(function() { + return this.HideSpinner(); + }, this) + }; + return tryCharge.save(attrs, options); + }; + return ClientsBillingView; + })(); +}).call(this); +}, "views/clients/confirm_email": function(exports, require, module) {(function() { + var clientsConfirmEmailTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsConfirmEmailTemplate = require('templates/clients/confirm_email'); + exports.ClientsConfirmEmailView = (function() { + __extends(ClientsConfirmEmailView, UberView); + function ClientsConfirmEmailView() { + ClientsConfirmEmailView.__super__.constructor.apply(this, arguments); + } + ClientsConfirmEmailView.prototype.id = 'confirm_email_view'; + ClientsConfirmEmailView.prototype.className = 'view_container'; + ClientsConfirmEmailView.prototype.render = function(token) { + var attrs; + $(this.el).html(clientsConfirmEmailTemplate()); + attrs = { + data: { + email_token: token + }, + success: __bind(function(data, textStatus, jqXHR) { + var show_dashboard; + this.HideSpinner(); + show_dashboard = function() { + return app.routers.clients.navigate('!/dashboard', true); + }; + if (data.status === 'OK') { + $('.success_message').show(); + return _.delay(show_dashboard, 3000); + } else if (data.status === 'ALREADY_COMFIRMED') { + $('.already_confirmed_message').show(); + return _.delay(show_dashboard, 3000); + } else { + return $('.error_message').show(); + } + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('.error_message').show(); + }, this), + complete: function(status) { + return $('#attempt_text').hide(); + }, + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/self" + }; + $.ajax(attrs); + this.ShowSpinner('submit'); + return this; + }; + return ClientsConfirmEmailView; + })(); +}).call(this); +}, "views/clients/dashboard": function(exports, require, module) {(function() { + var clientsDashboardTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsDashboardTemplate = require('templates/clients/dashboard'); + exports.ClientsDashboardView = (function() { + var displayFirstTrip; + __extends(ClientsDashboardView, UberView); + function ClientsDashboardView() { + this.showAllTrips = __bind(this.showAllTrips, this); + this.render = __bind(this.render, this); + ClientsDashboardView.__super__.constructor.apply(this, arguments); + } + ClientsDashboardView.prototype.id = 'dashboard_view'; + ClientsDashboardView.prototype.className = 'view_container'; + ClientsDashboardView.prototype.events = { + 'click a.confirmation': 'confirmationClick', + 'click #resend_email': 'resendEmail', + 'click #resend_mobile': 'resendMobile', + 'click #show_all_trips': 'showAllTrips' + }; + ClientsDashboardView.prototype.render = function() { + var displayPage, downloadTrips; + this.HideSpinner(); + displayPage = __bind(function() { + $(this.el).html(clientsDashboardTemplate()); + this.confirmationsSetup(); + return this.RequireMaps(__bind(function() { + if (USER.trips.models[0]) { + if (!USER.trips.models[0].get("points")) { + return USER.trips.models[0].fetch({ + data: { + relationships: 'points' + }, + success: __bind(function() { + this.CacheData("USERtrips", USER.trips); + return displayFirstTrip(); + }, this) + }); + } else { + return displayFirstTrip(); + } + } + }, this)); + }, this); + downloadTrips = __bind(function() { + return this.DownloadUserTrips(displayPage, false, 10); + }, this); + this.RefreshUserInfo(downloadTrips); + return this; + }; + displayFirstTrip = __bind(function() { + var bounds, endPos, map, myOptions, path, polyline, startPos; + myOptions = { + zoom: 12, + mapTypeId: google.maps.MapTypeId.ROADMAP, + zoomControl: false, + rotateControl: false, + panControl: false, + mapTypeControl: false, + scrollwheel: false + }; + if (USER.trips.length === 10) { + $("#show_all_trips").show(); + } + if (USER.trips.length > 0) { + map = new google.maps.Map(document.getElementById("trip_details_map"), myOptions); + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(USER.trips.models[0].get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + map.fitBounds(bounds); + startPos = new google.maps.Marker({ + position: _.first(path), + map: map, + title: t('Trip started here'), + icon: 'https://uber-static.s3.amazonaws.com/marker_start.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: map, + title: t('Trip ended here'), + icon: 'https://uber-static.s3.amazonaws.com/marker_end.png' + }); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + return polyline.setMap(map); + } + }, ClientsDashboardView); + ClientsDashboardView.prototype.confirmationsSetup = function() { + var blink, cardForm, element, _ref, _ref2, _ref3, _ref4, _ref5; + blink = function(element) { + var opacity; + opacity = 0.5; + if (element.css('opacity') === "0.5") { + opacity = 1.0; + } + return element.fadeTo(2000, opacity, function() { + return blink(element); + }); + }; + if (((_ref = window.USER) != null ? (_ref2 = _ref.payment_gateway) != null ? (_ref3 = _ref2.payment_profiles) != null ? _ref3.length : void 0 : void 0 : void 0) === 0) { + element = $('#confirmed_credit_card'); + cardForm = new app.views.clients.modules.creditcard; + $('#card.info').append(cardForm.render().el); + blink(element); + } + if (((_ref4 = window.USER) != null ? _ref4.confirm_email : void 0) === false) { + element = $('#confirmed_email'); + blink(element); + } + if ((((_ref5 = window.USER) != null ? _ref5.confirm_mobile : void 0) != null) === false) { + element = $('#confirmed_mobile'); + return blink(element); + } + }; + ClientsDashboardView.prototype.confirmationClick = function(e) { + e.preventDefault(); + $('.info').hide(); + $('#more_info').show(); + switch (e.currentTarget.id) { + case "card": + return $('#card.info').slideToggle(); + case "mobile": + return $('#mobile.info').slideToggle(); + case "email": + return $('#email.info').slideToggle(); + } + }; + ClientsDashboardView.prototype.resendEmail = function(e) { + var $el; + e.preventDefault(); + $el = $(e.currentTarget); + $el.removeAttr('href').prop({ + disabled: true + }); + $el.html(t("Sending Email")); + return $.ajax({ + type: 'GET', + url: API + '/users/request_confirm_email', + data: { + token: USER.token + }, + dataType: 'json', + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Resend Email Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.html(t("Resend Email Failed")); + }, this) + }); + }; + ClientsDashboardView.prototype.resendMobile = function(e) { + var $el; + e.preventDefault(); + $el = $(e.currentTarget); + $el.removeAttr('href').prop({ + disabled: true + }); + $el.html("Sending message..."); + return $.ajax({ + type: 'GET', + url: API + '/users/request_confirm_mobile', + data: { + token: USER.token + }, + dataType: 'json', + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Resend Text Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.html(t("Resend Text Failed")); + }, this) + }); + }; + ClientsDashboardView.prototype.showAllTrips = function(e) { + e.preventDefault(); + $(e.currentTarget).hide(); + return this.DownloadUserTrips(this.render, true, 1000); + }; + return ClientsDashboardView; + }).call(this); +}).call(this); +}, "views/clients/forgot_password": function(exports, require, module) {(function() { + var clientsForgotPasswordTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsForgotPasswordTemplate = require('templates/clients/forgot_password'); + exports.ClientsForgotPasswordView = (function() { + __extends(ClientsForgotPasswordView, UberView); + function ClientsForgotPasswordView() { + ClientsForgotPasswordView.__super__.constructor.apply(this, arguments); + } + ClientsForgotPasswordView.prototype.id = 'forgotpassword_view'; + ClientsForgotPasswordView.prototype.className = 'view_container modal_view_container'; + ClientsForgotPasswordView.prototype.events = { + "submit #password_reset": "passwordReset", + "click #password_reset_submit": "passwordReset", + "submit #forgot_password": "forgotPassword", + "click #forgot_password_submit": "forgotPassword" + }; + ClientsForgotPasswordView.prototype.render = function(token) { + this.HideSpinner(); + $(this.el).html(clientsForgotPasswordTemplate({ + token: token + })); + this.delegateEvents(); + return this; + }; + ClientsForgotPasswordView.prototype.forgotPassword = function(e) { + var attrs; + e.preventDefault(); + $('.success_message').hide(); + $(".error_message").hide(); + attrs = { + data: { + login: $("#login").val() + }, + success: __bind(function(data, textStatus, jqXHR) { + this.HideSpinner(); + $('.success_message').show(); + return $("#forgot_password").hide(); + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('.error_message').show(); + }, this), + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/forgot_password" + }; + $.ajax(attrs); + return this.ShowSpinner('submit'); + }; + ClientsForgotPasswordView.prototype.passwordReset = function(e) { + var attrs; + e.preventDefault(); + attrs = { + data: { + email_token: $("#token").val(), + password: $("#password").val() + }, + success: __bind(function(data, textStatus, jqXHR) { + this.HideSpinner(); + $.cookie('token', data.token); + amplify.store('USERjson', data); + app.refreshMenu(); + return location.hash = '!/dashboard'; + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('#error_reset').show(); + }, this), + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/self" + }; + $.ajax(attrs); + return this.ShowSpinner('submit'); + }; + return ClientsForgotPasswordView; + })(); +}).call(this); +}, "views/clients/invite": function(exports, require, module) {(function() { + var clientsInviteTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsInviteTemplate = require('templates/clients/invite'); + exports.ClientsInviteView = (function() { + __extends(ClientsInviteView, UberView); + function ClientsInviteView() { + ClientsInviteView.__super__.constructor.apply(this, arguments); + } + ClientsInviteView.prototype.id = 'invite_view'; + ClientsInviteView.prototype.className = 'view_container'; + ClientsInviteView.prototype.render = function() { + this.ReadUserInfo(); + this.HideSpinner(); + $(this.el).html(clientsInviteTemplate()); + console.log(screen); + return this; + }; + return ClientsInviteView; + })(); +}).call(this); +}, "views/clients/login": function(exports, require, module) {(function() { + var clientsLoginTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsLoginTemplate = require('templates/clients/login'); + exports.ClientsLoginView = (function() { + __extends(ClientsLoginView, UberView); + function ClientsLoginView() { + ClientsLoginView.__super__.constructor.apply(this, arguments); + } + ClientsLoginView.prototype.id = 'login_view'; + ClientsLoginView.prototype.className = 'view_container modal_view_container'; + ClientsLoginView.prototype.events = { + 'submit form': 'authenticate', + 'click button': 'authenticate' + }; + ClientsLoginView.prototype.initialize = function() { + _.bindAll(this, 'render'); + return this.render(); + }; + ClientsLoginView.prototype.render = function() { + this.HideSpinner(); + $(this.el).html(clientsLoginTemplate()); + this.delegateEvents(); + return this.place(); + }; + ClientsLoginView.prototype.authenticate = function(e) { + e.preventDefault(); + return $.ajax({ + type: 'POST', + url: API + '/auth/web_login/client', + data: { + login: $("#login").val(), + password: $("#password").val() + }, + dataType: 'json', + success: function(data, textStatus, jqXHR) { + $.cookie('user', JSON.stringify(data)); + $.cookie('token', data.token); + amplify.store('USERjson', data); + $('header').html(app.views.shared.menu.render().el); + return app.routers.clients.navigate('!/dashboard', true); + }, + error: function(jqXHR, textStatus, errorThrown) { + $.cookie('user', null); + $.cookie('token', null); + if (jqXHR.status === 403) { + $.cookie('redirected_user', JSON.stringify(JSON.parse(jqXHR.responseText).error_obj), { + domain: '.uber.com' + }); + window.location = 'http://partners.uber.com/'; + } + return $('.error_message').html(JSON.parse(jqXHR.responseText).error).hide().fadeIn(); + } + }); + }; + return ClientsLoginView; + })(); +}).call(this); +}, "views/clients/modules/credit_card": function(exports, require, module) {(function() { + var creditCardTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + creditCardTemplate = require('templates/clients/modules/credit_card'); + exports.CreditCardView = (function() { + __extends(CreditCardView, UberView); + function CreditCardView() { + CreditCardView.__super__.constructor.apply(this, arguments); + } + CreditCardView.prototype.id = 'creditcard_view'; + CreditCardView.prototype.className = 'module_container'; + CreditCardView.prototype.events = { + 'submit #credit_card_form': 'processNewCard', + 'click #new_card': 'processNewCard', + 'change #card_number': 'showCardType', + 'click .edit_card_show': 'showEditCard', + 'click .edit_card': 'editCard', + 'click .delete_card': 'deleteCard', + 'click .make_default': 'makeDefault', + 'change .use_case': 'saveUseCase' + }; + CreditCardView.prototype.initialize = function() { + return app.collections.paymentprofiles.bind("refresh", __bind(function() { + return this.RefreshUserInfo(__bind(function() { + this.render("all"); + return this.HideSpinner(); + }, this)); + }, this)); + }; + CreditCardView.prototype.render = function(cards) { + if (cards == null) { + cards = "new"; + } + if (cards === "all") { + app.collections.paymentprofiles.reset(USER.payment_gateway.payment_profiles); + cards = app.collections.paymentprofiles; + } + $(this.el).html(creditCardTemplate({ + cards: cards + })); + return this; + }; + CreditCardView.prototype.processNewCard = function(e) { + var $el, attrs, model, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $("#credit_card_form"); + $el.find('.error_message').html(""); + attrs = { + card_number: $el.find('#card_number').val(), + card_code: $el.find('#card_code').val(), + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + use_case: $el.find('#use_case').val(), + "default": $el.find('#default_check').prop("checked") + }; + options = { + statusCode: { + 200: __bind(function(e) { + this.HideSpinner(); + $('#cc_form_wrapper').hide(); + app.collections.paymentprofiles.trigger("refresh"); + $(this.el).remove(); + $("a#add_card").show(); + return $('section').html(app.views.clients.billing.render().el); + }, this), + 406: __bind(function(e) { + var error, errors, _i, _len, _ref, _results; + this.HideSpinner(); + errors = JSON.parse(e.responseText); + _ref = _.keys(errors); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + error = _ref[_i]; + _results.push(error === "top_of_form" ? $("#top_of_form").html(errors[error]) : $("#credit_card_form").find("#" + error).parent().find(".error_message").html(errors[error])); + } + return _results; + }, this), + 420: __bind(function(e) { + this.HideSpinner(); + return $("#top_of_form").html(t("Unable to Verify Card")); + }, this) + } + }; + this.ShowSpinner("submit"); + model = new app.models.paymentprofile; + model.save(attrs, options); + return app.collections.paymentprofiles.add(model); + }; + CreditCardView.prototype.showCardType = function(e) { + var $el, reAmerica, reDiscover, reMaster, reVisa, validCard; + reVisa = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/; + reMaster = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/; + reAmerica = /^6011-?\d{4}-?\d{4}-?\d{4}$/; + reDiscover = /^3[4,7]\d{13}$/; + $el = $("#card_logos"); + validCard = false; + if (e.currentTarget.value.match(reVisa)) { + validCard = true; + } else if (e.currentTarget.value.match(reMaster)) { + $el.css('background-position', "-60px"); + validCard = true; + } else if (e.currentTarget.value.match(reAmerica)) { + $el.css('background-position', "-120px"); + validCard = true; + } else if (e.currentTarget.value.match(reDiscover)) { + $el.css('background-position', "-180px"); + validCard = true; + } + if (validCard) { + $el.css('width', "60px"); + return $el.css('margin-left', "180px"); + } else { + $el.css('width', "250px"); + return $el.css('margin-left', "80px"); + } + }; + CreditCardView.prototype.showEditCard = function(e) { + var $el, id; + e.preventDefault(); + $el = $(e.currentTarget); + if ($el.html() === t("Edit")) { + id = $el.html(t("Cancel")).parents("tr").attr("id").substring(1); + return $("#e" + id).show(); + } else { + id = $el.html(t("Edit")).parents("tr").attr("id").substring(1); + return $("#e" + id).hide(); + } + }; + CreditCardView.prototype.editCard = function(e) { + var $el, attrs, id, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + $el.attr('disabled', 'disabled'); + this.ShowSpinner('submit'); + attrs = { + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + card_code: $el.find('#card_code').val() + }; + options = { + success: __bind(function(response) { + this.HideSpinner(); + this.ShowSuccess(t("Credit Card Update Succeeded")); + $("#e" + id).hide(); + $("#d" + id).find(".edit_card_show").html(t("Edit")); + return app.collections.paymentprofiles.trigger("refresh"); + }, this), + error: __bind(function(e) { + this.HideSpinner(); + this.ShowError(t("Credit Card Update Failed")); + return $el.removeAttr('disabled'); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + CreditCardView.prototype.deleteCard = function(e) { + var $el, id, options; + e.preventDefault(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + this.ClearGlobalStatus(); + this.ShowSpinner('submit'); + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Credit Card Delete Succeeded")); + $("form").hide(); + app.collections.paymentprofiles.trigger("refresh"); + return $('section').html(app.views.clients.billing.render().el); + }, this), + error: __bind(function(xhr, e) { + this.HideSpinner(); + return this.ShowError(t("Credit Card Delete Failed")); + }, this) + }; + return app.collections.paymentprofiles.models[id].destroy(options); + }; + CreditCardView.prototype.saveUseCase = function(e) { + var $el, attrs, id, options, use_case; + this.ClearGlobalStatus(); + $el = $(e.currentTarget); + use_case = $el.val(); + id = $el.parents("tr").attr("id").substring(1); + attrs = { + use_case: use_case + }; + options = { + success: __bind(function(response) { + return this.ShowSuccess(t("Credit Card Update Category Succeeded")); + }, this), + error: __bind(function(e) { + return this.ShowError(t("Credit Card Update Category Failed")); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + CreditCardView.prototype.makeDefault = function(e) { + var $el, attrs, id, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + attrs = { + "default": true + }; + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Credit Card Update Default Succeeded")); + return app.collections.paymentprofiles.trigger("refresh"); + }, this), + error: __bind(function(e) { + return this.ShowError(t("Credit Card Update Default Failed")); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + return CreditCardView; + })(); +}).call(this); +}, "views/clients/promotions": function(exports, require, module) {(function() { + var clientsPromotionsTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsPromotionsTemplate = require('templates/clients/promotions'); + exports.ClientsPromotionsView = (function() { + __extends(ClientsPromotionsView, UberView); + function ClientsPromotionsView() { + this.render = __bind(this.render, this); + ClientsPromotionsView.__super__.constructor.apply(this, arguments); + } + ClientsPromotionsView.prototype.id = 'promotions_view'; + ClientsPromotionsView.prototype.className = 'view_container'; + ClientsPromotionsView.prototype.events = { + 'submit form': 'submitPromo', + 'click button': 'submitPromo' + }; + ClientsPromotionsView.prototype.initialize = function() { + if (this.model) { + return this.RefreshUserInfo(this.render); + } + }; + ClientsPromotionsView.prototype.render = function() { + var renderTemplate; + this.ReadUserInfo(); + renderTemplate = __bind(function() { + $(this.el).html(clientsPromotionsTemplate({ + promos: window.USER.unexpired_client_promotions || [] + })); + return this.HideSpinner(); + }, this); + this.DownloadUserPromotions(renderTemplate); + return this; + }; + ClientsPromotionsView.prototype.submitPromo = function(e) { + var attrs, model, options, refreshTable; + e.preventDefault(); + this.ClearGlobalStatus(); + refreshTable = __bind(function() { + $('section').html(this.render().el); + return this.HideSpinner(); + }, this); + attrs = { + code: $('#code').val() + }; + options = { + success: __bind(function(response) { + this.HideSpinner(); + if (response.get('first_name')) { + return this.ShowSuccess("Your promotion has been applied in the form of an account credit. Click here to check your balance."); + } else { + this.ShowSuccess("Your promotion has successfully been applied"); + return this.RefreshUserInfo(this.render, true); + } + }, this), + statusCode: { + 400: __bind(function(e) { + this.ShowError(JSON.parse(e.responseText).error); + return this.HideSpinner(); + }, this) + } + }; + this.ShowSpinner("submit"); + model = new app.models.promotions; + return model.save(attrs, options); + }; + return ClientsPromotionsView; + })(); +}).call(this); +}, "views/clients/request": function(exports, require, module) {(function() { + var clientsRequestTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsRequestTemplate = require('templates/clients/request'); + exports.ClientsRequestView = (function() { + __extends(ClientsRequestView, UberView); + function ClientsRequestView() { + this.AjaxCall = __bind(this.AjaxCall, this); + this.AskDispatch = __bind(this.AskDispatch, this); + this.removeMarkers = __bind(this.removeMarkers, this); + this.displaySearchLoc = __bind(this.displaySearchLoc, this); + this.displayFavLoc = __bind(this.displayFavLoc, this); + this.showFavLoc = __bind(this.showFavLoc, this); + this.addToFavLoc = __bind(this.addToFavLoc, this); + this.removeCabs = __bind(this.removeCabs, this); + this.requestRide = __bind(this.requestRide, this); + this.rateTrip = __bind(this.rateTrip, this); + this.locationChange = __bind(this.locationChange, this); + this.panToLocation = __bind(this.panToLocation, this); + this.clickLocation = __bind(this.clickLocation, this); + this.searchLocation = __bind(this.searchLocation, this); + this.mouseoutLocation = __bind(this.mouseoutLocation, this); + this.mouseoverLocation = __bind(this.mouseoverLocation, this); + this.fetchTripDetails = __bind(this.fetchTripDetails, this); + this.submitRating = __bind(this.submitRating, this); + this.setStatus = __bind(this.setStatus, this); + this.initialize = __bind(this.initialize, this); + ClientsRequestView.__super__.constructor.apply(this, arguments); + } + ClientsRequestView.prototype.id = 'request_view'; + ClientsRequestView.prototype.className = 'view_container'; + ClientsRequestView.prototype.pollInterval = 2 * 1000; + ClientsRequestView.prototype.events = { + "submit #search_form": "searchAddress", + "click .locations_link": "locationLinkHandle", + "mouseover .location_row": "mouseoverLocation", + "mouseout .location_row": "mouseoutLocation", + "click .location_row": "clickLocation", + "click #search_location": "searchLocation", + "click #pickupHandle": "pickupHandle", + "click .stars": "rateTrip", + "submit #rating_form": "submitRating", + "click #addToFavButton": "showFavLoc", + "click #favLocNickname": "selectInputText", + "submit #favLoc_form": "addToFavLoc" + }; + ClientsRequestView.prototype.status = ""; + ClientsRequestView.prototype.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker.png"; + ClientsRequestView.prototype.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker.png"; + ClientsRequestView.prototype.initialize = function() { + var displayCabs; + displayCabs = __bind(function() { + return this.AskDispatch("NearestCab"); + }, this); + this.showCabs = _.throttle(displayCabs, this.pollInterval); + return this.numSearchToDisplay = 1; + }; + ClientsRequestView.prototype.setStatus = function(status) { + var autocomplete; + if (this.status === status) { + return; + } + try { + google.maps.event.trigger(this.map, 'resize'); + } catch (_e) {} + switch (status) { + case "init": + this.AskDispatch("StatusClient"); + this.status = "init"; + return this.ShowSpinner("load"); + case "ready": + this.HideSpinner(); + $(".panel").hide(); + $("#top_bar").fadeIn(); + $("#location_panel").fadeIn(); + $("#location_panel_control").fadeIn(); + $("#pickupHandle").attr("class", "button_green").fadeIn().find("span").html(t("Request Pickup")); + this.pickup_icon.setDraggable(true); + this.map.panTo(this.pickup_icon.getPosition()); + this.showCabs(); + try { + this.pickup_icon.setMap(this.map); + this.displayFavLoc(); + autocomplete = new google.maps.places.Autocomplete(document.getElementById('address'), { + types: ['geocode'] + }); + autocomplete.bindTo('bounds', this.map); + } catch (_e) {} + return this.status = "ready"; + case "searching": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#status_message").html(t("Requesting Closest Driver")); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "searching"; + case "waiting": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "waiting"; + case "arriving": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "arriving"; + case "riding": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").fadeIn().attr("class", "button_red").find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.status = "riding"; + return $("#status_message").html(t("En Route")); + case "rate": + this.HideSpinner(); + $(".panel").hide(); + $("#pickupHandle").fadeOut(); + $("#trip_completed_panel").fadeIn(); + $('#status_message').html(t("Rate Last Trip")); + return this.status = "rate"; + } + }; + ClientsRequestView.prototype.render = function() { + this.ReadUserInfo(); + this.HideSpinner(); + this.ShowSpinner("load"); + $(this.el).html(clientsRequestTemplate()); + this.cabs = []; + this.RequireMaps(__bind(function() { + var center, myOptions, streetViewPano; + center = new google.maps.LatLng(37.7749295, -122.4194155); + this.markers = []; + this.pickup_icon = new google.maps.Marker({ + position: center, + draggable: true, + clickable: true, + icon: this.pickupMarker + }); + this.geocoder = new google.maps.Geocoder(); + myOptions = { + zoom: 12, + center: center, + mapTypeId: google.maps.MapTypeId.ROADMAP, + rotateControl: false, + rotateControl: false, + panControl: false + }; + this.map = new google.maps.Map($(this.el).find("#map_wrapper_right")[0], myOptions); + if (this.status === "ready") { + this.pickup_icon.setMap(this.map); + } + if (geo_position_js.init()) { + geo_position_js.getCurrentPosition(__bind(function(data) { + var location; + location = new google.maps.LatLng(data.coords.latitude, data.coords.longitude); + this.pickup_icon.setPosition(location); + this.map.panTo(location); + return this.map.setZoom(16); + }, this)); + } + this.setStatus("init"); + streetViewPano = this.map.getStreetView(); + google.maps.event.addListener(streetViewPano, 'visible_changed', __bind(function() { + if (streetViewPano.getVisible()) { + this.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker_large.png"; + this.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker_large.png"; + } else { + this.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker.png"; + this.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker.png"; + } + this.pickup_icon.setIcon(this.pickupMarker); + return _.each(this.cabs, __bind(function(cab) { + return cab.setIcon(this.cabMarker); + }, this)); + }, this)); + if (this.status === "ready") { + return this.displayFavLoc(); + } + }, this)); + return this; + }; + ClientsRequestView.prototype.submitRating = function(e) { + var $el, message, rating; + e.preventDefault(); + $el = $(e.currentTarget); + rating = 0; + _(5).times(function(num) { + if ($el.find(".stars#" + (num + 1)).attr("src") === "/web/img/star_active.png") { + return rating = num + 1; + } + }); + if (rating === 0) { + $("#status_message").html("").html(t("Rate Before Submitting")); + } else { + this.ShowSpinner("submit"); + this.AskDispatch("RatingDriver", { + rating: rating + }); + } + message = $el.find("#comments").val().toString(); + if (message.length > 5) { + return this.AskDispatch("Feedback", { + message: message + }); + } + }; + ClientsRequestView.prototype.fetchTripDetails = function(id) { + var trip; + trip = new app.models.trip({ + id: id + }); + return trip.fetch({ + data: { + relationships: 'points,driver,city' + }, + dataType: 'json', + success: __bind(function() { + var bounds, endPos, path, polyline, startPos; + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(trip.get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + startPos = new google.maps.Marker({ + position: _.first(path), + map: this.map, + title: t("Trip started here"), + icon: 'https://uber-static.s3.amazonaws.com/carstart.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: this.map, + title: t("Trip ended here"), + icon: 'https://uber-static.s3.amazonaws.com/carstop.png' + }); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + polyline.setMap(this.map); + this.map.fitBounds(bounds); + $("#tripTime").html(app.helpers.parseDateTime(trip.get('pickup_local_time'), trip.get('city.timezone'))); + $("#tripDist").html(app.helpers.RoundNumber(trip.get('distance'), 2)); + $("#tripDur").html(app.helpers.FormatSeconds(trip.get('duration'))); + return $("#tripFare").html(app.helpers.FormatCurrency(trip.get('fare'))); + }, this) + }); + }; + ClientsRequestView.prototype.searchAddress = function(e) { + var $locationsDiv, address, alphabet, bounds, showResults; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + try { + e.preventDefault(); + } catch (_e) {} + $('.error_message').html(""); + $locationsDiv = $("
    "); + address = $('#address').val(); + bounds = new google.maps.LatLngBounds(); + if (address.length < 5) { + $('#status_message').html(t("Address too short")).fadeIn(); + return false; + } + showResults = __bind(function(address, index) { + var first_cell, row, second_cell; + if (index < this.numSearchToDisplay) { + first_cell = ""; + second_cell = "" + address.formatted_address + ""; + row = $("").attr("id", "s" + index).attr("class", "location_row").html(first_cell + second_cell); + $locationsDiv.append(row); + } + if (index === this.numSearchToDisplay) { + $locationsDiv.append("" + (t('or did you mean')) + " "); + return $locationsDiv.append("" + address.formatted_address + ""); + } + }, this); + return this.geocoder.geocode({ + address: address + }, __bind(function(result, status) { + if (status !== "OK") { + $('.error_message').html(t("Search Address Failed")).fadeIn(); + return; + } + _.each(result, showResults); + $("#search_results").html($locationsDiv); + this.locationChange("search"); + this.searchResults = result; + return this.displaySearchLoc(); + }, this)); + }; + ClientsRequestView.prototype.mouseoverLocation = function(e) { + var $el, id, marker; + $el = $(e.currentTarget); + id = $el.attr("id").substring(1); + marker = this.markers[id]; + return marker.setAnimation(google.maps.Animation.BOUNCE); + }; + ClientsRequestView.prototype.mouseoutLocation = function(e) { + var $el, id, marker; + $el = $(e.currentTarget); + id = $el.attr("id").substring(1); + marker = this.markers[id]; + return marker.setAnimation(null); + }; + ClientsRequestView.prototype.searchLocation = function(e) { + e.preventDefault(); + $("#address").val($(e.currentTarget).html()); + return this.searchAddress(); + }; + ClientsRequestView.prototype.favoriteClick = function(e) { + var index, location; + e.preventDefault(); + $(".favorites").attr("href", ""); + index = $(e.currentTarget).removeAttr("href").attr("id"); + location = new google.maps.LatLng(USER.locations[index].latitude, USER.locations[index].longitude); + return this.panToLocation(location); + }; + ClientsRequestView.prototype.clickLocation = function(e) { + var id; + id = $(e.currentTarget).attr("id").substring(1); + return this.panToLocation(this.markers[id].getPosition()); + }; + ClientsRequestView.prototype.panToLocation = function(location) { + this.map.panTo(location); + this.map.setZoom(16); + return this.pickup_icon.setPosition(location); + }; + ClientsRequestView.prototype.locationLinkHandle = function(e) { + var panelName; + e.preventDefault(); + panelName = $(e.currentTarget).attr("id"); + return this.locationChange(panelName); + }; + ClientsRequestView.prototype.locationChange = function(type) { + $(".locations_link").attr("href", "").css("font-weight", "normal"); + switch (type) { + case "favorite": + $(".search_results").attr("href", ""); + $(".locations_link#favorite").removeAttr("href").css("font-weight", "bold"); + $("#search_results").hide(); + $("#favorite_results").fadeIn(); + return this.displayFavLoc(); + case "search": + $(".favorites").attr("href", ""); + $(".locations_link#search").removeAttr("href").css("font-weight", "bold"); + $("#favorite_results").hide(); + $("#search_results").fadeIn(); + return this.displaySearchLoc(); + } + }; + ClientsRequestView.prototype.rateTrip = function(e) { + var rating; + rating = $(e.currentTarget).attr("id"); + $(".stars").attr("src", "/web/img/star_inactive.png"); + return _(rating).times(function(index) { + return $(".stars#" + (index + 1)).attr("src", "/web/img/star_active.png"); + }); + }; + ClientsRequestView.prototype.pickupHandle = function(e) { + var $el, callback, message; + e.preventDefault(); + $el = $(e.currentTarget).find("span"); + switch ($el.html()) { + case t("Request Pickup"): + _.delay(this.requestRide, 3000); + $("#status_message").html(t("Sending pickup request...")); + $el.html(t("Cancel Pickup")).parent().attr("class", "button_red"); + this.pickup_icon.setDraggable(false); + this.map.panTo(this.pickup_icon.getPosition()); + return this.map.setZoom(18); + case t("Cancel Pickup"): + if (this.status === "ready") { + $el.html(t("Request Pickup")).parent().attr("class", "button_green"); + return this.pickup_icon.setDraggable(true); + } else { + callback = __bind(function(v, m, f) { + if (v) { + this.AskDispatch("PickupCanceledClient"); + return this.setStatus("ready"); + } + }, this); + message = t("Cancel Request Prompt"); + if (this.status === "arriving") { + message = 'Cancel Request Arrived Prompt'; + } + return $.prompt(message, { + buttons: { + Ok: true, + Cancel: false + }, + callback: callback + }); + } + } + }; + ClientsRequestView.prototype.requestRide = function() { + if ($("#pickupHandle").find("span").html() === t("Cancel Pickup")) { + this.AskDispatch("Pickup"); + return this.setStatus("searching"); + } + }; + ClientsRequestView.prototype.removeCabs = function() { + _.each(this.cabs, __bind(function(point) { + return point.setMap(null); + }, this)); + return this.cabs = []; + }; + ClientsRequestView.prototype.addToFavLoc = function(e) { + var $el, lat, lng, nickname; + e.preventDefault(); + $el = $(e.currentTarget); + $el.find(".error_message").html(""); + nickname = $el.find("#favLocNickname").val().toString(); + lat = $el.find("#pickupLat").val().toString(); + lng = $el.find("#pickupLng").val().toString(); + if (nickname.length < 3) { + $el.find(".error_message").html(t("Favorite Location Nickname Length Error")); + return; + } + this.ShowSpinner("submit"); + return $.ajax({ + type: 'POST', + url: API + "/locations", + dataType: 'json', + data: { + token: USER.token, + nickname: nickname, + latitude: lat, + longitude: lng + }, + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Favorite Location Save Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.find(".error_message").html(t("Favorite Location Save Failed")); + }, this), + complete: __bind(function(data) { + return this.HideSpinner(); + }, this) + }); + }; + ClientsRequestView.prototype.showFavLoc = function(e) { + $(e.currentTarget).fadeOut(); + return $("#favLoc_form").fadeIn(); + }; + ClientsRequestView.prototype.selectInputText = function(e) { + e.currentTarget.focus(); + return e.currentTarget.select(); + }; + ClientsRequestView.prototype.displayFavLoc = function() { + var alphabet, bounds; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.removeMarkers(); + bounds = new google.maps.LatLngBounds(); + _.each(USER.locations, __bind(function(location, index) { + var marker; + marker = new google.maps.Marker({ + position: new google.maps.LatLng(location.latitude, location.longitude), + map: this.map, + title: t("Favorite Location Title", { + id: alphabet != null ? alphabet[index] : void 0 + }), + icon: "https://www.google.com/mapfiles/marker" + alphabet[index] + ".png" + }); + this.markers.push(marker); + bounds.extend(marker.getPosition()); + return google.maps.event.addListener(marker, 'click', __bind(function() { + return this.pickup_icon.setPosition(marker.getPosition()); + }, this)); + }, this)); + this.pickup_icon.setPosition(_.first(this.markers).getPosition()); + return this.map.fitBounds(bounds); + }; + ClientsRequestView.prototype.displaySearchLoc = function() { + var alphabet; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.removeMarkers(); + return _.each(this.searchResults, __bind(function(result, index) { + var marker; + if (index < this.numSearchToDisplay) { + marker = new google.maps.Marker({ + position: result.geometry.location, + map: this.map, + title: t("Search Location Title", { + id: alphabet != null ? alphabet[index] : void 0 + }), + icon: "https://www.google.com/mapfiles/marker" + alphabet[index] + ".png" + }); + this.markers.push(marker); + return this.panToLocation(result.geometry.location); + } + }, this)); + }; + ClientsRequestView.prototype.removeMarkers = function() { + _.each(this.markers, __bind(function(marker) { + return marker.setMap(null); + }, this)); + return this.markers = []; + }; + ClientsRequestView.prototype.AskDispatch = function(ask, options) { + var attrs, lowestETA, processData, showCab; + if (ask == null) { + ask = ""; + } + if (options == null) { + options = {}; + } + switch (ask) { + case "NearestCab": + attrs = { + latitude: this.pickup_icon.getPosition().lat(), + longitude: this.pickup_icon.getPosition().lng() + }; + lowestETA = 99999; + showCab = __bind(function(cab) { + var point; + point = new google.maps.Marker({ + position: new google.maps.LatLng(cab.latitude, cab.longitude), + map: this.map, + icon: this.cabMarker, + title: t("ETA Message", { + minutes: app.helpers.FormatSeconds(cab != null ? cab.eta : void 0, true) + }) + }); + if (cab.eta < lowestETA) { + lowestETA = cab.eta; + } + return this.cabs.push(point); + }, this); + processData = __bind(function(data, textStatus, jqXHR) { + if (this.status === "ready") { + this.removeCabs(); + if (data.sorry) { + $("#status_message").html(data.sorry).fadeIn(); + } else { + _.each(data.driverLocations, showCab); + $("#status_message").html(t("Nearest Cab Message", { + minutes: app.helpers.FormatSeconds(lowestETA, true) + })).fadeIn(); + } + if (Backbone.history.fragment === "!/request") { + return _.delay(this.showCabs, this.pollInterval); + } + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "StatusClient": + processData = __bind(function(data, textStatus, jqXHR) { + var bounds, cabLocation, locationSaved, point, userLocation; + if (data.messageType === "OK") { + switch (data.status) { + case "completed": + this.removeCabs(); + this.setStatus("rate"); + return this.fetchTripDetails(data.tripID); + case "open": + return this.setStatus("ready"); + case "begintrip": + this.setStatus("riding"); + cabLocation = new google.maps.LatLng(data.latitude, data.longitude); + this.removeCabs(); + this.pickup_icon.setMap(null); + point = new google.maps.Marker({ + position: cabLocation, + map: this.map, + icon: this.cabMarker + }); + this.cabs.push(point); + this.map.panTo(point.getPosition()); + $("#rideName").html(data.driverName); + $("#ridePhone").html(data.driverMobile); + $("#ride_address_wrapper").hide(); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + break; + case "pending": + this.setStatus("searching"); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + break; + case "accepted": + case "arrived": + if (data.status === "accepted") { + this.setStatus("waiting"); + $("#status_message").html(t("Arrival ETA Message", { + minutes: app.helpers.FormatSeconds(data.eta, true) + })); + } else { + this.setStatus("arriving"); + $("#status_message").html(t("Arriving Now Message")); + } + userLocation = new google.maps.LatLng(data.pickupLocation.latitude, data.pickupLocation.longitude); + cabLocation = new google.maps.LatLng(data.latitude, data.longitude); + this.pickup_icon.setPosition(userLocation); + this.removeCabs(); + $("#rideName").html(data.driverName); + $("#ridePhone").html(data.driverMobile); + if ($("#rideAddress").html() === "") { + locationSaved = false; + _.each(USER.locations, __bind(function(location) { + if (parseFloat(location.latitude) === parseFloat(data.pickupLocation.latitude) && parseFloat(location.longitude) === parseFloat(data.pickupLocation.longitude)) { + return locationSaved = true; + } + }, this)); + if (locationSaved) { + $("#addToFavButton").hide(); + } + $("#pickupLat").val(data.pickupLocation.latitude); + $("#pickupLng").val(data.pickupLocation.longitude); + this.geocoder.geocode({ + location: userLocation + }, __bind(function(result, status) { + $("#rideAddress").html(result[0].formatted_address); + return $("#favLocNickname").val("" + result[0].address_components[0].short_name + " " + result[0].address_components[1].short_name); + }, this)); + } + point = new google.maps.Marker({ + position: cabLocation, + map: this.map, + icon: this.cabMarker + }); + this.cabs.push(point); + bounds = bounds = new google.maps.LatLngBounds(); + bounds.extend(cabLocation); + bounds.extend(userLocation); + this.map.fitBounds(bounds); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + } + } + }, this); + return this.AjaxCall(ask, processData); + case "Pickup": + attrs = { + latitude: this.pickup_icon.getPosition().lat(), + longitude: this.pickup_icon.getPosition().lng() + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "Error") { + return $("#status_message").html(data.description); + } else { + return this.AskDispatch("StatusClient"); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "PickupCanceledClient": + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + return this.setStatus("ready"); + } else { + return $("#status_message").html(data.description); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "RatingDriver": + attrs = { + rating: options.rating + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + this.setStatus("init"); + } else { + $("status_message").html(t("Rating Driver Failed")); + } + return this.HideSpinner(); + }, this); + return this.AjaxCall(ask, processData, attrs); + case "Feedback": + attrs = { + message: options.message + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + return alert("rated"); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + } + }; + ClientsRequestView.prototype.AjaxCall = function(type, successCallback, attrs) { + if (attrs == null) { + attrs = {}; + } + _.extend(attrs, { + token: USER.token, + messageType: type, + app: "client", + version: "1.0.60", + device: "web" + }); + return $.ajax({ + type: 'POST', + url: DISPATCH + "/", + processData: false, + data: JSON.stringify(attrs), + success: successCallback, + dataType: 'json', + error: __bind(function(jqXHR, textStatus, errorThrown) { + $("#status_message").html(errorThrown); + return this.HideSpinner(); + }, this) + }); + }; + return ClientsRequestView; + })(); +}).call(this); +}, "views/clients/settings": function(exports, require, module) {(function() { + var clientsSettingsTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsSettingsTemplate = require('templates/clients/settings'); + exports.ClientsSettingsView = (function() { + __extends(ClientsSettingsView, UberView); + function ClientsSettingsView() { + this.render = __bind(this.render, this); + this.initialize = __bind(this.initialize, this); + ClientsSettingsView.__super__.constructor.apply(this, arguments); + } + ClientsSettingsView.prototype.id = 'settings_view'; + ClientsSettingsView.prototype.className = 'view_container'; + ClientsSettingsView.prototype.events = { + 'submit #profile_pic_form': 'processPicUpload', + 'click #submit_pic': 'processPicUpload', + 'click a.setting_change': "changeTab", + 'submit #edit_info_form': "submitInfo", + 'click #change_password': 'changePass' + }; + ClientsSettingsView.prototype.divs = { + 'info_div': "Information", + 'pic_div': "Picture" + }; + ClientsSettingsView.prototype.pageTitle = t("Settings") + " | " + t("Uber"); + ClientsSettingsView.prototype.tabTitle = { + 'info_div': t("Information"), + 'pic_div': t("Picture") + }; + ClientsSettingsView.prototype.initialize = function() { + return this.mixin(require('web-lib/mixins/i18n_phone_form').i18nPhoneForm); + }; + ClientsSettingsView.prototype.render = function(type) { + if (type == null) { + type = "info"; + } + this.RefreshUserInfo(__bind(function() { + var $el, alphabet; + this.delegateEvents(); + this.HideSpinner(); + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + $el = $(this.el); + $(this.el).html(clientsSettingsTemplate({ + type: type + })); + $el.find("#" + type + "_div").show(); + $el.find("a[href='" + type + "_div']").parent().addClass("active"); + return document.title = "" + this.tabTitle[type + '_div'] + " " + this.pageTitle; + }, this)); + this.delegateEvents(); + return this; + }; + ClientsSettingsView.prototype.changeTab = function(e) { + var $eTarget, $el, div, link, pageDiv, _i, _j, _len, _len2, _ref, _ref2; + e.preventDefault(); + $eTarget = $(e.currentTarget); + this.ClearGlobalStatus(); + $el = $(this.el); + _ref = $el.find(".setting_change"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + $(link).parent().removeClass("active"); + } + $eTarget.parent().addClass("active"); + _ref2 = _.keys(this.divs); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + div = _ref2[_j]; + $el.find("#" + div).hide(); + } + pageDiv = $eTarget.attr('href'); + $el.find("#" + pageDiv).show(); + Backbone.history.navigate("!/settings/" + (this.divs[pageDiv].toLowerCase().replace(" ", "-")), false); + document.title = "" + this.tabTitle[pageDiv] + " " + this.pageTitle; + if (pageDiv === "loc_div") { + try { + google.maps.event.trigger(this.map, 'resize'); + return this.map.fitBounds(this.bounds); + } catch (_e) {} + } + }; + ClientsSettingsView.prototype.submitInfo = function(e) { + var $e, attrs, client, options; + $('#global_status').find('.success_message').text(''); + $('#global_status').find('.error_message').text(''); + $('.error_message').text(''); + e.preventDefault(); + $e = $(e.currentTarget); + attrs = $e.serializeToJson(); + attrs['mobile_country_id'] = this.$('#mobile_country_id').val(); + if (attrs['password'] === '') { + delete attrs['password']; + } + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Information Update Succeeded")); + return this.RefreshUserInfo(); + }, this), + error: __bind(function(model, data) { + var errors; + if (data.status === 406) { + errors = JSON.parse(data.responseText); + return _.each(_.keys(errors), function(field) { + return $("#" + field).parent().find('span.error_message').text(errors[field]); + }); + } else { + return this.ShowError(t("Information Update Failed")); + } + }, this), + type: "PUT" + }; + client = new app.models.client({ + id: USER.id + }); + return client.save(attrs, options); + }; + ClientsSettingsView.prototype.changePass = function(e) { + e.preventDefault(); + $(e.currentTarget).hide(); + return $("#password").show(); + }; + ClientsSettingsView.prototype.processPicUpload = function(e) { + e.preventDefault(); + this.ShowSpinner("submit"); + return $.ajaxFileUpload({ + url: API + '/user_pictures', + secureuri: false, + fileElementId: 'picture', + data: { + token: USER.token + }, + dataType: 'json', + complete: __bind(function(data, status) { + this.HideSpinner(); + if (status === 'success') { + this.ShowSuccess(t("Picture Update Succeeded")); + return this.RefreshUserInfo(__bind(function() { + return $("#settingsProfPic").attr("src", USER.picture_url + ("?" + (Math.floor(Math.random() * 1000)))); + }, this)); + } else { + if (data.error) { + return this.ShowError(data.error); + } else { + return this.ShowError("Picture Update Failed"); + } + } + }, this) + }); + }; + return ClientsSettingsView; + })(); +}).call(this); +}, "views/clients/sign_up": function(exports, require, module) {(function() { + var clientsSignUpTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsSignUpTemplate = require('templates/clients/sign_up'); + exports.ClientsSignUpView = (function() { + __extends(ClientsSignUpView, UberView); + function ClientsSignUpView() { + ClientsSignUpView.__super__.constructor.apply(this, arguments); + } + ClientsSignUpView.prototype.id = 'signup_view'; + ClientsSignUpView.prototype.className = 'view_container'; + ClientsSignUpView.prototype.initialize = function() { + this.mixin(require('web-lib/mixins/i18n_phone_form').i18nPhoneForm); + return $('#location_country').live('change', function() { + if (!$('#mobile').val()) { + return $('#mobile_country').find("option[value=" + ($(this).val()) + "]").attr('selected', 'selected').end().trigger('change'); + } + }); + }; + ClientsSignUpView.prototype.events = { + 'submit form': 'signup', + 'click button': 'signup', + 'change #card_number': 'showCardType', + 'change #location_country': 'countryChange' + }; + ClientsSignUpView.prototype.render = function(invite) { + this.HideSpinner(); + $(this.el).html(clientsSignUpTemplate({ + invite: invite + })); + return this; + }; + ClientsSignUpView.prototype.signup = function(e) { + var $el, attrs, client, error_messages, options; + e.preventDefault(); + $el = $("form"); + $el.find('#terms_error').hide(); + if (!$el.find('#signup_terms input[type=checkbox]').attr('checked')) { + $('#spinner.submit').hide(); + $el.find('#terms_error').show(); + return; + } + error_messages = $el.find('.error_message').html(""); + attrs = { + first_name: $el.find('#first_name').val(), + last_name: $el.find('#last_name').val(), + email: $el.find('#email').val(), + password: $el.find('#password').val(), + location_country: $el.find('#location_country option:selected').attr('data-iso2'), + location: $el.find('#location').val(), + language: $el.find('#language').val(), + mobile_country: $el.find('#mobile_country option:selected').attr('data-iso2'), + mobile: $el.find('#mobile').val(), + card_number: $el.find('#card_number').val(), + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + card_code: $el.find('#card_code').val(), + use_case: $el.find('#use_case').val(), + promotion_code: $el.find('#promotion_code').val() + }; + options = { + statusCode: { + 200: function(response) { + $.cookie('token', response.token); + amplify.store('USERjson', response); + app.refreshMenu(); + return app.routers.clients.navigate('!/dashboard', true); + }, + 406: function(e) { + var error, errors, _i, _len, _ref, _results; + errors = JSON.parse(e.responseText); + _ref = _.keys(errors); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + error = _ref[_i]; + _results.push($('#' + error).parent().find('span').html($('#' + error).parent().find('span').html() + " " + errors[error])); + } + return _results; + } + }, + complete: __bind(function(response) { + return this.HideSpinner(); + }, this) + }; + client = new app.models.client; + $('.spinner#submit').show(); + return client.save(attrs, options); + }; + ClientsSignUpView.prototype.countryChange = function(e) { + var $e; + $e = $(e.currentTarget); + return $("#mobile_country").val($e.val()).trigger('change'); + }; + ClientsSignUpView.prototype.showCardType = function(e) { + var $el, reAmerica, reDiscover, reMaster, reVisa, validCard; + reVisa = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/; + reMaster = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/; + reAmerica = /^6011-?\d{4}-?\d{4}-?\d{4}$/; + reDiscover = /^3[4,7]\d{13}$/; + $el = $("#card_logos_signup"); + validCard = false; + if (e.currentTarget.value.match(reVisa)) { + $el.find("#overlay_left").css('width', "0px"); + return $el.find("#overlay_right").css('width', "75%"); + } else if (e.currentTarget.value.match(reMaster)) { + $el.find("#overlay_left").css('width', "25%"); + return $el.find("#overlay_right").css('width', "50%"); + } else if (e.currentTarget.value.match(reAmerica)) { + $el.find("#overlay_left").css('width', "75%"); + $el.find("#overlay_right").css('width', "0px"); + return console.log("amex"); + } else if (e.currentTarget.value.match(reDiscover)) { + $el.find("#overlay_left").css('width', "50%"); + return $el.find("#overlay_right").css('width', "25%"); + } else { + $el.find("#overlay_left").css('width', "0px"); + return $el.find("#overlay_right").css('width', "0px"); + } + }; + return ClientsSignUpView; + })(); +}).call(this); +}, "views/clients/trip_detail": function(exports, require, module) {(function() { + var clientsTripDetailTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsTripDetailTemplate = require('templates/clients/trip_detail'); + exports.TripDetailView = (function() { + __extends(TripDetailView, UberView); + function TripDetailView() { + this.resendReceipt = __bind(this.resendReceipt, this); + TripDetailView.__super__.constructor.apply(this, arguments); + } + TripDetailView.prototype.id = 'trip_detail_view'; + TripDetailView.prototype.className = 'view_container'; + TripDetailView.prototype.events = { + 'click a#fare_review': 'showFareReview', + 'click #fare_review_hide': 'hideFareReview', + 'submit #form_review_form': 'submitFareReview', + 'click #submit_fare_review': 'submitFareReview', + 'click .resendReceipt': 'resendReceipt' + }; + TripDetailView.prototype.render = function(id) { + if (id == null) { + id = 'invalid'; + } + this.ReadUserInfo(); + this.HideSpinner(); + this.model = new app.models.trip({ + id: id + }); + this.model.fetch({ + data: { + relationships: 'points,driver,city.country' + }, + dataType: 'json', + success: __bind(function() { + var trip; + trip = this.model; + $(this.el).html(clientsTripDetailTemplate({ + trip: trip + })); + this.RequireMaps(__bind(function() { + var bounds, endPos, map, myOptions, path, polyline, startPos; + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(this.model.get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + myOptions = { + zoom: 12, + center: path[0], + mapTypeId: google.maps.MapTypeId.ROADMAP, + zoomControl: false, + rotateControl: false, + panControl: false, + mapTypeControl: false, + scrollwheel: false + }; + map = new google.maps.Map(document.getElementById("trip_details_map"), myOptions); + map.fitBounds(bounds); + startPos = new google.maps.Marker({ + position: _.first(path), + map: map, + title: t("Trip started here"), + icon: 'https://uber-static.s3.amazonaws.com/marker_start.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: map, + title: t("Trip ended here"), + icon: 'https://uber-static.s3.amazonaws.com/marker_end.png' + }); + startPos.setMap(map); + endPos.setMap(map); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + return polyline.setMap(map); + }, this)); + return this.HideSpinner(); + }, this) + }); + this.ShowSpinner('load'); + this.delegateEvents(); + return this; + }; + TripDetailView.prototype.showFareReview = function(e) { + e.preventDefault(); + $('#fare_review_box').slideDown(); + return $('#fare_review').hide(); + }; + TripDetailView.prototype.hideFareReview = function(e) { + e.preventDefault(); + $('#fare_review_box').slideUp(); + return $('#fare_review').show(); + }; + TripDetailView.prototype.submitFareReview = function(e) { + var attrs, errorMessage, id, options; + e.preventDefault(); + errorMessage = $(".error_message"); + errorMessage.hide(); + id = $("#tripid").val(); + this.model = new app.models.trip({ + id: id + }); + attrs = { + note: $('#form_review_message').val(), + note_type: 'client_fare_review' + }; + options = { + success: __bind(function(response) { + $(".success_message").fadeIn(); + return $("#fare_review_form_wrapper").slideUp(); + }, this), + error: __bind(function(error) { + return errorMessage.fadeIn(); + }, this) + }; + return this.model.save(attrs, options); + }; + TripDetailView.prototype.resendReceipt = function(e) { + var $e; + e.preventDefault(); + $e = $(e.currentTarget); + this.$(".resendReceiptSuccess").empty().show(); + this.$(".resentReceiptError").empty().show(); + e.preventDefault(); + $('#spinner').show(); + return $.ajax('/api/trips/func/resend_receipt', { + data: { + token: $.cookie('token'), + trip_id: this.model.id + }, + type: 'POST', + complete: __bind(function(xhr) { + var response; + response = JSON.parse(xhr.responseText); + $('#spinner').hide(); + switch (xhr.status) { + case 200: + this.$(".resendReceiptSuccess").html("Receipt has been emailed"); + return this.$(".resendReceiptSuccess").fadeOut(2000); + default: + this.$(".resendReceiptError").html("Receipt has failed to be emailed"); + return this.$(".resendReceiptError").fadeOut(2000); + } + }, this) + }); + }; + return TripDetailView; + })(); +}).call(this); +}, "views/shared/menu": function(exports, require, module) {(function() { + var menuTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + menuTemplate = require('templates/shared/menu'); + exports.SharedMenuView = (function() { + __extends(SharedMenuView, Backbone.View); + function SharedMenuView() { + SharedMenuView.__super__.constructor.apply(this, arguments); + } + SharedMenuView.prototype.id = 'menu_view'; + SharedMenuView.prototype.render = function() { + var type; + if ($.cookie('token') === null) { + type = 'guest'; + } else { + type = 'client'; + } + $(this.el).html(menuTemplate({ + type: type + })); + return this; + }; + return SharedMenuView; + })(); +}).call(this); +}, "web-lib/collections/countries": function(exports, require, module) {(function() { + var UberCollection; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberCollection = require('web-lib/uber_collection').UberCollection; + exports.CountriesCollection = (function() { + __extends(CountriesCollection, UberCollection); + function CountriesCollection() { + CountriesCollection.__super__.constructor.apply(this, arguments); + } + CountriesCollection.prototype.model = app.models.country; + CountriesCollection.prototype.url = '/countries'; + return CountriesCollection; + })(); +}).call(this); +}, "web-lib/collections/vehicle_types": function(exports, require, module) {(function() { + var UberCollection, vehicleType, _ref; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberCollection = require('web-lib/uber_collection').UberCollection; + vehicleType = (typeof app !== "undefined" && app !== null ? (_ref = app.models) != null ? _ref.vehicleType : void 0 : void 0) || require('models/vehicle_type').VehicleType; + exports.VehicleTypesCollection = (function() { + __extends(VehicleTypesCollection, UberCollection); + function VehicleTypesCollection() { + VehicleTypesCollection.__super__.constructor.apply(this, arguments); + } + VehicleTypesCollection.prototype.model = vehicleType; + VehicleTypesCollection.prototype.url = '/vehicle_types'; + VehicleTypesCollection.prototype.defaultColumns = ['id', 'created_at', 'updated_at', 'deleted_at', 'created_by_user_id', 'updated_by_user_id', 'city_id', 'type', 'make', 'model', 'capacity', 'minimum_year', 'actions']; + VehicleTypesCollection.prototype.tableColumns = function(cols) { + var actions, c, capacity, city_id, columnValues, created_at, created_by_user_id, deleted_at, headerRow, id, make, minimum_year, model, type, updated_at, updated_by_user_id, _i, _len; + id = { + sTitle: 'Id' + }; + created_at = { + sTitle: 'Created At (UTC)', + 'sType': 'string' + }; + updated_at = { + sTitle: 'Updated At (UTC)', + 'sType': 'string' + }; + deleted_at = { + sTitle: 'Deleted At (UTC)', + 'sType': 'string' + }; + created_by_user_id = { + sTitle: 'Created By' + }; + updated_by_user_id = { + sTitle: 'Updated By' + }; + city_id = { + sTitle: 'City' + }; + type = { + sTitle: 'Type' + }; + make = { + sTitle: 'Make' + }; + model = { + sTitle: 'Model' + }; + capacity = { + sTitle: 'Capacity' + }; + minimum_year = { + sTitle: 'Min. Year' + }; + actions = { + sTitle: 'Actions' + }; + columnValues = { + id: id, + created_at: created_at, + updated_at: updated_at, + deleted_at: deleted_at, + created_by_user_id: created_by_user_id, + updated_by_user_id: updated_by_user_id, + city_id: city_id, + type: type, + make: make, + model: model, + capacity: capacity, + minimum_year: minimum_year, + actions: actions + }; + headerRow = []; + for (_i = 0, _len = cols.length; _i < _len; _i++) { + c = cols[_i]; + if (columnValues[c]) { + headerRow.push(columnValues[c]); + } + } + return headerRow; + }; + return VehicleTypesCollection; + })(); +}).call(this); +}, "web-lib/helpers": function(exports, require, module) {(function() { + var __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + exports.helpers = { + pin: function(num, color) { + if (color == null) { + color = 'FF0000'; + } + return ""; + }, + reverseGeocode: function(latitude, longitude) { + if (latitude && longitude) { + return "" + latitude + ", " + longitude + ""; + } else { + return ''; + } + }, + linkedName: function(model) { + var first_name, id, last_name, role, url; + role = model.role || model.get('role'); + id = model.id || model.get('id'); + first_name = model.first_name || model.get('first_name'); + last_name = model.last_name || model.get('last_name'); + url = "/" + role + "s/" + id; + return "" + first_name + " " + last_name + ""; + }, + linkedVehicle: function(vehicle, vehicleType) { + return " " + (vehicleType != null ? vehicleType.get('make') : void 0) + " " + (vehicleType != null ? vehicleType.get('model') : void 0) + " " + (vehicle.get('year')) + " "; + }, + linkedUserId: function(userType, userId) { + return "" + userType + " " + userId + ""; + }, + timeDelta: function(start, end) { + var delta; + if (typeof start === 'string') { + start = this.parseDate(start); + } + if (typeof end === 'string') { + end = this.parseDate(end); + } + if (end && start) { + delta = end.getTime() - start.getTime(); + return this.formatSeconds(delta / 1000); + } else { + return '00:00'; + } + }, + formatSeconds: function(s) { + var minutes, seconds; + s = Math.floor(s); + minutes = Math.floor(s / 60); + seconds = s - minutes * 60; + return "" + (this.leadingZero(minutes)) + ":" + (this.leadingZero(seconds)); + }, + formatCurrency: function(strValue, reverseSign, currency) { + var currency_locale, lc, mf; + if (reverseSign == null) { + reverseSign = false; + } + if (currency == null) { + currency = null; + } + strValue = String(strValue); + if (reverseSign) { + strValue = ~strValue.indexOf('-') ? strValue.split('-').join('') : ['-', strValue].join(''); + } + currency_locale = i18n.currencyToLocale[currency]; + try { + if (!(currency_locale != null) || currency_locale === i18n.locale) { + return i18n.jsworld.mf.format(strValue); + } else { + lc = new jsworld.Locale(POSIX_LC[currency_locale]); + mf = new jsworld.MonetaryFormatter(lc); + return mf.format(strValue); + } + } catch (error) { + i18n.log(error); + return strValue; + } + }, + formatTripFare: function(trip, type) { + var _ref, _ref2; + if (type == null) { + type = "fare"; + } + if (!trip.get('fare')) { + return 'n/a'; + } + if (((_ref = trip.get('fare_breakdown_local')) != null ? _ref.currency : void 0) != null) { + return app.helpers.formatCurrency(trip.get("" + type + "_local"), false, (_ref2 = trip.get('fare_breakdown_local')) != null ? _ref2.currency : void 0); + } else if (trip.get("" + type + "_string") != null) { + return trip.get("" + type + "_string"); + } else if (trip.get("" + type + "_local") != null) { + return trip.get("" + type + "_local"); + } else { + return 'n/a'; + } + }, + formatPhoneNumber: function(phoneNumber, countryCode) { + if (countryCode == null) { + countryCode = "+1"; + } + if (phoneNumber != null) { + phoneNumber = String(phoneNumber); + switch (countryCode) { + case '+1': + return countryCode + ' ' + phoneNumber.substring(0, 3) + '-' + phoneNumber.substring(3, 6) + '-' + phoneNumber.substring(6, 10); + case '+33': + return countryCode + ' ' + phoneNumber.substring(0, 1) + ' ' + phoneNumber.substring(1, 3) + ' ' + phoneNumber.substring(3, 5) + ' ' + phoneNumber.substring(5, 7) + ' ' + phoneNumber.substring(7, 9); + default: + countryCode + phoneNumber; + } + } + return "" + countryCode + " " + phoneNumber; + }, + parseDate: function(d, cityTime, tz) { + var city_filter, parsed, _ref; + if (cityTime == null) { + cityTime = true; + } + if (tz == null) { + tz = null; + } + if (((_ref = !d.substr(-6, 1)) === '+' || _ref === '-') || d.length === 19) { + d += '+00:00'; + } + if (/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/.test(d)) { + parsed = d.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/); + d = new Date(); + d.setUTCFullYear(parsed[1]); + d.setUTCMonth(parsed[2] - 1); + d.setUTCDate(parsed[3]); + d.setUTCHours(parsed[4]); + d.setUTCMinutes(parsed[5]); + d.setUTCSeconds(parsed[6]); + } else { + d = Date.parse(d); + } + if (typeof d === 'number') { + d = new Date(d); + } + d = new timezoneJS.Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), 'Etc/UTC'); + if (tz) { + d.convertToTimezone(tz); + } else if (cityTime) { + city_filter = $.cookie('city_filter'); + if (city_filter) { + tz = $("#city_filter option[value=" + city_filter + "]").attr('data-timezone'); + if (tz) { + d.convertToTimezone(tz); + } + } + } + return d; + }, + dateToTimezone: function(d) { + var city_filter, tz; + d = new timezoneJS.Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), 'Etc/UTC'); + city_filter = $.cookie('city_filter'); + if (city_filter) { + tz = $("#city_filter option[value=" + city_filter + "]").attr('data-timezone'); + d.convertToTimezone(tz); + } + return d; + }, + fixAMPM: function(d, formatted) { + if (d.hours >= 12) { + return formatted.replace(/\b[AP]M\b/, 'PM'); + } else { + return formatted.replace(/\b[AP]M\b/, 'AM'); + } + }, + formatDate: function(d, time, timezone) { + var formatted; + if (time == null) { + time = true; + } + if (timezone == null) { + timezone = null; + } + d = this.parseDate(d, true, timezone); + formatted = time ? ("" + (i18n.jsworld.dtf.formatDate(d)) + " ") + this.formatTime(d, d.getTimezoneInfo()) : i18n.jsworld.dtf.formatDate(d); + return this.fixAMPM(d, formatted); + }, + formatDateLong: function(d, time, timezone) { + if (time == null) { + time = true; + } + if (timezone == null) { + timezone = null; + } + d = this.parseDate(d, true, timezone); + timezone = d.getTimezoneInfo().tzAbbr; + if (time) { + return (i18n.jsworld.dtf.formatDateTime(d)) + (" " + timezone); + } else { + return i18n.jsworld.dtf.formatDate(d); + } + }, + formatTimezoneJSDate: function(d) { + var day, hours, jsDate, minutes, month, year; + year = d.getFullYear(); + month = this.leadingZero(d.getMonth()); + day = this.leadingZero(d.getDate()); + hours = this.leadingZero(d.getHours()); + minutes = this.leadingZero(d.getMinutes()); + jsDate = new Date(year, month, day, hours, minutes, 0); + return jsDate.toDateString(); + }, + formatTime: function(d, timezone) { + var formatted; + if (timezone == null) { + timezone = null; + } + formatted = ("" + (i18n.jsworld.dtf.formatTime(d))) + (timezone != null ? " " + (timezone != null ? timezone.tzAbbr : void 0) : ""); + return this.fixAMPM(d, formatted); + }, + formatISODate: function(d) { + var pad; + pad = function(n) { + if (n < 10) { + return '0' + n; + } + return n; + }; + return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()) + 'Z'; + }, + formatExpDate: function(d) { + var month, year; + d = this.parseDate(d); + year = d.getFullYear(); + month = this.leadingZero(d.getMonth() + 1); + return "" + year + "-" + month; + }, + formatLatLng: function(lat, lng, precision) { + if (precision == null) { + precision = 8; + } + return parseFloat(lat).toFixed(precision) + ',' + parseFloat(lng).toFixed(precision); + }, + leadingZero: function(num) { + if (num < 10) { + return "0" + num; + } else { + return num; + } + }, + roundNumber: function(num, dec) { + return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); + }, + notesToHTML: function(notes) { + var i, note, notesHTML, _i, _len; + notesHTML = ''; + i = 1; + if (notes) { + for (_i = 0, _len = notes.length; _i < _len; _i++) { + note = notes[_i]; + notesHTML += "" + note['userid'] + "     " + (this.formatDate(note['created_at'])) + "

    " + note['note'] + "

    "; + notesHTML += "
    "; + } + } + return notesHTML.replace("'", '"e'); + }, + formatPhone: function(n) { + var parts, phone, regexObj; + n = "" + n; + regexObj = /^(?:\+?1[-. ]?)?(?:\(?([0-9]{3})\)?[-. ]?)?([0-9]{3})[-. ]?([0-9]{4})$/; + if (regexObj.test(n)) { + parts = n.match(regexObj); + phone = ""; + if (parts[1]) { + phone += "(" + parts[1] + ") "; + } + phone += "" + parts[2] + "-" + parts[3]; + } else { + phone = n; + } + return phone; + }, + usStates: ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'], + onboardingPages: ['applied', 'ready_to_interview', 'pending_interview', 'interviewed', 'accepted', 'ready_to_onboard', 'pending_onboarding', 'active', 'waitlisted', 'rejected'], + driverBreadCrumb: function(loc, model) { + var onboardingPage, out, _i, _len, _ref; + out = "Drivers > "; + if (!(model != null)) { + out += ""; + } else { + out += "" + (this.onboardingUrlToName(model.get('driver_status'))) + ""; + out += " > " + (this.linkedName(model)) + " (" + (model.get('role')) + ") #" + (model.get('id')); + } + return out; + }, + onboardingUrlToName: function(url) { + return url != null ? url.replace(/_/g, " ").replace(/(^|\s)([a-z])/g, function(m, p1, p2) { + return p1 + p2.toUpperCase(); + }) : void 0; + }, + formatVehicle: function(vehicle) { + if (vehicle.get('make') && vehicle.get('model') && vehicle.get('license_plate')) { + return "" + (vehicle.get('make')) + " " + (vehicle.get('model')) + " (" + (vehicle.get('license_plate')) + ")"; + } + }, + docArbitraryFields: function(docName, cityDocs) { + var doc, field, out, _i, _j, _len, _len2, _ref; + out = ""; + for (_i = 0, _len = cityDocs.length; _i < _len; _i++) { + doc = cityDocs[_i]; + if (doc.name === docName && __indexOf.call(_.keys(doc), "metaFields") >= 0) { + _ref = doc.metaFields; + for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { + field = _ref[_j]; + out += "" + field.label + ":
    "; + } + } + } + return out; + }, + capitaliseFirstLetter: function(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + }, + createDocUploadForm: function(docName, driverId, vehicleId, cityMeta, vehicleName, expirationRequired) { + var ddocs, expDropdowns, pdocs, vdocs; + if (driverId == null) { + driverId = "None"; + } + if (vehicleId == null) { + vehicleId = "None"; + } + if (cityMeta == null) { + cityMeta = []; + } + if (vehicleName == null) { + vehicleName = false; + } + if (expirationRequired == null) { + expirationRequired = false; + } + ddocs = cityMeta["driverRequiredDocs"] || []; + pdocs = cityMeta["partnerRequiredDocs"] || []; + vdocs = cityMeta["vehicleRequiredDocs"] || []; + expDropdowns = "Expiration Date:\n -\n"; + return "
    \n
    \n \n \n \n\n
    \n " + (vehicleName ? vehicleName : "") + " " + docName + "\n
    \n\n
    \n \n
    \n\n
    \n " + (expirationRequired ? expDropdowns : "") + "\n
    \n\n
    \n " + (app.helpers.docArbitraryFields(docName, _.union(ddocs, pdocs, vdocs))) + "\n
    \n\n
    \n \n
    \n\n
    \n
    "; + }, + countrySelector: function(name, options) { + var countries, countryCodePrefix, defaultOptions; + if (options == null) { + options = {}; + } + defaultOptions = { + selectedKey: 'telephone_code', + selectedValue: '+1', + silent: false + }; + _.extend(defaultOptions, options); + options = defaultOptions; + countries = new app.collections.countries(); + countries.fetch({ + data: { + limit: 300 + }, + success: function(countries) { + var $option, $select, country, selected, _i, _len, _ref; + selected = false; + _ref = countries.models || []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + country = _ref[_i]; + $select = $("select[name=" + name + "]"); + $option = $('').val(country.id).attr('data-iso2', country.get('iso2')).attr('data-prefix', country.get('telephone_code')).html(country.get('name')); + if (country.get(options.selectedKey) === options.selectedValue && !selected) { + selected = true; + $option.attr('selected', 'selected'); + } + $select.append($option); + } + if (selected && !options.silent) { + return $select.val(options.selected).trigger('change'); + } + } + }); + countryCodePrefix = options.countryCodePrefix ? "data-country-code-prefix='" + options.countryCodePrefix + "'" : ''; + return ""; + }, + missingDocsOnDriver: function(driver) { + var city, docsReq, documents, partnerDocs; + city = driver.get('city'); + documents = driver.get('documents'); + if ((city != null) && (documents != null)) { + docsReq = _.pluck(city != null ? city.get('meta')["driverRequiredDocs"] : void 0, "name"); + if (driver.get('role') === "partner") { + partnerDocs = _.pluck(city != null ? city.get('meta')["partnerRequiredDocs"] : void 0, "name"); + docsReq = _.union(docsReq, partnerDocs); + } + return _.reject(docsReq, __bind(function(doc) { + return __indexOf.call((documents != null ? documents.pluck("name") : void 0) || [], doc) >= 0; + }, this)); + } else { + return []; + } + } + }; +}).call(this); +}, "web-lib/i18n": function(exports, require, module) {(function() { + exports.i18n = { + defaultLocale: 'en_US', + cookieName: '_LOCALE_', + locales: { + 'en_US': "English (US)", + 'fr_FR': "Français" + }, + currencyToLocale: { + 'USD': 'en_US', + 'EUR': 'fr_FR' + }, + logglyKey: 'd2d5a9bc-7ebe-4538-a180-81e62c705b1b', + logglyHost: 'https://logs.loggly.com', + init: function() { + this.castor = new window.loggly({ + url: this.logglyHost + '/inputs/' + this.logglyKey + '?rt=1', + level: 'error' + }); + this.setLocale($.cookie(this.cookieName) || this.defaultLocale); + window.t = _.bind(this.t, this); + this.loadLocaleTranslations(this.locale); + if (!(this[this.defaultLocale] != null)) { + return this.loadLocaleTranslations(this.defaultLocale); + } + }, + loadLocaleTranslations: function(locale) { + var loadPaths, path, _i, _len, _results; + loadPaths = ['web-lib/translations/' + locale, 'web-lib/translations/' + locale.slice(0, 2), 'translations/' + locale, 'translations/' + locale.slice(0, 2)]; + _results = []; + for (_i = 0, _len = loadPaths.length; _i < _len; _i++) { + path = loadPaths[_i]; + locale = path.substring(path.lastIndexOf('/') + 1); + if (this[locale] == null) { + this[locale] = {}; + } + _results.push((function() { + try { + return _.extend(this[locale], require(path).translations); + } catch (error) { + + } + }).call(this)); + } + return _results; + }, + getLocale: function() { + return this.locale; + }, + setLocale: function(locale) { + var message, parts, _ref; + parts = locale.split('_'); + this.locale = parts[0].toLowerCase(); + if (parts.length > 1) { + this.locale += "_" + (parts[1].toUpperCase()); + } + if (this.locale) { + $.cookie(this.cookieName, this.locale, { + path: '/', + domain: '.uber.com' + }); + } + try { + ((_ref = this.jsworld) != null ? _ref : this.jsworld = {}).lc = new jsworld.Locale(POSIX_LC[this.locale]); + this.jsworld.mf = new jsworld.MonetaryFormatter(this.jsworld.lc); + this.jsworld.nf = new jsworld.NumericFormatter(this.jsworld.lc); + this.jsworld.dtf = new jsworld.DateTimeFormatter(this.jsworld.lc); + this.jsworld.np = new jsworld.NumericParser(this.jsworld.lc); + this.jsworld.mp = new jsworld.MonetaryParser(this.jsworld.lc); + return this.jsworld.dtp = new jsworld.DateTimeParser(this.jsworld.lc); + } catch (error) { + message = 'JsWorld error with locale: ' + this.locale; + return this.log({ + message: message, + error: error + }); + } + }, + getTemplate: function(id) { + var _ref, _ref2; + return ((_ref = this[this.locale]) != null ? _ref[id] : void 0) || ((_ref2 = this[this.locale.slice(0, 2)]) != null ? _ref2[id] : void 0); + }, + getTemplateDefault: function(id) { + var _ref, _ref2; + return ((_ref = this[this.defaultLocale]) != null ? _ref[id] : void 0) || ((_ref2 = this[this.defaultLocale.slice(0, 2)]) != null ? _ref2[id] : void 0); + }, + getTemplateOrDefault: function(id) { + return this.getTemplate(id) || this.getTemplateDefault(id); + }, + t: function(id, vars) { + var errStr, locale, template; + if (vars == null) { + vars = {}; + } + locale = this.getLocale(); + template = this.getTemplate(id); + if (template == null) { + if (/dev|test/.test(window.location.host)) { + template = "(?) " + id; + } else { + template = this.getTemplateDefault(id); + } + errStr = "Missing [" + locale + "] translation for [" + id + "] at [" + window.location.hash + "] - Default template is [" + template + "]"; + this.log({ + error: errStr, + locale: locale, + id: id, + defaultTemplate: template + }); + } + if (template) { + return _.template(template, vars); + } else { + return id; + } + }, + log: function(error) { + if (/dev/.test(window.location.host)) { + if ((typeof console !== "undefined" && console !== null ? console.log : void 0) != null) { + return console.log(error); + } + } else { + _.extend(error, { + host: window.location.host, + hash: window.location.hash + }); + return this.castor.error(JSON.stringify(error)); + } + } + }; +}).call(this); +}, "web-lib/mixins/i18n_phone_form": function(exports, require, module) {(function() { + exports.i18nPhoneForm = { + _events: { + 'change select[data-country-code-prefix]': 'setCountryCodePrefix' + }, + setCountryCodePrefix: function(e) { + var $el, prefix; + $el = $(e.currentTarget); + prefix = $el.find('option:selected').attr('data-prefix'); + return $("#" + ($el.attr('data-country-code-prefix'))).text(prefix); + } + }; +}).call(this); +}, "web-lib/models/country": function(exports, require, module) {(function() { + var UberModel; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberModel = require('web-lib/uber_model').UberModel; + exports.Country = (function() { + __extends(Country, UberModel); + function Country() { + Country.__super__.constructor.apply(this, arguments); + } + Country.prototype.url = function() { + if (this.id) { + return "/countries/" + this.id; + } else { + return '/countries'; + } + }; + return Country; + })(); +}).call(this); +}, "web-lib/models/vehicle_type": function(exports, require, module) {(function() { + var UberModel; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberModel = require('web-lib/uber_model').UberModel; + exports.VehicleType = (function() { + __extends(VehicleType, UberModel); + function VehicleType() { + this.toString = __bind(this.toString, this); + VehicleType.__super__.constructor.apply(this, arguments); + } + VehicleType.prototype.endpoint = 'vehicle_types'; + VehicleType.prototype.toTableRow = function(cols) { + var actions, c, capacity, city_id, columnValues, created_at, created_by_user_id, deleted_at, id, make, minimum_year, model, rows, type, updated_at, updated_by_user_id, _i, _len, _ref; + id = "" + (this.get('id')) + ""; + if (this.get('created_at')) { + created_at = app.helpers.formatDate(this.get('created_at')); + } + if (this.get('updated_at')) { + updated_at = app.helpers.formatDate(this.get('updated_at')); + } + if (this.get('deleted_at')) { + deleted_at = app.helpers.formatDate(this.get('deleted_at')); + } + created_by_user_id = "" + (this.get('created_by_user_id')) + ""; + updated_by_user_id = "" + (this.get('updated_by_user_id')) + ""; + city_id = (_ref = this.get('city')) != null ? _ref.get('display_name') : void 0; + type = this.get('type'); + make = this.get('make'); + model = this.get('model'); + capacity = this.get('capacity'); + minimum_year = this.get('minimum_year'); + actions = "Show"; + if (!this.get('deleted_at')) { + actions += " Edit"; + actions += " Delete"; + } + columnValues = { + id: id, + created_at: created_at, + updated_at: updated_at, + deleted_at: deleted_at, + created_by_user_id: created_by_user_id, + updated_by_user_id: updated_by_user_id, + city_id: city_id, + type: type, + make: make, + model: model, + capacity: capacity, + minimum_year: minimum_year, + actions: actions + }; + rows = []; + for (_i = 0, _len = cols.length; _i < _len; _i++) { + c = cols[_i]; + rows.push(columnValues[c] ? columnValues[c] : '-'); + } + return rows; + }; + VehicleType.prototype.toString = function() { + return this.get('make') + ' ' + this.get('model') + ' ' + this.get('type') + (" (" + (this.get('capacity')) + ")"); + }; + return VehicleType; + })(); +}).call(this); +}, "web-lib/templates/footer": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var locale, title, _ref; + __out.push('\n\n\n\n\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "web-lib/translations/en": function(exports, require, module) {(function() { + exports.translations = { + "Info": "Info", + "Learn More": "Learn More", + "Pricing": "Pricing", + "FAQ": "FAQ", + "Support": "Support", + "Support & FAQ": "Support & FAQ", + "Contact Us": "Contact Us", + "Jobs": "Jobs", + "Phones": "Phones", + "Text Message": "Text Message", + "iPhone": "iPhone", + "Android": "Android", + "Drivers": "Drivers", + "Apply": "Apply", + "Sign In": "Sign In", + "Social": "Social", + "Twitter": "Twitter", + "Facebook": "Facebook", + "Blog": "Blog", + "Legal": "Legal", + "Company_Footer": "Company", + "Privacy Policy": "Privacy Policy", + "Terms": "Terms", + "Copyright © Uber Technologies, Inc.": "Copyright © Uber Technologies, Inc.", + "Language:": "Language:", + "Apply to Drive": "Apply to Drive", + "Expiration": "Expiration", + "Fare": "Fare", + "Driver": "Driver ", + "Dashboard": "Dashboard", + "Forgot Password": "Forgot Password", + "Trip Details": "Trip Details", + "Save": "Save", + "Cancel": "Cancel", + "Edit": "Edit", + "Password": "Password", + "First Name": "First Name", + "Last Name": "Last Name", + "Email Address": "Email Address", + "Submit": "Submit", + "Mobile Number": "Mobile Number", + "Zip Code": "Zip Code", + "Sign Out": "Sign Out", + "Confirm Email Message": "Attempting to confirm email...", + "Upload": "Upload", + "Rating": "Rating", + "Pickup Time": "Pickup Time", + "2011": "2011", + "2012": "2012", + "2013": "2013", + "2014": "2014", + "2015": "2015", + "2016": "2016", + "2017": "2017", + "2018": "2018", + "2019": "2019", + "2020": "2020", + "2021": "2021", + "2022": "2022", + "01": "01", + "02": "02", + "03": "03", + "04": "04", + "05": "05", + "06": "06", + "07": "07", + "08": "08", + "09": "09", + "10": "10", + "11": "11", + "12": "12" + }; +}).call(this); +}, "web-lib/translations/fr": function(exports, require, module) {(function() { + exports.translations = { + "Info": "Info", + "Learn More": "En Savoir Plus", + "Pricing": "Calcul du Prix", + "Support & FAQ": "Aide & FAQ", + "Contact Us": "Contactez Nous", + "Jobs": "Emplois", + "Phones": "Téléphones", + "Text Message": "SMS", + "iPhone": "iPhone", + "Android": "Android", + "Apply to Drive": "Candidature Chauffeur", + "Sign In": "Connexion", + "Social": "Contact", + "Twitter": "Twitter", + "Facebook": "Facebook", + "Blog": "Blog", + "Privacy Policy": "Protection des Données Personelles", + "Terms": "Conditions Générales", + "Copyright © Uber Technologies, Inc.": "© Uber, Inc.", + "Language:": "Langue:", + "Forgot Password": "Mot de passe oublié", + "Company_Footer": "À Propos d'Uber", + "Expiration": "Expiration", + "Fare": "Tarif", + "Driver": "Chauffeur", + "Drivers": "Chauffeurs", + "Dashboard": "Tableau de bord", + "Forgot Password": "Mot de passe oublié", + "Forgot Password?": "Mot de passe oublié?", + "Trip Details": "Détails de la course", + "Save": "Enregistrer", + "Cancel": "Annuler", + "Edit": "Modifier", + "Password": "Mot de passe", + "First Name": "Prénom", + "Last Name": "Nom", + "Email Address": "E-mail", + "Submit": "Soumettre", + "Mobile Number": "Téléphone Portable", + "Zip Code": "Code Postal", + "Sign Out": "Se déconnecter", + "Confirm Email Message": "E-mail de confirmation", + "Upload": "Télécharger", + "Rating": "Notation", + "Pickup Time": "Heure de prise en charge", + "2011": "2011", + "2012": "2012", + "2013": "2013", + "2014": "2014", + "2015": "2015", + "2016": "2016", + "2017": "2017", + "2018": "2018", + "2019": "2019", + "2020": "2020", + "2021": "2021", + "2022": "2022", + "01": "01", + "02": "02", + "03": "03", + "04": "04", + "05": "05", + "06": "06", + "07": "07", + "08": "08", + "09": "09", + "10": "10", + "11": "11", + "12": "12" + }; +}).call(this); +}, "web-lib/uber_collection": function(exports, require, module) {(function() { + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberCollection = (function() { + __extends(UberCollection, Backbone.Collection); + function UberCollection() { + UberCollection.__super__.constructor.apply(this, arguments); + } + UberCollection.prototype.parse = function(data) { + var model, tmp, _i, _in, _len, _out; + _in = data.resources || data; + _out = []; + if (data.meta) { + this.meta = data.meta; + } + for (_i = 0, _len = _in.length; _i < _len; _i++) { + model = _in[_i]; + tmp = new this.model; + tmp.set(tmp.parse(model)); + _out.push(tmp); + } + return _out; + }; + UberCollection.prototype.isRenderable = function() { + if (this.models.length) { + return true; + } + }; + UberCollection.prototype.toTableRows = function(cols) { + var tableRows; + tableRows = []; + _.each(this.models, function(model) { + return tableRows.push(model.toTableRow(cols)); + }); + return tableRows; + }; + return UberCollection; + })(); +}).call(this); +}, "web-lib/uber_model": function(exports, require, module) {(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }; + exports.UberModel = (function() { + __extends(UberModel, Backbone.Model); + function UberModel() { + this.refetch = __bind(this.refetch, this); + this.fetch = __bind(this.fetch, this); + this.save = __bind(this.save, this); + this.parse = __bind(this.parse, this); + UberModel.__super__.constructor.apply(this, arguments); + } + UberModel.prototype.endpoint = 'set_api_endpoint_in_subclass'; + UberModel.prototype.refetchOptions = {}; + UberModel.prototype.url = function(type) { + var endpoint_path; + endpoint_path = "/" + this.endpoint; + if (this.get('id')) { + return endpoint_path + ("/" + (this.get('id'))); + } else { + return endpoint_path; + } + }; + UberModel.prototype.isRenderable = function() { + var i, key, value, _ref; + i = 0; + _ref = this.attributes; + for (key in _ref) { + if (!__hasProp.call(_ref, key)) continue; + value = _ref[key]; + if (this.attributes.hasOwnProperty(key)) { + i += 1; + } + if (i > 1) { + return true; + } + } + return !(i === 1); + }; + UberModel.prototype.parse = function(response) { + var attrs, key, model, models, _i, _j, _k, _len, _len2, _len3, _ref, _ref2; + if (typeof response === 'object') { + _ref = _.intersection(_.keys(app.models), _.keys(response)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if (response[key]) { + attrs = this.parse(response[key]); + if (typeof attrs === 'object') { + response[key] = new app.models[key](attrs); + } + } + } + _ref2 = _.intersection(_.keys(app.collections), _.keys(response)); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + key = _ref2[_j]; + models = response[key]; + if (_.isArray(models)) { + response[key] = new app.collections[key]; + for (_k = 0, _len3 = models.length; _k < _len3; _k++) { + model = models[_k]; + attrs = app.collections[key].prototype.model.prototype.parse(model); + response[key].add(new response[key].model(attrs)); + } + } + } + } + return response; + }; + UberModel.prototype.save = function(attributes, options) { + var attr, _i, _j, _len, _len2, _ref, _ref2; + if (options == null) { + options = {}; + } + _ref = _.intersection(_.keys(app.models), _.keys(this.attributes)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + attr = _ref[_i]; + if (typeof this.get(attr) === "object") { + this.unset(attr, { + silent: true + }); + } + } + _ref2 = _.intersection(_.keys(app.collections), _.keys(this.attributes)); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + attr = _ref2[_j]; + if (typeof this.get(attr) === "object") { + this.unset(attr, { + silent: true + }); + } + } + if ((options != null) && options.diff && (attributes != null) && attributes !== {}) { + attributes['id'] = this.get('id'); + attributes['token'] = this.get('token'); + this.clear({ + 'silent': true + }); + this.set(attributes, { + silent: true + }); + } + if (__indexOf.call(_.keys(options), "data") < 0 && __indexOf.call(_.keys(this.refetchOptions || {}), "data") >= 0) { + options.data = this.refetchOptions.data; + } + return Backbone.Model.prototype.save.call(this, attributes, options); + }; + UberModel.prototype.fetch = function(options) { + this.refetchOptions = options; + return Backbone.Model.prototype.fetch.call(this, options); + }; + UberModel.prototype.refetch = function() { + return this.fetch(this.refetchOptions); + }; + return UberModel; + })(); +}).call(this); +}, "web-lib/uber_router": function(exports, require, module) {(function() { + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberRouter = (function() { + __extends(UberRouter, Backbone.Router); + function UberRouter() { + UberRouter.__super__.constructor.apply(this, arguments); + } + UberRouter.prototype.datePickers = function(format) { + if (format == null) { + format = "%Z-%m-%dT%H:%i:%s%:"; + } + $('.datepicker').AnyTime_noPicker(); + return $('.datepicker').AnyTime_picker({ + 'format': format, + 'formatUtcOffset': '%@' + }); + }; + UberRouter.prototype.autoGrowInput = function() { + return $('.editable input').autoGrowInput(); + }; + UberRouter.prototype.windowTitle = function(title) { + return $(document).attr('title', title); + }; + return UberRouter; + })(); +}).call(this); +}, "web-lib/uber_show_view": function(exports, require, module) {(function() { + var UberView; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + UberView = require('web-lib/uber_view').UberView; + exports.UberShowView = (function() { + __extends(UberShowView, UberView); + function UberShowView() { + UberShowView.__super__.constructor.apply(this, arguments); + } + UberShowView.prototype.view = 'show'; + UberShowView.prototype.events = { + 'click #edit': 'edit', + 'submit form': 'save', + 'click .cancel': 'cancel' + }; + UberShowView.prototype.errors = null; + UberShowView.prototype.showTemplate = null; + UberShowView.prototype.editTemplate = null; + UberShowView.prototype.initialize = function() { + if (this.init_hook) { + this.init_hook(); + } + _.bindAll(this, 'render'); + return this.model.bind('change', this.render); + }; + UberShowView.prototype.render = function() { + var $el; + $el = $(this.el); + this.selectView(); + if (this.view === 'show') { + $el.html(this.showTemplate({ + model: this.model + })); + } else if (this.view === 'edit') { + $el.html(this.editTemplate({ + model: this.model, + errors: this.errors || {}, + collections: this.collections || {} + })); + } else { + $el.html(this.newTemplate({ + model: this.model, + errors: this.errors || {}, + collections: this.collections || {} + })); + } + if (this.render_hook) { + this.render_hook(); + } + this.errors = null; + this.userIdsToLinkedNames(); + this.datePickers(); + return this.place(); + }; + UberShowView.prototype.selectView = function() { + var url; + if (this.options.urlRendering) { + url = window.location.hash; + if (url.match(/\/new/)) { + return this.view = 'new'; + } else if (url.match(/\/edit/)) { + return this.view = 'edit'; + } else { + return this.view = 'show'; + } + } + }; + UberShowView.prototype.edit = function(e) { + e.preventDefault(); + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id') + '/edit'; + } else { + this.view = 'edit'; + } + return this.model.change(); + }; + UberShowView.prototype.save = function(e) { + var attributes, ele, form_attrs, _i, _len, _ref; + e.preventDefault(); + attributes = $(e.currentTarget).serializeToJson(); + form_attrs = {}; + _ref = $('input[type="radio"]'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + ele = _ref[_i]; + if ($(ele).is(':checked')) { + form_attrs[$(ele).attr('name')] = $(ele).attr('value'); + } + } + attributes = _.extend(attributes, form_attrs); + if (this.relationships) { + attributes = _.extend(attributes, { + relationships: this.relationships + }); + } + if (this.filter_attributes != null) { + this.filter_attributes(attributes); + } + return this.model.save(attributes, { + silent: true, + success: __bind(function(model) { + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id'); + } else { + this.view = 'show'; + } + return this.flash('success', "Uber save!"); + }, this), + statusCode: { + 406: __bind(function(xhr) { + this.errors = JSON.parse(xhr.responseText); + return this.flash('error', 'That was not Uber.'); + }, this) + }, + error: __bind(function(model, xhr) { + var code, message, responseJSON, responseText; + code = xhr.status; + responseText = xhr.responseText; + if (responseText) { + responseJSON = JSON.parse(responseText); + } + if (responseJSON && (typeof responseJSON === 'object') && (responseJSON.hasOwnProperty('error'))) { + message = responseJSON.error; + } + return this.flash('error', (code || 'Unknown') + ' error' + (': ' + message || '')); + }, this), + complete: __bind(function() { + return this.model.change(); + }, this) + }); + }; + UberShowView.prototype.cancel = function(e) { + e.preventDefault(); + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id'); + } else { + this.view = 'show'; + } + return this.model.fetch({ + silent: true, + complete: __bind(function() { + return this.model.change(); + }, this) + }); + }; + return UberShowView; + })(); +}).call(this); +}, "web-lib/uber_sync": function(exports, require, module) {(function() { + var methodType; + var __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }; + methodType = { + create: 'POST', + update: 'PUT', + "delete": 'DELETE', + read: 'GET' + }; + exports.UberSync = function(method, model, options) { + var token; + options.type = methodType[method]; + options.url = _.isString(this.url) ? '/api' + this.url : '/api' + this.url(options.type); + options.data = _.extend({}, options.data); + if (__indexOf.call(_.keys(options.data), "city_id") < 0) { + if ($.cookie('city_filter')) { + _.extend(options.data, { + city_id: $.cookie('city_filter') + }); + } + } else { + delete options.data['city_id']; + } + if (options.type === 'POST' || options.type === 'PUT') { + _.extend(options.data, model.toJSON()); + } + token = $.cookie('token') ? $.cookie('token') : typeof USER !== "undefined" && USER !== null ? USER.get('token') : ""; + _.extend(options.data, { + token: token + }); + if (method === "delete") { + options.contentType = 'application/json'; + options.data = JSON.stringify(options.data); + } + return $.ajax(options); + }; +}).call(this); +}, "web-lib/uber_view": function(exports, require, module) {(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberView = (function() { + __extends(UberView, Backbone.View); + function UberView() { + this.processDocumentUpload = __bind(this.processDocumentUpload, this); + UberView.__super__.constructor.apply(this, arguments); + } + UberView.prototype.className = 'view_container'; + UberView.prototype.hashId = function() { + return parseInt(location.hash.split('/')[2]); + }; + UberView.prototype.place = function(content) { + var $target; + $target = this.options.scope ? this.options.scope.find(this.options.selector) : $(this.options.selector); + $target[this.options.method || 'html'](content || this.el); + this.delegateEvents(); + $('#spinner').hide(); + return this; + }; + UberView.prototype.mixin = function(m, args) { + var events, self; + if (args == null) { + args = {}; + } + self = this; + events = m._events; + _.extend(this, m); + if (m.initialize) { + m.initialize(self, args); + } + return _.each(_.keys(events), function(key) { + var event, func, selector, split; + split = key.split(' '); + event = split[0]; + selector = split[1]; + func = events[key]; + return $(self.el).find(selector).live(event, function(e) { + return self[func](e); + }); + }); + }; + UberView.prototype.datePickers = function(format) { + if (format == null) { + format = "%Z-%m-%dT%H:%i:%s%:"; + } + $('.datepicker').AnyTime_noPicker(); + return $('.datepicker').AnyTime_picker({ + 'format': format, + 'formatUtcOffset': '%@' + }); + }; + UberView.prototype.dataTable = function(collection, selector, options, params, cols) { + var defaults; + if (selector == null) { + selector = 'table'; + } + if (options == null) { + options = {}; + } + if (params == null) { + params = {}; + } + if (cols == null) { + cols = []; + } + $(selector).empty(); + if (!cols.length) { + cols = collection.defaultColumns; + } + defaults = { + aoColumns: collection.tableColumns(cols), + bDestroy: true, + bSort: false, + bProcessing: true, + bFilter: false, + bServerSide: true, + bPaginate: true, + bScrollInfinite: true, + bScrollCollapse: true, + sScrollY: '600px', + iDisplayLength: 50, + fnServerData: function(source, data, callback) { + var defaultParams; + defaultParams = { + limit: data[4].value, + offset: data[3].value + }; + return collection.fetch({ + data: _.extend(defaultParams, params), + success: function() { + return callback({ + aaData: collection.toTableRows(cols), + iTotalRecords: collection.meta.count, + iTotalDisplayRecords: collection.meta.count + }); + }, + error: function() { + return new Error({ + message: 'Loading error.' + }); + } + }); + }, + fnRowCallback: function(nRow, aData, iDisplayIndex, iDisplayIndexFull) { + $('[data-tooltip]', nRow).qtip({ + content: { + attr: 'data-tooltip' + }, + style: { + classes: "ui-tooltip-light ui-tooltip-rounded ui-tooltip-shadow" + } + }); + return nRow; + } + }; + return $(this.el).find(selector).dataTable(_.extend(defaults, options)); + }; + UberView.prototype.dataTableLocal = function(collection, selector, options, params, cols) { + var $dataTable, defaults; + if (selector == null) { + selector = 'table'; + } + if (options == null) { + options = {}; + } + if (params == null) { + params = {}; + } + if (cols == null) { + cols = []; + } + $(selector).empty(); + if (!cols.length || cols.length === 0) { + cols = collection.defaultColumns; + } + defaults = { + aaData: collection.toTableRows(cols), + aoColumns: collection.tableColumns(cols), + bDestroy: true, + bSort: false, + bProcessing: true, + bFilter: false, + bScrollInfinite: true, + bScrollCollapse: true, + sScrollY: '600px', + iDisplayLength: -1 + }; + $dataTable = $(this.el).find(selector).dataTable(_.extend(defaults, options)); + _.delay(__bind(function() { + if ($dataTable && $dataTable.length > 0) { + return $dataTable.fnAdjustColumnSizing(); + } + }, this), 1); + return $dataTable; + }; + UberView.prototype.reverseGeocode = function() { + var $el; + return ''; + $el = $(this.el); + return this.requireMaps(function() { + var geocoder; + geocoder = new google.maps.Geocoder(); + return $el.find('[data-point]').each(function() { + var $this, latLng, point; + $this = $(this); + point = JSON.parse($this.attr('data-point')); + latLng = new google.maps.LatLng(point.latitude, point.longitude); + return geocoder.geocode({ + latLng: latLng + }, function(data, status) { + if (status === google.maps.GeocoderStatus.OK) { + return $this.text(data[0].formatted_address); + } + }); + }); + }); + }; + UberView.prototype.userIdsToLinkedNames = function() { + var $el; + $el = $(this.el); + return $el.find('a[data-user-id][data-user-type]').each(function() { + var $this, user, userType; + $this = $(this); + userType = $this.attr('data-user-type') === 'user' ? 'client' : $this.attr('data-user-type'); + user = new app.models[userType]({ + id: $this.attr('data-user-id') + }); + return user.fetch({ + success: function(user) { + return $this.html(app.helpers.linkedName(user)).attr('href', "!/" + user.role + "s/" + user.id); + }, + error: function() { + if ($this.attr('data-user-type') === 'user') { + user = new app.models['driver']({ + id: $this.attr('data-user-id') + }); + return user.fetch({ + success: function(user) { + return $this.html(app.helpers.linkedName(user)).attr('href', "!/driver/" + user.id); + } + }); + } + } + }); + }); + }; + UberView.prototype.selectedCity = function() { + var $selected, city, cityFilter; + cityFilter = $.cookie('city_filter'); + $selected = $("#city_filter option[value=" + cityFilter + "]"); + if (city_filter && $selected.length) { + return city = { + lat: parseFloat($selected.attr('data-lat')), + lng: parseFloat($selected.attr('data-lng')), + timezone: $selected.attr('data-timezone') + }; + } else { + return city = { + lat: 37.775, + lng: -122.45, + timezone: 'Etc/UTC' + }; + } + }; + UberView.prototype.updateModel = function(e, success) { + var $el, attrs, model, self; + e.preventDefault(); + $el = $(e.currentTarget); + self = this; + model = new this.model.__proto__.constructor({ + id: this.model.id + }); + attrs = {}; + $el.find('[name]').each(function() { + var $this; + $this = $(this); + return attrs["" + ($this.attr('name'))] = $this.val(); + }); + self.model.set(attrs); + $el.find('span.error').text(''); + return model.save(attrs, { + complete: function(xhr) { + var response; + response = JSON.parse(xhr.responseText); + switch (xhr.status) { + case 200: + self.model = model; + $el.find('[name]').val(''); + if (success) { + return success(); + } + break; + case 406: + return _.each(response, function(error, field) { + return $el.find("[name=" + field + "]").parent().find('span.error').text(error); + }); + default: + return this.unanticipatedError(response); + } + } + }); + }; + UberView.prototype.autoUpdateModel = function(e) { + var $el, arg, model, self, val; + $el = $(e.currentTarget); + val = $el.val(); + self = this; + if (val !== this.model.get($el.attr('id'))) { + arg = {}; + arg[$el.attr('id')] = $el.is(':checkbox') ? $el.is(':checked') ? 1 : 0 : val; + $('.editable span').empty(); + this.model.set(arg); + model = new this.model.__proto__.constructor({ + id: this.model.id + }); + return model.save(arg, { + complete: function(xhr) { + var key, response, _i, _len, _ref, _results; + response = JSON.parse(xhr.responseText); + switch (xhr.status) { + case 200: + self.flash('success', 'Saved!'); + return $el.blur(); + case 406: + self.flash('error', 'That was not Uber.'); + _ref = _.keys(response); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push($el.parent().find('span').html(response[key])); + } + return _results; + break; + default: + return self.unanticipatedError; + } + } + }); + } + }; + UberView.prototype.unanticipatedError = function(response) { + return self.flash('error', response); + }; + UberView.prototype.flash = function(type, text) { + var $banner; + $banner = $("." + type); + $banner.find('p').text(text).end().css('border', '1px solid #999').animate({ + top: 0 + }, 500); + return setTimeout(function() { + return $banner.animate({ + top: -$banner.outerHeight() + }, 500); + }, 3000); + }; + UberView.prototype.requireMaps = function(callback) { + if (typeof google !== 'undefined' && google.maps) { + return callback(); + } else { + return $.getScript("https://www.google.com/jsapi?key=" + CONFIG.googleJsApiKey, function() { + return google.load('maps', 3, { + callback: callback, + other_params: 'sensor=false&language=en' + }); + }); + } + }; + UberView.prototype.select_drop_down = function(model, key) { + var value; + value = model.get(key); + if (value) { + return $("select[id='" + key + "'] option[value='" + value + "']").attr('selected', 'selected'); + } + }; + UberView.prototype.processDocumentUpload = function(e) { + var $fi, $form, arbData, curDate, data, expDate, expM, expY, expiration, fileElementId, invalid; + e.preventDefault(); + $form = $(e.currentTarget); + $fi = $("input[type=file]", $form); + $(".validationError").removeClass("validationError"); + if (!$fi.val()) { + return $fi.addClass("validationError"); + } else { + fileElementId = $fi.attr('id'); + expY = $("select[name=expiration-year]", $form).val(); + expM = $("select[name=expiration-month]", $form).val(); + invalid = false; + if (expY && expM) { + expDate = new Date(expY, expM, 28); + curDate = new Date(); + if (expDate < curDate) { + invalid = true; + $(".expiration", $form).addClass("validationError"); + } + expiration = "" + expY + "-" + expM + "-28T23:59:59Z"; + } + arbData = {}; + $(".arbitraryField", $form).each(__bind(function(i, e) { + arbData[$(e).attr('name')] = $(e).val(); + if ($(e).val() === "") { + invalid = true; + return $(e).addClass("validationError"); + } + }, this)); + if (!invalid) { + data = { + token: $.cookie('token') || USER.get('token'), + name: $("input[name=fileName]", $form).val(), + meta: escape(JSON.stringify(arbData)), + user_id: $("input[name=driver_id]", $form).val(), + vehicle_id: $("input[name=vehicle_id]", $form).val() + }; + if (expiration) { + data['expiration'] = expiration; + } + $("#spinner").show(); + return $.ajaxFileUpload({ + url: '/api/documents', + secureuri: false, + fileElementId: fileElementId, + data: data, + complete: __bind(function(resp, status) { + var key, _i, _len, _ref, _results; + $("#spinner").hide(); + if (status === "success") { + if (this.model) { + this.model.refetch(); + } else { + USER.refetch(); + } + } + if (status === "error") { + _ref = _.keys(resp); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push($("*[name=" + key + "]", $form).addClass("validationError")); + } + return _results; + } + }, this) + }); + } + } + }; + return UberView; + })(); +}).call(this); +}, "web-lib/views/footer": function(exports, require, module) {(function() { + var footerTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + footerTemplate = require('web-lib/templates/footer'); + exports.SharedFooterView = (function() { + __extends(SharedFooterView, Backbone.View); + function SharedFooterView() { + SharedFooterView.__super__.constructor.apply(this, arguments); + } + SharedFooterView.prototype.id = 'footer_view'; + SharedFooterView.prototype.events = { + 'click .language': 'intl_set_cookie_locale' + }; + SharedFooterView.prototype.render = function() { + $(this.el).html(footerTemplate()); + this.delegateEvents(); + return this; + }; + SharedFooterView.prototype.intl_set_cookie_locale = function(e) { + var _ref; + i18n.setLocale(e != null ? (_ref = e.srcElement) != null ? _ref.id : void 0 : void 0); + return location.reload(); + }; + return SharedFooterView; + })(); +}).call(this); +}}); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/embed-tokens.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/embed-tokens.js new file mode 100755 index 0000000..61307ee --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/embed-tokens.js @@ -0,0 +1,15 @@ +#! /usr/bin/env node + +global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); +var fs = require("fs"); +var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js + jsp = uglify.parser, + pro = uglify.uglify; + +var code = fs.readFileSync("embed-tokens.js", "utf8").replace(/^#.*$/mg, ""); +var ast = jsp.parse(code, null, true); + +// trololo +function fooBar() {} + +console.log(sys.inspect(ast, null, null)); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/goto.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/goto.js new file mode 100644 index 0000000..945960c --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/goto.js @@ -0,0 +1,26 @@ +function unique(arqw) { + var a = [], i, j + outer: for (i = 0; i < arqw.length; i++) { + for (j = 0; j < a.length; j++) { + if (a[j] == arqw[i]) { + continue outer + } + } + a[a.length] = arqw[i] + } + return a +} + + +function unique(arqw) { + var crap = [], i, j + outer: for (i = 0; i < arqw.length; i++) { + for (j = 0; j < crap.length; j++) { + if (crap[j] == arqw[i]) { + continue outer + } + } + crap[crap.length] = arqw[i] + } + return crap +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/goto2.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/goto2.js new file mode 100644 index 0000000..d13b2bc --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/goto2.js @@ -0,0 +1,8 @@ +function q(qooo) { + var a; + foo: for(;;) { + a++; + if (something) break foo; + return qooo; + } +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/hoist.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/hoist.js new file mode 100644 index 0000000..4bf2b94 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/hoist.js @@ -0,0 +1,33 @@ +function foo(arg1, arg2, arg3, arg4, arg5, arg6) { + var a = 5; + { + var d = 10, mak = 20, buz = 30; + var q = buz * 2; + } + if (moo) { + var a, b, c; + } + for (var arg1 = 0, d = 20; arg1 < 10; ++arg1) + console.log(arg3); + for (var i in mak) {} + for (j in d) {} + var d; + + function test() { + + }; + + //test(); + + (function moo(first, second){ + console.log(first); + })(1); + + (function moo(first, second){ + console.log(moo()); + })(1); +} + + +var foo; +var bar; diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/instrument.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/instrument.js new file mode 100644 index 0000000..c6a9d79 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/instrument.js @@ -0,0 +1,97 @@ +// sample on how to use the parser and walker API to instrument some code + +var jsp = require("uglify-js").parser; +var pro = require("uglify-js").uglify; + +function instrument(code) { + var ast = jsp.parse(code, false, true); // true for the third arg specifies that we want + // to have start/end tokens embedded in the + // statements + var w = pro.ast_walker(); + + // we're gonna need this to push elements that we're currently looking at, to avoid + // endless recursion. + var analyzing = []; + function do_stat() { + var ret; + if (this[0].start && analyzing.indexOf(this) < 0) { + // without the `analyzing' hack, w.walk(this) would re-enter here leading + // to infinite recursion + analyzing.push(this); + ret = [ "splice", // XXX: "block" is safer + [ [ "stat", + [ "call", [ "name", "trace" ], + [ [ "string", this[0].toString() ], + [ "num", this[0].start.line ], + [ "num", this[0].start.col ], + [ "num", this[0].end.line ], + [ "num", this[0].end.col ]]]], + w.walk(this) ]]; + analyzing.pop(this); + } + return ret; + }; + var new_ast = w.with_walkers({ + "stat" : do_stat, + "label" : do_stat, + "break" : do_stat, + "continue" : do_stat, + "debugger" : do_stat, + "var" : do_stat, + "const" : do_stat, + "return" : do_stat, + "throw" : do_stat, + "try" : do_stat, + "defun" : do_stat, + "if" : do_stat, + "while" : do_stat, + "do" : do_stat, + "for" : do_stat, + "for-in" : do_stat, + "switch" : do_stat, + "with" : do_stat + }, function(){ + return w.walk(ast); + }); + return pro.gen_code(new_ast, { beautify: true }); +} + + + + +////// test code follows. + +var code = instrument(test.toString()); +console.log(code); + +function test() { + // simple stats + a = 5; + c += a + b; + "foo"; + + // var + var foo = 5; + const bar = 6, baz = 7; + + // switch block. note we can't track case lines the same way. + switch ("foo") { + case "foo": + return 1; + case "bar": + return 2; + } + + // for/for in + for (var i = 0; i < 5; ++i) { + console.log("Hello " + i); + } + for (var i in [ 1, 2, 3]) { + console.log(i); + } + + // note however that the following is broken. I guess we + // should add the block brackets in this case... + for (var i = 0; i < 5; ++i) + console.log("foo"); +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/instrument2.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/instrument2.js new file mode 100644 index 0000000..6aee5f3 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/instrument2.js @@ -0,0 +1,138 @@ +// sample on how to use the parser and walker API to instrument some code + +var jsp = require("uglify-js").parser; +var pro = require("uglify-js").uglify; + +function instrument(code) { + var ast = jsp.parse(code, false, true); // true for the third arg specifies that we want + // to have start/end tokens embedded in the + // statements + var w = pro.ast_walker(); + + function trace (line, comment) { + var code = pro.gen_code(line, { beautify: true }); + var data = line[0] + + var args = [] + if (!comment) comment = "" + if (typeof data === "object") { + code = code.split(/\n/).shift() + args = [ [ "string", data.toString() ], + [ "string", code ], + [ "num", data.start.line ], + [ "num", data.start.col ], + [ "num", data.end.line ], + [ "num", data.end.col ]] + } else { + args = [ [ "string", data ], + [ "string", code ]] + + } + return [ "call", [ "name", "trace" ], args ]; + } + + // we're gonna need this to push elements that we're currently looking at, to avoid + // endless recursion. + var analyzing = []; + function do_stat() { + var ret; + if (this[0].start && analyzing.indexOf(this) < 0) { + // without the `analyzing' hack, w.walk(this) would re-enter here leading + // to infinite recursion + analyzing.push(this); + ret = [ "splice", + [ [ "stat", trace(this) ], + w.walk(this) ]]; + analyzing.pop(this); + } + return ret; + } + + function do_cond(c, t, f) { + return [ this[0], w.walk(c), + ["seq", trace(t), w.walk(t) ], + ["seq", trace(f), w.walk(f) ]]; + } + + function do_binary(c, l, r) { + if (c !== "&&" && c !== "||") { + return [this[0], c, w.walk(l), w.walk(r)]; + } + return [ this[0], c, + ["seq", trace(l), w.walk(l) ], + ["seq", trace(r), w.walk(r) ]]; + } + + var new_ast = w.with_walkers({ + "stat" : do_stat, + "label" : do_stat, + "break" : do_stat, + "continue" : do_stat, + "debugger" : do_stat, + "var" : do_stat, + "const" : do_stat, + "return" : do_stat, + "throw" : do_stat, + "try" : do_stat, + "defun" : do_stat, + "if" : do_stat, + "while" : do_stat, + "do" : do_stat, + "for" : do_stat, + "for-in" : do_stat, + "switch" : do_stat, + "with" : do_stat, + "conditional" : do_cond, + "binary" : do_binary + }, function(){ + return w.walk(ast); + }); + return pro.gen_code(new_ast, { beautify: true }); +} + + +////// test code follows. + +var code = instrument(test.toString()); +console.log(code); + +function test() { + // simple stats + a = 5; + c += a + b; + "foo"; + + // var + var foo = 5; + const bar = 6, baz = 7; + + // switch block. note we can't track case lines the same way. + switch ("foo") { + case "foo": + return 1; + case "bar": + return 2; + } + + // for/for in + for (var i = 0; i < 5; ++i) { + console.log("Hello " + i); + } + for (var i in [ 1, 2, 3]) { + console.log(i); + } + + for (var i = 0; i < 5; ++i) + console.log("foo"); + + for (var i = 0; i < 5; ++i) { + console.log("foo"); + } + + var k = plurp() ? 1 : 0; + var x = a ? doX(y) && goZoo("zoo") + : b ? blerg({ x: y }) + : null; + + var x = X || Y; +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/liftvars.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/liftvars.js new file mode 100644 index 0000000..2f4b7fe --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/liftvars.js @@ -0,0 +1,8 @@ +var UNUSED_VAR1 = 19; + +function main() { + var unused_var2 = 20; + alert(100); +} + +main(); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/test.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/test.js new file mode 100755 index 0000000..f295fba --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/test.js @@ -0,0 +1,30 @@ +#! /usr/bin/env node + +global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); +var fs = require("fs"); +var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js + jsp = uglify.parser, + pro = uglify.uglify; + +var code = fs.readFileSync("hoist.js", "utf8"); +var ast = jsp.parse(code); + +ast = pro.ast_lift_variables(ast); + +var w = pro.ast_walker(); +ast = w.with_walkers({ + "function": function() { + var node = w.dive(this); // walk depth first + console.log(pro.gen_code(node, { beautify: true })); + return node; + }, + "name": function(name) { + return [ this[0], "X" ]; + } +}, function(){ + return w.walk(ast); +}); + +console.log(pro.gen_code(ast, { + beautify: true +})); diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/uglify-hangs.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/uglify-hangs.js new file mode 100644 index 0000000..0d5b7e0 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/uglify-hangs.js @@ -0,0 +1,3930 @@ +/** + * @fileoverview + * + * JsWorld + * + *

    Javascript library for localised formatting and parsing of: + *

      + *
    • Numbers + *
    • Dates and times + *
    • Currency + *
    + * + *

    The library classes are configured with standard POSIX locale definitions + * derived from Unicode's Common Locale Data Repository (CLDR). + * + *

    Website: JsWorld + * + * @author Vladimir Dzhuvinov + * @version 2.5 (2011-12-23) + */ + + + +/** + * @namespace Namespace container for the JsWorld library objects. + */ +jsworld = {}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 date/time + * string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the + * current date/time will be used. + * @param {Boolean} [withTZ] Include timezone offset, default false. + * + * @returns {String} The date/time formatted as YYYY-MM-DD HH:MM:SS. + */ +jsworld.formatIsoDateTime = function(d, withTZ) { + + if (typeof d === "undefined") + d = new Date(); // now + + if (typeof withTZ === "undefined") + withTZ = false; + + var s = jsworld.formatIsoDate(d) + " " + jsworld.formatIsoTime(d); + + if (withTZ) { + + var diff = d.getHours() - d.getUTCHours(); + var hourDiff = Math.abs(diff); + + var minuteUTC = d.getUTCMinutes(); + var minute = d.getMinutes(); + + if (minute != minuteUTC && minuteUTC < 30 && diff < 0) + hourDiff--; + + if (minute != minuteUTC && minuteUTC > 30 && diff > 0) + hourDiff--; + + var minuteDiff; + if (minute != minuteUTC) + minuteDiff = ":30"; + else + minuteDiff = ":00"; + + var timezone; + if (hourDiff < 10) + timezone = "0" + hourDiff + minuteDiff; + + else + timezone = "" + hourDiff + minuteDiff; + + if (diff < 0) + timezone = "-" + timezone; + + else + timezone = "+" + timezone; + + s = s + timezone; + } + + return s; +}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 date string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the current + * date will be used. + * + * @returns {String} The date formatted as YYYY-MM-DD. + */ +jsworld.formatIsoDate = function(d) { + + if (typeof d === "undefined") + d = new Date(); // now + + var year = d.getFullYear(); + var month = d.getMonth() + 1; + var day = d.getDate(); + + return year + "-" + jsworld._zeroPad(month, 2) + "-" + jsworld._zeroPad(day, 2); +}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 time string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the current + * time will be used. + * + * @returns {String} The time formatted as HH:MM:SS. + */ +jsworld.formatIsoTime = function(d) { + + if (typeof d === "undefined") + d = new Date(); // now + + var hour = d.getHours(); + var minute = d.getMinutes(); + var second = d.getSeconds(); + + return jsworld._zeroPad(hour, 2) + ":" + jsworld._zeroPad(minute, 2) + ":" + jsworld._zeroPad(second, 2); +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted date/time string to a JavaScript + * Date object. + * + * @param {String} isoDateTimeVal An ISO-8601 formatted date/time string. + * + *

    Accepted formats: + * + *

      + *
    • YYYY-MM-DD HH:MM:SS + *
    • YYYYMMDD HHMMSS + *
    • YYYY-MM-DD HHMMSS + *
    • YYYYMMDD HH:MM:SS + *
    + * + * @returns {Date} The corresponding Date object. + * + * @throws Error on a badly formatted date/time string or on a invalid date. + */ +jsworld.parseIsoDateTime = function(isoDateTimeVal) { + + if (typeof isoDateTimeVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "YYYY-MM-DD HH:MM:SS" format + var matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); + + // If unsuccessful, try to match "YYYYMMDD HHMMSS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/); + + // ... try to match "YYYY-MM-DD HHMMSS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/); + + // ... try to match "YYYYMMDD HH:MM:SS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date/time string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var year = parseInt(matches[1], 10); + var month = parseInt(matches[2], 10); + var day = parseInt(matches[3], 10); + + var hour = parseInt(matches[4], 10); + var mins = parseInt(matches[5], 10); + var secs = parseInt(matches[6], 10); + + // Simple value range check, leap years not checked + // Note: the originial ISO time spec for leap hours (24:00:00) and seconds (00:00:60) is not supported + if (month < 1 || month > 12 || + day < 1 || day > 31 || + hour < 0 || hour > 23 || + mins < 0 || mins > 59 || + secs < 0 || secs > 59 ) + + throw "Error: Invalid ISO-8601 date/time value"; + + var d = new Date(year, month - 1, day, hour, mins, secs); + + // Check if the input date was valid + // (JS Date does automatic forward correction) + if (d.getDate() != day || d.getMonth() +1 != month) + throw "Error: Invalid date"; + + return d; +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted date string to a JavaScript + * Date object. + * + * @param {String} isoDateVal An ISO-8601 formatted date string. + * + *

    Accepted formats: + * + *

      + *
    • YYYY-MM-DD + *
    • YYYYMMDD + *
    + * + * @returns {Date} The corresponding Date object. + * + * @throws Error on a badly formatted date string or on a invalid date. + */ +jsworld.parseIsoDate = function(isoDateVal) { + + if (typeof isoDateVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "YYYY-MM-DD" format + var matches = isoDateVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/); + + // If unsuccessful, try to match "YYYYMMDD" format + if (matches === null) + matches = isoDateVal.match(/^(\d\d\d\d)(\d\d)(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var year = parseInt(matches[1], 10); + var month = parseInt(matches[2], 10); + var day = parseInt(matches[3], 10); + + // Simple value range check, leap years not checked + if (month < 1 || month > 12 || + day < 1 || day > 31 ) + + throw "Error: Invalid ISO-8601 date value"; + + var d = new Date(year, month - 1, day); + + // Check if the input date was valid + // (JS Date does automatic forward correction) + if (d.getDate() != day || d.getMonth() +1 != month) + throw "Error: Invalid date"; + + return d; +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted time string to a JavaScript + * Date object. + * + * @param {String} isoTimeVal An ISO-8601 formatted time string. + * + *

    Accepted formats: + * + *

      + *
    • HH:MM:SS + *
    • HHMMSS + *
    + * + * @returns {Date} The corresponding Date object, with year, month and day set + * to zero. + * + * @throws Error on a badly formatted time string. + */ +jsworld.parseIsoTime = function(isoTimeVal) { + + if (typeof isoTimeVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "HH:MM:SS" format + var matches = isoTimeVal.match(/^(\d\d):(\d\d):(\d\d)/); + + // If unsuccessful, try to match "HHMMSS" format + if (matches === null) + matches = isoTimeVal.match(/^(\d\d)(\d\d)(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date/time string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var hour = parseInt(matches[1], 10); + var mins = parseInt(matches[2], 10); + var secs = parseInt(matches[3], 10); + + // Simple value range check, leap years not checked + if (hour < 0 || hour > 23 || + mins < 0 || mins > 59 || + secs < 0 || secs > 59 ) + + throw "Error: Invalid ISO-8601 time value"; + + return new Date(0, 0, 0, hour, mins, secs); +}; + + +/** + * @private + * + * @description Trims leading and trailing whitespace from a string. + * + *

    Used non-regexp the method from http://blog.stevenlevithan.com/archives/faster-trim-javascript + * + * @param {String} str The string to trim. + * + * @returns {String} The trimmed string. + */ +jsworld._trim = function(str) { + + var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'; + + for (var i = 0; i < str.length; i++) { + + if (whitespace.indexOf(str.charAt(i)) === -1) { + str = str.substring(i); + break; + } + } + + for (i = str.length - 1; i >= 0; i--) { + if (whitespace.indexOf(str.charAt(i)) === -1) { + str = str.substring(0, i + 1); + break; + } + } + + return whitespace.indexOf(str.charAt(0)) === -1 ? str : ''; +}; + + + +/** + * @private + * + * @description Returns true if the argument represents a decimal number. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents a decimal number, + * otherwise false. + */ +jsworld._isNumber = function(arg) { + + if (typeof arg == "number") + return true; + + if (typeof arg != "string") + return false; + + // ensure string + var s = arg + ""; + + return (/^-?(\d+|\d*\.\d+)$/).test(s); +}; + + +/** + * @private + * + * @description Returns true if the argument represents a decimal integer. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents an integer, otherwise + * false. + */ +jsworld._isInteger = function(arg) { + + if (typeof arg != "number" && typeof arg != "string") + return false; + + // convert to string + var s = arg + ""; + + return (/^-?\d+$/).test(s); +}; + + +/** + * @private + * + * @description Returns true if the argument represents a decimal float. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents a float, otherwise false. + */ +jsworld._isFloat = function(arg) { + + if (typeof arg != "number" && typeof arg != "string") + return false; + + // convert to string + var s = arg + ""; + + return (/^-?\.\d+?$/).test(s); +}; + + +/** + * @private + * + * @description Checks if the specified formatting option is contained + * within the options string. + * + * @param {String} option The option to search for. + * @param {String} optionsString The options string. + * + * @returns {Boolean} true if the flag is found, else false + */ +jsworld._hasOption = function(option, optionsString) { + + if (typeof option != "string" || typeof optionsString != "string") + return false; + + if (optionsString.indexOf(option) != -1) + return true; + else + return false; +}; + + +/** + * @private + * + * @description String replacement function. + * + * @param {String} s The string to work on. + * @param {String} target The string to search for. + * @param {String} replacement The replacement. + * + * @returns {String} The new string. + */ +jsworld._stringReplaceAll = function(s, target, replacement) { + + var out; + + if (target.length == 1 && replacement.length == 1) { + // simple char/char case somewhat faster + out = ""; + + for (var i = 0; i < s.length; i++) { + + if (s.charAt(i) == target.charAt(0)) + out = out + replacement.charAt(0); + else + out = out + s.charAt(i); + } + + return out; + } + else { + // longer target and replacement strings + out = s; + + var index = out.indexOf(target); + + while (index != -1) { + + out = out.replace(target, replacement); + + index = out.indexOf(target); + } + + return out; + } +}; + + +/** + * @private + * + * @description Tests if a string starts with the specified substring. + * + * @param {String} testedString The string to test. + * @param {String} sub The string to match. + * + * @returns {Boolean} true if the test succeeds. + */ +jsworld._stringStartsWith = function (testedString, sub) { + + if (testedString.length < sub.length) + return false; + + for (var i = 0; i < sub.length; i++) { + if (testedString.charAt(i) != sub.charAt(i)) + return false; + } + + return true; +}; + + +/** + * @private + * + * @description Gets the requested precision from an options string. + * + *

    Example: ".3" returns 3 decimal places precision. + * + * @param {String} optionsString The options string. + * + * @returns {integer Number} The requested precision, -1 if not specified. + */ +jsworld._getPrecision = function (optionsString) { + + if (typeof optionsString != "string") + return -1; + + var m = optionsString.match(/\.(\d)/); + if (m) + return parseInt(m[1], 10); + else + return -1; +}; + + +/** + * @private + * + * @description Takes a decimal numeric amount (optionally as string) and + * returns its integer and fractional parts packed into an object. + * + * @param {Number|String} amount The amount, e.g. "123.45" or "-56.78" + * + * @returns {object} Parsed amount object with properties: + * {String} integer : the integer part + * {String} fraction : the fraction part + */ +jsworld._splitNumber = function (amount) { + + if (typeof amount == "number") + amount = amount + ""; + + var obj = {}; + + // remove negative sign + if (amount.charAt(0) == "-") + amount = amount.substring(1); + + // split amount into integer and decimal parts + var amountParts = amount.split("."); + if (!amountParts[1]) + amountParts[1] = ""; // we need "" instead of null + + obj.integer = amountParts[0]; + obj.fraction = amountParts[1]; + + return obj; +}; + + +/** + * @private + * + * @description Formats the integer part using the specified grouping + * and thousands separator. + * + * @param {String} intPart The integer part of the amount, as string. + * @param {String} grouping The grouping definition. + * @param {String} thousandsSep The thousands separator. + * + * @returns {String} The formatted integer part. + */ +jsworld._formatIntegerPart = function (intPart, grouping, thousandsSep) { + + // empty separator string? no grouping? + // -> return immediately with no formatting! + if (thousandsSep == "" || grouping == "-1") + return intPart; + + // turn the semicolon-separated string of integers into an array + var groupSizes = grouping.split(";"); + + // the formatted output string + var out = ""; + + // the intPart string position to process next, + // start at string end, e.g. "10000000 0) { + + // get next group size (if any, otherwise keep last) + if (groupSizes.length > 0) + size = parseInt(groupSizes.shift(), 10); + + // int parse error? + if (isNaN(size)) + throw "Error: Invalid grouping"; + + // size is -1? -> no more grouping, so just copy string remainder + if (size == -1) { + out = intPart.substring(0, pos) + out; + break; + } + + pos -= size; // move to next sep. char. position + + // position underrun? -> just copy string remainder + if (pos < 1) { + out = intPart.substring(0, pos + size) + out; + break; + } + + // extract group and apply sep. char. + out = thousandsSep + intPart.substring(pos, pos + size) + out; + } + + return out; +}; + + +/** + * @private + * + * @description Formats the fractional part to the specified decimal + * precision. + * + * @param {String} fracPart The fractional part of the amount + * @param {integer Number} precision The desired decimal precision + * + * @returns {String} The formatted fractional part. + */ +jsworld._formatFractionPart = function (fracPart, precision) { + + // append zeroes up to precision if necessary + for (var i=0; fracPart.length < precision; i++) + fracPart = fracPart + "0"; + + return fracPart; +}; + + +/** + * @private + * + * @desription Converts a number to string and pad it with leading zeroes if the + * string is shorter than length. + * + * @param {integer Number} number The number value subjected to selective padding. + * @param {integer Number} length If the number has fewer digits than this length + * apply padding. + * + * @returns {String} The formatted string. + */ +jsworld._zeroPad = function(number, length) { + + // ensure string + var s = number + ""; + + while (s.length < length) + s = "0" + s; + + return s; +}; + + +/** + * @private + * @description Converts a number to string and pads it with leading spaces if + * the string is shorter than length. + * + * @param {integer Number} number The number value subjected to selective padding. + * @param {integer Number} length If the number has fewer digits than this length + * apply padding. + * + * @returns {String} The formatted string. + */ +jsworld._spacePad = function(number, length) { + + // ensure string + var s = number + ""; + + while (s.length < length) + s = " " + s; + + return s; +}; + + + +/** + * @class + * Represents a POSIX-style locale with its numeric, monetary and date/time + * properties. Also provides a set of locale helper methods. + * + *

    The locale properties follow the POSIX standards: + * + *

    + * + * @public + * @constructor + * @description Creates a new locale object (POSIX-style) with the specified + * properties. + * + * @param {object} properties An object containing the raw locale properties: + * + * @param {String} properties.decimal_point + * + * A string containing the symbol that shall be used as the decimal + * delimiter (radix character) in numeric, non-monetary formatted + * quantities. This property cannot be omitted and cannot be set to the + * empty string. + * + * + * @param {String} properties.thousands_sep + * + * A string containing the symbol that shall be used as a separator for + * groups of digits to the left of the decimal delimiter in numeric, + * non-monetary formatted monetary quantities. + * + * + * @param {String} properties.grouping + * + * Defines the size of each group of digits in formatted non-monetary + * quantities. The operand is a sequence of integers separated by + * semicolons. Each integer specifies the number of digits in each group, + * with the initial integer defining the size of the group immediately + * preceding the decimal delimiter, and the following integers defining + * the preceding groups. If the last integer is not -1, then the size of + * the previous group (if any) shall be repeatedly used for the + * remainder of the digits. If the last integer is -1, then no further + * grouping shall be performed. + * + * + * @param {String} properties.int_curr_symbol + * + * The first three letters signify the ISO-4217 currency code, + * the fourth letter is the international symbol separation character + * (normally a space). + * + * + * @param {String} properties.currency_symbol + * + * The local shorthand currency symbol, e.g. "$" for the en_US locale + * + * + * @param {String} properties.mon_decimal_point + * + * The symbol to be used as the decimal delimiter (radix character) + * + * + * @param {String} properties.mon_thousands_sep + * + * The symbol to be used as a separator for groups of digits to the + * left of the decimal delimiter. + * + * + * @param {String} properties.mon_grouping + * + * A string that defines the size of each group of digits. The + * operand is a sequence of integers separated by semicolons (";"). + * Each integer specifies the number of digits in each group, with the + * initial integer defining the size of the group preceding the + * decimal delimiter, and the following integers defining the + * preceding groups. If the last integer is not -1, then the size of + * the previous group (if any) must be repeatedly used for the + * remainder of the digits. If the last integer is -1, then no + * further grouping is to be performed. + * + * + * @param {String} properties.positive_sign + * + * The string to indicate a non-negative monetary amount. + * + * + * @param {String} properties.negative_sign + * + * The string to indicate a negative monetary amount. + * + * + * @param {integer Number} properties.frac_digits + * + * An integer representing the number of fractional digits (those to + * the right of the decimal delimiter) to be written in a formatted + * monetary quantity using currency_symbol. + * + * + * @param {integer Number} properties.int_frac_digits + * + * An integer representing the number of fractional digits (those to + * the right of the decimal delimiter) to be written in a formatted + * monetary quantity using int_curr_symbol. + * + * + * @param {integer Number} properties.p_cs_precedes + * + * An integer set to 1 if the currency_symbol precedes the value for a + * monetary quantity with a non-negative value, and set to 0 if the + * symbol succeeds the value. + * + * + * @param {integer Number} properties.n_cs_precedes + * + * An integer set to 1 if the currency_symbol precedes the value for a + * monetary quantity with a negative value, and set to 0 if the symbol + * succeeds the value. + * + * + * @param {integer Number} properties.p_sep_by_space + * + * Set to a value indicating the separation of the currency_symbol, + * the sign string, and the value for a non-negative formatted monetary + * quantity: + * + *

    0 No space separates the currency symbol and value.

    + * + *

    1 If the currency symbol and sign string are adjacent, a space + * separates them from the value; otherwise, a space separates + * the currency symbol from the value.

    + * + *

    2 If the currency symbol and sign string are adjacent, a space + * separates them; otherwise, a space separates the sign string + * from the value.

    + * + * + * @param {integer Number} properties.n_sep_by_space + * + * Set to a value indicating the separation of the currency_symbol, + * the sign string, and the value for a negative formatted monetary + * quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.p_sign_posn + * + * An integer set to a value indicating the positioning of the + * positive_sign for a monetary quantity with a non-negative value: + * + *

    0 Parentheses enclose the quantity and the currency_symbol.

    + * + *

    1 The sign string precedes the quantity and the currency_symbol.

    + * + *

    2 The sign string succeeds the quantity and the currency_symbol.

    + * + *

    3 The sign string precedes the currency_symbol.

    + * + *

    4 The sign string succeeds the currency_symbol.

    + * + * + * @param {integer Number} properties.n_sign_posn + * + * An integer set to a value indicating the positioning of the + * negative_sign for a negative formatted monetary quantity. Rules same + * as for p_sign_posn. + * + * + * @param {integer Number} properties.int_p_cs_precedes + * + * An integer set to 1 if the int_curr_symbol precedes the value for a + * monetary quantity with a non-negative value, and set to 0 if the + * symbol succeeds the value. + * + * + * @param {integer Number} properties.int_n_cs_precedes + * + * An integer set to 1 if the int_curr_symbol precedes the value for a + * monetary quantity with a negative value, and set to 0 if the symbol + * succeeds the value. + * + * + * @param {integer Number} properties.int_p_sep_by_space + * + * Set to a value indicating the separation of the int_curr_symbol, + * the sign string, and the value for a non-negative internationally + * formatted monetary quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.int_n_sep_by_space + * + * Set to a value indicating the separation of the int_curr_symbol, + * the sign string, and the value for a negative internationally + * formatted monetary quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.int_p_sign_posn + * + * An integer set to a value indicating the positioning of the + * positive_sign for a positive monetary quantity formatted with the + * international format. Rules same as for p_sign_posn. + * + * + * @param {integer Number} properties.int_n_sign_posn + * + * An integer set to a value indicating the positioning of the + * negative_sign for a negative monetary quantity formatted with the + * international format. Rules same as for p_sign_posn. + * + * + * @param {String[] | String} properties.abday + * + * The abbreviated weekday names, corresponding to the %a conversion + * specification. The property must be either an array of 7 strings or + * a string consisting of 7 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the abbreviated name + * of the day corresponding to Sunday, the second the abbreviated name + * of the day corresponding to Monday, and so on. + * + * + * @param {String[] | String} properties.day + * + * The full weekday names, corresponding to the %A conversion + * specification. The property must be either an array of 7 strings or + * a string consisting of 7 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the full name of the + * day corresponding to Sunday, the second the full name of the day + * corresponding to Monday, and so on. + * + * + * @param {String[] | String} properties.abmon + * + * The abbreviated month names, corresponding to the %b conversion + * specification. The property must be either an array of 12 strings or + * a string consisting of 12 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the abbreviated name + * of the first month of the year (January), the second the abbreviated + * name of the second month, and so on. + * + * + * @param {String[] | String} properties.mon + * + * The full month names, corresponding to the %B conversion + * specification. The property must be either an array of 12 strings or + * a string consisting of 12 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the full name of the + * first month of the year (January), the second the full name of the second + * month, and so on. + * + * + * @param {String} properties.d_fmt + * + * The appropriate date representation. The string may contain any + * combination of characters and conversion specifications (%). + * + * + * @param {String} properties.t_fmt + * + * The appropriate time representation. The string may contain any + * combination of characters and conversion specifications (%). + * + * + * @param {String} properties.d_t_fmt + * + * The appropriate date and time representation. The string may contain + * any combination of characters and conversion specifications (%). + * + * + * @param {String[] | String} properties.am_pm + * + * The appropriate representation of the ante-meridiem and post-meridiem + * strings, corresponding to the %p conversion specification. The property + * must be either an array of 2 strings or a string consisting of 2 + * semicolon-separated substrings, each surrounded by double-quotes. + * The first string must represent the ante-meridiem designation, the + * last string the post-meridiem designation. + * + * + * @throws @throws Error on a undefined or invalid locale property. + */ +jsworld.Locale = function(properties) { + + + /** + * @private + * + * @description Identifies the class for internal library purposes. + */ + this._className = "jsworld.Locale"; + + + /** + * @private + * + * @description Parses a day or month name definition list, which + * could be a ready JS array, e.g. ["Mon", "Tue", "Wed"...] or + * it could be a string formatted according to the classic POSIX + * definition e.g. "Mon";"Tue";"Wed";... + * + * @param {String[] | String} namesAn array or string defining + * the week/month names. + * @param {integer Number} expectedItems The number of expected list + * items, e.g. 7 for weekdays, 12 for months. + * + * @returns {String[]} The parsed (and checked) items. + * + * @throws Error on missing definition, unexpected item count or + * missing double-quotes. + */ + this._parseList = function(names, expectedItems) { + + var array = []; + + if (names == null) { + throw "Names not defined"; + } + else if (typeof names == "object") { + // we got a ready array + array = names; + } + else if (typeof names == "string") { + // we got the names in the classic POSIX form, do parse + array = names.split(";", expectedItems); + + for (var i = 0; i < array.length; i++) { + // check for and strip double quotes + if (array[i][0] == "\"" && array[i][array[i].length - 1] == "\"") + array[i] = array[i].slice(1, -1); + else + throw "Missing double quotes"; + } + } + else { + throw "Names must be an array or a string"; + } + + if (array.length != expectedItems) + throw "Expected " + expectedItems + " items, got " + array.length; + + return array; + }; + + + /** + * @private + * + * @description Validates a date/time format string, such as "H:%M:%S". + * Checks that the argument is of type "string" and is not empty. + * + * @param {String} formatString The format string. + * + * @returns {String} The validated string. + * + * @throws Error on null or empty string. + */ + this._validateFormatString = function(formatString) { + + if (typeof formatString == "string" && formatString.length > 0) + return formatString; + else + throw "Empty or no string"; + }; + + + // LC_NUMERIC + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing locale properties"; + + + if (typeof properties.decimal_point != "string") + throw "Error: Invalid/missing decimal_point property"; + + this.decimal_point = properties.decimal_point; + + + if (typeof properties.thousands_sep != "string") + throw "Error: Invalid/missing thousands_sep property"; + + this.thousands_sep = properties.thousands_sep; + + + if (typeof properties.grouping != "string") + throw "Error: Invalid/missing grouping property"; + + this.grouping = properties.grouping; + + + // LC_MONETARY + + if (typeof properties.int_curr_symbol != "string") + throw "Error: Invalid/missing int_curr_symbol property"; + + if (! /[A-Za-z]{3}.?/.test(properties.int_curr_symbol)) + throw "Error: Invalid int_curr_symbol property"; + + this.int_curr_symbol = properties.int_curr_symbol; + + + if (typeof properties.currency_symbol != "string") + throw "Error: Invalid/missing currency_symbol property"; + + this.currency_symbol = properties.currency_symbol; + + + if (typeof properties.frac_digits != "number" && properties.frac_digits < 0) + throw "Error: Invalid/missing frac_digits property"; + + this.frac_digits = properties.frac_digits; + + + // may be empty string/null for currencies with no fractional part + if (properties.mon_decimal_point === null || properties.mon_decimal_point == "") { + + if (this.frac_digits > 0) + throw "Error: Undefined mon_decimal_point property"; + else + properties.mon_decimal_point = ""; + } + + if (typeof properties.mon_decimal_point != "string") + throw "Error: Invalid/missing mon_decimal_point property"; + + this.mon_decimal_point = properties.mon_decimal_point; + + + if (typeof properties.mon_thousands_sep != "string") + throw "Error: Invalid/missing mon_thousands_sep property"; + + this.mon_thousands_sep = properties.mon_thousands_sep; + + + if (typeof properties.mon_grouping != "string") + throw "Error: Invalid/missing mon_grouping property"; + + this.mon_grouping = properties.mon_grouping; + + + if (typeof properties.positive_sign != "string") + throw "Error: Invalid/missing positive_sign property"; + + this.positive_sign = properties.positive_sign; + + + if (typeof properties.negative_sign != "string") + throw "Error: Invalid/missing negative_sign property"; + + this.negative_sign = properties.negative_sign; + + + + if (properties.p_cs_precedes !== 0 && properties.p_cs_precedes !== 1) + throw "Error: Invalid/missing p_cs_precedes property, must be 0 or 1"; + + this.p_cs_precedes = properties.p_cs_precedes; + + + if (properties.n_cs_precedes !== 0 && properties.n_cs_precedes !== 1) + throw "Error: Invalid/missing n_cs_precedes, must be 0 or 1"; + + this.n_cs_precedes = properties.n_cs_precedes; + + + if (properties.p_sep_by_space !== 0 && + properties.p_sep_by_space !== 1 && + properties.p_sep_by_space !== 2) + throw "Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2"; + + this.p_sep_by_space = properties.p_sep_by_space; + + + if (properties.n_sep_by_space !== 0 && + properties.n_sep_by_space !== 1 && + properties.n_sep_by_space !== 2) + throw "Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2"; + + this.n_sep_by_space = properties.n_sep_by_space; + + + if (properties.p_sign_posn !== 0 && + properties.p_sign_posn !== 1 && + properties.p_sign_posn !== 2 && + properties.p_sign_posn !== 3 && + properties.p_sign_posn !== 4) + throw "Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.p_sign_posn = properties.p_sign_posn; + + + if (properties.n_sign_posn !== 0 && + properties.n_sign_posn !== 1 && + properties.n_sign_posn !== 2 && + properties.n_sign_posn !== 3 && + properties.n_sign_posn !== 4) + throw "Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.n_sign_posn = properties.n_sign_posn; + + + if (typeof properties.int_frac_digits != "number" && properties.int_frac_digits < 0) + throw "Error: Invalid/missing int_frac_digits property"; + + this.int_frac_digits = properties.int_frac_digits; + + + if (properties.int_p_cs_precedes !== 0 && properties.int_p_cs_precedes !== 1) + throw "Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1"; + + this.int_p_cs_precedes = properties.int_p_cs_precedes; + + + if (properties.int_n_cs_precedes !== 0 && properties.int_n_cs_precedes !== 1) + throw "Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1"; + + this.int_n_cs_precedes = properties.int_n_cs_precedes; + + + if (properties.int_p_sep_by_space !== 0 && + properties.int_p_sep_by_space !== 1 && + properties.int_p_sep_by_space !== 2) + throw "Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2"; + + this.int_p_sep_by_space = properties.int_p_sep_by_space; + + + if (properties.int_n_sep_by_space !== 0 && + properties.int_n_sep_by_space !== 1 && + properties.int_n_sep_by_space !== 2) + throw "Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2"; + + this.int_n_sep_by_space = properties.int_n_sep_by_space; + + + if (properties.int_p_sign_posn !== 0 && + properties.int_p_sign_posn !== 1 && + properties.int_p_sign_posn !== 2 && + properties.int_p_sign_posn !== 3 && + properties.int_p_sign_posn !== 4) + throw "Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_p_sign_posn = properties.int_p_sign_posn; + + + if (properties.int_n_sign_posn !== 0 && + properties.int_n_sign_posn !== 1 && + properties.int_n_sign_posn !== 2 && + properties.int_n_sign_posn !== 3 && + properties.int_n_sign_posn !== 4) + throw "Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_n_sign_posn = properties.int_n_sign_posn; + + + // LC_TIME + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing time locale properties"; + + + // parse the supported POSIX LC_TIME properties + + // abday + try { + this.abday = this._parseList(properties.abday, 7); + } + catch (error) { + throw "Error: Invalid abday property: " + error; + } + + // day + try { + this.day = this._parseList(properties.day, 7); + } + catch (error) { + throw "Error: Invalid day property: " + error; + } + + // abmon + try { + this.abmon = this._parseList(properties.abmon, 12); + } catch (error) { + throw "Error: Invalid abmon property: " + error; + } + + // mon + try { + this.mon = this._parseList(properties.mon, 12); + } catch (error) { + throw "Error: Invalid mon property: " + error; + } + + // d_fmt + try { + this.d_fmt = this._validateFormatString(properties.d_fmt); + } catch (error) { + throw "Error: Invalid d_fmt property: " + error; + } + + // t_fmt + try { + this.t_fmt = this._validateFormatString(properties.t_fmt); + } catch (error) { + throw "Error: Invalid t_fmt property: " + error; + } + + // d_t_fmt + try { + this.d_t_fmt = this._validateFormatString(properties.d_t_fmt); + } catch (error) { + throw "Error: Invalid d_t_fmt property: " + error; + } + + // am_pm + try { + var am_pm_strings = this._parseList(properties.am_pm, 2); + this.am = am_pm_strings[0]; + this.pm = am_pm_strings[1]; + } catch (error) { + // ignore empty/null string errors + this.am = ""; + this.pm = ""; + } + + + /** + * @public + * + * @description Returns the abbreviated name of the specified weekday. + * + * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero + * corresponds to Sunday, one to Monday, etc. If omitted the + * method will return an array of all abbreviated weekday + * names. + * + * @returns {String | String[]} The abbreviated name of the specified weekday + * or an array of all abbreviated weekday names. + * + * @throws Error on invalid argument. + */ + this.getAbbreviatedWeekdayName = function(weekdayNum) { + + if (typeof weekdayNum == "undefined" || weekdayNum === null) + return this.abday; + + if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) + throw "Error: Invalid weekday argument, must be an integer [0..6]"; + + return this.abday[weekdayNum]; + }; + + + /** + * @public + * + * @description Returns the name of the specified weekday. + * + * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero + * corresponds to Sunday, one to Monday, etc. If omitted the + * method will return an array of all weekday names. + * + * @returns {String | String[]} The name of the specified weekday or an + * array of all weekday names. + * + * @throws Error on invalid argument. + */ + this.getWeekdayName = function(weekdayNum) { + + if (typeof weekdayNum == "undefined" || weekdayNum === null) + return this.day; + + if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) + throw "Error: Invalid weekday argument, must be an integer [0..6]"; + + return this.day[weekdayNum]; + }; + + + /** + * @public + * + * @description Returns the abbreviated name of the specified month. + * + * @param {integer Number} [monthNum] An integer between 0 and 11. Zero + * corresponds to January, one to February, etc. If omitted the + * method will return an array of all abbreviated month names. + * + * @returns {String | String[]} The abbreviated name of the specified month + * or an array of all abbreviated month names. + * + * @throws Error on invalid argument. + */ + this.getAbbreviatedMonthName = function(monthNum) { + + if (typeof monthNum == "undefined" || monthNum === null) + return this.abmon; + + if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) + throw "Error: Invalid month argument, must be an integer [0..11]"; + + return this.abmon[monthNum]; + }; + + + /** + * @public + * + * @description Returns the name of the specified month. + * + * @param {integer Number} [monthNum] An integer between 0 and 11. Zero + * corresponds to January, one to February, etc. If omitted the + * method will return an array of all month names. + * + * @returns {String | String[]} The name of the specified month or an array + * of all month names. + * + * @throws Error on invalid argument. + */ + this.getMonthName = function(monthNum) { + + if (typeof monthNum == "undefined" || monthNum === null) + return this.mon; + + if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) + throw "Error: Invalid month argument, must be an integer [0..11]"; + + return this.mon[monthNum]; + }; + + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) character for + * numeric quantities. + * + * @returns {String} The radix character. + */ + this.getDecimalPoint = function() { + + return this.decimal_point; + }; + + + /** + * @public + * + * @description Gets the local shorthand currency symbol. + * + * @returns {String} The currency symbol. + */ + this.getCurrencySymbol = function() { + + return this.currency_symbol; + }; + + + /** + * @public + * + * @description Gets the internaltion currency symbol (ISO-4217 code). + * + * @returns {String} The international currency symbol. + */ + this.getIntCurrencySymbol = function() { + + return this.int_curr_symbol.substring(0,3); + }; + + + /** + * @public + * + * @description Gets the position of the local (shorthand) currency + * symbol relative to the amount. Assumes a non-negative amount. + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.currencySymbolPrecedes = function() { + + if (this.p_cs_precedes == 1) + return true; + else + return false; + }; + + + /** + * @public + * + * @description Gets the position of the international (ISO-4217 code) + * currency symbol relative to the amount. Assumes a non-negative + * amount. + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.intCurrencySymbolPrecedes = function() { + + if (this.int_p_cs_precedes == 1) + return true; + else + return false; + + }; + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) for monetary + * quantities. + * + * @returns {String} The radix character. + */ + this.getMonetaryDecimalPoint = function() { + + return this.mon_decimal_point; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits for local + * (shorthand) symbol formatting. + * + * @returns {integer Number} The number of fractional digits. + */ + this.getFractionalDigits = function() { + + return this.frac_digits; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits for + * international (ISO-4217 code) formatting. + * + * @returns {integer Number} The number of fractional digits. + */ + this.getIntFractionalDigits = function() { + + return this.int_frac_digits; + }; +}; + + + +/** + * @class + * Class for localised formatting of numbers. + * + *

    See: + * POSIX LC_NUMERIC. + * + * + * @public + * @constructor + * @description Creates a new numeric formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_NUMERIC formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.NumericFormatter = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + + /** + * @public + * + * @description Formats a decimal numeric value according to the preset + * locale. + * + * @param {Number|String} number The number to format. + * @param {String} [options] Options to modify the formatted output: + *

      + *
    • "^" suppress grouping + *
    • "+" force positive sign for positive amounts + *
    • "~" suppress positive/negative sign + *
    • ".n" specify decimal precision 'n' + *
    + * + * @returns {String} The formatted number. + * + * @throws "Error: Invalid input" on bad input. + */ + this.format = function(number, options) { + + if (typeof number == "string") + number = jsworld._trim(number); + + if (! jsworld._isNumber(number)) + throw "Error: The input is not a number"; + + var floatAmount = parseFloat(number, 10); + + // get the required precision + var reqPrecision = jsworld._getPrecision(options); + + // round to required precision + if (reqPrecision != -1) + floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision); + + + // convert the float number to string and parse into + // object with properties integer and fraction + var parsedAmount = jsworld._splitNumber(String(floatAmount)); + + // format integer part with grouping chars + var formattedIntegerPart; + + if (floatAmount === 0) + formattedIntegerPart = "0"; + else + formattedIntegerPart = jsworld._hasOption("^", options) ? + parsedAmount.integer : + jsworld._formatIntegerPart(parsedAmount.integer, + this.lc.grouping, + this.lc.thousands_sep); + + // format the fractional part + var formattedFractionPart = + reqPrecision != -1 ? + jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision) : + parsedAmount.fraction; + + + // join the integer and fraction parts using the decimal_point property + var formattedAmount = + formattedFractionPart.length ? + formattedIntegerPart + this.lc.decimal_point + formattedFractionPart : + formattedIntegerPart; + + // prepend sign? + if (jsworld._hasOption("~", options) || floatAmount === 0) { + // suppress both '+' and '-' signs, i.e. return abs value + return formattedAmount; + } + else { + if (jsworld._hasOption("+", options) || floatAmount < 0) { + if (floatAmount > 0) + // force '+' sign for positive amounts + return "+" + formattedAmount; + else if (floatAmount < 0) + // prepend '-' sign + return "-" + formattedAmount; + else + // zero case + return formattedAmount; + } + else { + // positive amount with no '+' sign + return formattedAmount; + } + } + }; +}; + + +/** + * @class + * Class for localised formatting of dates and times. + * + *

    See: + * POSIX LC_TIME. + * + * @public + * @constructor + * @description Creates a new date/time formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_TIME formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.DateTimeFormatter = function(locale) { + + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance."; + + this.lc = locale; + + + /** + * @public + * + * @description Formats a date according to the preset locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted date, e.g. "2010-31-03" + * or "2010-03-31 23:59:59". + * + * @returns {String} The formatted date + * + * @throws Error on invalid date argument + */ + this.formatDate = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 date string + try { + d = jsworld.parseIsoDate(date); + } catch (error) { + // try full ISO-8601 date/time string + d = jsworld.parseIsoDateTime(date); + } + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.d_fmt); + }; + + + /** + * @public + * + * @description Formats a time according to the preset locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted time, e.g. "23:59:59" + * or "2010-03-31 23:59:59". + * + * @returns {String} The formatted time. + * + * @throws Error on invalid date argument. + */ + this.formatTime = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 time string + try { + d = jsworld.parseIsoTime(date); + } catch (error) { + // try full ISO-8601 date/time string + d = jsworld.parseIsoDateTime(date); + } + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.t_fmt); + }; + + + /** + * @public + * + * @description Formats a date/time value according to the preset + * locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted date/time, e.g. + * "2010-03-31 23:59:59". + * + * @returns {String} The formatted time. + * + * @throws Error on invalid argument. + */ + this.formatDateTime = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 format + d = jsworld.parseIsoDateTime(date); + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.d_t_fmt); + }; + + + /** + * @private + * + * @description Apples formatting to the Date object according to the + * format string. + * + * @param {Date} d A valid Date instance. + * @param {String} s The formatting string with '%' placeholders. + * + * @returns {String} The formatted string. + */ + this._applyFormatting = function(d, s) { + + s = s.replace(/%%/g, '%'); + s = s.replace(/%a/g, this.lc.abday[d.getDay()]); + s = s.replace(/%A/g, this.lc.day[d.getDay()]); + s = s.replace(/%b/g, this.lc.abmon[d.getMonth()]); + s = s.replace(/%B/g, this.lc.mon[d.getMonth()]); + s = s.replace(/%d/g, jsworld._zeroPad(d.getDate(), 2)); + s = s.replace(/%e/g, jsworld._spacePad(d.getDate(), 2)); + s = s.replace(/%F/g, d.getFullYear() + + "-" + + jsworld._zeroPad(d.getMonth()+1, 2) + + "-" + + jsworld._zeroPad(d.getDate(), 2)); + s = s.replace(/%h/g, this.lc.abmon[d.getMonth()]); // same as %b + s = s.replace(/%H/g, jsworld._zeroPad(d.getHours(), 2)); + s = s.replace(/%I/g, jsworld._zeroPad(this._hours12(d.getHours()), 2)); + s = s.replace(/%k/g, d.getHours()); + s = s.replace(/%l/g, this._hours12(d.getHours())); + s = s.replace(/%m/g, jsworld._zeroPad(d.getMonth()+1, 2)); + s = s.replace(/%n/g, "\n"); + s = s.replace(/%M/g, jsworld._zeroPad(d.getMinutes(), 2)); + s = s.replace(/%p/g, this._getAmPm(d.getHours())); + s = s.replace(/%P/g, this._getAmPm(d.getHours()).toLocaleLowerCase()); // safe? + s = s.replace(/%R/g, jsworld._zeroPad(d.getHours(), 2) + + ":" + + jsworld._zeroPad(d.getMinutes(), 2)); + s = s.replace(/%S/g, jsworld._zeroPad(d.getSeconds(), 2)); + s = s.replace(/%T/g, jsworld._zeroPad(d.getHours(), 2) + + ":" + + jsworld._zeroPad(d.getMinutes(), 2) + + ":" + + jsworld._zeroPad(d.getSeconds(), 2)); + s = s.replace(/%w/g, this.lc.day[d.getDay()]); + s = s.replace(/%y/g, new String(d.getFullYear()).substring(2)); + s = s.replace(/%Y/g, d.getFullYear()); + + s = s.replace(/%Z/g, ""); // to do: ignored until a reliable TMZ method found + + s = s.replace(/%[a-zA-Z]/g, ""); // ignore all other % sequences + + return s; + }; + + + /** + * @private + * + * @description Does 24 to 12 hour conversion. + * + * @param {integer Number} hour24 Hour [0..23]. + * + * @returns {integer Number} Corresponding hour [1..12]. + */ + this._hours12 = function(hour24) { + + if (hour24 === 0) + return 12; // 00h is 12AM + + else if (hour24 > 12) + return hour24 - 12; // 1PM to 11PM + + else + return hour24; // 1AM to 12PM + }; + + + /** + * @private + * + * @description Gets the appropriate localised AM or PM string depending + * on the day hour. Special cases: midnight is 12AM, noon is 12PM. + * + * @param {integer Number} hour24 Hour [0..23]. + * + * @returns {String} The corresponding localised AM or PM string. + */ + this._getAmPm = function(hour24) { + + if (hour24 < 12) + return this.lc.am; + else + return this.lc.pm; + }; +}; + + + +/** + * @class Class for localised formatting of currency amounts. + * + *

    See: + * POSIX LC_MONETARY. + * + * @public + * @constructor + * @description Creates a new monetary formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_MONETARY formatting properties. + * @param {String} [currencyCode] Set the currency explicitly by + * passing its international ISO-4217 code, e.g. "USD", "EUR", "GBP". + * Use this optional parameter to override the default local currency + * @param {String} [altIntSymbol] Non-local currencies are formatted + * with their international ISO-4217 code to prevent ambiguity. + * Use this optional argument to force a different symbol, such as the + * currency's shorthand sign. This is mostly useful when the shorthand + * sign is both internationally recognised and identifies the currency + * uniquely (e.g. the Euro sign). + * + * @throws Error on constructor failure. + */ +jsworld.MonetaryFormatter = function(locale, currencyCode, altIntSymbol) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + /** + * @private + * @description Lookup table to determine the fraction digits for a + * specific currency; most currencies subdivide at 1/100 (2 fractional + * digits), so we store only those that deviate from the default. + * + *

    The data is from Unicode's CLDR version 1.7.0. The two currencies + * with non-decimal subunits (MGA and MRO) are marked as having no + * fractional digits as well as all currencies that have no subunits + * in circulation. + * + *

    It is "hard-wired" for referential convenience and is only looked + * up when an overriding currencyCode parameter is supplied. + */ + this.currencyFractionDigits = { + "AFN" : 0, "ALL" : 0, "AMD" : 0, "BHD" : 3, "BIF" : 0, + "BYR" : 0, "CLF" : 0, "CLP" : 0, "COP" : 0, "CRC" : 0, + "DJF" : 0, "GNF" : 0, "GYD" : 0, "HUF" : 0, "IDR" : 0, + "IQD" : 0, "IRR" : 0, "ISK" : 0, "JOD" : 3, "JPY" : 0, + "KMF" : 0, "KRW" : 0, "KWD" : 3, "LAK" : 0, "LBP" : 0, + "LYD" : 3, "MGA" : 0, "MMK" : 0, "MNT" : 0, "MRO" : 0, + "MUR" : 0, "OMR" : 3, "PKR" : 0, "PYG" : 0, "RSD" : 0, + "RWF" : 0, "SLL" : 0, "SOS" : 0, "STD" : 0, "SYP" : 0, + "TND" : 3, "TWD" : 0, "TZS" : 0, "UGX" : 0, "UZS" : 0, + "VND" : 0, "VUV" : 0, "XAF" : 0, "XOF" : 0, "XPF" : 0, + "YER" : 0, "ZMK" : 0 + }; + + + // optional currencyCode argument? + if (typeof currencyCode == "string") { + // user wanted to override the local currency + this.currencyCode = currencyCode.toUpperCase(); + + // must override the frac digits too, for some + // currencies have 0, 2 or 3! + var numDigits = this.currencyFractionDigits[this.currencyCode]; + if (typeof numDigits != "number") + numDigits = 2; // default for most currencies + this.lc.frac_digits = numDigits; + this.lc.int_frac_digits = numDigits; + } + else { + // use local currency + this.currencyCode = this.lc.int_curr_symbol.substring(0,3).toUpperCase(); + } + + // extract intl. currency separator + this.intSep = this.lc.int_curr_symbol.charAt(3); + + // flag local or intl. sign formatting? + if (this.currencyCode == this.lc.int_curr_symbol.substring(0,3)) { + // currency matches the local one? -> + // formatting with local symbol and parameters + this.internationalFormatting = false; + this.curSym = this.lc.currency_symbol; + } + else { + // currency doesn't match the local -> + + // do we have an overriding currency symbol? + if (typeof altIntSymbol == "string") { + // -> force formatting with local parameters, using alt symbol + this.curSym = altIntSymbol; + this.internationalFormatting = false; + } + else { + // -> force formatting with intl. sign and parameters + this.internationalFormatting = true; + } + } + + + /** + * @public + * + * @description Gets the currency symbol used in formatting. + * + * @returns {String} The currency symbol. + */ + this.getCurrencySymbol = function() { + + return this.curSym; + }; + + + /** + * @public + * + * @description Gets the position of the currency symbol relative to + * the amount. Assumes a non-negative amount and local formatting. + * + * @param {String} intFlag Optional flag to force international + * formatting by passing the string "i". + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.currencySymbolPrecedes = function(intFlag) { + + if (typeof intFlag == "string" && intFlag == "i") { + // international formatting was forced + if (this.lc.int_p_cs_precedes == 1) + return true; + else + return false; + + } + else { + // check whether local formatting is on or off + if (this.internationalFormatting) { + if (this.lc.int_p_cs_precedes == 1) + return true; + else + return false; + } + else { + if (this.lc.p_cs_precedes == 1) + return true; + else + return false; + } + } + }; + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) used in formatting. + * + * @returns {String} The radix character. + */ + this.getDecimalPoint = function() { + + return this.lc.mon_decimal_point; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits. Assumes local + * formatting. + * + * @param {String} intFlag Optional flag to force international + * formatting by passing the string "i". + * + * @returns {integer Number} The number of fractional digits. + */ + this.getFractionalDigits = function(intFlag) { + + if (typeof intFlag == "string" && intFlag == "i") { + // international formatting was forced + return this.lc.int_frac_digits; + } + else { + // check whether local formatting is on or off + if (this.internationalFormatting) + return this.lc.int_frac_digits; + else + return this.lc.frac_digits; + } + }; + + + /** + * @public + * + * @description Formats a monetary amount according to the preset + * locale. + * + *

    +	 * For local currencies the native shorthand symbol will be used for
    +	 * formatting.
    +	 * Example:
    +	 *        locale is en_US
    +	 *        currency is USD
    +	 *        -> the "$" symbol will be used, e.g. $123.45
    +	 *        
    +	 * For non-local currencies the international ISO-4217 code will be
    +	 * used for formatting.
    +	 * Example:
    +	 *       locale is en_US (which has USD as currency)
    +	 *       currency is EUR
    +	 *       -> the ISO three-letter code will be used, e.g. EUR 123.45
    +	 *
    +	 * If the currency is non-local, but an alternative currency symbol was
    +	 * provided, this will be used instead.
    +	 * Example
    +	 *       locale is en_US (which has USD as currency)
    +	 *       currency is EUR
    +	 *       an alternative symbol is provided - "€"
    +	 *       -> the alternative symbol will be used, e.g. €123.45
    +	 * 
    + * + * @param {Number|String} amount The amount to format as currency. + * @param {String} [options] Options to modify the formatted output: + *
      + *
    • "^" suppress grouping + *
    • "!" suppress the currency symbol + *
    • "~" suppress the currency symbol and the sign (positive or negative) + *
    • "i" force international sign (ISO-4217 code) formatting + *
    • ".n" specify decimal precision + * + * @returns The formatted currency amount as string. + * + * @throws "Error: Invalid amount" on bad amount. + */ + this.format = function(amount, options) { + + // if the amount is passed as string, check that it parses to a float + var floatAmount; + + if (typeof amount == "string") { + amount = jsworld._trim(amount); + floatAmount = parseFloat(amount); + + if (typeof floatAmount != "number" || isNaN(floatAmount)) + throw "Error: Amount string not a number"; + } + else if (typeof amount == "number") { + floatAmount = amount; + } + else { + throw "Error: Amount not a number"; + } + + // get the required precision, ".n" option arg overrides default locale config + var reqPrecision = jsworld._getPrecision(options); + + if (reqPrecision == -1) { + if (this.internationalFormatting || jsworld._hasOption("i", options)) + reqPrecision = this.lc.int_frac_digits; + else + reqPrecision = this.lc.frac_digits; + } + + // round + floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision); + + + // convert the float amount to string and parse into + // object with properties integer and fraction + var parsedAmount = jsworld._splitNumber(String(floatAmount)); + + // format integer part with grouping chars + var formattedIntegerPart; + + if (floatAmount === 0) + formattedIntegerPart = "0"; + else + formattedIntegerPart = jsworld._hasOption("^", options) ? + parsedAmount.integer : + jsworld._formatIntegerPart(parsedAmount.integer, + this.lc.mon_grouping, + this.lc.mon_thousands_sep); + + + // format the fractional part + var formattedFractionPart; + + if (reqPrecision == -1) { + // pad fraction with trailing zeros accoring to default locale [int_]frac_digits + if (this.internationalFormatting || jsworld._hasOption("i", options)) + formattedFractionPart = + jsworld._formatFractionPart(parsedAmount.fraction, this.lc.int_frac_digits); + else + formattedFractionPart = + jsworld._formatFractionPart(parsedAmount.fraction, this.lc.frac_digits); + } + else { + // pad fraction with trailing zeros according to optional format parameter + formattedFractionPart = + jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision); + } + + + // join integer and decimal parts using the mon_decimal_point property + var quantity; + + if (this.lc.frac_digits > 0 || formattedFractionPart.length) + quantity = formattedIntegerPart + this.lc.mon_decimal_point + formattedFractionPart; + else + quantity = formattedIntegerPart; + + + // do final formatting with sign and symbol + if (jsworld._hasOption("~", options)) { + return quantity; + } + else { + var suppressSymbol = jsworld._hasOption("!", options) ? true : false; + + var sign = floatAmount < 0 ? "-" : "+"; + + if (this.internationalFormatting || jsworld._hasOption("i", options)) { + + // format with ISO-4217 code (suppressed or not) + if (suppressSymbol) + return this._formatAsInternationalCurrencyWithNoSym(sign, quantity); + else + return this._formatAsInternationalCurrency(sign, quantity); + } + else { + // format with local currency code (suppressed or not) + if (suppressSymbol) + return this._formatAsLocalCurrencyWithNoSym(sign, quantity); + else + return this._formatAsLocalCurrency(sign, quantity); + } + } + }; + + + /** + * @private + * + * @description Assembles the final string with sign, separator and symbol as local + * currency. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string. + */ + this._formatAsLocalCurrency = function (sign, q) { + + // assemble final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + if (sign == "+") { + + // parentheses + if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return "(" + q + this.curSym + ")"; + } + else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return "(" + this.curSym + q + ")"; + } + else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return "(" + q + " " + this.curSym + ")"; + } + else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return "(" + this.curSym + " " + q + ")"; + } + + // sign before q + sym + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q + this.curSym; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q + " " + this.curSym; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + " " + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + " " + q + this.curSym; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + this.curSym + q; + } + + // sign after q + sym + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.curSym + q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.curSym + " " + q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.curSym + q + " " + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign + this.curSym; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign + this.curSym; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + this.curSym + " " + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign + " " + this.curSym; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + this.curSym + q; + } + + // sign after symbol + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.curSym + this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.curSym + this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.curSym + " " + this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return "(" + q + this.curSym + ")"; + } + else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return "(" + this.curSym + q + ")"; + } + else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return "(" + q + " " + this.curSym + ")"; + } + else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return "(" + this.curSym + " " + q + ")"; + } + + // sign before q + sym + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q + this.curSym; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q + " " + this.curSym; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + " " + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + " " + q + this.curSym; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + this.curSym + q; + } + + // sign after q + sym + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.curSym + q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.curSym + " " + q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.curSym + q + " " + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign + this.curSym; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign + this.curSym; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + this.curSym + " " + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign + " " + this.curSym; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + this.curSym + q; + } + + // sign after symbol + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.curSym + this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.curSym + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.curSym + this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.curSym + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.curSym + " " + this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC MONETARY definition"; + }; + + + /** + * @private + * + * @description Assembles the final string with sign, separator and ISO-4217 + * currency code. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string. + */ + this._formatAsInternationalCurrency = function (sign, q) { + + // assemble the final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + + if (sign == "+") { + + // parentheses + if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return "(" + q + this.currencyCode + ")"; + } + else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return "(" + this.currencyCode + q + ")"; + } + else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return "(" + q + this.intSep + this.currencyCode + ")"; + } + else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return "(" + this.currencyCode + this.intSep + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q + this.intSep + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + this.intSep + q + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + this.currencyCode + q; + } + + // sign after q + sym + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.intSep + q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + q + this.intSep + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign + this.intSep + this.currencyCode; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + this.currencyCode + q; + } + + // sign after symbol + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.currencyCode + this.intSep + this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return "(" + q + this.currencyCode + ")"; + } + else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return "(" + this.currencyCode + q + ")"; + } + else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return "(" + q + this.intSep + this.currencyCode + ")"; + } + else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return "(" + this.currencyCode + this.intSep + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q + this.intSep + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + this.intSep + q + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + this.currencyCode + q; + } + + // sign after q + sym + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.intSep + q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + q + this.intSep + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.currencyCode + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign + this.intSep + this.currencyCode; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + this.currencyCode + q; + } + + // sign after symbol + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.currencyCode + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.currencyCode + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.currencyCode + this.intSep + this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC MONETARY definition"; + }; + + + /** + * @private + * + * @description Assembles the final string with sign and separator, but suppress the + * local currency symbol. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string + */ + this._formatAsLocalCurrencyWithNoSym = function (sign, q) { + + // assemble the final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + + if (sign == "+") { + + // parentheses + if (this.lc.p_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + + // sign after q + sym + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return q + " " + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + + // sign after symbol + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + " " + q; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { + return q + " " + this.lc.positive_sign; + } + else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.n_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + + // sign after q + sym + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return q + " " + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + + // sign after symbol + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + " " + q; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { + return q + " " + this.lc.negative_sign; + } + else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC MONETARY definition"; + }; + + + /** + * @private + * + * @description Assembles the final string with sign and separator, but suppress + * the ISO-4217 currency code. + * + * @param {String} sign The amount sign: "+" or "-". + * @param {String} q The formatted quantity (unsigned). + * + * @returns {String} The final formatted string. + */ + this._formatAsInternationalCurrencyWithNoSym = function (sign, q) { + + // assemble the final formatted amount by going over all possible value combinations of: + // sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} + + if (sign == "+") { + + // parentheses + if (this.lc.int_p_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + + // sign after q + sym + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return q + this.intSep + this.lc.positive_sign; + } + + // sign before sym + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + + // sign after symbol + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { + return q + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + this.intSep + q; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { + return q + this.intSep + this.lc.positive_sign; + } + else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { + return this.lc.positive_sign + q; + } + + } + else if (sign == "-") { + + // parentheses enclose q + sym + if (this.lc.int_n_sign_posn === 0) { + return "(" + q + ")"; + } + + // sign before q + sym + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + + // sign after q + sym + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return q + this.intSep + this.lc.negative_sign; + } + + // sign before sym + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + + // sign after symbol + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { + return q + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + this.intSep + q; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { + return q + this.intSep + this.lc.negative_sign; + } + else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { + return this.lc.negative_sign + q; + } + } + + // throw error if we fall through + throw "Error: Invalid POSIX LC_MONETARY definition"; + }; +}; + + +/** + * @class + * Class for parsing localised number strings. + * + * @public + * @constructor + * @description Creates a new numeric parser for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_NUMERIC formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.NumericParser = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + + /** + * @public + * + * @description Parses a numeric string formatted according to the + * preset locale. Leading and trailing whitespace is ignored; the number + * may also be formatted without thousands separators. + * + * @param {String} formattedNumber The formatted number. + * + * @returns {Number} The parsed number. + * + * @throws Error on a parse exception. + */ + this.parse = function(formattedNumber) { + + if (typeof formattedNumber != "string") + throw "Parse error: Argument must be a string"; + + // trim whitespace + var s = jsworld._trim(formattedNumber); + + // remove any thousand separator symbols + s = jsworld._stringReplaceAll(formattedNumber, this.lc.thousands_sep, ""); + + // replace any local decimal point symbols with the symbol used + // in JavaScript "." + s = jsworld._stringReplaceAll(s, this.lc.decimal_point, "."); + + // test if the string represents a number + if (jsworld._isNumber(s)) + return parseFloat(s, 10); + else + throw "Parse error: Invalid number string"; + }; +}; + + +/** + * @class + * Class for parsing localised date and time strings. + * + * @public + * @constructor + * @description Creates a new date/time parser for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_TIME formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.DateTimeParser = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance."; + + this.lc = locale; + + + /** + * @public + * + * @description Parses a time string formatted according to the + * POSIX LC_TIME t_fmt property of the preset locale. + * + * @param {String} formattedTime The formatted time. + * + * @returns {String} The parsed time in ISO-8601 format (HH:MM:SS), e.g. + * "23:59:59". + * + * @throws Error on a parse exception. + */ + this.parseTime = function(formattedTime) { + + if (typeof formattedTime != "string") + throw "Parse error: Argument must be a string"; + + var dt = this._extractTokens(this.lc.t_fmt, formattedTime); + + var timeDefined = false; + + if (dt.hour !== null && dt.minute !== null && dt.second !== null) { + timeDefined = true; + } + else if (dt.hourAmPm !== null && dt.am !== null && dt.minute !== null && dt.second !== null) { + if (dt.am) { + // AM [12(midnight), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 0; + else + dt.hour = parseInt(dt.hourAmPm, 10); + } + else { + // PM [12(noon), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 12; + else + dt.hour = parseInt(dt.hourAmPm, 10) + 12; + } + timeDefined = true; + } + + if (timeDefined) + return jsworld._zeroPad(dt.hour, 2) + + ":" + + jsworld._zeroPad(dt.minute, 2) + + ":" + + jsworld._zeroPad(dt.second, 2); + else + throw "Parse error: Invalid/ambiguous time string"; + }; + + + /** + * @public + * + * @description Parses a date string formatted according to the + * POSIX LC_TIME d_fmt property of the preset locale. + * + * @param {String} formattedDate The formatted date, must be valid. + * + * @returns {String} The parsed date in ISO-8601 format (YYYY-MM-DD), + * e.g. "2010-03-31". + * + * @throws Error on a parse exception. + */ + this.parseDate = function(formattedDate) { + + if (typeof formattedDate != "string") + throw "Parse error: Argument must be a string"; + + var dt = this._extractTokens(this.lc.d_fmt, formattedDate); + + var dateDefined = false; + + if (dt.year !== null && dt.month !== null && dt.day !== null) { + dateDefined = true; + } + + if (dateDefined) + return jsworld._zeroPad(dt.year, 4) + + "-" + + jsworld._zeroPad(dt.month, 2) + + "-" + + jsworld._zeroPad(dt.day, 2); + else + throw "Parse error: Invalid date string"; + }; + + + /** + * @public + * + * @description Parses a date/time string formatted according to the + * POSIX LC_TIME d_t_fmt property of the preset locale. + * + * @param {String} formattedDateTime The formatted date/time, must be + * valid. + * + * @returns {String} The parsed date/time in ISO-8601 format + * (YYYY-MM-DD HH:MM:SS), e.g. "2010-03-31 23:59:59". + * + * @throws Error on a parse exception. + */ + this.parseDateTime = function(formattedDateTime) { + + if (typeof formattedDateTime != "string") + throw "Parse error: Argument must be a string"; + + var dt = this._extractTokens(this.lc.d_t_fmt, formattedDateTime); + + var timeDefined = false; + var dateDefined = false; + + if (dt.hour !== null && dt.minute !== null && dt.second !== null) { + timeDefined = true; + } + else if (dt.hourAmPm !== null && dt.am !== null && dt.minute !== null && dt.second !== null) { + if (dt.am) { + // AM [12(midnight), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 0; + else + dt.hour = parseInt(dt.hourAmPm, 10); + } + else { + // PM [12(noon), 1 .. 11] + if (dt.hourAmPm == 12) + dt.hour = 12; + else + dt.hour = parseInt(dt.hourAmPm, 10) + 12; + } + timeDefined = true; + } + + if (dt.year !== null && dt.month !== null && dt.day !== null) { + dateDefined = true; + } + + if (dateDefined && timeDefined) + return jsworld._zeroPad(dt.year, 4) + + "-" + + jsworld._zeroPad(dt.month, 2) + + "-" + + jsworld._zeroPad(dt.day, 2) + + " " + + jsworld._zeroPad(dt.hour, 2) + + ":" + + jsworld._zeroPad(dt.minute, 2) + + ":" + + jsworld._zeroPad(dt.second, 2); + else + throw "Parse error: Invalid/ambiguous date/time string"; + }; + + + /** + * @private + * + * @description Parses a string according to the specified format + * specification. + * + * @param {String} fmtSpec The format specification, e.g. "%I:%M:%S %p". + * @param {String} s The string to parse. + * + * @returns {object} An object with set properties year, month, day, + * hour, minute and second if the corresponding values are + * found in the parsed string. + * + * @throws Error on a parse exception. + */ + this._extractTokens = function(fmtSpec, s) { + + // the return object containing the parsed date/time properties + var dt = { + // for date and date/time strings + "year" : null, + "month" : null, + "day" : null, + + // for time and date/time strings + "hour" : null, + "hourAmPm" : null, + "am" : null, + "minute" : null, + "second" : null, + + // used internally only + "weekday" : null + }; + + + // extract and process each token in the date/time spec + while (fmtSpec.length > 0) { + + // Do we have a valid "%\w" placeholder in stream? + if (fmtSpec.charAt(0) == "%" && fmtSpec.charAt(1) != "") { + + // get placeholder + var placeholder = fmtSpec.substring(0,2); + + if (placeholder == "%%") { + // escaped '%'' + s = s.substring(1); + } + else if (placeholder == "%a") { + // abbreviated weekday name + for (var i = 0; i < this.lc.abday.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.abday[i])) { + dt.weekday = i; + s = s.substring(this.lc.abday[i].length); + break; + } + } + + if (dt.weekday === null) + throw "Parse error: Unrecognised abbreviated weekday name (%a)"; + } + else if (placeholder == "%A") { + // weekday name + for (var i = 0; i < this.lc.day.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.day[i])) { + dt.weekday = i; + s = s.substring(this.lc.day[i].length); + break; + } + } + + if (dt.weekday === null) + throw "Parse error: Unrecognised weekday name (%A)"; + } + else if (placeholder == "%b" || placeholder == "%h") { + // abbreviated month name + for (var i = 0; i < this.lc.abmon.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.abmon[i])) { + dt.month = i + 1; + s = s.substring(this.lc.abmon[i].length); + break; + } + } + + if (dt.month === null) + throw "Parse error: Unrecognised abbreviated month name (%b)"; + } + else if (placeholder == "%B") { + // month name + for (var i = 0; i < this.lc.mon.length; i++) { + + if (jsworld._stringStartsWith(s, this.lc.mon[i])) { + dt.month = i + 1; + s = s.substring(this.lc.mon[i].length); + break; + } + } + + if (dt.month === null) + throw "Parse error: Unrecognised month name (%B)"; + } + else if (placeholder == "%d") { + // day of the month [01..31] + if (/^0[1-9]|[1-2][0-9]|3[0-1]/.test(s)) { + dt.day = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised day of the month (%d)"; + } + else if (placeholder == "%e") { + // day of the month [1..31] + + // Note: if %e is leading in fmt string -> space padded! + + var day = s.match(/^\s?(\d{1,2})/); + dt.day = parseInt(day, 10); + + if (isNaN(dt.day) || dt.day < 1 || dt.day > 31) + throw "Parse error: Unrecognised day of the month (%e)"; + + s = s.substring(day.length); + } + else if (placeholder == "%F") { + // equivalent to %Y-%m-%d (ISO-8601 date format) + + // year [nnnn] + if (/^\d\d\d\d/.test(s)) { + dt.year = parseInt(s.substring(0,4), 10); + s = s.substring(4); + } + else { + throw "Parse error: Unrecognised date (%F)"; + } + + // - + if (jsworld._stringStartsWith(s, "-")) + s = s.substring(1); + else + throw "Parse error: Unrecognised date (%F)"; + + // month [01..12] + if (/^0[1-9]|1[0-2]/.test(s)) { + dt.month = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised date (%F)"; + + // - + if (jsworld._stringStartsWith(s, "-")) + s = s.substring(1); + else + throw "Parse error: Unrecognised date (%F)"; + + // day of the month [01..31] + if (/^0[1-9]|[1-2][0-9]|3[0-1]/.test(s)) { + dt.day = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised date (%F)"; + } + else if (placeholder == "%H") { + // hour [00..23] + if (/^[0-1][0-9]|2[0-3]/.test(s)) { + dt.hour = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised hour (%H)"; + } + else if (placeholder == "%I") { + // hour [01..12] + if (/^0[1-9]|1[0-2]/.test(s)) { + dt.hourAmPm = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised hour (%I)"; + } + else if (placeholder == "%k") { + // hour [0..23] + var h = s.match(/^(\d{1,2})/); + dt.hour = parseInt(h, 10); + + if (isNaN(dt.hour) || dt.hour < 0 || dt.hour > 23) + throw "Parse error: Unrecognised hour (%k)"; + + s = s.substring(h.length); + } + else if (placeholder == "%l") { + // hour AM/PM [1..12] + var h = s.match(/^(\d{1,2})/); + dt.hourAmPm = parseInt(h, 10); + + if (isNaN(dt.hourAmPm) || dt.hourAmPm < 1 || dt.hourAmPm > 12) + throw "Parse error: Unrecognised hour (%l)"; + + s = s.substring(h.length); + } + else if (placeholder == "%m") { + // month [01..12] + if (/^0[1-9]|1[0-2]/.test(s)) { + dt.month = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised month (%m)"; + } + else if (placeholder == "%M") { + // minute [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.minute = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised minute (%M)"; + } + else if (placeholder == "%n") { + // new line + + if (s.charAt(0) == "\n") + s = s.substring(1); + else + throw "Parse error: Unrecognised new line (%n)"; + } + else if (placeholder == "%p") { + // locale's equivalent of AM/PM + if (jsworld._stringStartsWith(s, this.lc.am)) { + dt.am = true; + s = s.substring(this.lc.am.length); + } + else if (jsworld._stringStartsWith(s, this.lc.pm)) { + dt.am = false; + s = s.substring(this.lc.pm.length); + } + else + throw "Parse error: Unrecognised AM/PM value (%p)"; + } + else if (placeholder == "%P") { + // same as %p but forced lower case + if (jsworld._stringStartsWith(s, this.lc.am.toLowerCase())) { + dt.am = true; + s = s.substring(this.lc.am.length); + } + else if (jsworld._stringStartsWith(s, this.lc.pm.toLowerCase())) { + dt.am = false; + s = s.substring(this.lc.pm.length); + } + else + throw "Parse error: Unrecognised AM/PM value (%P)"; + } + else if (placeholder == "%R") { + // same as %H:%M + + // hour [00..23] + if (/^[0-1][0-9]|2[0-3]/.test(s)) { + dt.hour = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%R)"; + + // : + if (jsworld._stringStartsWith(s, ":")) + s = s.substring(1); + else + throw "Parse error: Unrecognised time (%R)"; + + // minute [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.minute = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%R)"; + + } + else if (placeholder == "%S") { + // second [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.second = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised second (%S)"; + } + else if (placeholder == "%T") { + // same as %H:%M:%S + + // hour [00..23] + if (/^[0-1][0-9]|2[0-3]/.test(s)) { + dt.hour = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%T)"; + + // : + if (jsworld._stringStartsWith(s, ":")) + s = s.substring(1); + else + throw "Parse error: Unrecognised time (%T)"; + + // minute [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.minute = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%T)"; + + // : + if (jsworld._stringStartsWith(s, ":")) + s = s.substring(1); + else + throw "Parse error: Unrecognised time (%T)"; + + // second [00..59] + if (/^[0-5][0-9]/.test(s)) { + dt.second = parseInt(s.substring(0,2), 10); + s = s.substring(2); + } + else + throw "Parse error: Unrecognised time (%T)"; + } + else if (placeholder == "%w") { + // weekday [0..6] + if (/^\d/.test(s)) { + dt.weekday = parseInt(s.substring(0,1), 10); + s = s.substring(1); + } + else + throw "Parse error: Unrecognised weekday number (%w)"; + } + else if (placeholder == "%y") { + // year [00..99] + if (/^\d\d/.test(s)) { + var year2digits = parseInt(s.substring(0,2), 10); + + // this conversion to year[nnnn] is arbitrary!!! + if (year2digits > 50) + dt.year = 1900 + year2digits; + else + dt.year = 2000 + year2digits; + + s = s.substring(2); + } + else + throw "Parse error: Unrecognised year (%y)"; + } + else if (placeholder == "%Y") { + // year [nnnn] + if (/^\d\d\d\d/.test(s)) { + dt.year = parseInt(s.substring(0,4), 10); + s = s.substring(4); + } + else + throw "Parse error: Unrecognised year (%Y)"; + } + + else if (placeholder == "%Z") { + // time-zone place holder is not supported + + if (fmtSpec.length === 0) + break; // ignore rest of fmt spec + } + + // remove the spec placeholder that was just parsed + fmtSpec = fmtSpec.substring(2); + } + else { + // If we don't have a placeholder, the chars + // at pos. 0 of format spec and parsed string must match + + // Note: Space chars treated 1:1 ! + + if (fmtSpec.charAt(0) != s.charAt(0)) + throw "Parse error: Unexpected symbol \"" + s.charAt(0) + "\" in date/time string"; + + fmtSpec = fmtSpec.substring(1); + s = s.substring(1); + } + } + + // parsing finished, return composite date/time object + return dt; + }; +}; + + +/** + * @class + * Class for parsing localised currency amount strings. + * + * @public + * @constructor + * @description Creates a new monetary parser for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_MONETARY formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.MonetaryParser = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + + this.lc = locale; + + + /** + * @public + * + * @description Parses a currency amount string formatted according to + * the preset locale. Leading and trailing whitespace is ignored; the + * amount may also be formatted without thousands separators. Both + * the local (shorthand) symbol and the ISO 4217 code are accepted to + * designate the currency in the formatted amount. + * + * @param {String} formattedCurrency The formatted currency amount. + * + * @returns {Number} The parsed amount. + * + * @throws Error on a parse exception. + */ + this.parse = function(formattedCurrency) { + + if (typeof formattedCurrency != "string") + throw "Parse error: Argument must be a string"; + + // Detect the format type and remove the currency symbol + var symbolType = this._detectCurrencySymbolType(formattedCurrency); + + var formatType, s; + + if (symbolType == "local") { + formatType = "local"; + s = formattedCurrency.replace(this.lc.getCurrencySymbol(), ""); + } + else if (symbolType == "int") { + formatType = "int"; + s = formattedCurrency.replace(this.lc.getIntCurrencySymbol(), ""); + } + else if (symbolType == "none") { + formatType = "local"; // assume local + s = formattedCurrency; + } + else + throw "Parse error: Internal assert failure"; + + // Remove any thousands separators + s = jsworld._stringReplaceAll(s, this.lc.mon_thousands_sep, ""); + + // Replace any local radix char with JavaScript's "." + s = s.replace(this.lc.mon_decimal_point, "."); + + // Remove all whitespaces + s = s.replace(/\s*/g, ""); + + // Remove any local non-negative sign + s = this._removeLocalNonNegativeSign(s, formatType); + + // Replace any local minus sign with JavaScript's "-" and put + // it in front of the amount if necessary + // (special parentheses rule checked too) + s = this._normaliseNegativeSign(s, formatType); + + // Finally, we should be left with a bare parsable decimal number + if (jsworld._isNumber(s)) + return parseFloat(s, 10); + else + throw "Parse error: Invalid currency amount string"; + }; + + + /** + * @private + * + * @description Tries to detect the symbol type used in the specified + * formatted currency string: local(shorthand), + * international (ISO-4217 code) or none. + * + * @param {String} formattedCurrency The the formatted currency string. + * + * @return {String} With possible values "local", "int" or "none". + */ + this._detectCurrencySymbolType = function(formattedCurrency) { + + // Check for whichever sign (int/local) is longer first + // to cover cases such as MOP/MOP$ and ZAR/R + + if (this.lc.getCurrencySymbol().length > this.lc.getIntCurrencySymbol().length) { + + if (formattedCurrency.indexOf(this.lc.getCurrencySymbol()) != -1) + return "local"; + else if (formattedCurrency.indexOf(this.lc.getIntCurrencySymbol()) != -1) + return "int"; + else + return "none"; + } + else { + if (formattedCurrency.indexOf(this.lc.getIntCurrencySymbol()) != -1) + return "int"; + else if (formattedCurrency.indexOf(this.lc.getCurrencySymbol()) != -1) + return "local"; + else + return "none"; + } + }; + + + /** + * @private + * + * @description Removes a local non-negative sign in a formatted + * currency string if it is found. This is done according to the + * locale properties p_sign_posn and int_p_sign_posn. + * + * @param {String} s The input string. + * @param {String} formatType With possible values "local" or "int". + * + * @returns {String} The processed string. + */ + this._removeLocalNonNegativeSign = function(s, formatType) { + + s = s.replace(this.lc.positive_sign, ""); + + // check for enclosing parentheses rule + if (((formatType == "local" && this.lc.p_sign_posn === 0) || + (formatType == "int" && this.lc.int_p_sign_posn === 0) ) && + /\(\d+\.?\d*\)/.test(s)) { + s = s.replace("(", ""); + s = s.replace(")", ""); + } + + return s; + }; + + + /** + * @private + * + * @description Replaces a local negative sign with the standard + * JavaScript minus ("-") sign placed in the correct position + * (preceding the amount). This is done according to the locale + * properties for negative sign symbol and relative position. + * + * @param {String} s The input string. + * @param {String} formatType With possible values "local" or "int". + * + * @returns {String} The processed string. + */ + this._normaliseNegativeSign = function(s, formatType) { + + // replace local negative symbol with JavaScript's "-" + s = s.replace(this.lc.negative_sign, "-"); + + // check for enclosing parentheses rule and replace them + // with negative sign before the amount + if ((formatType == "local" && this.lc.n_sign_posn === 0) || + (formatType == "int" && this.lc.int_n_sign_posn === 0) ) { + + if (/^\(\d+\.?\d*\)$/.test(s)) { + + s = s.replace("(", ""); + s = s.replace(")", ""); + return "-" + s; + } + } + + // check for rule negative sign succeeding the amount + if (formatType == "local" && this.lc.n_sign_posn == 2 || + formatType == "int" && this.lc.int_n_sign_posn == 2 ) { + + if (/^\d+\.?\d*-$/.test(s)) { + s = s.replace("-", ""); + return "-" + s; + } + } + + // check for rule cur. sym. succeeds and sign adjacent + if (formatType == "local" && this.lc.n_cs_precedes === 0 && this.lc.n_sign_posn == 3 || + formatType == "local" && this.lc.n_cs_precedes === 0 && this.lc.n_sign_posn == 4 || + formatType == "int" && this.lc.int_n_cs_precedes === 0 && this.lc.int_n_sign_posn == 3 || + formatType == "int" && this.lc.int_n_cs_precedes === 0 && this.lc.int_n_sign_posn == 4 ) { + + if (/^\d+\.?\d*-$/.test(s)) { + s = s.replace("-", ""); + return "-" + s; + } + } + + return s; + }; +}; + +// end-of-file diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/uglify-hangs2.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/uglify-hangs2.js new file mode 100644 index 0000000..4e9f967 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/tmp/uglify-hangs2.js @@ -0,0 +1,166 @@ +jsworld.Locale = function(properties) { + + // LC_NUMERIC + + + this.frac_digits = properties.frac_digits; + + + // may be empty string/null for currencies with no fractional part + if (properties.mon_decimal_point === null || properties.mon_decimal_point == "") { + + if (this.frac_digits > 0) + throw "Error: Undefined mon_decimal_point property"; + else + properties.mon_decimal_point = ""; + } + + if (typeof properties.mon_decimal_point != "string") + throw "Error: Invalid/missing mon_decimal_point property"; + + this.mon_decimal_point = properties.mon_decimal_point; + + + if (typeof properties.mon_thousands_sep != "string") + throw "Error: Invalid/missing mon_thousands_sep property"; + + this.mon_thousands_sep = properties.mon_thousands_sep; + + + if (typeof properties.mon_grouping != "string") + throw "Error: Invalid/missing mon_grouping property"; + + this.mon_grouping = properties.mon_grouping; + + + if (typeof properties.positive_sign != "string") + throw "Error: Invalid/missing positive_sign property"; + + this.positive_sign = properties.positive_sign; + + + if (typeof properties.negative_sign != "string") + throw "Error: Invalid/missing negative_sign property"; + + this.negative_sign = properties.negative_sign; + + + if (properties.p_cs_precedes !== 0 && properties.p_cs_precedes !== 1) + throw "Error: Invalid/missing p_cs_precedes property, must be 0 or 1"; + + this.p_cs_precedes = properties.p_cs_precedes; + + + if (properties.n_cs_precedes !== 0 && properties.n_cs_precedes !== 1) + throw "Error: Invalid/missing n_cs_precedes, must be 0 or 1"; + + this.n_cs_precedes = properties.n_cs_precedes; + + + if (properties.p_sep_by_space !== 0 && + properties.p_sep_by_space !== 1 && + properties.p_sep_by_space !== 2) + throw "Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2"; + + this.p_sep_by_space = properties.p_sep_by_space; + + + if (properties.n_sep_by_space !== 0 && + properties.n_sep_by_space !== 1 && + properties.n_sep_by_space !== 2) + throw "Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2"; + + this.n_sep_by_space = properties.n_sep_by_space; + + + if (properties.p_sign_posn !== 0 && + properties.p_sign_posn !== 1 && + properties.p_sign_posn !== 2 && + properties.p_sign_posn !== 3 && + properties.p_sign_posn !== 4) + throw "Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.p_sign_posn = properties.p_sign_posn; + + + if (properties.n_sign_posn !== 0 && + properties.n_sign_posn !== 1 && + properties.n_sign_posn !== 2 && + properties.n_sign_posn !== 3 && + properties.n_sign_posn !== 4) + throw "Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.n_sign_posn = properties.n_sign_posn; + + + if (typeof properties.int_frac_digits != "number" && properties.int_frac_digits < 0) + throw "Error: Invalid/missing int_frac_digits property"; + + this.int_frac_digits = properties.int_frac_digits; + + + if (properties.int_p_cs_precedes !== 0 && properties.int_p_cs_precedes !== 1) + throw "Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1"; + + this.int_p_cs_precedes = properties.int_p_cs_precedes; + + + if (properties.int_n_cs_precedes !== 0 && properties.int_n_cs_precedes !== 1) + throw "Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1"; + + this.int_n_cs_precedes = properties.int_n_cs_precedes; + + + if (properties.int_p_sep_by_space !== 0 && + properties.int_p_sep_by_space !== 1 && + properties.int_p_sep_by_space !== 2) + throw "Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2"; + + this.int_p_sep_by_space = properties.int_p_sep_by_space; + + + if (properties.int_n_sep_by_space !== 0 && + properties.int_n_sep_by_space !== 1 && + properties.int_n_sep_by_space !== 2) + throw "Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2"; + + this.int_n_sep_by_space = properties.int_n_sep_by_space; + + + if (properties.int_p_sign_posn !== 0 && + properties.int_p_sign_posn !== 1 && + properties.int_p_sign_posn !== 2 && + properties.int_p_sign_posn !== 3 && + properties.int_p_sign_posn !== 4) + throw "Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_p_sign_posn = properties.int_p_sign_posn; + + + if (properties.int_n_sign_posn !== 0 && + properties.int_n_sign_posn !== 1 && + properties.int_n_sign_posn !== 2 && + properties.int_n_sign_posn !== 3 && + properties.int_n_sign_posn !== 4) + throw "Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_n_sign_posn = properties.int_n_sign_posn; + + + // LC_TIME + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing time locale properties"; + + + // parse the supported POSIX LC_TIME properties + + // abday + try { + this.abday = this._parseList(properties.abday, 7); + } + catch (error) { + throw "Error: Invalid abday property: " + error; + } + +} diff --git a/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/uglify-js.js b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/uglify-js.js new file mode 100644 index 0000000..6e14a63 --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/node_modules/uglify-js/uglify-js.js @@ -0,0 +1,18 @@ +//convienence function(src, [options]); +function uglify(orig_code, options){ + options || (options = {}); + var jsp = uglify.parser; + var pro = uglify.uglify; + + var ast = jsp.parse(orig_code, options.strict_semicolons); // parse code and get the initial AST + ast = pro.ast_mangle(ast, options.mangle_options); // get a new AST with mangled names + ast = pro.ast_squeeze(ast, options.squeeze_options); // get an AST with compression optimizations + var final_code = pro.gen_code(ast, options.gen_options); // compressed code here + return final_code; +}; + +uglify.parser = require("./lib/parse-js"); +uglify.uglify = require("./lib/process"); +uglify.consolidator = require("./lib/consolidator"); + +module.exports = uglify diff --git a/dist/node_modules/hbs/node_modules/handlebars/package.json b/dist/node_modules/hbs/node_modules/handlebars/package.json new file mode 100644 index 0000000..321f3ca --- /dev/null +++ b/dist/node_modules/hbs/node_modules/handlebars/package.json @@ -0,0 +1,25 @@ +{ + "name": "handlebars", + "description": "Extension of the Mustache logicless template language", + "version": "1.0.5beta", + "homepage": "http://www.handlebarsjs.com/", + "keywords": [ + "handlebars mustache template html" + ], + "repository": { + "type": "git", + "url": "git://github.com/kpdecker/handlebars.js.git" + }, + "engines": { + "node": ">=0.4.7" + }, + "dependencies": { + "optimist": "~0.3", + "uglify-js": "~1.2" + }, + "devDependencies": {}, + "main": "lib/handlebars.js", + "bin": { + "handlebars": "bin/handlebars" + } +} \ No newline at end of file diff --git a/dist/node_modules/hbs/package.json b/dist/node_modules/hbs/package.json new file mode 100644 index 0000000..289bbb3 --- /dev/null +++ b/dist/node_modules/hbs/package.json @@ -0,0 +1,26 @@ +{ + "name": "hbs", + "description": "Express.js template engine plugin for Handlebars", + "version": "1.0.5", + "homepage": "https://github.com/donpark/hbs", + "author": "Don Park (http://blog.docuverse.com)", + "main": "lib/hbs.js", + "directories": { + "lib": "./lib" + }, + "dependencies": { + "handlebars": "1.0.5beta" + }, + "devDependencies": { + "mocha": "1.2.x", + "npm": "*", + "request": ">=2.9.202" + }, + "scripts": { + "test": "mocha" + }, + "engines": { + "node": ">= 0.4.0" + }, + "optionalDependencies": {} +} diff --git a/dist/node_modules/ldapauth/.npmignore b/dist/node_modules/ldapauth/.npmignore new file mode 100644 index 0000000..c58b395 --- /dev/null +++ b/dist/node_modules/ldapauth/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/tmp diff --git a/dist/node_modules/ldapauth/AUTHORS b/dist/node_modules/ldapauth/AUTHORS new file mode 100644 index 0000000..ab9474a --- /dev/null +++ b/dist/node_modules/ldapauth/AUTHORS @@ -0,0 +1,2 @@ +Trent Mick (http://trentm.com) +Jacques Marneweck (https://github.com/jacques) diff --git a/dist/node_modules/ldapauth/CHANGES.md b/dist/node_modules/ldapauth/CHANGES.md new file mode 100644 index 0000000..38ac9cf --- /dev/null +++ b/dist/node_modules/ldapauth/CHANGES.md @@ -0,0 +1,24 @@ +# node-ldapauth Changelog + +## 2.2.0 + +- Update to latest ldapjs (0.5.6) and other deps. + + +## 2.1.0 + +- Update to ldapjs 0.4 (from 0.3). Crossing fingers that this doesn't cause breakage. + + +## 2.0.0 + +- Add `make check` for checking jsstyle. +- [issue #1] Update to bcrypt 0.5. This means increasing the base node from 0.4 + to 0.6, hence the major version bump. + + +## 1.0.2 + +First working version. + + diff --git a/dist/node_modules/ldapauth/LICENSE b/dist/node_modules/ldapauth/LICENSE new file mode 100644 index 0000000..9bc24a6 --- /dev/null +++ b/dist/node_modules/ldapauth/LICENSE @@ -0,0 +1,23 @@ +Copyright 2011 Trent Mick. +All rights reserved. + +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/dist/node_modules/ldapauth/Makefile b/dist/node_modules/ldapauth/Makefile new file mode 100644 index 0000000..5d506cc --- /dev/null +++ b/dist/node_modules/ldapauth/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (c) 2012 Trent Mick +# +# node-ldapauth Makefile +# + +#---- Files + +JSSTYLE_FILES := $(shell find lib -name *.js) + + + +#---- Targets + +all: + +.PHONY: cutarelease +cutarelease: + ./tools/cutarelease.py -p ldapauth -f package.json + +.PHONY: check-jsstyle +check-jsstyle: $(JSSTYLE_FILES) + ./tools/jsstyle -o indent=2,doxygen,unparenthesized-return=0,blank-after-start-comment=0 $(JSSTYLE_FILES) + +.PHONY: check +check: check-jsstyle + @echo "Check ok." + +.PHONY: prepush +prepush: check test + @echo "Okay to push." diff --git a/dist/node_modules/ldapauth/README.md b/dist/node_modules/ldapauth/README.md new file mode 100644 index 0000000..af31cc8 --- /dev/null +++ b/dist/node_modules/ldapauth/README.md @@ -0,0 +1,78 @@ +A simple node.js lib to authenticate against an LDAP server. + + +# Usage + + var LdapAuth = require('ldapauth'); + var options = { + url: 'ldaps://ldap.example.com:663', + ... + }; + var auth = new LdapAuth(options); + ... + auth.authenticate(username, password, function(err, user) { ... }); + ... + auth.close(function(err) { ... }) + + +# Install + + npm install ldapauth + + +# License + +MIT. See "LICENSE" file. + + +# `LdapAuth` Config Options + +[Use the source Luke](https://github.com/trentm/node-ldapauth/blob/master/lib/ldapauth.js#L25-45) + + +# express/connect basicAuth example + + var connect = require('connect'); + var LdapAuth = require('ldapauth'); + + // Config from a .json or .ini file or whatever. + var config = { + ldap: { + url: "ldaps://ldap.example.com:636", + adminDn: "uid=myadminusername,ou=users,o=example.com", + adminPassword: "mypassword", + searchBase: "ou=users,o=example.com", + searchFilter: "(uid={{username}})" + } + }; + + var ldap = new LdapAuth({ + url: config.ldap.url, + adminDn: config.ldap.adminDn, + adminPassword: config.ldap.adminPassword, + searchBase: config.ldap.searchBase, + searchFilter: config.ldap.searchFilter, + //log4js: require('log4js'), + cache: true + }); + + var basicAuthMiddleware = connect.basicAuth(function (username, password, callback) { + ldap.authenticate(username, password, function (err, user) { + if (err) { + console.log("LDAP auth error: %s", err); + } + callback(err, user) + }); + }); + + +# Development + +Check coding style before commit: + + make check + +To cut a release (tagging, npm publish, etc., see + for details): + + make cutarelease diff --git a/dist/node_modules/ldapauth/TODO.txt b/dist/node_modules/ldapauth/TODO.txt new file mode 100644 index 0000000..208f6f7 --- /dev/null +++ b/dist/node_modules/ldapauth/TODO.txt @@ -0,0 +1,2 @@ +- change to bunyan logging +- lib/cache.js -> https://github.com/trentm/node-expiring-lru-cache diff --git a/dist/node_modules/ldapauth/lib/cache.js b/dist/node_modules/ldapauth/lib/cache.js new file mode 100644 index 0000000..569fab4 --- /dev/null +++ b/dist/node_modules/ldapauth/lib/cache.js @@ -0,0 +1,89 @@ +/* + * Copyright 2011 Joyent, Inc. All rights reserved. + * + * An expiring LRU cache. + * + * Usage: + * var Cache = require('amon-common').Cache; + * // size, expiry, log, name + * this.accountCache = new Cache( 100, 300, log, 'account'); + * this.accountCache.set('hamish', {...}); + * ... + * this.accountCache.get('hamish') // -> {...} + */ + +var debug = console.warn; +var assert = require('assert'); +var LRU = require('lru-cache'); + + +/** + * A LRU and expiring cache. + * + * @param size {Number} Max number of entries to cache. + * @param expiry {Number} Number of seconds after which to expire entries. + * @param log {log4js Logger} Optional. + * All logging is at the Trace level. + * @param name {string} Optional name for this cache. Just used for logging. + */ +function Cache(size, expiry, log, name) { + assert.ok(size !== undefined); + assert.ok(expiry !== undefined); + this.size = size; + this.expiry = expiry * 1000; + this.log = log; + this.name = (name ? name + ' ' : ''); + this.items = LRU(this.size); +} + +// Debugging stuff: .getAll isn't in official lru-cache +//Cache.prototype.getAll = function getAll() { +// return this.items.getAll(); +//} + +Cache.prototype.reset = function reset() { + if (this.log) { + this.log.trace('%scache reset', this.name); + } + this.items.reset(); +} + +Cache.prototype.get = function get(key) { + assert.ok(key !== undefined); + var cached = this.items.get(key); + if (cached) { + if (((new Date()).getTime() - cached.ctime) <= this.expiry) { + if (this.log) { + this.log.trace('%scache hit: key="%s": %o', this.name, key, cached); + } + return cached.value; + } + } + if (this.log) { + this.log.trace('%scache miss: key="%s"', this.name, key); + } + return null; +} + +Cache.prototype.set = function set(key, value) { + assert.ok(key !== undefined); + var item = { + value: value, + ctime: new Date().getTime() + }; + if (this.log) { + this.log.trace('%scache set: key="%s": %o', this.name, key, item); + } + this.items.set(key, item); + return item; +} + +Cache.prototype.del = function del(key) { + if (this.log) { + this.log.trace('%scache del: key="%s"', this.name, key); + } + this.items.del(key); +} + + +module.exports = Cache; diff --git a/dist/node_modules/ldapauth/lib/ldapauth.js b/dist/node_modules/ldapauth/lib/ldapauth.js new file mode 100644 index 0000000..d1b0244 --- /dev/null +++ b/dist/node_modules/ldapauth/lib/ldapauth.js @@ -0,0 +1,194 @@ +/** + * Copyright 2011 (c) Trent Mick. + * + * LDAP auth. + * + * Usage: + * var LdapAuth = require('ldapauth'); + * var auth = new LdapAuth({url: 'ldaps://ldap.example.com:663', ...}); + * ... + * auth.authenticate(username, password, function (err, user) { ... }); + * ... + * auth.close(function (err) { ... }) + */ + +var assert = require('assert'); +var bcrypt = require('bcrypt'); +var ldap = require('ldapjs'); +var debug = console.warn; +var format = require('util').format; + + + +/** + * Create an LDAP auth class. Primary usage is the `.authenticate` method. + * + * @param opts {Object} Config options. Keys (required, unless says + * otherwise) are: + * url {String} E.g. 'ldaps://ldap.example.com:663' + * adminDn {String} E.g. 'uid=myapp,ou=users,o=example.com' + * adminPassword {String} Password for adminDn. + * searchBase {String} The base DN from which to search for users by + * username. E.g. 'ou=users,o=example.com' + * searchFilter {String} LDAP search filter with which to find a user by + * username, e.g. '(uid={{username}})'. Use the literal '{{username}}' + * to have the given username be interpolated in for the LDAP + * search. + * log4js {Module} Optional. The require'd log4js module to use for logging. + * logging. If given this will result in TRACE-level logging for + * ldapauth. + * verbose {Boolean} Optional, default false. If `log4js` is also given, + * this will add TRACE-level logging for ldapjs (quite verbose). + * cache {Boolean} Optional, default false. If true, then up to 100 + * credentials at a time will be cached for 5 minutes. + */ +function LdapAuth(opts) { + this.opts = opts; + assert.ok(opts.url); + assert.ok(opts.adminDn); + assert.ok(opts.searchBase); + assert.ok(opts.searchFilter); + + this.log = opts.log4js && opts.log4js.getLogger('ldapauth'); + + if (opts.cache) { + var Cache = require('./cache'); + this.userCache = new Cache(100, 300, this.log, 'user'); + } + + var clientOpts = {url: opts.url}; + if (opts.log4js && opts.verbose) { + clientOpts.log4js = opts.log4js; + } + this._adminClient = ldap.createClient(clientOpts); + this._adminBound = false; + this._userClient = ldap.createClient(clientOpts); + + this._salt = bcrypt.genSaltSync(); +} + + +LdapAuth.prototype.close = function (callback) { + if (! this._adminBound) { + callback() + } else { + this._adminClient.unbind(function (err) { + callback(err); + }); + } +} + + +/** + * Ensure that `this._adminClient` is bound. + */ +LdapAuth.prototype._adminBind = function (callback) { + if (this._adminBound) { + return callback(); + } + var self = this; + this._adminClient.bind(this.opts.adminDn, this.opts.adminPassword, + function (err) { + if (err) { + self.log && self.log.trace('ldap authenticate: bind error: %s', err); + return callback(err); + } + return callback(); + }); +} + + +/** + * Find the user record for the given username. + * + * @param username {String} + * @param callback {Function} `function (err, user)`. If no such user is + * found but no error processing, then `user` is undefined. + * + */ +LdapAuth.prototype._findUser = function (username, callback) { + var self = this; + self._adminBind(function (err) { + if (err) + return callback(err); + + var searchFilter = self.opts.searchFilter.replace('{{username}}', username); + var opts = {filter: searchFilter, scope: 'sub'}; + self._adminClient.search(self.opts.searchBase, opts, + function (err, result) { + if (err) { + self.log && self.log.trace('ldap authenticate: search error: %s', err); + return callback(err); + } + var items = []; + result.on('searchEntry', function (entry) { + items.push(entry.object); + }); + result.on('error', function (err) { + self.log && self.log.trace( + 'ldap authenticate: search error event: %s', err); + return callback(err); + }); + result.on('end', function (result) { + if (result.status !== 0) { + var err = 'non-zero status from LDAP search: ' + result.status; + self.log && self.log.trace('ldap authenticate: %s', err); + return callback(err); + } + switch (items.length) { + case 0: + return callback(); + case 1: + return callback(null, items[0]) + default: + return callback(format( + 'unexpected number of matches (%s) for "%s" username', + items.length, username)); + } + }); + }); + }); +} + + +/** + * + */ +LdapAuth.prototype.authenticate = function (username, password, callback) { + var self = this; + + if (self.opts.cache) { + // Check cache. 'cached' is `{password: , user: }`. + var cached = self.userCache.get(username); + if (cached && bcrypt.compareSync(password, cached.password)) { + return callback(null, cached.user) + } + } + + // 1. Find the user DN in question. + self._findUser(username, function (err, user) { + if (err) + return callback(err); + if (!user) + return callback(format('no such user: "%s"', username)); + // 2. Attempt to bind as that user to check password. + self._userClient.bind(user.dn, password, function (err) { + if (err) { + self.log && self.log.trace('ldap authenticate: bind error: %s', err); + return callback(err); + } + if (self.opts.cache) { + bcrypt.hash(password, self._salt, function (err, hash) { + self.userCache.set(username, {password: hash, user: user}); + return callback(null, user); + }); + } else { + return callback(null, user); + } + }); + }); +} + + + +module.exports = LdapAuth; diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/.lock-wscript b/dist/node_modules/ldapauth/node_modules/bcrypt/.lock-wscript new file mode 100644 index 0000000..accae2b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/.lock-wscript @@ -0,0 +1,9 @@ +argv = ['/usr/bin/node-waf', 'configure', 'build'] +blddir = '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/bcrypt/build' +commands = {'dist': 0, 'configure': True, 'distcheck': 0, 'install': 0, 'build': True, 'clean': 0, 'distclean': 0, 'check': 0, 'uninstall': 0} +cwd = '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/bcrypt' +environ = {'npm_config_color': 'true', 'npm_config_searchopts': '', 'npm_package_contributors_3_name': 'Ben Glow', 'npm_config_group': '10020', 'npm_config_browser': 'google-chrome', 'npm_config_global': '', 'npm_package_author_url': 'http://github.com/ncb000gt', 'npm_package_main': './bcrypt', 'npm_package_contributors_8_email': 'shtylman@gmail.com', 'SHELL': '/bin/bash', 'XDG_DATA_DIRS': '/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/', 'npm_package_contributors_12_url': 'https://github.com/seanmonstar', 'MANDATORY_PATH': '/usr/share/gconf/ubuntu.mandatory.path', 'QT_ACCESSIBILITY': '1', 'npm_config_pre': '', 'npm_config_cache': '/home/zoec/.npm', 'npm_config_logfd': '2', 'npm_config_tmp': '/tmp', 'npm_package_engines_node': '>= 0.6.0', 'npm_config_argv': '{"remain":[],"cooked":["rebuild"],"original":["rebuild"]}', 'npm_package_contributors_7_email': 'lloyd@hilaiel.com', 'npm_package_contributors_6_email': 'vincentcr@gmail.com', 'npm_package_scripts_preinstall': 'node-waf clean || (exit 0); node-waf configure build', 'npm_package_contributors_13_name': 'Fanie Oosthuysen', 'npm_package_contributors_13_email': 'fanie.oosthuysen@gmail.com', 'npm_package_contributors_7_name': 'Lloyd Hilaiel', 'npm_config_init_version': '0.0.0', 'DBUS_SESSION_BUS_ADDRESS': 'unix:abstract=/tmp/dbus-j5SQDeRAVn,guid=9c4d498d1ab4055e379af66900000051', 'GNOME_KEYRING_PID': '1931', 'npm_lifecycle_event': 'preinstall', 'npm_package_contributors_7_url': 'https://github.com/lloyd', 'DESKTOP_SESSION': 'ubuntu', 'npm_config_init_author_name': '', 'npm_package_contributors_11_url': 'https://github.com/tootallnate', 'npm_config_yes': '', 'npm_config_usage': '', 'npm_package_description': 'A bcrypt library for NodeJS.', 'npm_config_logprefix': 'true', 'npm_package_contributors_13_url': 'https://github.com/weareu', 'npm_config_ignore': '', 'npm_config_ca': '"-----BEGIN CERTIFICATE-----\\nMIIChzCCAfACCQDauvz/KHp8ejANBgkqhkiG9w0BAQUFADCBhzELMAkGA1UEBhMC\\nVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMQwwCgYDVQQKEwNucG0x\\nIjAgBgNVBAsTGW5wbSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxDjAMBgNVBAMTBW5w\\nbUNBMRcwFQYJKoZIhvcNAQkBFghpQGl6cy5tZTAeFw0xMTA5MDUwMTQ3MTdaFw0y\\nMTA5MDIwMTQ3MTdaMIGHMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNV\\nBAcTB09ha2xhbmQxDDAKBgNVBAoTA25wbTEiMCAGA1UECxMZbnBtIENlcnRpZmlj\\nYXRlIEF1dGhvcml0eTEOMAwGA1UEAxMFbnBtQ0ExFzAVBgkqhkiG9w0BCQEWCGlA\\naXpzLm1lMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLI4tIqPpRW+ACw9GE\\nOgBlJZwK5f8nnKCLK629Pv5yJpQKs3DENExAyOgDcyaF0HD0zk8zTp+ZsLaNdKOz\\nGn2U181KGprGKAXP6DU6ByOJDWmTlY6+Ad1laYT0m64fERSpHw/hjD3D+iX4aMOl\\ny0HdbT5m1ZGh6SJz3ZqxavhHLQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAC4ySDbC\\nl7W1WpLmtLGEQ/yuMLUf6Jy/vr+CRp4h+UzL+IQpCv8FfxsYE7dhf/bmWTEupBkv\\nyNL18lipt2jSvR3v6oAHAReotvdjqhxddpe5Holns6EQd1/xEZ7sB1YhQKJtvUrl\\nZNufy1Jf1r0ldEGeA+0ISck7s+xSh9rQD2Op\\n-----END CERTIFICATE-----"', 'npm_config_globalconfig': '/etc/npmrc', 'npm_package_contributors_10_url': 'https://github.com/bnoordhuis', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'npm_package_contributors_2_email': 'david@dtrejo.com', 'npm_config_parseable': '', 'npm_package_contributors_1_email': 'the.gol.effect@gmail.com', 'XDG_CURRENT_DESKTOP': 'Unity', 'npm_config_userignorefile': '/home/zoec/.npmignore', 'USER': 'zoec', 'npm_package_author_name': 'Nick Campbell', 'npm_package_contributors_0_url': 'https://github.com/Shadowfiend', 'npm_config_versions': '', 'XAUTHORITY': '/home/zoec/.Xauthority', 'LANGUAGE': 'en_GB:en', 'SESSION_MANAGER': 'local/axolotl:@/tmp/.ICE-unix/1942,unix/axolotl:/tmp/.ICE-unix/1942', 'SHLVL': '1', 'npm_config_init_author_url': '', 'DISPLAY': ':0.0', 'COMPIZ_CONFIG_PROFILE': 'ubuntu', 'WINDOWID': '77616815', 'GPG_AGENT_INFO': '/tmp/keyring-NPVAVZ/gpg:0:1', 'npm_config_proxy': '', 'npm_package_contributors_11_name': 'Nate Rajlich', 'npm_config_depth': 'null', 'npm_config_shell': '/bin/bash', 'npm_config_umask': '022', 'npm_package_contributors_4_name': 'NewITFarmer.com', 'GDMSESSION': 'ubuntu', 'npm_package_contributors_9_name': 'Vadim Graboys', 'npm_config_long': '', 'npm_package_contributors_0_name': 'Antonio Salazar Cardozo', 'npm_config_editor': 'vi', 'XDG_SEAT_PATH': '/org/freedesktop/DisplayManager/Seat0', 'npm_config_prefix': '/usr/local', 'npm_package_repository_type': 'git', 'npm_package_contributors_1_name': 'Van Nguyen', 'npm_config_npat': '', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/etc/xdg', 'UBUNTU_MENUPROXY': 'libappmenu.so', 'npm_package_contributors_11_email': 'nathan@tootallnate.net', 'npm_config_json': '', 'npm_config_searchsort': 'name', 'npm_package_contributors_12_email': 'sean.monstar@gmail.com', 'npm_lifecycle_script': 'node-waf clean || (exit 0); node-waf configure build', 'COLORTERM': 'gnome-terminal', 'npm_config_git': 'git', 'npm_package_contributors_1_url': 'https://github.com/thegoleffect', 'npm_package_contributors_6_url': 'https://github.com/vincentcr', 'npm_config_rollback': 'true', 'npm_package_repository_url': 'git://github.com/ncb000gt/node.bcrypt.js.git', 'npm_package_contributors_5_url': 'https://github.com/alfredwesterveld', 'HOME': '/home/zoec', 'LD_LIBRARY_PATH': '/opt/instantclient', 'LANG': 'en_GB.UTF-8', 'npm_config_save': '', 'npm_config_registry': 'https://registry.npmjs.org/', 'npm_config_unicode': 'true', 'npm_config_production': '', 'npm_config_message': '%s', 'npm_config_always_auth': '', '_': '/usr/bin/npm', 'npm_package_devDependencies_nodeunit': '>=0.6.4', 'npm_config_searchexclude': '', 'npm_config_loglevel': 'http', 'npm_package_contributors_8_url': 'https://github.com/shtylman', 'npm_package_scripts_test': 'node-gyp configure build && nodeunit test', 'npm_config_strict_ssl': 'true', 'npm_package_keywords_2': 'auth', 'npm_package_keywords_3': 'authentication', 'npm_package_keywords_0': 'bcrypt', 'npm_package_keywords_1': 'password', 'npm_package_keywords_6': 'crypto', 'npm_package_keywords_4': 'encryption', 'npm_package_keywords_5': 'crypt', 'npm_package_contributors_2_name': 'David Trejo', 'npm_package_contributors_5_email': 'alfredwesterveld@gmail.com', 'npm_config_tag': 'latest', 'npm_package_licenses_0_type': 'MIT', 'DEFAULTS_PATH': '/usr/share/gconf/ubuntu.default.path', 'npm_config_globalignorefile': '/etc/npmignore', 'npm_config_version': '', 'npm_package_bugs_url': 'http://github.com/ncb000gt/node.bcrypt.js/issues', 'npm_package_contributors_0_email': 'savedfastcool@gmail.com', 'npm_config_force': '', 'npm_config_username': '', 'npm_config_user': '', 'npm_config_link': '', 'npm_package_contributors_9_url': 'https://github.com/vadimg', 'npm_package_name': 'bcrypt', 'npm_config_userconfig': '/home/zoec/.npmrc', 'npm_config_dev': '', 'npm_package_contributors_10_name': 'Ben Noorduis', 'npm_config_rebuild_bundle': 'true', 'npm_config_npaturl': 'http://npat.npmjs.org/', 'LOGNAME': 'zoec', 'npm_package_contributors_6_name': 'Vincent C\xc3\xb4t\xc3\xa9-Roy', 'PATH': '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/bcrypt/node_modules/.bin:/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/.bin:/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/.bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games', 'GNOME_KEYRING_CONTROL': '/tmp/keyring-NPVAVZ', 'npm_package_contributors_5_name': 'Alfred Westerveld', 'npm_config_coverage': '', 'SSH_AGENT_PID': '1977', 'TERM': 'xterm', 'XDG_SESSION_PATH': '/org/freedesktop/DisplayManager/Session0', 'XDG_SESSION_COOKIE': '3e087ba740f35de0990666e900000009-1361534488.747126-1765586974', 'npm_config_onload_script': '', 'npm_package_contributors_3_url': 'https://github.com/pixelglow', 'npm_config_description': 'true', 'npm_package_contributors_8_name': 'Roman Shtylman', 'npm_config_bindist': '0.6-ares1.7.5-evundefined-openssl1.0.1-v83.7.12.22-linux-ia32-3.2.0-38-generic-pae', 'npm_config___DO_NOT_MODIFY_THIS_FILE___use__etc_npmrc_instead_': 'true', 'npm_package_contributors_12_name': 'Sean McArthur', 'npm_config_viewer': 'man', 'npm_config_showlevel': '2', 'npm_config_unsafe_perm': 'true', 'npm_package_contributors_4_url': 'https://github.com/newitfarmer', 'SSH_AUTH_SOCK': '/tmp/keyring-NPVAVZ/ssh', 'npm_config_proprietary_attribs': 'true', 'npm_package_version': '0.7.2', 'npm_config_https_proxy': '', 'npm_config_node_version': '0.6.12', 'npm_config_init_author_email': '', 'npm_package_contributors_2_url': 'https://github.com/dtrejo', 'npm_package_dependencies_bindings': '1.0.0', 'npm_package_contributors_3_email': 'glen.low@pixelglow.com', 'OLDPWD': '/home/zoec/Projects/XenClient/sync-wui/build', 'npm_config_outfd': '1', 'npm_config_bin_publish': '', 'NODE_PATH': '/usr/lib/nodejs:/usr/share/javascript', 'PWD': '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/bcrypt', 'npm_package_contributors_9_email': 'dimva13@gmail.com'} +files = [] +hash = 0 +options = {'compile_targets': None, 'force': False, 'verbose': 0, 'nocache': False, 'progress_bar': 0, 'destdir': '', 'keep': False, 'zones': '', 'blddir': '', 'prefix': '/usr/local/', 'jobs': 4, 'srcdir': '', 'check_cxx_compiler': 'g++ icpc sunc++'} +srcdir = '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/bcrypt' diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/.npmignore b/dist/node_modules/ldapauth/node_modules/bcrypt/.npmignore new file mode 100644 index 0000000..0babfbf --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/.npmignore @@ -0,0 +1,5 @@ +.lock* +build +*.node +*.sw[a-z] +node_modules diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/.travis.yml b/dist/node_modules/ldapauth/node_modules/bcrypt/.travis.yml new file mode 100644 index 0000000..bf6b2f0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - 0.6 + - 0.7 + - 0.8 +notifications: + email: + on_success: never diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/CHANGELOG b/dist/node_modules/ldapauth/node_modules/bcrypt/CHANGELOG new file mode 100644 index 0000000..05a13c9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/CHANGELOG @@ -0,0 +1,46 @@ +v0.5.0 +* Fix for issue around empty string params throwing Errors. +* Method deprecation. +* Upgrade from libeio/ev to libuv. (shtylman) +** --- NOTE --- Breaks 0.4.x compatability +* EV_MULTIPLICITY compile flag. + +v0.4.1 +* Thread safety fix around OpenSSL (GH-32). (bnoordhuis - through node) +* C++ code changes using delete and new instead of malloc and free. (shtylman) +* Compile options for speed, zoom. (shtylman) +* Move much of the type and variable checking to the JS. (shtylman) + +v0.4.0 +* Added getRounds function that will tell you the number of rounds within a hash/salt + +v0.3.2 +* Fix api issue with async salt gen first param + +v0.3.1 +* Compile under node 0.5.x + +v0.3.0 +* Internal Refactoring +* Remove pthread dependencies and locking +* Fix compiler warnings and a memory bug + +v0.2.4 +* Use threadsafe functions instead of pthread mutexes +* salt validation to make sure the salt is of the correct size and format + +v0.2.3 +* cygwin support + +v0.2.2 +* Remove dependency on libbsd, use libssl instead + +v0.2.0 +* Added async functionality +* API changes + * hashpw -> encrypt + * all old sync methods now end with _sync +* Removed libbsd(arc4random) dependency...now uses openssl which is more widely spread + +v0.1.2 +* Security fix. Wasn't reading rounds in properly and was always only using 4 rounds diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/LICENSE b/dist/node_modules/ldapauth/node_modules/bcrypt/LICENSE new file mode 100644 index 0000000..94e2ba5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Nicholas Campbell + +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/dist/node_modules/ldapauth/node_modules/bcrypt/Makefile b/dist/node_modules/ldapauth/node_modules/bcrypt/Makefile new file mode 100644 index 0000000..81735d6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/Makefile @@ -0,0 +1,22 @@ +TESTS = test/*.js + +all: test + +build: clean configure compile + +configure: + node-gyp configure + +compile: configure + node-gyp build + npm install . + +test: build + @./node_modules/nodeunit/bin/nodeunit \ + $(TESTS) + +clean: + node-gyp clean + + +.PHONY: clean test build diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/README.md b/dist/node_modules/ldapauth/node_modules/bcrypt/README.md new file mode 100644 index 0000000..63a78ca --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/README.md @@ -0,0 +1,239 @@ +node.bcrypt.js +============= + +[![Build Status](https://secure.travis-ci.org/ncb000gt/node.bcrypt.js.png)](http://travis-ci.org/#!/ncb000gt/node.bcrypt.js) + +Lib to help you hash passwords. +[bcrypt on wikipedia][bcryptwiki] + +Catalyst for this module: [How To Safely Store A Password][codahale] + +If You Are Submitting Bugs/Issues +============= + +Because we can't magically know what you are doing to expose an issue, it is best if you provide a snippet of code. This snippet need not include your secret sauce, but it must replicate the issue you are describing. The issues that get closed without resolution tend to be the ones without code examples. Thanks. + + +Version Compatability +============= + + + + + + + + + + + +
      Node VerBcrypt Version
      <= 0.4.x<= 0.4.x
      >= 0.6.x>= 0.5.x
      + +Windows users should make sure to have at least node 0.8.5 installed and version >= 0.7.1 of this module. + + +Security Issues/Concerns +============= + +As should be the case with any security tool, this library should be scrutinized by anyone using it. If you find or suspect an issue with the code- please bring it to my attention and I'll spend some time trying to make sure that this tool is as secure as possible. + +To make it easier for people using this tool to analyze what has been surveyed, here is a list of BCrypt related security issues/concerns as they've come up. + +* An [issue with passwords][jtr] was found with a version of the Blowfish algorithm developed for John the Ripper. This is not present in the OpenBSD version and is thus not a problem for this module. HT [zooko][zooko]. + +Dependencies +============= + +* NodeJS +* OpenSSL (Development Libraries (header files) for compilation) + * For Windows you'll need http://slproweb.com/products/Win32OpenSSL.html installed to the default location of `C:\OpenSSL-Win32` + * For 64 bit use the 64 bit version and install to `C:\OpenSSL-Win64` +* `node-gyp` + * Please check the dependencies for this tool at: https://github.com/TooTallNate/node-gyp/ + * Windows users will need the options for c# and c++ installed with their visual studio instance. + * Python 2.x + +From NPM +============ + +npm install bcrypt + + +From Source +============ + +Assuming you've already built node, you can compile the module with `node-gyp`: + +``` +git clone git://github.com/ncb000gt/node.bcrypt.js.git +cd node.bcrypt.js +node-gyp configure +node-gyp build +``` + +Note: if you do not have node-gyp installed, install it using: `npm install -g node-gyp` + +Usage - Sync +============ + +To hash a password: + +```javascript +var bcrypt = require('bcrypt'); +var salt = bcrypt.genSaltSync(10); +var hash = bcrypt.hashSync("B4c0/\/", salt); +// Store hash in your password DB. +``` + +To check a password: + +```javascript +// Load hash from your password DB. +bcrypt.compareSync("B4c0/\/", hash); // true +bcrypt.compareSync("not_bacon", hash); // false +``` + +Auto-gen a salt and hash: + +```javascript +var hash = bcrypt.hashSync('bacon', 8); +``` + +Usage - Async +============ + +To hash a password: + +```javascript +var bcrypt = require('bcrypt'); +bcrypt.genSalt(10, function(err, salt) { + bcrypt.hash("B4c0/\/", salt, function(err, hash) { + // Store hash in your password DB. + }); +}); +``` + +To check a password: + +```javascript +// Load hash from your password DB. +bcrypt.compare("B4c0/\/", hash, function(err, res) { + // res == true +}); +bcrypt.compare("not_bacon", hash, function(err, res) { + // res = false +}); +``` + +Auto-gen a salt and hash: + +```javascript +bcrypt.hash('bacon', 8, function(err, hash) { +}); +``` + +API +============ + +`BCrypt.` + + * `genSaltSync(rounds, seed_length)` + * `rounds` - [OPTIONAL] - the number of rounds to process the data for. (default - 10) + * `seed_length` - [OPTIONAL] - RAND_bytes wants a length. to make that a bit flexible, you can specify a seed_length. (default - 20) + * `genSalt(rounds, seed_length, cb)` + * `rounds` - [OPTIONAL] - the number of rounds to process the data for. (default - 10) + * `seed_length` - [OPTIONAL] - RAND_bytes wants a length. to make that a bit flexible, you can specify a seed_length. (default - 20) + * `cb` - [REQUIRED] - a callback to be fired once the salt has been generated. uses eio making it asynchronous. + * `err` - First parameter to the callback detailing any errors. + * `salt` - Second parameter to the callback providing the generated salt. + * `hashSync(data, salt)` + * `data` - [REQUIRED] - the data to be encrypted. + * `salt` - [REQUIRED] - the salt to be used in encryption. + * `hash(data, salt, cb)` + * `data` - [REQUIRED] - the data to be encrypted. + * `salt` - [REQUIRED] - the salt to be used to hash the password. if specified as a number then a salt will be generated and used (see examples). + * `cb` - [REQUIRED] - a callback to be fired once the data has been encrypted. uses eio making it asynchronous. + * `err` - First parameter to the callback detailing any errors. + * `encrypted` - Second parameter to the callback providing the encrypted form. + * `compareSync(data, encrypted)` + * `data` - [REQUIRED] - data to compare. + * `encrypted` - [REQUIRED] - data to be compared to. + * `compare(data, encrypted, cb)` + * `data` - [REQUIRED] - data to compare. + * `encrypted` - [REQUIRED] - data to be compared to. + * `cb` - [REQUIRED] - a callback to be fired once the data has been compared. uses eio making it asynchronous. + * `err` - First parameter to the callback detailing any errors. + * `same` - Second parameter to the callback providing whether the data and encrypted forms match [true | false]. + * `getRounds(encrypted)` - return the number of rounds used to encrypt a given hash + * `encrypted` - [REQUIRED] - hash from which the number of rounds used should be extracted. + + +Hash Info +============ + +The characters that comprise the resultant hash are `./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$`. + +Testing +============ + +If you create a pull request, tests better pass :) + + npm install + npm test + +Credits +============ + +The code for this comes from a few sources: + +* blowfish.cc - OpenBSD +* bcrypt.cc - OpenBSD +* bcrypt::gen_salt - [gen_salt inclusion to bcrypt][bcryptgs] +* bcrypt_node.cc - me + +Contributors +============ + +* [Antonio Salazar Cardozo][shadowfiend] - Early MacOS X support (when we used libbsd) +* [Ben Glow][pixelglow] - Fixes for thread safety with async calls +* [Van Nguyen][thegoleffect] - Found a timing attack in the comparator +* [NewITFarmer][newitfarmer] - Initial Cygwin support +* [David Trejo][dtrejo] - packaging fixes +* [Alfred Westerveld][alfredwesterveld] - packaging fixes +* [Vincent Côté-Roy][vincentr] - Testing around concurrency issues +* [Lloyd Hilaiel][lloyd] - Documentation fixes +* [Roman Shtylman][shtylman] - Code refactoring, general rot reduction, compile options, better memory management with delete and new, and an upgrade to libuv over eio/ev. +* [Vadim Graboys][vadimg] - Code changes to support 0.5.5+ +* [Ben Noordhuis][bnoordhuis] - Fixed a thread safety issue in nodejs that was perfectly mappable to this module. +* [Nate Rajlich][tootallnate] - Bindings and build process. +* [Sean McArthur][seanmonstar] - Windows Support +* [Fanie Oosthuysen][weareu] - Windows Support + +License +============ + +Unless stated elsewhere, file headers or otherwise, the license as stated in the LICENSE file. + + + +[bcryptwiki]: http://en.wikipedia.org/wiki/Crypt_(Unix)#Blowfish-based_scheme +[bcryptgs]: http://mail-index.netbsd.org/tech-crypto/2002/05/24/msg000204.html +[codahale]: http://codahale.com/how-to-safely-store-a-password/ +[gh13]: https://github.com/ncb000gt/node.bcrypt.js/issues/13 +[jtr]: http://www.openwall.com/lists/oss-security/2011/06/20/2 + +[shadowfiend]:https://github.com/Shadowfiend +[thegoleffect]:https://github.com/thegoleffect +[pixelglow]:https://github.com/pixelglow +[dtrejo]:https://github.com/dtrejo +[alfredwesterveld]:https://github.com/alfredwesterveld +[newitfarmer]:https://github.com/newitfarmer +[zooko]:https://twitter.com/zooko +[vincentr]:https://twitter.com/vincentcr +[lloyd]:https://github.com/lloyd +[shtylman]:https://github.com/shtylman +[vadimg]:https://github.com/vadimg +[bnoordhuis]:https://github.com/bnoordhuis +[tootallnate]:https://github.com/tootallnate +[seanmonstar]:https://github.com/seanmonstar +[weareu]:https://github.com/weareu diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/bcrypt.js b/dist/node_modules/ldapauth/node_modules/bcrypt/bcrypt.js new file mode 100644 index 0000000..199bc07 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/bcrypt.js @@ -0,0 +1,189 @@ +var bindings = require('bindings')('bcrypt_lib'); + +/// generate a salt (sync) +/// @param {Number} [rounds] number of rounds (default 10) +/// @param {Number} [seed_length] number of random bytes (default 20) +/// @return {String} salt +module.exports.gen_salt_sync = function(rounds, seed_length) { + console.log("DEPRECATION WARNING: `gen_salt_sync` has been deprecated. Please use `genSaltSync` instead."); + + return module.exports.genSaltSync(rounds, seed_length); +} + +module.exports.genSaltSync = function(rounds, seed_length) { + // default 10 rounds + if (!rounds) { + rounds = 10; + } else if (typeof rounds !== 'number') { + throw new Error('rounds must be a number'); + } + + // default length 20 + if (!seed_length) { + seed_length = 20; + } else if (typeof seed_length !== 'number') { + throw new Error('seed_length must be a number'); + } + + return bindings.gen_salt_sync(rounds, seed_length); +}; + +/// generate a salt +/// @param {Number} [rounds] number of rounds (default 10) +/// @param {Number} [seed_length] number of random bytes (default 20) +/// @param {Function} cb callback(err, salt) +module.exports.gen_salt = function(rounds, seed_length, cb) { + console.log("DEPRECATION WARNING: `gen_salt` has been deprecated. Please use `genSalt` instead."); + + return module.exports.genSalt(rounds, seed_length, cb); +} + +module.exports.genSalt = function(rounds, seed_length, cb) { + // if callback is first argument, then use defaults for others + if (typeof arguments[0] === 'function') { + // have to set callback first otherwise arguments are overriden + cb = arguments[0]; + rounds = 10; + seed_length = 20; + // callback is second argument + } else if (typeof arguments[1] === 'function') { + // have to set callback first otherwise arguments are overriden + cb = arguments[1]; + seed_length = 20; + } + + // default 10 rounds + if (!rounds) { + rounds = 10; + } else if (typeof rounds !== 'number') { + throw new Error('rounds must be a number'); + } + + // default length 20 + if (!seed_length) { + seed_length = 20; + } else if (typeof seed_length !== 'number') { + throw new Error('seed_length must be a number'); + } + + if (!cb) { + throw new Error('callback required for gen_salt'); + } + + return bindings.gen_salt(rounds, seed_length, cb); +}; + +/// hash data using a salt +/// @param {String} data the data to encrypt +/// @param {String} salt the salt to use when hashing +/// @return {String} hash +module.exports.encrypt_sync = function(data, salt) { + console.log("DEPRECATION WARNING: `encrypt_sync` has been deprecated. Please use `hashSync` instead."); + + return module.exports.hashSync(data, salt); +} + +module.exports.hashSync = function(data, salt) { + if (data == null || data == undefined || salt == null || salt == undefined) { + throw new Error('data and salt arguments required'); + } else if (typeof data !== 'string' && (typeof salt !== 'string' || typeof salt !== 'number')) { + throw new Error('data must be a string and salt must either be a salt string or a number of rounds'); + } + + if (typeof salt === 'number') { + salt = module.exports.genSaltSync(salt); + } + + return bindings.encrypt_sync(data, salt); +}; + +/// hash data using a salt +/// @param {String} data the data to encrypt +/// @param {String} salt the salt to use when hashing +/// @param {Function} cb callback(err, hash) +module.exports.encrypt = function(data, salt, cb) { + console.log("DEPRECATION WARNING: `encrypt` has been deprecated. Please use `hash` instead."); + + return module.exports.hash(data, salt, cb); +} + +module.exports.hash = function(data, salt, cb) { + if (data == null || data == undefined || salt == null || salt == undefined) { + throw new Error('data and salt arguments required'); + } else if (typeof data !== 'string' && (typeof salt !== 'string' || typeof salt !== 'number')) { + throw new Error('data must be a string and salt must either be a salt string or a number of rounds'); + } + + if (!cb) { + throw new Error('callback required for async compare'); + } else if (typeof cb !== 'function') { + throw new Error('callback must be a function'); + } + + if (typeof salt === 'number') { + return module.exports.genSalt(salt, function(err, salt) { + return bindings.encrypt(data, salt, cb); + }); + } + + return bindings.encrypt(data, salt, cb); +}; + +/// compare raw data to hash +/// @param {String} data the data to hash and compare +/// @param {String} hash expected hash +/// @return {bool} true if hashed data matches hash +module.exports.compare_sync = function(data, hash) { + console.log("DEPRECATION WARNING: `compare_sync` has been deprecated. Please use `compareSync` instead."); + + return module.exports.compareSync(data, hash); +} + +module.exports.compareSync = function(data, hash) { + if (data == null || data == undefined || hash == null || hash == undefined) { + throw new Error('data and hash arguments required'); + } else if (typeof data !== 'string' || typeof hash !== 'string') { + throw new Error('data and hash must be strings'); + } + + return bindings.compare_sync(data, hash); +}; + +/// compare raw data to hash +/// @param {String} data the data to hash and compare +/// @param {String} hash expected hash +/// @param {Function} cb callback(err, matched) - matched is true if hashed data matches hash +module.exports.compare = function(data, hash, cb) { + if (data == null || data == undefined || hash == null || hash == undefined) { + throw new Error('data and hash arguments required'); + } else if (typeof data !== 'string' || typeof hash !== 'string') { + throw new Error('data and hash must be strings'); + } + + if (!cb) { + throw new Error('callback required for async compare'); + } else if (typeof cb !== 'function') { + throw new Error('callback must be a function'); + } + + return bindings.compare(data, hash, cb); +}; + +/// @param {String} hash extract rounds from this hash +/// @return {Number} the number of rounds used to encrypt a given hash +module.exports.get_rounds = function(hash) { + console.log("DEPRECATION WARNING: `get_rounds` has been deprecated. Please use `getRounds` instead."); + + return module.exports.getRounds(hash); +} + +module.exports.getRounds = function(hash) { + if (hash == null || hash == undefined) { + throw new Error('hash argument required'); + } else if (typeof hash !== 'string') { + throw new Error('hash must be a string'); + } + + return bindings.get_rounds(hash); +}; + diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/binding.gyp b/dist/node_modules/ldapauth/node_modules/bcrypt/binding.gyp new file mode 100644 index 0000000..beae424 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/binding.gyp @@ -0,0 +1,42 @@ +{ + 'targets': [ + { + 'target_name': 'bcrypt_lib', + 'sources': [ + 'src/blowfish.cc', + 'src/bcrypt.cc', + 'src/bcrypt_node.cc' + ], + 'conditions': [ + [ 'OS=="win"', { + 'conditions': [ + # "openssl_root" is the directory on Windows of the OpenSSL files + ['target_arch=="x64"', { + 'variables': { + 'openssl_root%': 'C:/OpenSSL-Win64' + }, + }, { + 'variables': { + 'openssl_root%': 'C:/OpenSSL-Win32' + }, + }], + ], + 'defines': [ + 'uint=unsigned int', + ], + 'libraries': [ + '-l<(openssl_root)/lib/libeay32.lib', + ], + 'include_dirs': [ + '<(openssl_root)/include', + ], + }, { # OS!="win" + 'include_dirs': [ + # use node's bundled openssl headers on Unix platforms + '<(node_root_dir)/deps/openssl/openssl/include' + ], + }], + ], + } + ] +} diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/examples/async_compare.js b/dist/node_modules/ldapauth/node_modules/bcrypt/examples/async_compare.js new file mode 100644 index 0000000..e5a8daa --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/examples/async_compare.js @@ -0,0 +1,21 @@ +var bcrypt = require('../bcrypt'); + +var start = Date.now(); +bcrypt.genSalt(10, function(err, salt) { + console.log('salt: ' + salt); + console.log('salt cb end: ' + (Date.now() - start) + 'ms'); + bcrypt.hash('test', salt, function(err, crypted) { + console.log('crypted: ' + crypted); + console.log('crypted cb end: ' + (Date.now() - start) + 'ms'); + console.log('rounds used from hash:', bcrypt.getRounds(crypted)); + bcrypt.compare('test', crypted, function(err, res) { + console.log('compared true: ' + res); + console.log('compared true cb end: ' + (Date.now() - start) + 'ms'); + }); + bcrypt.compare('bacon', crypted, function(err, res) { + console.log('compared false: ' + res); + console.log('compared false cb end: ' + (Date.now() - start) + 'ms'); + }); + }); +}) +console.log('end: ' + (Date.now() - start) + 'ms'); diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/examples/forever_gen_salt.js b/dist/node_modules/ldapauth/node_modules/bcrypt/examples/forever_gen_salt.js new file mode 100644 index 0000000..761686a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/examples/forever_gen_salt.js @@ -0,0 +1,8 @@ +var bcrypt = require('../bcrypt'); + +(function printSalt() { + bcrypt.genSalt(10, function(err, salt) { + console.log('salt: ' + salt); + printSalt(); + }); +})() diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/node_modules/bindings/README.md b/dist/node_modules/ldapauth/node_modules/bcrypt/node_modules/bindings/README.md new file mode 100644 index 0000000..585cf51 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/node_modules/bindings/README.md @@ -0,0 +1,97 @@ +node-bindings +============= +### Helper module for loading your native module's .node file + +This is a helper module for authors of Node.js native addon modules. +It is basically the "swiss army knife" of `require()`ing your native module's +`.node` file. + +Throughout the course of Node's native addon history, addons have ended up being +compiled in a variety of different places, depending on which build tool and which +version of node was used. To make matters worse, now the _gyp_ build tool can +produce either a _Release_ or _Debug_ build, each being built into different +locations. + +This module checks _all_ the possible locations that a native addon would be built +at, and returns the first one that loads successfully. + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install bindings +``` + +Or add it to the `"dependencies"` section of your _package.json_ file. + + +Example +------- + +`require()`ing the proper bindings file for the current node version, platform +and architecture is as simple as: + +``` js +var bindings = require('bindings')('binding.node') + +// Use your bindings defined in your C files +bindings.your_c_function() +``` + + +Nice Error Output +----------------- + +When the `.node` file could not be loaded, `node-bindings` throws an Error with +a nice error message telling you exactly what was tried. You can also check the +`err.tries` Array property. + +``` +Error: Could not load the bindings file. Tried: + → /Users/nrajlich/ref/build/binding.node + → /Users/nrajlich/ref/build/Debug/binding.node + → /Users/nrajlich/ref/build/Release/binding.node + → /Users/nrajlich/ref/out/Debug/binding.node + → /Users/nrajlich/ref/Debug/binding.node + → /Users/nrajlich/ref/out/Release/binding.node + → /Users/nrajlich/ref/Release/binding.node + → /Users/nrajlich/ref/build/default/binding.node + → /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node + at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13) + at Object. (/Users/nrajlich/ref/lib/ref.js:5:47) + 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) + ... +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> + +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/dist/node_modules/ldapauth/node_modules/bcrypt/node_modules/bindings/bindings.js b/dist/node_modules/ldapauth/node_modules/bcrypt/node_modules/bindings/bindings.js new file mode 100644 index 0000000..c47a51b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/node_modules/bindings/bindings.js @@ -0,0 +1,151 @@ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , path = require('path') + , join = path.join + , dirname = path.dirname + , exists = fs.existsSync || path.existsSync + , defaults = { + arrow: process.env.NODE_BINDINGS_ARROW || ' → ' + , compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled' + , platform: process.platform + , arch: process.arch + , version: process.versions.node + , bindings: 'bindings.node' + , try: [ + // node-gyp's linked version in the "build" dir + [ 'module_root', 'build', 'bindings' ] + // node-waf and gyp_addon (a.k.a node-gyp) + , [ 'module_root', 'build', 'Debug', 'bindings' ] + , [ 'module_root', 'build', 'Release', 'bindings' ] + // Debug files, for development (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Debug', 'bindings' ] + , [ 'module_root', 'Debug', 'bindings' ] + // Release files, but manually compiled (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Release', 'bindings' ] + , [ 'module_root', 'Release', 'bindings' ] + // Legacy from node-waf, node <= 0.4.x + , [ 'module_root', 'build', 'default', 'bindings' ] + // Production "Release" buildtype binary (meh...) + , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ] + ] + } + +/** + * The main `bindings()` function loads the compiled bindings for a given module. + * It uses V8's Error API to determine the parent filename that this function is + * being invoked from, which is then used to find the root directory. + */ + +function bindings (opts) { + + // Argument surgery + if (typeof opts == 'string') { + opts = { bindings: opts } + } else if (!opts) { + opts = {} + } + opts.__proto__ = defaults + + // Get the module root + if (!opts.module_root) { + opts.module_root = exports.getRoot(exports.getFileName()) + } + + // Ensure the given bindings name ends with .node + if (path.extname(opts.bindings) != '.node') { + opts.bindings += '.node' + } + + var tries = [] + , i = 0 + , l = opts.try.length + , n + + for (; i (http://tootallnate.net)" +, "repository": { "type": "git", "url": "git://github.com/TooTallNate/node-bindings.git" } +, "main": "./bindings.js" +} diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/package.json b/dist/node_modules/ldapauth/node_modules/bcrypt/package.json new file mode 100644 index 0000000..b7d5a40 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/package.json @@ -0,0 +1,56 @@ +{ + "name": "bcrypt", + "description": "A bcrypt library for NodeJS.", + "keywords": [ + "bcrypt", + "password", + "auth", + "authentication", + "encryption", + "crypt", + "crypto" + ], + "main": "./bcrypt", + "version": "0.7.2", + "author": "Nick Campbell (http://github.com/ncb000gt)", + "engines": { + "node": ">= 0.6.0" + }, + "repository": { + "type": "git", + "url": "http://github.com/ncb000gt/node.bcrypt.js.git" + }, + "licenses": [ + { + "type": "MIT" + } + ], + "bugs": { + "url": "http://github.com/ncb000gt/node.bcrypt.js/issues" + }, + "scripts": { + "test": "node-gyp configure build && nodeunit test" + }, + "dependencies": { + "bindings": "1.0.0" + }, + "devDependencies": { + "nodeunit": ">=0.6.4" + }, + "contributors": [ + "Antonio Salazar Cardozo (https://github.com/Shadowfiend)", + "Van Nguyen (https://github.com/thegoleffect)", + "David Trejo (https://github.com/dtrejo)", + "Ben Glow (https://github.com/pixelglow)", + "NewITFarmer.com <> (https://github.com/newitfarmer)", + "Alfred Westerveld (https://github.com/alfredwesterveld)", + "Vincent Côté-Roy (https://github.com/vincentcr)", + "Lloyd Hilaiel (https://github.com/lloyd)", + "Roman Shtylman (https://github.com/shtylman)", + "Vadim Graboys (https://github.com/vadimg)", + "Ben Noorduis <> (https://github.com/bnoordhuis)", + "Nate Rajlich (https://github.com/tootallnate)", + "Sean McArthur (https://github.com/seanmonstar)", + "Fanie Oosthuysen (https://github.com/weareu)" + ] +} diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/src/bcrypt.cc b/dist/node_modules/ldapauth/node_modules/bcrypt/src/bcrypt.cc new file mode 100644 index 0000000..b07ccc6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/src/bcrypt.cc @@ -0,0 +1,322 @@ +/* $OpenBSD: bcrypt.c,v 1.24 2008/04/02 19:54:05 millert Exp $ */ + +/* + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* This password hashing algorithm was designed by David Mazieres + * and works as follows: + * + * 1. state := InitState () + * 2. state := ExpandKey (state, salt, password) 3. + * REPEAT rounds: + * state := ExpandKey (state, 0, salt) + * state := ExpandKey(state, 0, password) + * 4. ctext := "OrpheanBeholderScryDoubt" + * 5. REPEAT 64: + * ctext := Encrypt_ECB (state, ctext); + * 6. RETURN Concatenate (salt, ctext); + * + */ + +#include +#include +#include +#include +#include "node_blf.h" + +#ifdef _WIN32 +#define snprintf _snprintf +#endif + +//#if !defined(__APPLE__) && !defined(__MACH__) +//#include "bsd/stdlib.h" +//#endif + +/* This implementation is adaptable to current computing power. + * You can have up to 2^31 rounds which should be enough for some + * time to come. + */ + +/*char *bcrypt(const char *, const char *); +void encode_salt(char *, u_int8_t *, u_int16_t, u_int8_t); +char * bcrypt_gensalt(u_int8_t log_rounds);*/ + +static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t); +static void decode_base64(u_int8_t *, u_int16_t, u_int8_t *); + +const static char* error = ":"; + +const static u_int8_t Base64Code[] = +"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +const static u_int8_t index_64[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 1, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 255, 255, + 255, 255, 255, 255, 255, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 255, 255, 255, 255, 255, 255, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 255, 255, 255, 255, 255 +}; +#define CHAR64(c) ( (c) > 127 ? 255 : index_64[(c)]) + +static void +decode_base64(u_int8_t *buffer, u_int16_t len, u_int8_t *data) +{ + u_int8_t *bp = buffer; + u_int8_t *p = data; + u_int8_t c1, c2, c3, c4; + while (bp < buffer + len) { + c1 = CHAR64(*p); + c2 = CHAR64(*(p + 1)); + + /* Invalid data */ + if (c1 == 255 || c2 == 255) + break; + + *bp++ = (c1 << 2) | ((c2 & 0x30) >> 4); + if (bp >= buffer + len) + break; + + c3 = CHAR64(*(p + 2)); + if (c3 == 255) + break; + + *bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); + if (bp >= buffer + len) + break; + + c4 = CHAR64(*(p + 3)); + if (c4 == 255) + break; + *bp++ = ((c3 & 0x03) << 6) | c4; + + p += 4; + } +} + +void +encode_salt(char *salt, u_int8_t *csalt, u_int16_t clen, u_int8_t logr) +{ + salt[0] = '$'; + salt[1] = BCRYPT_VERSION; + salt[2] = 'a'; + salt[3] = '$'; + + snprintf(salt + 4, 4, "%2.2u$", logr); + + encode_base64((u_int8_t *) salt + 7, csalt, clen); +} + + +/* Generates a salt for this version of crypt. + Since versions may change. Keeping this here + seems sensible. + from: http://mail-index.netbsd.org/tech-crypto/2002/05/24/msg000204.html +*/ +void +bcrypt_gensalt(u_int8_t log_rounds, u_int8_t *seed, char *gsalt) +{ + if (log_rounds < 4) + log_rounds = 4; + else if (log_rounds > 31) + log_rounds = 31; + + encode_salt(gsalt, seed, BCRYPT_MAXSALT, log_rounds); +} + +/* We handle $Vers$log2(NumRounds)$salt+passwd$ + i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */ + +void +bcrypt(const char *key, const char *salt, char *encrypted) +{ + blf_ctx state; + u_int32_t rounds, i, k; + u_int16_t j; + u_int8_t key_len, salt_len, logr, minor; + u_int8_t ciphertext[4 * BCRYPT_BLOCKS+1] = "OrpheanBeholderScryDoubt"; + u_int8_t csalt[BCRYPT_MAXSALT]; + u_int32_t cdata[BCRYPT_BLOCKS]; + int n; + + /* Discard "$" identifier */ + salt++; + + if (*salt > BCRYPT_VERSION) { + /* How do I handle errors ? Return ':' */ + strcpy(encrypted, error); + return; + } + + /* Check for minor versions */ + if (salt[1] != '$') { + switch (salt[1]) { + case 'a': + /* 'ab' should not yield the same as 'abab' */ + minor = salt[1]; + salt++; + break; + default: + strcpy(encrypted, error); + return; + } + } else + minor = 0; + + /* Discard version + "$" identifier */ + salt += 2; + + if (salt[2] != '$') { + /* Out of sync with passwd entry */ + strcpy(encrypted, error); + return; + } + + /* Computer power doesn't increase linear, 2^x should be fine */ + n = atoi(salt); + if (n > 31 || n < 0) { + strcpy(encrypted, error); + return; + } + logr = (u_int8_t)n; + if ((rounds = (u_int32_t) 1 << logr) < BCRYPT_MINROUNDS) { + strcpy(encrypted, error); + return; + } + + /* Discard num rounds + "$" identifier */ + salt += 3; + + if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) { + strcpy(encrypted, error); + return; + } + + /* We dont want the base64 salt but the raw data */ + decode_base64(csalt, BCRYPT_MAXSALT, (u_int8_t *) salt); + salt_len = BCRYPT_MAXSALT; + key_len = strlen(key) + (minor >= 'a' ? 1 : 0); + + + /* Setting up S-Boxes and Subkeys */ + Blowfish_initstate(&state); + Blowfish_expandstate(&state, csalt, salt_len, + (u_int8_t *) key, key_len); + for (k = 0; k < rounds; k++) { + Blowfish_expand0state(&state, (u_int8_t *) key, key_len); + Blowfish_expand0state(&state, csalt, salt_len); + } + + /* This can be precomputed later */ + j = 0; + for (i = 0; i < BCRYPT_BLOCKS; i++) + cdata[i] = Blowfish_stream2word(ciphertext, 4 * BCRYPT_BLOCKS, &j); + + /* Now do the encryption */ + for (k = 0; k < 64; k++) + blf_enc(&state, cdata, BCRYPT_BLOCKS / 2); + + for (i = 0; i < BCRYPT_BLOCKS; i++) { + ciphertext[4 * i + 3] = cdata[i] & 0xff; + cdata[i] = cdata[i] >> 8; + ciphertext[4 * i + 2] = cdata[i] & 0xff; + cdata[i] = cdata[i] >> 8; + ciphertext[4 * i + 1] = cdata[i] & 0xff; + cdata[i] = cdata[i] >> 8; + ciphertext[4 * i + 0] = cdata[i] & 0xff; + } + + i = 0; + encrypted[i++] = '$'; + encrypted[i++] = BCRYPT_VERSION; + if (minor) + encrypted[i++] = minor; + encrypted[i++] = '$'; + + snprintf(encrypted + i, 4, "%2.2u$", logr); + + encode_base64((u_int8_t *) encrypted + i + 3, csalt, BCRYPT_MAXSALT); + encode_base64((u_int8_t *) encrypted + strlen(encrypted), ciphertext, + 4 * BCRYPT_BLOCKS - 1); + memset(&state, 0, sizeof(state)); + memset(ciphertext, 0, sizeof(ciphertext)); + memset(csalt, 0, sizeof(csalt)); + memset(cdata, 0, sizeof(cdata)); +} + +u_int32_t bcrypt_get_rounds(const char * hash) +{ + /* skip past the leading "$" */ + if (!hash || *(hash++) != '$') return 0; + + /* skip past version */ + if (0 == (*hash++)) return 0; + if (*hash && *hash != '$') hash++; + if (*hash++ != '$') return 0; + + return atoi(hash); +} + +static void +encode_base64(u_int8_t *buffer, u_int8_t *data, u_int16_t len) +{ + u_int8_t *bp = buffer; + u_int8_t *p = data; + u_int8_t c1, c2; + while (p < data + len) { + c1 = *p++; + *bp++ = Base64Code[(c1 >> 2)]; + c1 = (c1 & 0x03) << 4; + if (p >= data + len) { + *bp++ = Base64Code[c1]; + break; + } + c2 = *p++; + c1 |= (c2 >> 4) & 0x0f; + *bp++ = Base64Code[c1]; + c1 = (c2 & 0x0f) << 2; + if (p >= data + len) { + *bp++ = Base64Code[c1]; + break; + } + c2 = *p++; + c1 |= (c2 >> 6) & 0x03; + *bp++ = Base64Code[c1]; + *bp++ = Base64Code[c2 & 0x3f]; + } + *bp = '\0'; +} diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/src/bcrypt_node.cc b/dist/node_modules/ldapauth/node_modules/bcrypt/src/bcrypt_node.cc new file mode 100644 index 0000000..85e179e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/src/bcrypt_node.cc @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2010, Nicholas Campbell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include "node_blf.h" + +//pulled from node commit - 97cada0 +#ifdef _WIN32 +# include +#else +# include +#endif + +#define NODE_LESS_THAN (!(NODE_VERSION_AT_LEAST(0, 5, 4))) + +using namespace v8; +using namespace node; + +#ifdef _WIN32 + +static HANDLE* locks; + + +static void crypto_lock_init(void) { + int i, n; + + n = CRYPTO_num_locks(); + locks = new HANDLE[n]; + + for (i = 0; i < n; i++) + if (!(locks[i] = CreateMutex(NULL, FALSE, NULL))) + abort(); +} + +static void crypto_lock_cb(int mode, int n, const char* file, int line) { + if (mode & CRYPTO_LOCK) + WaitForSingleObject(locks[n], INFINITE); + else + ReleaseMutex(locks[n]); +} + +static unsigned long crypto_id_cb(void) { + return (unsigned long) GetCurrentThreadId(); +} + +#else /* !_WIN32 */ + +static pthread_rwlock_t* locks; + +static void crypto_lock_init(void) { + + const int n = CRYPTO_num_locks(); + locks = new pthread_rwlock_t[n]; + + for (int i = 0; i < n; i++) + if (pthread_rwlock_init(locks + i, NULL)) + abort(); +} + +static void crypto_lock_cb(int mode, int n, const char* file, int line) { + if (mode & CRYPTO_LOCK) { + if (mode & CRYPTO_READ) pthread_rwlock_rdlock(locks + n); + if (mode & CRYPTO_WRITE) pthread_rwlock_wrlock(locks + n); + } else { + pthread_rwlock_unlock(locks + n); + } +} + + +static unsigned long crypto_id_cb(void) { + return (unsigned long) pthread_self(); +} + +#endif /* !_WIN32 */ + +namespace { + +struct baton_base { + v8::Persistent callback; + std::string error; + + virtual ~baton_base() { + callback.Dispose(); + } +}; + +struct salt_baton : baton_base { + std::string salt; + int rand_len; + ssize_t rounds; +}; + +struct encrypt_baton : baton_base { + std::string salt; + std::string input; + std::string output; +}; + +struct compare_baton : baton_base { + std::string input; + std::string encrypted; + bool result; +}; + +int GetSeed(uint8_t* seed, int size) { + + // try to get good random bytes first + if (RAND_bytes((unsigned char *)seed, size) > 0) { + return 1; + } + + return RAND_pseudo_bytes(seed, size); +} + +bool ValidateSalt(const char* salt) { + + if (!salt || *salt != '$') { + return false; + } + + // discard $ + salt++; + + if (*salt > BCRYPT_VERSION) { + return false; + } + + if (salt[1] != '$') { + switch (salt[1]) { + case 'a': + salt++; + break; + default: + return false; + } + } + + // discard version + $ + salt += 2; + + if (salt[2] != '$') { + return false; + } + + int n = atoi(salt); + if (n > 31 || n < 0) { + return false; + } + + if (((uint8_t)1 << (uint8_t)n) < BCRYPT_MINROUNDS) { + return false; + } + + salt += 3; + if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT) { + return false; + } + + return true; +} + +/* SALT GENERATION */ +void GenSaltAsync(uv_work_t* req) { + + salt_baton* baton = static_cast(req->data); + + std::auto_ptr seed (new uint8_t[baton->rand_len]); + switch(GetSeed(seed.get(), baton->rand_len)) { + case -1: + baton->error = "Rand operation not supported."; + case 0: + baton->error = "Rand operation did not generate a cryptographically sound seed."; + } + + char salt[_SALT_LEN]; + bcrypt_gensalt(baton->rounds, seed.get(), salt); + baton->salt = std::string(salt); +} + +void GenSaltAsyncAfter(uv_work_t* req) { + HandleScope scope; + + salt_baton* baton = static_cast(req->data); + delete req; + + Handle argv[2]; + + if (!baton->error.empty()) { + argv[0] = Exception::Error(String::New(baton->error.c_str())); + argv[1] = Undefined(); + } + else { + argv[0] = Undefined(); + argv[1] = Encode(baton->salt.c_str(), baton->salt.size(), BINARY); + } + + TryCatch try_catch; // don't quite see the necessity of this + + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + + if (try_catch.HasCaught()) + FatalException(try_catch); + + delete baton; +} + +Handle GenerateSalt(const Arguments &args) { + HandleScope scope; + + const ssize_t rounds = args[0]->Int32Value(); + const int rand_len = args[1]->Int32Value(); + Local callback = Local::Cast(args[2]); + + salt_baton* baton = new salt_baton(); + + baton->callback = Persistent::New(callback); + baton->rand_len = rand_len; + baton->rounds = rounds; + + uv_work_t* req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, GenSaltAsync, GenSaltAsyncAfter); + + + return Undefined(); +} + +Handle GenerateSaltSync(const Arguments& args) { + HandleScope scope; + + const ssize_t rounds = args[0]->Int32Value(); + const int size = args[1]->Int32Value(); + + std::auto_ptr seed (new uint8_t[size]); + switch(GetSeed(seed.get(), size)) { + case -1: + return ThrowException(Exception::Error(String::New("Rand operation not supported."))); + case 0: + return ThrowException(Exception::Error(String::New("Rand operation did not generate a cryptographically sound seed."))); + } + + char salt[_SALT_LEN]; + bcrypt_gensalt(rounds, seed.get(), salt); + + return scope.Close(Encode(salt, strlen(salt), BINARY)); +} + +/* ENCRYPT DATA - USED TO BE HASHPW */ +void EncryptAsync(uv_work_t* req) { + encrypt_baton* baton = static_cast(req->data); + + if (!(ValidateSalt(baton->salt.c_str()))) { + baton->error = "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue"; + } + + char bcrypted[_PASSWORD_LEN]; + bcrypt(baton->input.c_str(), baton->salt.c_str(), bcrypted); + baton->output = std::string(bcrypted); +} + +void EncryptAsyncAfter(uv_work_t* req) { + HandleScope scope; + + encrypt_baton* baton = static_cast(req->data); + delete req; + + Handle argv[2]; + + if (!baton->error.empty()) { + argv[0] = Exception::Error(String::New(baton->error.c_str())); + argv[1] = Undefined(); + } + else { + argv[0] = Undefined(); + argv[1] = Encode(baton->output.c_str(), baton->output.size(), BINARY); + } + + TryCatch try_catch; // don't quite see the necessity of this + + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + + if (try_catch.HasCaught()) + FatalException(try_catch); + + delete baton; +} + +Handle Encrypt(const Arguments& args) { + HandleScope scope; + + String::Utf8Value data(args[0]->ToString()); + String::Utf8Value salt(args[1]->ToString()); + Local callback = Local::Cast(args[2]); + + encrypt_baton* baton = new encrypt_baton(); + baton->callback = Persistent::New(callback); + baton->input = std::string(*data); + baton->salt = std::string(*salt); + + uv_work_t* req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, EncryptAsync, EncryptAsyncAfter); + + return Undefined(); +} + +Handle EncryptSync(const Arguments& args) { + HandleScope scope; + + String::Utf8Value data(args[0]->ToString()); + String::Utf8Value salt(args[1]->ToString()); + + if (!(ValidateSalt(*salt))) { + return ThrowException(Exception::Error(String::New("Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue"))); + } + + char bcrypted[_PASSWORD_LEN]; + bcrypt(*data, *salt, bcrypted); + return scope.Close(Encode(bcrypted, strlen(bcrypted), BINARY)); +} + +/* COMPARATOR */ +bool CompareStrings(const char* s1, const char* s2) { + + bool eq = true; + int s1_len = strlen(s1); + int s2_len = strlen(s2); + + if (s1_len != s2_len) { + eq = false; + } + + const int max_len = (s2_len < s1_len) ? s1_len : s2_len; + + // to prevent timing attacks, should check entire string + // don't exit after found to be false + for (int i = 0; i < max_len; ++i) { + if (s1_len >= i && s2_len >= i && s1[i] != s2[i]) { + eq = false; + } + } + + return eq; +} + +void CompareAsync(uv_work_t* req) { + compare_baton* baton = static_cast(req->data); + + char bcrypted[_PASSWORD_LEN]; + bcrypt(baton->input.c_str(), baton->encrypted.c_str(), bcrypted); + baton->result = CompareStrings(bcrypted, baton->encrypted.c_str()); +} + +void CompareAsyncAfter(uv_work_t* req) { + HandleScope scope; + + compare_baton* baton = static_cast(req->data); + delete req; + + Handle argv[2]; + + if (!baton->error.empty()) { + argv[0] = Exception::Error(String::New(baton->error.c_str())); + argv[1] = Undefined(); + } else { + argv[0] = Undefined(); + argv[1] = Boolean::New(baton->result); + } + + TryCatch try_catch; // don't quite see the necessity of this + + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + + if (try_catch.HasCaught()) + FatalException(try_catch); + + // done with the baton + // free the memory and callback + delete baton; +} + +Handle Compare(const Arguments& args) { + HandleScope scope; + + String::Utf8Value input(args[0]->ToString()); + String::Utf8Value encrypted(args[1]->ToString()); + Local callback = Local::Cast(args[2]); + + compare_baton* baton = new compare_baton(); + baton->callback = Persistent::New(callback); + baton->input = std::string(*input); + baton->encrypted = std::string(*encrypted); + + uv_work_t* req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, CompareAsync, CompareAsyncAfter); + + return Undefined(); +} + +Handle CompareSync(const Arguments& args) { + HandleScope scope; + + String::Utf8Value pw(args[0]->ToString()); + String::Utf8Value hash(args[1]->ToString()); + + char bcrypted[_PASSWORD_LEN]; + bcrypt(*pw, *hash, bcrypted); + return Boolean::New(CompareStrings(bcrypted, *hash)); +} + +Handle GetRounds(const Arguments& args) { + HandleScope scope; + + String::Utf8Value hash(args[0]->ToString()); + u_int32_t rounds; + if (!(rounds = bcrypt_get_rounds(*hash))) { + return ThrowException(Exception::Error(String::New("invalid hash provided"))); + } + + return Integer::New(rounds); +} + +} // anonymous namespace + +// bind the bcrypt module +extern "C" void init(Handle target) { + HandleScope scope; + + crypto_lock_init(); + CRYPTO_set_locking_callback(crypto_lock_cb); + CRYPTO_set_id_callback(crypto_id_cb); + + NODE_SET_METHOD(target, "gen_salt_sync", GenerateSaltSync); + NODE_SET_METHOD(target, "encrypt_sync", EncryptSync); + NODE_SET_METHOD(target, "compare_sync", CompareSync); + NODE_SET_METHOD(target, "get_rounds", GetRounds); + NODE_SET_METHOD(target, "gen_salt", GenerateSalt); + NODE_SET_METHOD(target, "encrypt", Encrypt); + NODE_SET_METHOD(target, "compare", Compare); +}; + +NODE_MODULE(bcrypt_lib, init); diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/src/blowfish.cc b/dist/node_modules/ldapauth/node_modules/bcrypt/src/blowfish.cc new file mode 100644 index 0000000..5a5ca15 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/src/blowfish.cc @@ -0,0 +1,686 @@ +/* $OpenBSD: blowfish.c,v 1.18 2004/11/02 17:23:26 hshoexer Exp $ */ +/* + * Blowfish block cipher for OpenBSD + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Implementation advice by David Mazieres . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code is derived from section 14.3 and the given source + * in section V of Applied Cryptography, second edition. + * Blowfish is an unpatented fast block cipher designed by + * Bruce Schneier. + */ + +#if 0 +#include /* used for debugging */ +#include +#endif + +#include + +#include "node_blf.h" + +#undef inline +#ifdef __GNUC__ +#define inline __inline +#else /* !__GNUC__ */ +#define inline +#endif /* !__GNUC__ */ + +/* Function for Feistel Networks */ + +#define F(s, x) ((((s)[ (((x)>>24)&0xFF)] \ + + (s)[0x100 + (((x)>>16)&0xFF)]) \ + ^ (s)[0x200 + (((x)>> 8)&0xFF)]) \ + + (s)[0x300 + ( (x) &0xFF)]) + +#define BLFRND(s,p,i,j,n) (i ^= F(s,j) ^ (p)[n]) + +void +Blowfish_encipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) +{ + u_int32_t Xl; + u_int32_t Xr; + u_int32_t *s = c->S[0]; + u_int32_t *p = c->P; + + Xl = *xl; + Xr = *xr; + + Xl ^= p[0]; + BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2); + BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4); + BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6); + BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8); + BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10); + BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12); + BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14); + BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16); + + *xl = Xr ^ p[17]; + *xr = Xl; +} + +void +Blowfish_decipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) +{ + u_int32_t Xl; + u_int32_t Xr; + u_int32_t *s = c->S[0]; + u_int32_t *p = c->P; + + Xl = *xl; + Xr = *xr; + + Xl ^= p[17]; + BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15); + BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13); + BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11); + BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9); + BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7); + BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5); + BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3); + BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1); + + *xl = Xr ^ p[0]; + *xr = Xl; +} + +void +Blowfish_initstate(blf_ctx *c) +{ + /* P-box and S-box tables initialized with digits of Pi */ + + static const blf_ctx initstate = + { { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a}, + { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7}, + { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0}, + { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6} + }, + { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + } }; + + *c = initstate; +} + +u_int32_t +Blowfish_stream2word(const u_int8_t *data, u_int16_t databytes, + u_int16_t *current) +{ + u_int8_t i; + u_int16_t j; + u_int32_t temp; + + temp = 0x00000000; + j = *current; + + for (i = 0; i < 4; i++, j++) { + if (j >= databytes) + j = 0; + temp = (temp << 8) | data[j]; + } + + *current = j; + return temp; +} + +void +Blowfish_expand0state(blf_ctx *c, const u_int8_t *key, u_int16_t keybytes) +{ + u_int16_t i; + u_int16_t j; + u_int16_t k; + u_int32_t temp; + u_int32_t datal; + u_int32_t datar; + + j = 0; + for (i = 0; i < BLF_N + 2; i++) { + /* Extract 4 int8 to 1 int32 from keystream */ + temp = Blowfish_stream2word(key, keybytes, &j); + c->P[i] = c->P[i] ^ temp; + } + + j = 0; + datal = 0x00000000; + datar = 0x00000000; + for (i = 0; i < BLF_N + 2; i += 2) { + Blowfish_encipher(c, &datal, &datar); + + c->P[i] = datal; + c->P[i + 1] = datar; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + Blowfish_encipher(c, &datal, &datar); + + c->S[i][k] = datal; + c->S[i][k + 1] = datar; + } + } +} + + +void +Blowfish_expandstate(blf_ctx *c, const u_int8_t *data, u_int16_t databytes, + const u_int8_t *key, u_int16_t keybytes) +{ + u_int16_t i; + u_int16_t j; + u_int16_t k; + u_int32_t temp; + u_int32_t datal; + u_int32_t datar; + + j = 0; + for (i = 0; i < BLF_N + 2; i++) { + /* Extract 4 int8 to 1 int32 from keystream */ + temp = Blowfish_stream2word(key, keybytes, &j); + c->P[i] = c->P[i] ^ temp; + } + + j = 0; + datal = 0x00000000; + datar = 0x00000000; + for (i = 0; i < BLF_N + 2; i += 2) { + datal ^= Blowfish_stream2word(data, databytes, &j); + datar ^= Blowfish_stream2word(data, databytes, &j); + Blowfish_encipher(c, &datal, &datar); + + c->P[i] = datal; + c->P[i + 1] = datar; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + datal ^= Blowfish_stream2word(data, databytes, &j); + datar ^= Blowfish_stream2word(data, databytes, &j); + Blowfish_encipher(c, &datal, &datar); + + c->S[i][k] = datal; + c->S[i][k + 1] = datar; + } + } + +} + +void +blf_key(blf_ctx *c, const u_int8_t *k, u_int16_t len) +{ + /* Initialize S-boxes and subkeys with Pi */ + Blowfish_initstate(c); + + /* Transform S-boxes and subkeys with key */ + Blowfish_expand0state(c, k, len); +} + +void +blf_enc(blf_ctx *c, u_int32_t *data, u_int16_t blocks) +{ + u_int32_t *d; + u_int16_t i; + + d = data; + for (i = 0; i < blocks; i++) { + Blowfish_encipher(c, d, d + 1); + d += 2; + } +} + +void +blf_dec(blf_ctx *c, u_int32_t *data, u_int16_t blocks) +{ + u_int32_t *d; + u_int16_t i; + + d = data; + for (i = 0; i < blocks; i++) { + Blowfish_decipher(c, d, d + 1); + d += 2; + } +} + +void +blf_ecb_encrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i; + + for (i = 0; i < len; i += 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_encipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + data += 8; + } +} + +void +blf_ecb_decrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i; + + for (i = 0; i < len; i += 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + data += 8; + } +} + +void +blf_cbc_encrypt(blf_ctx *c, u_int8_t *iv, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i, j; + + for (i = 0; i < len; i += 8) { + for (j = 0; j < 8; j++) + data[j] ^= iv[j]; + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_encipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + iv = data; + data += 8; + } +} + +void +blf_cbc_decrypt(blf_ctx *c, u_int8_t *iva, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int8_t *iv; + u_int32_t i, j; + + iv = data + len - 16; + data = data + len - 8; + for (i = len - 8; i >= 8; i -= 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + for (j = 0; j < 8; j++) + data[j] ^= iv[j]; + iv -= 8; + data -= 8; + } + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + for (j = 0; j < 8; j++) + data[j] ^= iva[j]; +} + +#if 0 +void +report(u_int32_t data[], u_int16_t len) +{ + u_int16_t i; + for (i = 0; i < len; i += 2) + printf("Block %0hd: %08lx %08lx.\n", + i / 2, data[i], data[i + 1]); +} +void +main(void) +{ + + blf_ctx c; + char key[] = "AAAAA"; + char key2[] = "abcdefghijklmnopqrstuvwxyz"; + + u_int32_t data[10]; + u_int32_t data2[] = + {0x424c4f57l, 0x46495348l}; + + u_int16_t i; + + /* First test */ + for (i = 0; i < 10; i++) + data[i] = i; + + blf_key(&c, (u_int8_t *) key, 5); + blf_enc(&c, data, 5); + blf_dec(&c, data, 1); + blf_dec(&c, data + 2, 4); + printf("Should read as 0 - 9.\n"); + report(data, 10); + + /* Second test */ + blf_key(&c, (u_int8_t *) key2, strlen(key2)); + blf_enc(&c, data2, 1); + printf("\nShould read as: 0x324ed0fe 0xf413a203.\n"); + report(data2, 2); + blf_dec(&c, data2, 1); + report(data2, 2); +} +#endif diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/src/node_blf.h b/dist/node_modules/ldapauth/node_modules/bcrypt/src/node_blf.h new file mode 100644 index 0000000..ad674f0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/src/node_blf.h @@ -0,0 +1,111 @@ +/* $OpenBSD: blf.h,v 1.7 2007/03/14 17:59:41 grunk Exp $ */ +/* + * Blowfish - a fast block cipher designed by Bruce Schneier + * + * Copyright 1997 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NODE_BLF_H_ +#define _NODE_BLF_H_ + +/* Solaris compatibility */ +#ifdef __sun +#define u_int8_t uint8_t +#define u_int16_t uint16_t +#define u_int32_t uint32_t +#define u_int64_t uint64_t +#endif + +#ifdef _WIN32 +#define u_int8_t unsigned __int8 +#define u_int16_t unsigned __int16 +#define u_int32_t unsigned __int32 +#define u_int64_t unsigned __int64 +#endif + +#define BCRYPT_VERSION '2' +#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */ +#define BCRYPT_BLOCKS 6 /* Ciphertext blocks */ +#define BCRYPT_MINROUNDS 16 /* we have log2(rounds) in salt */ + +/* Schneier specifies a maximum key length of 56 bytes. + * This ensures that every key bit affects every cipher + * bit. However, the subkeys can hold up to 72 bytes. + * Warning: For normal blowfish encryption only 56 bytes + * of the key affect all cipherbits. + */ + +#define BLF_N 16 /* Number of Subkeys */ +#define BLF_MAXKEYLEN ((BLF_N-2)*4) /* 448 bits */ +#define BLF_MAXUTILIZED ((BLF_N+2)*4) /* 576 bits */ + +#define _PASSWORD_LEN 128 /* max length, not counting NUL */ +#define _SALT_LEN 32 /* max length */ + +/* Blowfish context */ +typedef struct BlowfishContext { + u_int32_t S[4][256]; /* S-Boxes */ + u_int32_t P[BLF_N + 2]; /* Subkeys */ +} blf_ctx; + +/* Raw access to customized Blowfish + * blf_key is just: + * Blowfish_initstate( state ) + * Blowfish_expand0state( state, key, keylen ) + */ + +void Blowfish_encipher(blf_ctx *, u_int32_t *, u_int32_t *); +void Blowfish_decipher(blf_ctx *, u_int32_t *, u_int32_t *); +void Blowfish_initstate(blf_ctx *); +void Blowfish_expand0state(blf_ctx *, const u_int8_t *, u_int16_t); +void Blowfish_expandstate +(blf_ctx *, const u_int8_t *, u_int16_t, const u_int8_t *, u_int16_t); + +/* Standard Blowfish */ + +void blf_key(blf_ctx *, const u_int8_t *, u_int16_t); +void blf_enc(blf_ctx *, u_int32_t *, u_int16_t); +void blf_dec(blf_ctx *, u_int32_t *, u_int16_t); + +void blf_ecb_encrypt(blf_ctx *, u_int8_t *, u_int32_t); +void blf_ecb_decrypt(blf_ctx *, u_int8_t *, u_int32_t); + +void blf_cbc_encrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); +void blf_cbc_decrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); + +/* Converts u_int8_t to u_int32_t */ +u_int32_t Blowfish_stream2word(const u_int8_t *, u_int16_t , u_int16_t *); + +/* bcrypt functions*/ +void bcrypt_gensalt(u_int8_t, u_int8_t*, char *); +void bcrypt(const char *, const char *, char *); +void encode_salt(char *, u_int8_t *, u_int16_t, u_int8_t); +u_int32_t bcrypt_get_rounds(const char *); + +#endif diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/test/async.js b/dist/node_modules/ldapauth/node_modules/bcrypt/test/async.js new file mode 100644 index 0000000..ff3e115 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/test/async.js @@ -0,0 +1,137 @@ +var bcrypt = require('../bcrypt'); + +module.exports = { + test_salt_length: function(assert) { + assert.expect(1); + bcrypt.genSalt(10, function(err, salt) { + assert.equals(29, salt.length, "Salt isn't the correct length."); + assert.done(); + }); + }, + test_salt_no_params: function(assert) { + assert.throws(function() {bcrypt.genSalt();}, "Should throw an Error. genSalt requires a callback."); + assert.done(); + }, + test_salt_only_cb: function(assert) { + assert.doesNotThrow(function() {bcrypt.genSalt(function(err, salt) {});}, "Should not throw an Error. Rounds and seed length are optional."); + assert.done(); + }, + test_salt_rounds_is_string_number: function(assert) { + assert.throws(function() {bcrypt.genSalt('10');}, "Should throw an Error. No params."); + assert.done(); + }, + test_salt_rounds_is_NaN: function(assert) { + assert.throws(function() {bcrypt.genSalt('b');}, "Should throw an Error. genSalt requires rounds to be a number."); + assert.done(); + }, + test_hash: function(assert) { + assert.expect(1); + bcrypt.genSalt(10, function(err, salt) { + bcrypt.hash('password', salt, function(err, res) { + assert.ok(res, "Res should be defined."); + assert.done(); + }); + }); + }, + test_hash_rounds: function(assert) { + assert.expect(1); + bcrypt.hash('bacon', 8, function(err, hash) { + assert.equals(bcrypt.getRounds(hash), 8, "Number of rounds should be that specified in the function call."); + assert.done(); + }); + }, + test_hash_empty_strings: function(assert) { + assert.expect(2); + bcrypt.genSalt(10, function(err, salt) { + bcrypt.hash('', salt, function(err, res) { + assert.ok(res, "Res should be defined even with an empty pw."); + bcrypt.hash('', '', function(err, res) { + if (err) { + assert.ok(err); + } else { + assert.fail(); + } + + assert.done(); + }); + }); + }); + }, + test_hash_no_params: function(assert) { + assert.throws(function() {bcrypt.hash();}, "Should throw an Error. No Params."); + assert.done(); + }, + test_hash_one_param: function(assert) { + assert.throws(function() {bcrypt.hash('password');}, "Should throw an Error. No salt."); + assert.done(); + }, + test_hash_not_hash_str: function(assert) { + assert.throws(function() {bcrypt.hash('password', 1);}, "Should throw an Error. hash should be a string."); + assert.done(); + }, + test_hash_salt_validity: function(assert) { + assert.expect(3); + bcrypt.hash('password', '$2a$10$somesaltyvaluertsetrse', function(err, enc) { + assert.equal(err, undefined); + bcrypt.hash('password', 'some$value', function(err, enc) { + assert.notEqual(err, undefined); + assert.equal(err.message, "Invalid salt. Salt must be in the form of: $Vers$log2(NumRounds)$saltvalue"); + assert.done(); + }); + }); + }, + test_verify_salt: function(assert) { + assert.expect(2); + bcrypt.genSalt(10, function(err, salt) { + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '10'); + assert.done(); + }); + }, + test_verify_salt_min_rounds: function(assert) { + assert.expect(2); + bcrypt.genSalt(1, function(err, salt) { + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '4'); + assert.done(); + }); + }, + test_verify_salt_max_rounds: function(assert) { + assert.expect(2); + bcrypt.genSalt(100, function(err, salt) { + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '31'); + assert.done(); + }); + }, + test_hash_compare: function(assert) { + assert.expect(3); + bcrypt.genSalt(10, function(err, salt) { + assert.equals(29, salt.length, "Salt isn't the correct length."); + bcrypt.hash("test", salt, function(err, hash) { + bcrypt.compare("test", hash, function(err, res) { + assert.equal(res, true, "These hashes should be equal."); + bcrypt.compare("blah", hash, function(err, res) { + assert.equal(res, false, "These hashes should not be equal."); + assert.done(); + }); + }); + }); + }); + }, + test_hash_compare_empty_strings: function(assert) { + assert.expect(2); + var hash = bcrypt.hashSync("test", bcrypt.genSaltSync(10)); + + bcrypt.compare("", hash, function(err, res) { + assert.equal(res, false, "These hashes should be equal."); + bcrypt.compare("", "", function(err, res) { + assert.equal(res, false, "These hashes should be equal."); + assert.done(); + }); + }); + } +}; diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/test/deprecated.js b/dist/node_modules/ldapauth/node_modules/bcrypt/test/deprecated.js new file mode 100644 index 0000000..0802bd9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/test/deprecated.js @@ -0,0 +1,34 @@ +var bcrypt = require('../bcrypt'); + +module.exports = { + test_encrypt: function(assert) { + assert.expect(1); + bcrypt.gen_salt(10, function(err, salt) { + bcrypt.encrypt('password', salt, function(err, res) { + assert.ok(res, "Res should be defined."); + assert.done(); + }); + }); + }, + test_gen_salt_sync: function(assert) { + var salt = bcrypt.gen_salt_sync(10); + assert.equals(29, salt.length, "Salt isn't the correct length."); + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '10'); + assert.done(); + }, + test_encrypt_compare_sync: function(assert) { + var salt = bcrypt.gen_salt_sync(10); + assert.equals(29, salt.length, "Salt isn't the correct length."); + var hash = bcrypt.encrypt_sync("test", salt); + assert.ok(bcrypt.compare_sync("test", hash), "These hashes should be equal."); + assert.ok(!(bcrypt.compare_sync("blah", hash)), "These hashes should not be equal."); + assert.done(); + }, + test_get_rounds_sync: function(assert) { + var hash = bcrypt.encrypt_sync("test", bcrypt.gen_salt_sync(9)); + assert.equals(9, bcrypt.get_rounds(hash), "get_rounds can't extract rounds"); + assert.done(); + } +} diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/test/repetitions.js b/dist/node_modules/ldapauth/node_modules/bcrypt/test/repetitions.js new file mode 100644 index 0000000..f05b716 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/test/repetitions.js @@ -0,0 +1,118 @@ +var bcrypt = require('../bcrypt'); + +var EXPECTED = 2500; //number of times to iterate these tests... + +module.exports = { + test_salt_length: function(assert) { + assert.expect(EXPECTED); + var n = 0; + for (var i = 0; i < EXPECTED; i++) { + bcrypt.genSalt(10, function(err, salt) { + assert.equals(29, salt.length, "Salt ("+salt+") isn't the correct length. It is: " + salt.length); + n++; + }); + } + + function checkVal() { + if (n == EXPECTED) { + assert.done(); + } else { + setTimeout(checkVal, 100); + } + } + setTimeout(checkVal, 100); + }, + test_hash_length: function(assert) { + assert.expect(EXPECTED); + var SALT = '$2a$04$TnjywYklQbbZjdjBgBoA4e'; + var n = 0; + for (var i = 0; i < EXPECTED; i++) { + bcrypt.hash('test', SALT, function(err, crypted) { + assert.equals(60, crypted.length, "Encrypted ("+crypted+") isn't the correct length. It is: " + crypted.length); + n++; + }); + } + + function checkVal() { + if (n == EXPECTED) { + assert.done(); + } else { + setTimeout(checkVal, 100); + } + } + setTimeout(checkVal, 100); + }, + test_compare: function(assert) { + assert.expect(EXPECTED); + var HASH = '$2a$04$TnjywYklQbbZjdjBgBoA4e9G7RJt9blgMgsCvUvus4Iv4TENB5nHy'; + var n = 0; + for (var i = 0; i < EXPECTED; i++) { + bcrypt.compare('test', HASH, function(err, match) { + assert.equal(true, match, "No match."); + n++; + }); + } + + function checkVal() { + if (n == EXPECTED) { + assert.done(); + } else { + setTimeout(checkVal, 100); + } + } + setTimeout(checkVal, 100); + }, + test_hash_and_compare: function(assert) { + assert.expect((EXPECTED-1)*3); + var salt = bcrypt.genSaltSync(4), + idx = 0, + good_done = false, + bad_done = false; + + function next() { + return test('secret' + Math.random()); + } + + function test(password) { + idx += 1; + return bcrypt.hash(password, salt, function(err, hash) { + if (err) throw err; + //console.log('\nbcrypt iter ' + idx); + + assert.ok(hash); + + bcrypt.compare(password, hash, function(err, res) { + //if (err) throw err; + assert.ok(res); + if (idx >= (EXPECTED-1)) { + good_done = true; + } + }); + + bcrypt.compare('bad' + password, hash, function(err, res) { + //if (err) throw err; + assert.ok(!res); + if (idx >= (EXPECTED-1)) { + bad_done = true; + } + }); + + if (idx < ((EXPECTED)-1)) { + next(); + } else { + function checkDone() { + if (idx >= (EXPECTED-1) && good_done && bad_done) { + assert.done(); + } else { + setTimeout(checkDone, 100); + } + } + + setTimeout(checkDone, 100); + } + }); + } + + next(); + } +}; diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/test/sync.js b/dist/node_modules/ldapauth/node_modules/bcrypt/test/sync.js new file mode 100644 index 0000000..60cd576 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/test/sync.js @@ -0,0 +1,109 @@ +var bcrypt = require('../bcrypt'); + +module.exports = { + test_salt_length: function(assert) { + var salt = bcrypt.genSaltSync(10); + assert.equals(29, salt.length, "Salt isn't the correct length."); + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '10'); + assert.done(); + }, + test_salt_no_params: function(assert) { + // same as test_verify_salt except using default rounds of 10 + var salt = bcrypt.genSaltSync(); + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '10'); + assert.done(); + }, + test_salt_rounds_is_string_number: function(assert) { + assert.throws(function() {bcrypt.genSaltSync('10');}, "Should throw an Error. No params."); + assert.done(); + }, + test_salt_rounds_is_NaN: function(assert) { + assert.throws(function() {bcrypt.genSaltSync('b');}, "Should throw an Error. gen_salt requires rounds to be a number."); + assert.done(); + }, + test_hash: function(assert) { + assert.ok(bcrypt.hashSync('password', bcrypt.genSaltSync(10)), "Shouldn't throw an Error."); + assert.done(); + }, + test_hash_rounds: function(assert) { + var hash = bcrypt.hashSync('password', 8); + assert.equals(bcrypt.getRounds(hash), 8, "Number of rounds should equal 8."); + assert.done(); + }, + test_hash_empty_string: function(assert) { + assert.ok(bcrypt.hashSync('', bcrypt.genSaltSync(10)), "Shouldn't throw an Error."); + assert.throws(function() {bcrypt.hashSync('password', '')}, "Should have thrown an Error related to the salt."); + assert.throws(function() {bcrypt.hashSync('', '')}, "Should have thrown an Error related to the salt."); + assert.done(); + }, + test_hash_pw_no_params: function(assert) { + assert.throws(function() {bcrypt.hashSync();}, "Should throw an Error. No Params."); + assert.done(); + }, + test_hash_pw_one_param: function(assert) { + assert.throws(function() {bcrypt.hashSync('password');}, "Should throw an Error. No salt."); + assert.done(); + }, + test_hash_pw_not_hash_str: function(assert) { + assert.throws(function() {bcrypt.hashSync('password', {});}, "Should throw an Error. hash should be a string or number."); + assert.done(); + }, + test_hash_salt_validity: function(assert) { + assert.expect(2); + assert.ok(bcrypt.hashSync('password', '$2a$10$somesaltyvaluertsetrse')); + assert.throws(function() { + bcrypt.hashSync('password', 'some$value'); + }); + assert.done(); + }, + test_verify_salt: function(assert) { + var salt = bcrypt.genSaltSync(10); + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '10'); + assert.done(); + }, + test_verify_salt_min_rounds: function(assert) { + var salt = bcrypt.genSaltSync(1); + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '4'); + assert.done(); + }, + test_verify_salt_max_rounds: function(assert) { + var salt = bcrypt.genSaltSync(100); + var split_salt = salt.split('$'); + assert.ok(split_salt[1], '2a'); + assert.ok(split_salt[2], '31'); + assert.done(); + }, + test_hash_compare: function(assert) { + var salt = bcrypt.genSaltSync(10); + assert.equals(29, salt.length, "Salt isn't the correct length."); + var hash = bcrypt.hashSync("test", salt); + assert.ok(bcrypt.compareSync("test", hash), "These hashes should be equal."); + assert.ok(!(bcrypt.compareSync("blah", hash)), "These hashes should not be equal."); + assert.done(); + }, + test_hash_compare_empty_strings: function(assert) { + assert.ok(!(bcrypt.compareSync("", "password")), "These hashes should not be equal."); + assert.ok(!(bcrypt.compareSync("", "")), "These hashes should not be equal."); + assert.ok(!(bcrypt.compareSync("password", "")), "These hashes should not be equal."); + assert.done(); + }, + test_getRounds: function(assert) { + var hash = bcrypt.hashSync("test", bcrypt.genSaltSync(9)); + assert.equals(9, bcrypt.getRounds(hash), "getRounds can't extract rounds"); + assert.done(); + }, + test_getRounds: function(assert) { + var hash = bcrypt.hashSync("test", bcrypt.genSaltSync(9)); + assert.equals(9, bcrypt.getRounds(hash), "getRounds can't extract rounds"); + assert.throws(function() {bcrypt.getRounds(''); }, "Must pass a valid hash to getRounds"); + assert.done(); + } +}; diff --git a/dist/node_modules/ldapauth/node_modules/bcrypt/wscript b/dist/node_modules/ldapauth/node_modules/bcrypt/wscript new file mode 100644 index 0000000..7da36fd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/bcrypt/wscript @@ -0,0 +1,60 @@ +# -*- mode: python -*- + +import Options, Utils, sys, re, os + +srcdir = "." +blddir = "build" +VERSION = "0.0.1" +node_version = os.popen("node --version").read() + +def set_options(opt): + opt.tool_options("compiler_cxx") + +def configure(conf): + conf.check_tool("compiler_cxx") + conf.check_tool("node_addon") + o = Options.options + + nodepath = 'NODE_PATH' in os.environ and os.environ['NODE_PATH'] or None + + libpath = ['/lib', '/usr/lib', '/usr/local/lib', '/opt/local/lib', '/usr/sfw/lib'] + if nodepath: + libpath.append(nodepath) + includes = ['/usr/include', '/usr/includes', '/usr/local/includes', '/opt/local/includes', '/usr/sfw/lib']; + + libssl = conf.check(lib="ssl", + header_name='openssl/rand.h', + includes=includes, + libpath=libpath, + mandatory=True, + uselib_store='OPENSSL') + + if sys.platform == "cygwin": + libcrypto = conf.check(lib="crypto", + includes=includes, + libpath=libpath, + uselib_store='CRYPTO') + libz = conf.check(lib="z", + includes=includes, + libpath=libpath, + uselib_store='Z') + +def build(bld): + bcryptnode = bld.new_task_gen("cxx", "shlib", "node_addon") + cxxflags = [ "-O3" ] + if not node_version.startswith("v0.4"): + cxxflags.append("-DEV_MULTIPLICITY=1") + bcryptnode.cxxflags = cxxflags + bcryptnode.target = "bcrypt_lib" + bcryptnode.source = """ + src/blowfish.cc + src/bcrypt.cc + src/bcrypt_node.cc + """ + uselib = "OPENSSL" + if sys.platform == "cygwin": + uselib += " CRYPTO Z" + bcryptnode.uselib = uselib + +def test(t): + Utils.exec_command('make test') diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/.dir-locals.el b/dist/node_modules/ldapauth/node_modules/ldapjs/.dir-locals.el new file mode 100644 index 0000000..7f37d53 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/.dir-locals.el @@ -0,0 +1,6 @@ +((nil . ((indent-tabs-mode . nil) + (tab-width . 8) + (fill-column . 80))) + (js-mode . ((js-indent-level . 2) + (indent-tabs-mode . nil) + ))) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/.gitmodules b/dist/node_modules/ldapauth/node_modules/ldapjs/.gitmodules new file mode 100644 index 0000000..8ec36e0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/.gitmodules @@ -0,0 +1,9 @@ +[submodule "deps/javascriptlint"] + path = deps/javascriptlint + url = https://github.com/davepacheco/javascriptlint +[submodule "deps/jsstyle"] + path = deps/jsstyle + url = https://github.com/davepacheco/jsstyle +[submodule "deps/restdown"] + path = deps/restdown + url = https://github.com/trentm/restdown diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/.npmignore new file mode 100644 index 0000000..c71239f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/.npmignore @@ -0,0 +1,5 @@ +build +node_modules +*.log +*.ldif +*.tar.* diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/.travis.yml b/dist/node_modules/ldapauth/node_modules/ldapjs/.travis.yml new file mode 100644 index 0000000..2d26206 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.6 diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/LICENSE b/dist/node_modules/ldapauth/node_modules/ldapjs/LICENSE new file mode 100644 index 0000000..9b5dcdb --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Mark Cavage, All rights reserved. + +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/dist/node_modules/ldapauth/node_modules/ldapjs/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/Makefile new file mode 100644 index 0000000..c5bdca9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/Makefile @@ -0,0 +1,62 @@ +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile: basic Makefile for template API service +# +# This Makefile is a template for new repos. It contains only repo-specific +# logic and uses included makefiles to supply common targets (javascriptlint, +# jsstyle, restdown, etc.), which are used by other repos as well. You may well +# need to rewrite most of this file, but you shouldn't need to touch the +# included makefiles. +# +# If you find yourself adding support for new targets that could be useful for +# other projects too, you should add these to the original versions of the +# included Makefiles (in eng.git) so that other teams can use them too. +# + +# +# Tools +# +NPM := npm +TAP := ./node_modules/.bin/tap + +# +# Files +# +DOC_FILES = client.md \ + dn.md \ + errors.md \ + examples.md \ + filters.md \ + guide.md \ + index.md \ + persistent_search.md \ + server.md + +JS_FILES := $(shell find lib test -name '*.js') +JSL_CONF_NODE = tools/jsl.node.conf +JSL_FILES_NODE = $(JS_FILES) +JSSTYLE_FILES = $(JS_FILES) +JSSTYLE_FLAGS = -f tools/jsstyle.conf + +CLEAN_FILES += node_modules $(SHRINKWRAP) cscope.files + +include ./tools/mk/Makefile.defs + +# Repo-specific targets +# +.PHONY: all +all: $(TAP) $(REPO_DEPS) + $(NPM) rebuild + +$(TAP): | $(NPM_EXEC) + $(NPM) install + +CLEAN_FILES += $(TAP) ./node_modules/tap + +.PHONY: test +test: $(TAP) + $(TAP) test/*.test.js + +include ./tools/mk/Makefile.deps +include ./tools/mk/Makefile.targ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/README.md new file mode 100644 index 0000000..0adc28c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/README.md @@ -0,0 +1,45 @@ +ldapjs makes the LDAP protocol a first class citizen in Node.js. + +## Usage + +For full docs, head on over to . + + var ldap = require('ldapjs'); + + var server = ldap.createServer(); + + server.search('dc=example', function(req, res, next) { + var obj = { + dn: req.dn.toString(), + attributes: { + objectclass: ['organization', 'top'], + o: 'example' + } + }; + + if (req.filter.matches(obj.attributes)) + res.send(obj); + + res.end(); + }); + + server.listen(1389, function() { + console.log('ldapjs listening at ' + server.url); + }); + +To run that, assuming you've got the [OpenLDAP](http://www.openldap.org/) client +on your system: + + ldapsearch -H ldap://localhost:1389 -x -b dc=example objectclass=* + +## Installation + + npm install ldapjs + +## License + +MIT. + +## Bugs + +See . diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-add b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-add new file mode 100755 index 0000000..ebd1507 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-add @@ -0,0 +1,150 @@ +#!/usr/bin/env node +// -*- mode: js -*- +// Copyright 2011 Mark Cavage. All rights reserved. + +var fs = require('fs'); +var path = require('path'); +var url = require('url'); + +var nopt = require('nopt'); + +var ldap = require('../lib/index'); + + + +///--- Globals + +var log4js = ldap.log4js; + +nopt.typeDefs.DN = { + type: ldap.DN, + validate: function(data, k, val) { + data[k] = ldap.parseDN(val); + } +}; + +var opts = { + 'debug': Number, + 'binddn': ldap.DN, + 'file': String, + 'password': String, + 'url': url +}; + +var shortOpts = { + 'd': ['--debug'], + 'D': ['--binddn'], + 'f': ['--file'], + 'w': ['--password'], + 'u': ['--url'] +}; + + + +///--- Helpers + +function usage(code, message) { + var _opts = ''; + Object.keys(shortOpts).forEach(function(k) { + if (!Array.isArray(shortOpts[k])) + return; + var longOpt = shortOpts[k][0].replace('--', ''); + var type = opts[longOpt].name || 'string'; + if (type && type === 'boolean') type = ''; + type = type.toLowerCase(); + + _opts += ' [--' + longOpt + ' ' + type + ']'; + }); + _opts += ' [JSON]'; + + var msg = (message ? message + '\n' : '') + + 'usage: ' + path.basename(process.argv[1]) + _opts; + + process.stderr.write(msg + '\n'); + process.exit(code); +} + + +function perror(err) { + if (parsed.debug) { + process.stderr.write(err.stack + '\n'); + } else { + process.stderr.write(err.message + '\n'); + } + process.exit(1); +} + + + +///--- Mainline + +log4js.setGlobalLogLevel('INFO'); +var parsed; + +try { + parsed = nopt(opts, shortOpts, process.argv, 2); + if (parsed.file) { + parsed.file = JSON.parse(fs.readFileSync(parsed.file, 'utf8')); + if (!Array.isArray(parsed.file)) + parsed.file = [parsed.file]; + } +} catch (e) { + usage(1, e.toString()); +} + +if (parsed.help) + usage(0); +if (!parsed.file) { + parsed.file = []; + parsed.argv.remain.forEach(function(a) { + var o =JSON.parse(a); + if (Array.isArray(o)) { + o.forEach(function(i) { + parsed.file.push(o); + }); + return; + } + parsed.file.push(o); + }); +} + +if (!parsed.file) + usage(1, 'either -f or stdin must be used for adding objects'); + +if (parsed.debug) + log4js.setGlobalLogLevel(parsed.debug > 1 ? 'TRACE' : 'DEBUG'); +if (!parsed.url) + parsed.url = 'ldap://127.0.0.1:389'; +if (!parsed.binddn) + parsed.binddn = ''; +if (!parsed.password) + parsed.password = ''; + +var client = ldap.createClient({ + url: parsed.url, + log4js: log4js +}); + +client.on('error', function(err) { + perror(err); +}); + +client.bind(parsed.binddn, parsed.password, function(err, res) { + if (err) + perror(err); + + var finished = 0; + function callback(err) { + if (err) + perror(err); + + if (++finished === parsed.file.length) + client.unbind(function () { return; }); + } + + parsed.file.forEach(function(entry) { + var dn = entry.dn; + delete entry.dn; + client.add(dn, entry, callback); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-compare b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-compare new file mode 100755 index 0000000..9a961f1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-compare @@ -0,0 +1,147 @@ +#!/usr/bin/env node +// -*- mode: js -*- +// Copyright 2011 Mark Cavage. All rights reserved. + +var fs = require('fs'); +var path = require('path'); +var url = require('url'); + +var nopt = require('nopt'); + +var ldap = require('../lib/index'); + + + +///--- Globals + +var log4js = ldap.log4js; + +nopt.typeDefs.DN = { + type: ldap.DN, + validate: function(data, k, val) { + data[k] = ldap.parseDN(val); + } +}; + +var opts = { + 'attribute': String, + 'debug': Number, + 'binddn': ldap.DN, + 'file': String, + 'password': String, + 'url': url, + 'value': String, +}; + +var shortOpts = { + 'a': ['--attribute'], + 'd': ['--debug'], + 'D': ['--binddn'], + 'f': ['--file'], + 'w': ['--password'], + 'u': ['--url'], + 'v': ['--value'] +}; + + + +///--- Helpers + +function usage(code, message) { + var _opts = ''; + Object.keys(shortOpts).forEach(function(k) { + if (!Array.isArray(shortOpts[k])) + return; + var longOpt = shortOpts[k][0].replace('--', ''); + var type = opts[longOpt].name || 'string'; + if (type && type === 'boolean') type = ''; + type = type.toLowerCase(); + + _opts += ' [--' + longOpt + ' ' + type + ']'; + }); + _opts += ' DN'; + + var msg = (message ? message + '\n' : '') + + 'usage: ' + path.basename(process.argv[1]) + _opts; + + process.stderr.write(msg + '\n'); + process.exit(code); +} + + +function perror(err) { + if (parsed.debug) { + process.stderr.write(err.stack + '\n'); + } else { + process.stderr.write(err.message + '\n'); + } + process.exit(1); +} + + + +///--- Mainline + +log4js.setGlobalLogLevel('INFO'); +var parsed; + +try { + parsed = nopt(opts, shortOpts, process.argv, 2); +} catch (e) { + usage(1, e.toString()); +} + +if (parsed.help) + usage(0); + +if (parsed.argv.remain.length < 1) + usage(1, 'DN required'); +try { + parsed.argv.remain.forEach(function(dn) { + ldap.parseDN(dn); + }); +} catch (e) { + usage(1, e.toString()); +} + +if (!parsed.attribute || typeof(parsed.value) !== 'string') + usage(1, 'attribute and value required'); + +if (parsed.debug) + log4js.setGlobalLogLevel(parsed.debug > 1 ? 'TRACE' : 'DEBUG'); +if (!parsed.url) + parsed.url = 'ldap://127.0.0.1:389'; +if (!parsed.binddn) + parsed.binddn = ''; +if (!parsed.password) + parsed.password = ''; + +var client = ldap.createClient({ + url: parsed.url, + log4js: log4js +}); + +client.on('error', function(err) { + perror(err); +}); + +client.bind(parsed.binddn, parsed.password, function(err, res) { + if (err) + perror(err); + + var finished = 0; + parsed.argv.remain.forEach(function(dn) { + client.compare(dn, parsed.attribute, parsed.value, function(err, match) { + if (err) + perror(err); + + process.stdout.write(match + '\n'); + + if (++finished === parsed.argv.remain.length) { + client.unbind(function() { + return; + }); + } + }); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-delete b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-delete new file mode 100755 index 0000000..d5afa70 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-delete @@ -0,0 +1,134 @@ +#!/usr/bin/env node +// -*- mode: js -*- +// Copyright 2011 Mark Cavage. All rights reserved. + +var fs = require('fs'); +var path = require('path'); +var url = require('url'); + +var nopt = require('nopt'); + +var ldap = require('../lib/index'); + + + +///--- Globals + +var log4js = ldap.log4js; + +nopt.typeDefs.DN = { + type: ldap.DN, + validate: function(data, k, val) { + data[k] = ldap.parseDN(val); + } +}; + +var opts = { + 'debug': Number, + 'binddn': ldap.DN, + 'password': String, + 'url': url +}; + +var shortOpts = { + 'd': ['--debug'], + 'D': ['--binddn'], + 'w': ['--password'], + 'u': ['--url'] +}; + + + +///--- Helpers + +function usage(code, message) { + var _opts = ''; + Object.keys(shortOpts).forEach(function(k) { + if (!Array.isArray(shortOpts[k])) + return; + var longOpt = shortOpts[k][0].replace('--', ''); + var type = opts[longOpt].name || 'string'; + if (type && type === 'boolean') type = ''; + type = type.toLowerCase(); + + _opts += ' [--' + longOpt + ' ' + type + ']'; + }); + _opts += ' DN'; + + var msg = (message ? message + '\n' : '') + + 'usage: ' + path.basename(process.argv[1]) + _opts; + + process.stderr.write(msg + '\n'); + process.exit(code); +} + + +function perror(err) { + if (parsed.debug) { + process.stderr.write(err.stack + '\n'); + } else { + process.stderr.write(err.message + '\n'); + } + process.exit(1); +} + + + +///--- Mainline + +log4js.setGlobalLogLevel('INFO'); +var parsed; + +try { + parsed = nopt(opts, shortOpts, process.argv, 2); +} catch (e) { + usage(1, e.toString()); +} + +if (parsed.help) + usage(0); +if (parsed.argv.remain.length < 1) + usage(1, 'DN required'); +try { + parsed.argv.remain.forEach(function(dn) { + ldap.parseDN(dn); + }); +} catch (e) { + usage(1, e.toString()); +} + +if (parsed.debug) + log4js.setGlobalLogLevel(parsed.debug > 1 ? 'TRACE' : 'DEBUG'); +if (!parsed.url) + parsed.url = 'ldap://127.0.0.1:389'; +if (!parsed.binddn) + parsed.binddn = ''; +if (!parsed.password) + parsed.password = ''; + +var client = ldap.createClient({ + url: parsed.url, + log4js: log4js +}); + +client.on('error', function(err) { + perror(err); +}); + +client.bind(parsed.binddn, parsed.password, function(err, res) { + if (err) + perror(err); + + var finished = 0; + function callback(err) { + if (err) + perror(err); + + if (++finished === parsed.argv.remain.length) + client.unbind(function () { return; }); + } + + parsed.argv.remain.forEach(function(dn) { + client.del(dn, callback); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-modify b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-modify new file mode 100755 index 0000000..cca7338 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-modify @@ -0,0 +1,161 @@ +#!/usr/bin/env node +// -*- mode: js -*- +// Copyright 2011 Mark Cavage. All rights reserved. + +var fs = require('fs'); +var path = require('path'); +var url = require('url'); + +var nopt = require('nopt'); + +var ldap = require('../lib/index'); + + + +///--- Globals + +var log4js = ldap.log4js; + +nopt.typeDefs.DN = { + type: ldap.DN, + validate: function(data, k, val) { + data[k] = ldap.parseDN(val); + } +}; + +var opts = { + 'attribute': String, + 'debug': Number, + 'binddn': ldap.DN, + 'file': String, + 'password': String, + 'type': String, + 'url': url, + 'value': Array, +}; + +var shortOpts = { + 'a': ['--attribute'], + 'd': ['--debug'], + 'D': ['--binddn'], + 'f': ['--file'], + 'w': ['--password'], + 't': ['--type'], + 'u': ['--url'], + 'v': ['--value'] +}; + + + +///--- Helpers + +function usage(code, message) { + var _opts = ''; + Object.keys(shortOpts).forEach(function(k) { + if (!Array.isArray(shortOpts[k])) + return; + var longOpt = shortOpts[k][0].replace('--', ''); + var type = opts[longOpt].name || 'string'; + if (type && type === 'boolean') type = ''; + type = type.toLowerCase(); + + _opts += ' [--' + longOpt + ' ' + type + ']'; + }); + _opts += ' DN'; + + var msg = (message ? message + '\n' : '') + + 'usage: ' + path.basename(process.argv[1]) + _opts; + + process.stderr.write(msg + '\n'); + process.exit(code); +} + + +function perror(err) { + if (parsed.debug) { + process.stderr.write(err.stack + '\n'); + } else { + process.stderr.write(err.message + '\n'); + } + process.exit(1); +} + + + +///--- Mainline + +log4js.setGlobalLogLevel('INFO'); +var parsed; + +try { + parsed = nopt(opts, shortOpts, process.argv, 2); +} catch (e) { + usage(1, e.toString()); +} + +if (parsed.help) + usage(0); + +if (parsed.argv.remain.length < 1) + usage(1, 'DN required'); +try { + parsed.argv.remain.forEach(function(dn) { + ldap.parseDN(dn); + }); +} catch (e) { + usage(1, e.toString()); +} + +if (!parsed.type) + parsed.type = 'replace'; +if (!parsed.attribute || !Array.isArray(parsed.value)) + usage(1, 'attribute and value required'); + +if (parsed.debug) + log4js.setGlobalLogLevel(parsed.debug > 1 ? 'TRACE' : 'DEBUG'); +if (!parsed.url) + parsed.url = 'ldap://127.0.0.1:389'; +if (!parsed.binddn) + parsed.binddn = ''; +if (!parsed.password) + parsed.password = ''; + +var client = ldap.createClient({ + url: parsed.url, + log4js: log4js +}); + +client.on('error', function(err) { + perror(err); +}); + +client.bind(parsed.binddn, parsed.password, function(err, res) { + if (err) + perror(err); + + var finished = 0; + var mod = {}; + mod[parsed.attribute] = []; + parsed.value.forEach(function(v) { + mod[parsed.attribute].push(v); + }); + var change = new ldap.Change({ + type: parsed.type, + modification: mod + }); + + function callback(err) { + if (err) + perror(err); + + if (++finished === parsed.argv.remain.length) { + client.unbind(function() { + return; + }); + } + } + + parsed.argv.remain.forEach(function(dn) { + client.modify(dn, change, callback); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-search b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-search new file mode 100755 index 0000000..66853bd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/bin/ldapjs-search @@ -0,0 +1,198 @@ +#!/usr/bin/env node +// -*- mode: js -*- +// Copyright 2011 Mark Cavage. All rights reserved. + +var path = require('path'); +var url = require('url'); + +var nopt = require('nopt'); + +var ldap = require('../lib/index'); + + + +///--- Globals + +var log4js = ldap.log4js; + +nopt.typeDefs.DN = { + type: ldap.DN, + validate: function(data, k, val) { + data[k] = ldap.parseDN(val); + } +}; + +nopt.typeDefs.Filter = { + type: ldap.Filter, + validate: function(data, k, val) { + data[k] = ldap.parseFilter(val); + } +}; + + +var opts = { + 'debug': Number, + 'base': ldap.DN, + 'binddn': ldap.DN, + 'control': Array, + 'password': String, + 'persistent': Boolean, + 'paged': Number, + 'scope': String, + 'timeout': Number, + 'url': url +}; + +var shortOpts = { + 'c': ['--control'], + 'd': ['--debug'], + 'b': ['--base'], + 'D': ['--binddn'], + 'w': ['--password'], + 'p': ['--persistent'], + 'g': ['--paged'], + 's': ['--scope'], + 't': ['--timeout'], + 'u': ['--url'] +}; + + + +///--- Helpers + +function usage(code, message) { + var _opts = ''; + Object.keys(shortOpts).forEach(function(k) { + if (!Array.isArray(shortOpts[k])) + return; + var longOpt = shortOpts[k][0].replace('--', ''); + var type = opts[longOpt].name || 'string'; + if (type && type === 'boolean') type = ''; + type = type.toLowerCase(); + + _opts += ' [--' + longOpt + ' ' + type + ']'; + }); + _opts += ' filter [attributes...]'; + + var msg = (message ? message + '\n' : '') + + 'usage: ' + path.basename(process.argv[1]) + _opts; + + process.stderr.write(msg + '\n'); + process.exit(code); +} + + +function perror(err) { + if (parsed.debug) { + process.stderr.write(err.stack + '\n'); + } else { + process.stderr.write(err.message + '\n'); + } + process.exit(1); +} + + + +///--- Mainline + +log4js.setGlobalLogLevel('INFO'); +var parsed; + +try { + parsed = nopt(opts, shortOpts, process.argv, 2); +} catch (e) { + usage(1, e.toString()); +} + +if (parsed.help) + usage(0); +if (parsed.argv.remain.length < 1) + usage(1, 'filter required'); + +try { + ldap.parseFilter(parsed.argv.remain[0]); +} catch (e) { + usage(1, e.message); +} + + +if (parsed.debug) + log4js.setGlobalLogLevel(parsed.debug > 1 ? 'TRACE' : 'DEBUG'); +if (!parsed.url) + parsed.url = 'ldap://127.0.0.1:389'; +if (!parsed.binddn) + parsed.binddn = ''; +if (!parsed.password) + parsed.password = ''; +if (!parsed.base) + parsed.base = ''; +if (!parsed.control) + parsed.control = []; +if (!parsed.persistent) + parsed.persistent = false; + +var client = ldap.createClient({ + url: parsed.url, + log4js: log4js, + timeout: parsed.timeout || false +}); + +client.on('error', function(err) { + perror(err); +}); + +client.on('timeout', function(req) { + process.stderr.write('Timeout reached\n'); + process.exit(1); +}); + +client.bind(parsed.binddn, parsed.password, function(err, res) { + if (err) + perror(err); + + var controls = []; + parsed.control.forEach(function(c) { + controls.push(new ldap.Control({ + type: c, + criticality: true + })); + }); + if (parsed.persistent) { + var pCtrl = new ldap.PersistentSearchControl({ + type: '2.16.840.1.113730.3.4.3', + value: { + changeTypes: 15, + changesOnly: false, + returnECs: true + } + }); + controls.push(pCtrl); + } + if (parsed.paged) { + var ctrl = new ldap.PagedResultsControl({ value: { size: parsed.paged } }); + controls.push(ctrl); + } + var req = { + scope: parsed.scope || 'sub', + filter: parsed.argv.remain[0], + attributes: parsed.argv.remain.length > 1 ? parsed.argv.remain.slice(1) : [] + }; + client.search(parsed.base, req, controls, function(err, res) { + if (err) + perror(err); + + res.on('searchEntry', function(entry) { + process.stdout.write(JSON.stringify(entry.object, null, 2) + '\n'); + }); + res.on('error', function(err) { + perror(err); + }); + res.on('end', function(res) { + if (res.status !== 0) + process.stderr.write(ldap.getMessage(res.status) + '\n'); + client.unbind(function() { + return; + }); + }); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/.npmignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/.npmignore @@ -0,0 +1 @@ +build diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/COPYING b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/Makefile new file mode 100644 index 0000000..398c106 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/Makefile @@ -0,0 +1,68 @@ +BUILDOS=$(shell uname -s) +BUILDDIR = build +INSTALLDIRS = \ + $(BUILDDIR)/install \ + $(BUILDDIR)/install/javascriptlint \ + +CSRCS = \ + nodepos.c \ + pyspidermonkey.c + +OBJECTS = $(CSRCS:%.c=$(BUILDDIR)/%.o) +CFLAGS += -fno-strict-aliasing -O -fPIC + +SOLDFLAGS += -shared +CPPFLAGS += -DNDEBUG -D_REENTRANT \ + -Ispidermonkey/src -Ispidermonkey/src/build \ + -I/usr/include \ + + +PY_PYTHON=$(shell python -c "import sys; print(sys.executable)") +PY_PREFIX=$(shell $(PY_PYTHON) -c "import sys; print(sys.prefix)") +PY_VERSION=$(shell $(PY_PYTHON) -c "import sys; print('.'.join(map(str, sys.version_info[:2])))") +ifeq ($(BUILDOS),Darwin) + PY_ARCH=$(shell $(PY_PYTHON) -c 'import sys; print (sys.maxint > 2**32 and "x86_64" or "i386")') + SOLDFLAGS += $(PY_PREFIX)/Python + CC=gcc -arch $(PY_ARCH) +else + PY_BIT=$(shell $(PY_PYTHON) -c 'import sys; print (sys.maxint > 2**32 and "64" or "32")') + CFLAGS += -m$(PY_BIT) +endif + +CPPFLAGS += -I$(PY_PREFIX)/include/python$(PY_VERSION) +SOFILE = $(BUILDDIR)/pyspidermonkey.so + +all: $(SOFILE) + +$(BUILDDIR) $(INSTALLDIRS): + mkdir -p $@ + +$(OBJECTS): spidermonkey/src/build/libjs.a spidermonkey/src/build/js_operating_system.h + +$(SOFILE): $(OBJECTS) + $(CC) $(CFLAGS) $(SOLDFLAGS) $(LDFLAGS) $(OBJECTS) -Lspidermonkey/src/build -ljs -o $@ + +$(BUILDDIR)/%.o: javascriptlint/pyspidermonkey/%.c | $(BUILDDIR) + $(CC) -o $@ -c $(CFLAGS) $(CPPFLAGS) $< + +spidermonkey/src/build/libjs.a: + (cd spidermonkey/src && CC="$(CC)" CFLAGS="$(CFLAGS)" $(MAKE)) + +spidermonkey/src/build/js_operating_system.h: + echo "#define XP_UNIX" > $@ + +clean: + -rm -rf $(BUILDDIR) $(INSTALLDIRS) + -(cd spidermonkey/src && $(MAKE) clean) + +install: $(SOFILE) javascriptlint/jsl javascriptlint/jsl | $(INSTALLDIRS) + cp $(SOFILE) build/install + cp javascriptlint/*.py build/install/javascriptlint + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/jsl >build/install/jsl + chmod +x build/install/jsl + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/jsl.py >build/install/javascriptlint/jsl.py + chmod +x build/install/javascriptlint/jsl.py + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/jsparse.py >build/install/javascriptlint/jsparse.py + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/lint.py >build/install/javascriptlint/lint.py + +.PHONY: install diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/README.md new file mode 100644 index 0000000..1e281bc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/README.md @@ -0,0 +1,74 @@ +Overview +-------- + +This is a fork of Matthias Miller's JavaScript Lint. For the original, see: + + http://javascriptlint.com/ + +This tool has two important features that are uncommon among JavaScript lint +tools: + +- It does not conflate style with lint. Style refers to arbitrary code + formatting rules (like leading whitespace rules). Lint refers to potential + program correctness issues (like missing "break" statements inside a switch). + The line is certainly fuzzy, as in the case of JavaScript semicolon style, + but that's why: + +- It's configurable. Each individual warning can be turned on or off, and + warnings can be overridden for individual lines of code. This is essential + for cases where potentially dangerous behavior is being deliberately used + carefully. + +If you want a style checker, see http://github.com/davepacheco/jsstyle. + + +Synopsis +-------- + + # make install + ... + # build/install/jsl + usage: jsl [options] [files] + + options: + -h, --help show this help message and exit + --conf=CONF set the conf file + --profile turn on hotshot profiling + --recurse recursively search directories on the command line + --enable-wildcards resolve wildcards in the command line + --dump dump this script + --unittest run the python unittests + --quiet minimal output + --verbose verbose output + --nologo suppress version information + --nofilelisting suppress file names + --nosummary suppress lint summary + --help:conf display the default configuration file + +You can define a configuration file for jsl to enable or disable particular +warnings and to define global objects (like "window"). See the --help:conf +option. + + +Supported Platforms +------------------- + +This branch of JSL has been tested on: + +- SmartOS (illumos-based), both 32-bit and 64-bit. +- Mac OS X Snow Leopard, Lion, and Mountain Lion. +- Debian Squeeze (6.0.5). + +All of these use Python 2.6 or later. + +History +------- + +This version forked from the Subversion repo at revision 302 (2011-04-06). +I'll happily look at incorporating new patches from upstream, though the +project has been pretty quiet for the last many months. + +The main purpose of this fork is to fix building on illumos-based systems. +Rather than fix the complex spidermonkey build system to work on illumos, I +stripped out a bunch of unnecessary pieces and Makefiles and wrote a new set of +Makefiles. The result now builds on Mac OS X and Linux as well. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/__init__.py b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/__init__.py new file mode 100644 index 0000000..2716426 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/__init__.py @@ -0,0 +1,3 @@ +# vim: ts=4 sw=4 expandtab +from jsl import main + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/conf.py b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/conf.py new file mode 100644 index 0000000..66c31dd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/conf.py @@ -0,0 +1,260 @@ +# vim: ts=4 sw=4 expandtab +import os +import unittest + +import fs +import util +import warnings + +def _getwarningsconf(): + lines = [] + for name in sorted(warnings.warnings.keys()): + message = warnings.warnings[name] + sign = '+' + if name == 'block_without_braces': + sign = '-' + assert len(name) < 29 + lines.append(sign + name.ljust(29) + '# ' + message) + return '\n'.join(lines) + +DEFAULT_CONF = """\ +# +# Configuration File for JavaScript Lint %(version)s +# Developed by Matthias Miller (http://www.JavaScriptLint.com) +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# +%(warnings)s + + +### Output format +# Customize the format of the error message. +# __FILE__ indicates current file path +# __FILENAME__ indicates current file name +# __LINE__ indicates current line +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message +# +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# +-legacy_control_comments + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" ++always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: +#+define window +#+define document + + +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# +""" % { + 'version': '', # TODO + 'warnings': _getwarningsconf(), +} + + +class ConfError(Exception): + def __init__(self, error): + Exception.__init__(self, error) + self.lineno = None + self.path = None + +class Setting: + wants_parm = False + wants_dir = False + +class DeprecatedSetting(Setting): + wants_parm = False + value = None + def load(self, enabled): + raise ConfError, 'This setting is deprecated.' + +class BooleanSetting(Setting): + wants_parm = False + def __init__(self, default): + self.value = default + def load(self, enabled): + self.value = enabled + +class StringSetting(Setting): + wants_parm = True + def __init__(self, default): + self.value = default + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value = parm + +class DeclareSetting(Setting): + wants_parm = True + def __init__(self): + self.value = [] + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value.append(parm) + +class ProcessSetting(Setting): + wants_parm = True + wants_dir = True + def __init__(self, recurse_setting): + self.value = [] + self._recurse = recurse_setting + def load(self, enabled, parm, dir): + if dir: + parm = os.path.join(dir, parm) + self.value.append((self._recurse.value, parm)) + +class JSVersionSetting(Setting): + wants_parm = True + value = util.JSVersion.default() + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + + self.value = util.JSVersion.fromtype(parm) + if not self.value: + raise ConfError, 'Invalid JavaScript version: %s' % parm + +class Conf: + def __init__(self): + recurse = BooleanSetting(False) + self._settings = { + 'recurse': recurse, + 'output-format': StringSetting('__FILE__(__LINE__): __ERROR__'), + 'lambda_assign_requires_semicolon': DeprecatedSetting(), + 'legacy_control_comments': BooleanSetting(False), + 'jscript_function_extensions': DeprecatedSetting(), + 'always_use_option_explicit': BooleanSetting(True), + 'define': DeclareSetting(), + 'context': BooleanSetting(True), + 'process': ProcessSetting(recurse), + 'default-version': JSVersionSetting(), + # SpiderMonkey warnings + 'no_return_value': BooleanSetting(True), + 'equal_as_assign': BooleanSetting(True), + 'anon_no_return_value': BooleanSetting(True) + } + for name in warnings.warnings: + self._settings[name] = BooleanSetting(True) + self.loadline('-block_without_braces') + + def loadfile(self, path): + path = os.path.abspath(path) + conf = fs.readfile(path) + try: + self.loadtext(conf, dir=os.path.dirname(path)) + except ConfError, error: + error.path = path + raise + + def loadtext(self, conf, dir=None): + lines = conf.splitlines() + for lineno in range(0, len(lines)): + try: + self.loadline(lines[lineno], dir) + except ConfError, error: + error.lineno = lineno + raise + + def loadline(self, line, dir=None): + assert not '\r' in line + assert not '\n' in line + + # Allow comments + if '#' in line: + line = line[:line.find('#')] + line = line.rstrip() + if not line: + return + + # Parse the +/- + if line.startswith('+'): + enabled = True + elif line.startswith('-'): + enabled = False + else: + raise ConfError, 'Expected + or -.' + line = line[1:] + + # Parse the key/parms + name = line.split()[0].lower() + parm = line[len(name):].lstrip() + + # Load the setting + setting = self._settings[name] + args = { + 'enabled': enabled + } + if setting.wants_parm: + args['parm'] = parm + elif parm: + raise ConfError, 'The %s setting does not expect a parameter.' % name + if setting.wants_dir: + args['dir'] = dir + setting.load(**args) + + def __getitem__(self, name): + if name == 'paths': + name = 'process' + elif name == 'declarations': + name = 'define' + return self._settings[name].value + +class TestConf(unittest.TestCase): + def testDefaultConf(self): + # Make sure the string version corresponds with the code. + fromstr = Conf() + fromstr.loadtext(DEFAULT_CONF) + fromcode = Conf() + settings = set(fromcode._settings.keys() + fromstr._settings.keys()) + for setting in settings: + self.assertEquals(fromcode[setting], fromstr[setting], + 'Mismatched defaults for %s' % setting) + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/fs.py b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/fs.py new file mode 100644 index 0000000..cc05dbb --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/fs.py @@ -0,0 +1,23 @@ +# vim: ts=4 sw=4 expandtab +import codecs +import os + +def readfile(path): + file = codecs.open(path, 'r', 'utf-8') + contents = file.read() + if contents and contents[0] == unicode(codecs.BOM_UTF8, 'utf8'): + contents = contents[1:] + + if contents.startswith('#!'): + idx = contents.find('\n'); + if idx != -1: + contents = '\n' + contents[idx + 1:]; + + return contents + +def normpath(path): + path = os.path.abspath(path) + path = os.path.normcase(path) + path = os.path.normpath(path) + return path + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/htmlparse.py b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/htmlparse.py new file mode 100644 index 0000000..5d38bce --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/htmlparse.py @@ -0,0 +1,51 @@ +# vim: ts=4 sw=4 expandtab +import HTMLParser +import unittest + +class _Parser(HTMLParser.HTMLParser): + def __init__(self): + HTMLParser.HTMLParser.__init__(self) + self._tags = [] + + def handle_starttag(self, tag, attributes): + if tag.lower() == 'script': + attr = dict(attributes) + self._tags.append({ + 'type': 'start', + 'lineno': self.lineno, + 'offset': self.offset, + 'len': len(self.get_starttag_text()), + 'attr': attr + }) + + def handle_endtag(self, tag): + if tag.lower() == 'script': + self._tags.append({ + 'type': 'end', + 'lineno': self.lineno, + 'offset': self.offset, + }) + + def unknown_decl(self, data): + # Ignore unknown declarations instead of raising an exception. + pass + + def gettags(self): + return self._tags + +def findscripttags(s): + """ Note that the lineno is 1-based. + """ + parser = _Parser() + parser.feed(s) + parser.close() + return parser.gettags() + +class TestHTMLParse(unittest.TestCase): + def testConditionalComments(self): + html = """ + +This is not Internet Explorer +""" + findscripttags(html) + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsl b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsl new file mode 100755 index 0000000..a18794b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsl @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +import os +import sys + +oldcwd = os.getcwd(); +os.chdir(os.path.dirname(sys.argv[0])); +basedir = os.getcwd(); +os.chdir(oldcwd); + +sys.path.insert(0, basedir) + +import javascriptlint +javascriptlint.main() diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsl.py b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsl.py new file mode 100755 index 0000000..4eee0b6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsl.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# vim: ts=4 sw=4 expandtab +import codecs +import fnmatch +import glob +import os +import sys +import unittest +from optparse import OptionParser + +import conf +import htmlparse +import jsparse +import lint +import util + +_lint_results = { + 'warnings': 0, + 'errors': 0 +} + +def _dump(paths): + for path in paths: + script = util.readfile(path) + jsparse.dump_tree(script) + +def _lint(paths, conf_, printpaths): + def lint_error(path, line, col, errname, errdesc): + _lint_results['warnings'] = _lint_results['warnings'] + 1 + print util.format_error(conf_['output-format'], path, line, col, + errname, errdesc) + lint.lint_files(paths, lint_error, conf=conf_, printpaths=printpaths) + +def _resolve_paths(path, recurse): + # Build a list of directories + paths = [] + + dir, pattern = os.path.split(path) + for cur_root, cur_dirs, cur_files in os.walk(dir): + paths.extend(os.path.join(cur_root, file) for file in \ + fnmatch.filter(cur_files, pattern)) + if not recurse: + break + + # If no files have been found, return the original path/pattern. This will + # force an error to be thrown if no matching files were found. + return paths or [path] + +def printlogo(): + # TODO: Print version number. + print "JavaScript Lint" + print "Developed by Matthias Miller (http://www.JavaScriptLint.com)" + +def _profile_enabled(func, *args, **kwargs): + import tempfile + import hotshot + import hotshot.stats + handle, filename = tempfile.mkstemp() + profile = hotshot.Profile(filename) + profile.runcall(func, *args, **kwargs) + profile.close() + stats = hotshot.stats.load(filename) + stats = stats.sort_stats("time") + stats.print_stats() +def _profile_disabled(func, *args, **kwargs): + func(*args, **kwargs) + +def main(): + parser = OptionParser(usage="%prog [options] [files]") + add = parser.add_option + add("--conf", dest="conf", metavar="CONF", + help="set the conf file") + add("--profile", dest="profile", action="store_true", default=False, + help="turn on hotshot profiling") + add("--recurse", dest="recurse", action="store_true", default=False, + help="recursively search directories on the command line") + if os.name == 'nt': + add("--disable-wildcards", dest="wildcards", action="store_false", + default=True, help="do not resolve wildcards in the command line") + else: + add("--enable-wildcards", dest="wildcards", action="store_true", + default=False, help="resolve wildcards in the command line") + add("--dump", dest="dump", action="store_true", default=False, + help="dump this script") + add("--unittest", dest="unittest", action="store_true", default=False, + help="run the python unittests") + add("--quiet", dest="verbosity", action="store_const", const=0, + help="minimal output") + add("--verbose", dest="verbosity", action="store_const", const=2, + help="verbose output") + add("--nologo", dest="printlogo", action="store_false", default=True, + help="suppress version information") + add("--nofilelisting", dest="printlisting", action="store_false", + default=True, help="suppress file names") + add("--nosummary", dest="printsummary", action="store_false", default=True, + help="suppress lint summary") + add("--help:conf", dest="showdefaultconf", action="store_true", default=False, + help="display the default configuration file") + parser.set_defaults(verbosity=1) + options, args = parser.parse_args() + + if len(sys.argv) == 1: + parser.print_help() + sys.exit() + + if options.showdefaultconf: + print conf.DEFAULT_CONF + sys.exit() + + if options.printlogo: + printlogo() + + conf_ = conf.Conf() + if options.conf: + conf_.loadfile(options.conf) + + profile_func = _profile_disabled + if options.profile: + profile_func = _profile_enabled + + if options.unittest: + suite = unittest.TestSuite(); + for module in [conf, htmlparse, jsparse, lint, util]: + suite.addTest(unittest.findTestCases(module)) + + runner = unittest.TextTestRunner(verbosity=options.verbosity) + runner.run(suite) + + paths = [] + for recurse, path in conf_['paths']: + paths.extend(_resolve_paths(path, recurse)) + for arg in args: + if options.wildcards: + paths.extend(_resolve_paths(arg, options.recurse)) + elif options.recurse and os.path.isdir(arg): + paths.extend(_resolve_paths(os.path.join(arg, '*'), True)) + else: + paths.append(arg) + if options.dump: + profile_func(_dump, paths) + else: + profile_func(_lint, paths, conf_, options.printlisting) + + if options.printsummary: + print '\n%i error(s), %i warnings(s)' % (_lint_results['errors'], + _lint_results['warnings']) + + if _lint_results['errors']: + sys.exit(3) + if _lint_results['warnings']: + sys.exit(1) + sys.exit(0) + +if __name__ == '__main__': + main() + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsparse.py b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsparse.py new file mode 100644 index 0000000..eec82a4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/jsparse.py @@ -0,0 +1,426 @@ +#!/usr/bin/env python +# vim: ts=4 sw=4 expandtab +""" Parses a script into nodes. """ +import bisect +import re +import unittest + +import spidermonkey +from spidermonkey import tok, op +from util import JSVersion + +_tok_names = dict(zip( + [getattr(tok, prop) for prop in dir(tok)], + ['tok.%s' % prop for prop in dir(tok)] +)) +_op_names = dict(zip( + [getattr(op, prop) for prop in dir(op)], + ['op.%s' % prop for prop in dir(op)] +)) + +NodePos = spidermonkey.NodePos + +class NodePositions: + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text, start_pos=None): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._start_pos = start_pos + self._lines = text.splitlines(True) + lines = [0] + [len(x) for x in self._lines] + for x in range(1, len(lines)): + lines[x] += lines[x-1] + self._line_offsets = lines + def from_offset(self, offset): + line = bisect.bisect(self._line_offsets, offset)-1 + col = offset - self._line_offsets[line] + if self._start_pos: + if line == 0: + col += self._start_pos.col + line += self._start_pos.line + return NodePos(line, col) + def to_offset(self, pos): + pos = self._to_rel_pos(pos) + offset = self._line_offsets[pos.line] + pos.col + assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num + return offset + def text(self, start, end): + assert start <= end + start, end = self._to_rel_pos(start), self._to_rel_pos(end) + # Trim the ending first in case it's a single line. + lines = self._lines[start.line:end.line+1] + lines[-1] = lines[-1][:end.col+1] + lines[0] = lines[0][start.col:] + return ''.join(lines) + def _to_rel_pos(self, pos): + " converts a position to a position relative to self._start_pos " + if not self._start_pos: + return pos + line, col = pos.line, pos.col + line -= self._start_pos.line + if line == 0: + col -= self._start_pos.col + assert line >= 0 and col >= 0 # out-of-bounds node position + return NodePos(line, col) + +class NodeRanges: + def __init__(self): + self._offsets = [] + def add(self, start, end): + i = bisect.bisect_left(self._offsets, start) + if i % 2 == 1: + i -= 1 + start = self._offsets[i] + + end = end + 1 + j = bisect.bisect_left(self._offsets, end) + if j % 2 == 1: + end = self._offsets[j] + j += 1 + + self._offsets[i:j] = [start,end] + def has(self, pos): + return bisect.bisect_right(self._offsets, pos) % 2 == 1 + +class _Node: + def add_child(self, node): + if node: + node.node_index = len(self.kids) + node.parent = self + self.kids.append(node) + + def start_pos(self): + try: + return self._start_pos + except AttributeError: + self._start_pos = NodePos(self._start_line, self._start_col) + return self._start_pos + + def end_pos(self): + try: + return self._end_pos + except AttributeError: + self._end_pos = NodePos(self._end_line, self._end_col) + return self._end_pos + + def __str__(self): + kind = self.kind + if not kind: + kind = '(none)' + return '%s>%s' % (_tok_names[kind], str(self.kids)) + + def is_equivalent(self, other, are_functions_equiv=False): + if not other: + return False + + # Bail out for functions + if not are_functions_equiv: + if self.kind == tok.FUNCTION: + return False + if self.kind == tok.LP and self.opcode == op.CALL: + return False + + if self.kind != other.kind: + return False + if self.opcode != other.opcode: + return False + + # Check atoms on names, properties, and string constants + if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: + return False + + # Check values on numbers + if self.kind == tok.NUMBER and self.dval != other.dval: + return False + + # Compare child nodes + if len(self.kids) != len(other.kids): + return False + for i in range(0, len(self.kids)): + # Watch for dead nodes + if not self.kids[i]: + if not other.kids[i]: return True + else: return False + if not self.kids[i].is_equivalent(other.kids[i]): + return False + + return True + +def isvalidversion(jsversion): + if jsversion is None: + return True + return spidermonkey.is_valid_version(jsversion.version) + +def findpossiblecomments(script, node_positions): + pos = 0 + single_line_re = r"//[^\r\n]*" + multi_line_re = r"/\*(.*?)\*/" + full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) + comment_re = re.compile(full_re, re.DOTALL) + + comments = [] + while True: + match = comment_re.search(script, pos) + if not match: + return comments + + # Get the comment text + comment_text = script[match.start():match.end()] + if comment_text.startswith('/*'): + comment_text = comment_text[2:-2] + opcode = 'JSOP_C_COMMENT' + else: + comment_text = comment_text[2:] + opcode = 'JSOP_CPP_COMMENT' + opcode = opcode[5:].lower() + + start_offset = match.start() + end_offset = match.end()-1 + + start_pos = node_positions.from_offset(start_offset) + end_pos = node_positions.from_offset(end_offset) + kwargs = { + 'kind': 'COMMENT', + 'atom': comment_text, + 'opcode': opcode, + '_start_line': start_pos.line, + '_start_col': start_pos.col, + '_end_line': end_pos.line, + '_end_col': end_pos.col, + 'parent': None, + 'kids': [], + 'node_index': None + } + comment_node = _Node() + comment_node.__dict__.update(kwargs) + comments.append(comment_node) + + # Start searching immediately after the start of the comment in case + # this one was within a string or a regexp. + pos = match.start()+1 + +def parse(script, jsversion, error_callback, startpos=None): + """ All node positions will be relative to startpos. This allows scripts + to be embedded in a file (for example, HTML). + """ + def _wrapped_callback(line, col, msg): + assert msg.startswith('JSMSG_') + msg = msg[6:].lower() + error_callback(line, col, msg) + + startpos = startpos or NodePos(0,0) + jsversion = jsversion or JSVersion.default() + assert isvalidversion(jsversion) + return spidermonkey.parse(script, jsversion.version, jsversion.e4x, + _Node, _wrapped_callback, + startpos.line, startpos.col) + +def filtercomments(possible_comments, node_positions, root_node): + comment_ignore_ranges = NodeRanges() + + def process(node): + if node.kind == tok.NUMBER: + node.atom = node_positions.text(node.start_pos(), node.end_pos()) + elif node.kind == tok.STRING or \ + (node.kind == tok.OBJECT and node.opcode == op.REGEXP): + start_offset = node_positions.to_offset(node.start_pos()) + end_offset = node_positions.to_offset(node.end_pos()) - 1 + comment_ignore_ranges.add(start_offset, end_offset) + for kid in node.kids: + if kid: + process(kid) + process(root_node) + + comments = [] + for comment in possible_comments: + start_offset = node_positions.to_offset(comment.start_pos()) + end_offset = node_positions.to_offset(comment.end_pos()) + if comment_ignore_ranges.has(start_offset): + continue + comment_ignore_ranges.add(start_offset, end_offset) + comments.append(comment) + return comments + +def findcomments(script, root_node, start_pos=None): + node_positions = NodePositions(script, start_pos) + possible_comments = findpossiblecomments(script, node_positions) + return filtercomments(possible_comments, node_positions, root_node) + +def is_compilable_unit(script, jsversion): + jsversion = jsversion or JSVersion.default() + assert isvalidversion(jsversion) + return spidermonkey.is_compilable_unit(script, jsversion.version, jsversion.e4x) + +def _dump_node(node, depth=0): + if node is None: + print ' '*depth, + print '(None)' + print + else: + print ' '*depth, + print '%s, %s' % (_tok_names[node.kind], _op_names[node.opcode]) + print ' '*depth, + print '%s - %s' % (node.start_pos(), node.end_pos()) + if hasattr(node, 'atom'): + print ' '*depth, + print 'atom: %s' % node.atom + if node.no_semi: + print ' '*depth, + print '(no semicolon)' + print + for node in node.kids: + _dump_node(node, depth+1) + +def dump_tree(script): + def error_callback(line, col, msg): + print '(%i, %i): %s', (line, col, msg) + node = parse(script, None, error_callback) + _dump_node(node) + +class TestComments(unittest.TestCase): + def _test(self, script, expected_comments): + root = parse(script, None, lambda line, col, msg: None) + comments = findcomments(script, root) + encountered_comments = [node.atom for node in comments] + self.assertEquals(encountered_comments, list(expected_comments)) + def testSimpleComments(self): + self._test('re = /\//g', ()) + self._test('re = /\///g', ()) + self._test('re = /\////g', ('g',)) + def testCComments(self): + self._test('/*a*//*b*/', ('a', 'b')) + self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) + self._test('a//*b*/c', ('*b*/c',)) + self._test('a///*b*/c', ('/*b*/c',)) + self._test('a/*//*/;', ('//',)) + self._test('a/*b*/+/*c*/d', ('b', 'c')) + +class TestNodePositions(unittest.TestCase): + def _test(self, text, expected_lines, expected_cols): + # Get a NodePos list + positions = NodePositions(text) + positions = [positions.from_offset(i) for i in range(0, len(text))] + encountered_lines = ''.join([str(x.line) for x in positions]) + encountered_cols = ''.join([str(x.col) for x in positions]) + self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) + self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) + def testSimple(self): + self._test( + 'abc\r\ndef\nghi\n\nj', + '0000 0 1111 2222 3 4', + '0123 4 0123 0123 0 0' + ) + self._test( + '\rabc', + '0 111', + '0 012' + ) + def testText(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') + self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') + def testOffset(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) + self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) + self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) + def testStartPos(self): + pos = NodePositions('abc\r\ndef\n\nghi', NodePos(3,4)) + self.assertEquals(pos.to_offset(NodePos(3, 4)), 0) + self.assertEquals(pos.to_offset(NodePos(3, 5)), 1) + self.assertEquals(pos.from_offset(0), NodePos(3, 4)) + self.assertEquals(pos.text(NodePos(3, 4), NodePos(3, 4)), 'a') + self.assertEquals(pos.text(NodePos(3, 4), NodePos(3, 6)), 'abc') + self.assertEquals(pos.text(NodePos(3, 6), NodePos(4, 2)), 'c\r\ndef') + +class TestNodeRanges(unittest.TestCase): + def testAdd(self): + r = NodeRanges() + r.add(5, 10) + self.assertEquals(r._offsets, [5,11]) + r.add(15, 20) + self.assertEquals(r._offsets, [5,11,15,21]) + r.add(21,22) + self.assertEquals(r._offsets, [5,11,15,23]) + r.add(4,5) + self.assertEquals(r._offsets, [4,11,15,23]) + r.add(9,11) + self.assertEquals(r._offsets, [4,12,15,23]) + r.add(10,20) + self.assertEquals(r._offsets, [4,23]) + r.add(4,22) + self.assertEquals(r._offsets, [4,23]) + r.add(30,30) + self.assertEquals(r._offsets, [4,23,30,31]) + def testHas(self): + r = NodeRanges() + r.add(5, 10) + r.add(15, 15) + assert not r.has(4) + assert r.has(5) + assert r.has(6) + assert r.has(9) + assert r.has(10) + assert not r.has(14) + assert r.has(15) + assert not r.has(16) + +class TestCompilableUnit(unittest.TestCase): + def test(self): + tests = ( + ('var s = "', False), + ('bogon()', True), + ('int syntax_error;', True), + ('a /* b', False), + ('re = /.*', False), + ('{ // missing curly', False) + ) + for text, expected in tests: + encountered = is_compilable_unit(text, JSVersion.default()) + self.assertEquals(encountered, expected) + # NOTE: This seems like a bug. + self.assert_(is_compilable_unit("/* test", JSVersion.default())) + +class TestLineOffset(unittest.TestCase): + def testErrorPos(self): + def geterror(script, startpos): + errors = [] + def onerror(line, col, msg): + errors.append((line, col, msg)) + parse(script, None, onerror, startpos) + self.assertEquals(len(errors), 1) + return errors[0] + self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error')) + self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error')) + self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error')) + self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error')) + def testNodePos(self): + def getnodepos(script, startpos): + root = parse(script, None, None, startpos) + self.assertEquals(root.kind, tok.LC) + var, = root.kids + self.assertEquals(var.kind, tok.VAR) + return var.start_pos() + self.assertEquals(getnodepos('var x;', None), NodePos(0,0)) + self.assertEquals(getnodepos(' var x;', None), NodePos(0,1)) + self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2,1)) + self.assertEquals(getnodepos('var x;', NodePos(3,4)), NodePos(3,4)) + self.assertEquals(getnodepos(' var x;', NodePos(3,4)), NodePos(3,5)) + self.assertEquals(getnodepos('\n\n var x;', NodePos(3,4)), NodePos(5,1)) + def testComments(self): + def testcomment(comment, startpos, expectedpos): + root = parse(comment, None, None, startpos) + comment, = findcomments(comment, root, startpos) + self.assertEquals(comment.start_pos(), expectedpos) + for comment in ('/*comment*/', '//comment'): + testcomment(comment, None, NodePos(0,0)) + testcomment(' %s' % comment, None, NodePos(0,1)) + testcomment('\n\n %s' % comment, None, NodePos(2,1)) + testcomment('%s' % comment, NodePos(3,4), NodePos(3,4)) + testcomment(' %s' % comment, NodePos(3,4), NodePos(3,5)) + testcomment('\n\n %s' % comment, NodePos(3,4), NodePos(5,1)) + +if __name__ == '__main__': + unittest.main() + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/lint.py b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/lint.py new file mode 100644 index 0000000..f2967a4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/javascriptlint/lint.py @@ -0,0 +1,713 @@ +#!/usr/bin/env python +# vim: ts=4 sw=4 expandtab +import os.path +import re + +import conf +import fs +import htmlparse +import jsparse +import visitation +import warnings +import unittest +import util + +from spidermonkey import tok, op + +_newline_kinds = ( + 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', + 'relop', 'hook', 'plus', 'minus', 'star', 'divop', 'eqop', 'shop', 'or', + 'and', 'bitor', 'bitxor', 'bitand', 'else', 'try' +) + +_globals = frozenset([ + 'Array', 'Boolean', 'Math', 'Number', 'String', 'RegExp', 'Script', 'Date', + 'isNaN', 'isFinite', 'parseFloat', 'parseInt', + 'eval', 'NaN', 'Infinity', + 'escape', 'unescape', 'uneval', + 'decodeURI', 'encodeURI', 'decodeURIComponent', 'encodeURIComponent', + 'Function', 'Object', + 'Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError', + 'SyntaxError', 'TypeError', 'URIError', + 'arguments', 'undefined' +]) + +def _find_function(node): + while node and node.kind != tok.FUNCTION: + node = node.parent + return node + +def _find_functions(node): + functions = [] + while node: + if node.kind == tok.FUNCTION: + functions.append(node) + node = node.parent + return functions + +def _parse_control_comment(comment): + """ Returns None or (keyword, parms) """ + atom = comment.atom.strip() + atom_lower = atom.lower() + if atom_lower.startswith('jsl:'): + control_comment = atom[4:] + elif atom.startswith('@') and atom.endswith('@'): + control_comment = atom[1:-1] + else: + return None + + control_comments = { + 'ignoreall': (False), + 'ignore': (False), + 'end': (False), + 'option explicit': (False), + 'import': (True), + 'fallthru': (False), + 'pass': (False), + 'declare': (True), + 'unused': (True), + 'content-type': (True), + } + if control_comment.lower() in control_comments: + keyword = control_comment.lower() + else: + keyword = control_comment.lower().split()[0] + if not keyword in control_comments: + return None + + parms = control_comment[len(keyword):].strip() + return (comment, keyword, parms) + +class Scope: + """ Outer-level scopes will never be associated with a node. + Inner-level scopes will always be associated with a node. + """ + def __init__(self): + self._parent = None + self._kids = [] + self._identifiers = {} + self._references = [] + self._unused = [] + self._node = None + def add_scope(self, node): + assert not node is None + self._kids.append(Scope()) + self._kids[-1]._parent = self + self._kids[-1]._node = node + return self._kids[-1] + def add_declaration(self, name, node, type_): + assert type_ in ('arg', 'function', 'var'), \ + 'Unrecognized identifier type: %s' % type_ + self._identifiers[name] = { + 'node': node, + 'type': type_ + } + def add_reference(self, name, node): + self._references.append((name, node)) + def set_unused(self, name, node): + self._unused.append((name, node)) + def get_identifier(self, name): + if name in self._identifiers: + return self._identifiers[name]['node'] + else: + return None + def get_identifier_type(self, name): + if name in self._identifiers: + return self._identifiers[name]['type'] + else: + return None + def get_identifiers(self): + "returns a list of names" + return self._identifiers.keys() + def resolve_identifier(self, name): + if name in self._identifiers: + return self, self._identifiers[name]['node'] + if self._parent: + return self._parent.resolve_identifier(name) + return None + def get_identifier_warnings(self): + """ Returns a tuple of unreferenced and undeclared, where each is a list + of (scope, name, node) tuples. + """ + unreferenced = {} + undeclared = [] + obstructive = [] + self._find_warnings(unreferenced, undeclared, obstructive, False) + + # Convert "unreferenced" from a dictionary of: + # { (scope, name): node } + # to a list of: + # [ (scope, name, node) ] + # sorted by node position. + unreferenced = [(key[0], key[1], node) for key, node + in unreferenced.items()] + unreferenced.sort(key=lambda x: x[2].start_pos()) + + return { + 'unreferenced': unreferenced, + 'undeclared': undeclared, + 'obstructive': obstructive, + } + def _find_warnings(self, unreferenced, undeclared, obstructive, + is_in_with_scope): + """ unreferenced is a dictionary, such that: + (scope, name): node + } + undeclared is a list, such that: [ + (scope, name, node) + ] + obstructive is a list, such that: [ + (scope, name, node) + ] + """ + if self._node and self._node.kind == tok.WITH: + is_in_with_scope = True + + # Add all identifiers as unreferenced. Children scopes will remove + # them if they are referenced. Variables need to be keyed by name + # instead of node, because function parameters share the same node. + for name, info in self._identifiers.items(): + unreferenced[(self, name)] = info['node'] + + # Check for variables that hide an identifier in a parent scope. + if self._parent: + for name, info in self._identifiers.items(): + if self._parent.resolve_identifier(name): + obstructive.append((self, name, info['node'])) + + # Remove all declared variables from the "unreferenced" set; add all + # undeclared variables to the "undeclared" list. + for name, node in self._references: + resolved = self.resolve_identifier(name) + if resolved: + # Make sure this isn't an assignment. + if node.parent.kind in (tok.ASSIGN, tok.INC, tok.DEC) and \ + node.node_index == 0 and \ + node.parent.parent.kind == tok.SEMI: + continue + unreferenced.pop((resolved[0], name), None) + else: + # with statements cannot have undeclared identifiers. + if not is_in_with_scope: + undeclared.append((self, name, node)) + + # Remove all variables that have been set as "unused". + for name, node in self._unused: + resolved = self.resolve_identifier(name) + if resolved: + unreferenced.pop((resolved[0], name), None) + else: + undeclared.append((self, name, node)) + + for child in self._kids: + child._find_warnings(unreferenced, undeclared, obstructive, + is_in_with_scope) + def find_scope(self, node): + for kid in self._kids: + scope = kid.find_scope(node) + if scope: + return scope + + # Always add it to the outer scope. + if not self._parent: + assert not self._node + return self + + # Conditionally add it to an inner scope. + assert self._node + if (node.start_pos() >= self._node.start_pos() and \ + node.end_pos() <= self._node.end_pos()): + return self + +class _Script: + def __init__(self): + self._imports = set() + self.scope = Scope() + def importscript(self, script): + self._imports.add(script) + def hasglobal(self, name): + return not self._findglobal(name, set()) is None + def _findglobal(self, name, searched): + """ searched is a set of all searched scripts """ + # Avoid recursion. + if self in searched: + return + + # Check this scope. + if self.scope.get_identifier(name): + return self + searched.add(self) + + # Search imported scopes. + for script in self._imports: + global_ = script._findglobal(name, searched) + if global_: + return global_ + +def _findhtmlscripts(contents, default_version): + starttag = None + nodepos = jsparse.NodePositions(contents) + for tag in htmlparse.findscripttags(contents): + if tag['type'] == 'start': + # Ignore nested start tags. + if not starttag: + jsversion = util.JSVersion.fromattr(tag['attr'], default_version) + starttag = dict(tag, jsversion=jsversion) + src = tag['attr'].get('src') + if src: + yield { + 'type': 'external', + 'jsversion': jsversion, + 'src': src, + } + elif tag['type'] == 'end': + if not starttag: + continue + + # htmlparse returns 1-based line numbers. Calculate the + # position of the script's contents. + tagpos = jsparse.NodePos(starttag['lineno']-1, starttag['offset']) + tagoffset = nodepos.to_offset(tagpos) + startoffset = tagoffset + starttag['len'] + startpos = nodepos.from_offset(startoffset) + endpos = jsparse.NodePos(tag['lineno']-1, tag['offset']) + endoffset = nodepos.to_offset(endpos) + script = contents[startoffset:endoffset] + + if not jsparse.isvalidversion(starttag['jsversion']) or \ + jsparse.is_compilable_unit(script, starttag['jsversion']): + if script.strip(): + yield { + 'type': 'inline', + 'jsversion': starttag['jsversion'], + 'pos': startpos, + 'contents': script, + } + starttag = None + else: + assert False, 'Invalid internal tag type %s' % tag['type'] + +def lint_files(paths, lint_error, conf=conf.Conf(), printpaths=True): + def lint_file(path, kind, jsversion): + def import_script(import_path, jsversion): + # The user can specify paths using backslashes (such as when + # linting Windows scripts on a posix environment. + import_path = import_path.replace('\\', os.sep) + import_path = os.path.join(os.path.dirname(path), import_path) + return lint_file(import_path, 'js', jsversion) + def _lint_error(*args): + return lint_error(normpath, *args) + + normpath = fs.normpath(path) + if normpath in lint_cache: + return lint_cache[normpath] + if printpaths: + print normpath + contents = fs.readfile(path) + lint_cache[normpath] = _Script() + + script_parts = [] + if kind == 'js': + script_parts.append((None, jsversion or conf['default-version'], contents)) + elif kind == 'html': + assert jsversion is None + for script in _findhtmlscripts(contents, conf['default-version']): + # TODO: Warn about foreign languages. + if not script['jsversion']: + continue + + if script['type'] == 'external': + other = import_script(script['src'], script['jsversion']) + lint_cache[normpath].importscript(other) + elif script['type'] == 'inline': + script_parts.append((script['pos'], script['jsversion'], + script['contents'])) + else: + assert False, 'Invalid internal script type %s' % \ + script['type'] + else: + assert False, 'Unsupported file kind: %s' % kind + + _lint_script_parts(script_parts, lint_cache[normpath], _lint_error, conf, import_script) + return lint_cache[normpath] + + lint_cache = {} + for path in paths: + ext = os.path.splitext(path)[1] + if ext.lower() in ['.htm', '.html']: + lint_file(path, 'html', None) + else: + lint_file(path, 'js', None) + +def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, + ignores, report_native, report_lint, import_callback): + def parse_error(row, col, msg): + if not msg in ('anon_no_return_value', 'no_return_value', + 'redeclared_var', 'var_hides_arg'): + parse_errors.append((jsparse.NodePos(row, col), msg)) + + def report(node, errname, pos=None, **errargs): + if errname == 'empty_statement' and node.kind == tok.LC: + for pass_ in passes: + if pass_.start_pos() > node.start_pos() and \ + pass_.end_pos() < node.end_pos(): + passes.remove(pass_) + return + + if errname == 'missing_break': + # Find the end of the previous case/default and the beginning of + # the next case/default. + assert node.kind in (tok.CASE, tok.DEFAULT) + prevnode = node.parent.kids[node.node_index-1] + expectedfallthru = prevnode.end_pos(), node.start_pos() + elif errname == 'missing_break_for_last_case': + # Find the end of the current case/default and the end of the + # switch. + assert node.parent.kind == tok.LC + expectedfallthru = node.end_pos(), node.parent.end_pos() + else: + expectedfallthru = None + + if expectedfallthru: + start, end = expectedfallthru + for fallthru in fallthrus: + # Look for a fallthru between the end of the current case or + # default statement and the beginning of the next token. + if fallthru.start_pos() > start and fallthru.end_pos() < end: + fallthrus.remove(fallthru) + return + + report_lint(node, errname, pos, **errargs) + + parse_errors = [] + declares = [] + unused_identifiers = [] + import_paths = [] + fallthrus = [] + passes = [] + + node_positions = jsparse.NodePositions(script, scriptpos) + possible_comments = jsparse.findpossiblecomments(script, node_positions) + + # Check control comments for the correct version. It may be this comment + # isn't a valid comment (for example, it might be inside a string literal) + # After parsing, validate that it's legitimate. + jsversionnode = None + for comment in possible_comments: + cc = _parse_control_comment(comment) + if cc: + node, keyword, parms = cc + if keyword == 'content-type': + ccversion = util.JSVersion.fromtype(parms) + if ccversion: + jsversion = ccversion + jsversionnode = node + else: + report(node, 'unsupported_version', version=parms) + + if not jsparse.isvalidversion(jsversion): + report_lint(jsversionnode, 'unsupported_version', scriptpos, + version=jsversion.version) + return + + root = jsparse.parse(script, jsversion, parse_error, scriptpos) + if not root: + # Report errors and quit. + for pos, msg in parse_errors: + report_native(pos, msg) + return + + comments = jsparse.filtercomments(possible_comments, node_positions, root) + + if jsversionnode is not None and jsversionnode not in comments: + # TODO + report(jsversionnode, 'incorrect_version') + + start_ignore = None + for comment in comments: + cc = _parse_control_comment(comment) + if cc: + node, keyword, parms = cc + if keyword == 'declare': + if not util.isidentifier(parms): + report(node, 'jsl_cc_not_understood') + else: + declares.append((parms, node)) + elif keyword == 'unused': + if not util.isidentifier(parms): + report(node, 'jsl_cc_not_understood') + else: + unused_identifiers.append((parms, node)) + elif keyword == 'ignore': + if start_ignore: + report(node, 'mismatch_ctrl_comments') + else: + start_ignore = node + elif keyword == 'end': + if start_ignore: + ignores.append((start_ignore.start_pos(), node.end_pos())) + start_ignore = None + else: + report(node, 'mismatch_ctrl_comments') + elif keyword == 'import': + if not parms: + report(node, 'jsl_cc_not_understood') + else: + import_paths.append(parms) + elif keyword == 'fallthru': + fallthrus.append(node) + elif keyword == 'pass': + passes.append(node) + else: + if comment.opcode == 'c_comment': + # Look for nested C-style comments. + nested_comment = comment.atom.find('/*') + if nested_comment < 0 and comment.atom.endswith('/'): + nested_comment = len(comment.atom) - 1 + # Report at the actual error of the location. Add two + # characters for the opening two characters. + if nested_comment >= 0: + pos = node_positions.from_offset(node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) + report(comment, 'nested_comment', pos=pos) + if comment.atom.lower().startswith('jsl:'): + report(comment, 'jsl_cc_not_understood') + elif comment.atom.startswith('@'): + report(comment, 'legacy_cc_not_understood') + if start_ignore: + report(start_ignore, 'mismatch_ctrl_comments') + + # Wait to report parse errors until loading jsl:ignore directives. + for pos, msg in parse_errors: + report_native(pos, msg) + + # Find all visitors and convert them into "onpush" callbacks that call "report" + visitors = { + 'push': warnings.make_visitors() + } + for event in visitors: + for kind, callbacks in visitors[event].items(): + visitors[event][kind] = [_getreporter(callback, report) for callback in callbacks] + + # Push the scope/variable checks. + visitation.make_visitors(visitors, [_get_scope_checks(script_cache.scope, report)]) + + # kickoff! + _lint_node(root, visitors) + + for fallthru in fallthrus: + report(fallthru, 'invalid_fallthru') + for fallthru in passes: + report(fallthru, 'invalid_pass') + + # Process imports by copying global declarations into the universal scope. + for path in import_paths: + script_cache.importscript(import_callback(path, jsversion)) + + for name, node in declares: + declare_scope = script_cache.scope.find_scope(node) + _warn_or_declare(declare_scope, name, 'var', node, report) + + for name, node in unused_identifiers: + unused_scope = script_cache.scope.find_scope(node) + unused_scope.set_unused(name, node) + +def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): + def report_lint(node, errname, pos=None, **errargs): + errdesc = warnings.format_error(errname, **errargs) + _report(pos or node.start_pos(), errname, errdesc, True) + + def report_native(pos, errname): + # TODO: Format the error. + _report(pos, errname, errname, False) + + def _report(pos, errname, errdesc, require_key): + try: + if not conf[errname]: + return + except KeyError, err: + if require_key: + raise + + for start, end in ignores: + if pos >= start and pos <= end: + return + + return lint_error(pos.line, pos.col, errname, errdesc) + + for scriptpos, jsversion, script in script_parts: + ignores = [] + _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, + report_native, report_lint, import_callback) + + scope = script_cache.scope + identifier_warnings = scope.get_identifier_warnings() + for decl_scope, name, node in identifier_warnings['undeclared']: + if name in conf['declarations']: + continue + if name in _globals: + continue + if not script_cache.hasglobal(name): + report_lint(node, 'undeclared_identifier', name=name) + for ref_scope, name, node in identifier_warnings['unreferenced']: + # Ignore the outer scope. + if ref_scope != scope: + type_ = ref_scope.get_identifier_type(name) + if type_ == 'arg': + report_lint(node, 'unreferenced_argument', name=name) + elif type_ == 'function': + report_lint(node, 'unreferenced_function', name=name) + elif type_ == 'var': + report_lint(node, 'unreferenced_variable', name=name) + else: + assert False, 'Unrecognized identifier type: %s' % type_ + for ref_scope, name, node in identifier_warnings['obstructive']: + report_lint(node, 'identifier_hides_another', name=name) + +def _getreporter(visitor, report): + def onpush(node): + try: + ret = visitor(node) + assert ret is None, 'visitor should raise an exception, not return a value' + except warnings.LintWarning, warning: + # TODO: This is ugly hardcoding to improve the error positioning of + # "missing_semicolon" errors. + if visitor.warning in ('missing_semicolon', 'missing_semicolon_for_lambda'): + pos = warning.node.end_pos() + else: + pos = None + report(warning.node, visitor.warning, pos=pos, **warning.errargs) + return onpush + +def _warn_or_declare(scope, name, type_, node, report): + parent_scope, other = scope.resolve_identifier(name) or (None, None) + if other and parent_scope == scope: + # Only warn about duplications in this scope. + # Other scopes will be checked later. + if other.kind == tok.FUNCTION and name in other.fn_args: + report(node, 'var_hides_arg', name=name) + else: + report(node, 'redeclared_var', name=name) + else: + scope.add_declaration(name, node, type_) + +def _get_scope_checks(scope, report): + scopes = [scope] + + class scope_checks: + """ This is a non-standard visitation class to track scopes. The + docstring is unused since this class never throws lint errors. + """ + @visitation.visit('push', tok.NAME) + def _name(self, node): + if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC: + return # left side of object literal + if node.parent.kind == tok.VAR: + _warn_or_declare(scopes[-1], node.atom, 'var', node, report) + return + if node.parent.kind == tok.CATCH: + scopes[-1].add_declaration(node.atom, node, 'var') + scopes[-1].add_reference(node.atom, node) + + @visitation.visit('push', tok.FUNCTION) + def _push_func(self, node): + if node.fn_name: + _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) + self._push_scope(node) + for var_name in node.fn_args: + scopes[-1].add_declaration(var_name, node, 'arg') + + @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH) + def _push_scope(self, node): + scopes.append(scopes[-1].add_scope(node)) + + @visitation.visit('pop', tok.FUNCTION, tok.LEXICALSCOPE, tok.WITH) + def _pop_scope(self, node): + scopes.pop() + + return scope_checks + + +def _lint_node(node, visitors): + + for kind in (node.kind, (node.kind, node.opcode)): + if kind in visitors['push']: + for visitor in visitors['push'][kind]: + visitor(node) + + for child in node.kids: + if child: + _lint_node(child, visitors) + + for kind in (node.kind, (node.kind, node.opcode)): + if kind in visitors['pop']: + for visitor in visitors['pop'][kind]: + visitor(node) + + +class TestLint(unittest.TestCase): + def testFindScript(self): + html = """ + + +hi&b +a +ok& +.. +ok& + + +""" + scripts = [(x.get('src'), x.get('contents')) + for x in _findhtmlscripts(html, util.JSVersion.default())] + self.assertEquals(scripts, [ + ('test.js', None), + (None, "") + ]) + def testJSVersion(self): + def parsetag(starttag, default_version=None): + script, = _findhtmlscripts(starttag + '/**/', \ + default_version) + return script + + script = parsetag(' + * + * It does not cope with malformed comment hiding hacks where --> is hidden + * by C-style comments, or on a dirty line. Such cases are already broken. + */ +#define TSF_IN_HTML_COMMENT 0x2000 + +/* Ignore keywords and return TOK_NAME instead to the parser. */ +#define TSF_KEYWORD_IS_NAME 0x4000 + +/* Unicode separators that are treated as line terminators, in addition to \n, \r */ +#define LINE_SEPARATOR 0x2028 +#define PARA_SEPARATOR 0x2029 + +/* + * Create a new token stream, either from an input buffer or from a file. + * Return null on file-open or memory-allocation failure. + * + * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient + * memory in the current context's temp pool. This memory is deallocated via + * JS_ARENA_RELEASE() after parsing is finished. + */ +extern JSTokenStream * +js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, + const char *filename, uintN lineno, JSPrincipals *principals); + +extern JS_FRIEND_API(JSTokenStream *) +js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length); + +extern JS_FRIEND_API(JSTokenStream *) +js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); + +extern JS_FRIEND_API(JSBool) +js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); + +extern JS_FRIEND_API(int) +js_fgets(char *buf, int size, FILE *file); + +/* + * If the given char array forms JavaScript keyword, return corresponding + * token. Otherwise return TOK_EOF. + */ +extern JSTokenType +js_CheckKeyword(const jschar *chars, size_t length); + +#define js_IsKeyword(chars, length) \ + (js_CheckKeyword(chars, length) != TOK_EOF) + +/* + * Friend-exported API entry point to call a mapping function on each reserved + * identifier in the scanner's keyword table. + */ +extern JS_FRIEND_API(void) +js_MapKeywords(void (*mapfun)(const char *)); + +/* + * Report a compile-time error by its number, using ts or cg to show context. + * Return true for a warning, false for an error. + */ +extern JSBool +js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...); + +extern JSBool +js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...); + +/* Steal some JSREPORT_* bits (see jsapi.h) to tell handle's type. */ +#define JSREPORT_HANDLE 0x300 +#define JSREPORT_TS 0x000 +#define JSREPORT_CG 0x100 +#define JSREPORT_PN 0x200 + +/* + * Look ahead one token and return its type. + */ +extern JSTokenType +js_PeekToken(JSContext *cx, JSTokenStream *ts); + +extern JSTokenType +js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts); + +/* + * Get the next token from ts. + */ +extern JSTokenType +js_GetToken(JSContext *cx, JSTokenStream *ts); + +/* + * Push back the last scanned token onto ts. + */ +extern void +js_UngetToken(JSTokenStream *ts); + +/* + * Get the next token from ts if its type is tt. + */ +extern JSBool +js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt); + +JS_END_EXTERN_C + +#endif /* jsscan_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscope.c b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscope.c new file mode 100644 index 0000000..49b55a6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscope.c @@ -0,0 +1,1776 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS symbol tables. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsarena.h" +#include "jsbit.h" +#include "jsclist.h" +#include "jsdhash.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsscope.h" +#include "jsstr.h" + +JSScope * +js_GetMutableScope(JSContext *cx, JSObject *obj) +{ + JSScope *scope, *newscope; + + scope = OBJ_SCOPE(obj); + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + if (scope->object == obj) + return scope; + newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), + obj); + if (!newscope) + return NULL; + JS_LOCK_SCOPE(cx, newscope); + obj->map = js_HoldObjectMap(cx, &newscope->map); + scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); + JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); + return newscope; +} + +/* + * JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized + * to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD + * entries, we use linear search and avoid allocating scope->table. + */ +#define SCOPE_HASH_THRESHOLD 6 +#define MIN_SCOPE_SIZE_LOG2 4 +#define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2) +#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) + +static void +InitMinimalScope(JSScope *scope) +{ + scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; + scope->entryCount = scope->removedCount = 0; + scope->table = NULL; + scope->lastProp = NULL; +} + +static JSBool +CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) +{ + int sizeLog2; + JSScopeProperty *sprop, **spp; + + JS_ASSERT(!scope->table); + JS_ASSERT(scope->lastProp); + + if (scope->entryCount > SCOPE_HASH_THRESHOLD) { + /* + * Ouch: calloc failed at least once already -- let's try again, + * overallocating to hold at least twice the current population. + */ + sizeLog2 = JS_CeilingLog2(2 * scope->entryCount); + scope->hashShift = JS_DHASH_BITS - sizeLog2; + } else { + JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2); + sizeLog2 = MIN_SCOPE_SIZE_LOG2; + } + + scope->table = (JSScopeProperty **) + calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *)); + if (!scope->table) { + if (report) + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); + + scope->hashShift = JS_DHASH_BITS - sizeLog2; + for (sprop = scope->lastProp; sprop; sprop = sprop->parent) { + spp = js_SearchScope(scope, sprop->id, JS_TRUE); + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + } + return JS_TRUE; +} + +JSScope * +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj) +{ + JSScope *scope; + + scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); + if (!scope) + return NULL; + + js_InitObjectMap(&scope->map, nrefs, ops, clasp); + scope->object = obj; + scope->flags = 0; + InitMinimalScope(scope); + +#ifdef JS_THREADSAFE + scope->ownercx = cx; + memset(&scope->lock, 0, sizeof scope->lock); + + /* + * Set u.link = NULL, not u.count = 0, in case the target architecture's + * null pointer has a non-zero integer representation. + */ + scope->u.link = NULL; + +#ifdef DEBUG + scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL; + scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0; +#endif +#endif + + JS_RUNTIME_METER(cx->runtime, liveScopes); + JS_RUNTIME_METER(cx->runtime, totalScopes); + return scope; +} + +#ifdef DEBUG_SCOPE_COUNT +extern void +js_unlog_scope(JSScope *scope); +#endif + +void +js_DestroyScope(JSContext *cx, JSScope *scope) +{ +#ifdef DEBUG_SCOPE_COUNT + js_unlog_scope(scope); +#endif + +#ifdef JS_THREADSAFE + /* Scope must be single-threaded at this point, so set scope->ownercx. */ + JS_ASSERT(scope->u.count == 0); + scope->ownercx = cx; + js_FinishLock(&scope->lock); +#endif + if (scope->table) + JS_free(cx, scope->table); + +#ifdef DEBUG + JS_LOCK_RUNTIME_VOID(cx->runtime, + cx->runtime->liveScopeProps -= scope->entryCount); +#endif + JS_RUNTIME_UNMETER(cx->runtime, liveScopes); + JS_free(cx, scope); +} + +#ifdef DUMP_SCOPE_STATS +typedef struct JSScopeStats { + jsrefcount searches; + jsrefcount steps; + jsrefcount hits; + jsrefcount misses; + jsrefcount stepHits; + jsrefcount stepMisses; + jsrefcount adds; + jsrefcount redundantAdds; + jsrefcount addFailures; + jsrefcount changeFailures; + jsrefcount compresses; + jsrefcount grows; + jsrefcount removes; + jsrefcount removeFrees; + jsrefcount uselessRemoves; + jsrefcount shrinks; +} JSScopeStats; + +JS_FRIEND_DATA(JSScopeStats) js_scope_stats; + +# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) +#else +# define METER(x) /* nothing */ +#endif + +/* + * Double hashing needs the second hash code to be relatively prime to table + * size, so we simply make hash2 odd. The inputs to multiplicative hash are + * the golden ratio, expressed as a fixed-point 32 bit fraction, and the int + * property index or named property's atom number (observe that most objects + * have either no indexed properties, or almost all indexed and a few names, + * so collisions between index and atom number are unlikely). + */ +#define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO) +#define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift)) +#define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) + +JS_FRIEND_API(JSScopeProperty **) +js_SearchScope(JSScope *scope, jsid id, JSBool adding) +{ + JSHashNumber hash0, hash1, hash2; + int hashShift, sizeLog2; + JSScopeProperty *stored, *sprop, **spp, **firstRemoved; + uint32 sizeMask; + + METER(searches); + if (!scope->table) { + /* Not enough properties to justify hashing: search from lastProp. */ + JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); + for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) { + if (sprop->id == id) { + METER(hits); + return spp; + } + } + METER(misses); + return spp; + } + + /* Compute the primary hash address. */ + hash0 = SCOPE_HASH0(id); + hashShift = scope->hashShift; + hash1 = SCOPE_HASH1(hash0, hashShift); + spp = scope->table + hash1; + + /* Miss: return space for a new entry. */ + stored = *spp; + if (SPROP_IS_FREE(stored)) { + METER(misses); + return spp; + } + + /* Hit: return entry. */ + sprop = SPROP_CLEAR_COLLISION(stored); + if (sprop && sprop->id == id) { + METER(hits); + return spp; + } + + /* Collision: double hash. */ + sizeLog2 = JS_DHASH_BITS - hashShift; + hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift); + sizeMask = JS_BITMASK(sizeLog2); + + /* Save the first removed entry pointer so we can recycle it if adding. */ + if (SPROP_IS_REMOVED(stored)) { + firstRemoved = spp; + } else { + firstRemoved = NULL; + if (adding && !SPROP_HAD_COLLISION(stored)) + SPROP_FLAG_COLLISION(spp, sprop); + } + + for (;;) { + METER(steps); + hash1 -= hash2; + hash1 &= sizeMask; + spp = scope->table + hash1; + + stored = *spp; + if (SPROP_IS_FREE(stored)) { + METER(stepMisses); + return (adding && firstRemoved) ? firstRemoved : spp; + } + + sprop = SPROP_CLEAR_COLLISION(stored); + if (sprop && sprop->id == id) { + METER(stepHits); + return spp; + } + + if (SPROP_IS_REMOVED(stored)) { + if (!firstRemoved) + firstRemoved = spp; + } else { + if (adding && !SPROP_HAD_COLLISION(stored)) + SPROP_FLAG_COLLISION(spp, sprop); + } + } + + /* NOTREACHED */ + return NULL; +} + +static JSBool +ChangeScope(JSContext *cx, JSScope *scope, int change) +{ + int oldlog2, newlog2; + uint32 oldsize, newsize, nbytes; + JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop; + + /* Grow, shrink, or compress by changing scope->table. */ + oldlog2 = JS_DHASH_BITS - scope->hashShift; + newlog2 = oldlog2 + change; + oldsize = JS_BIT(oldlog2); + newsize = JS_BIT(newlog2); + nbytes = SCOPE_TABLE_NBYTES(newsize); + table = (JSScopeProperty **) calloc(nbytes, 1); + if (!table) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + /* Now that we have a new table allocated, update scope members. */ + scope->hashShift = JS_DHASH_BITS - newlog2; + scope->removedCount = 0; + oldtable = scope->table; + scope->table = table; + + /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ + cx->runtime->gcMallocBytes += nbytes; + + /* Copy only live entries, leaving removed and free ones behind. */ + for (oldspp = oldtable; oldsize != 0; oldspp++) { + sprop = SPROP_FETCH(oldspp); + if (sprop) { + spp = js_SearchScope(scope, sprop->id, JS_TRUE); + JS_ASSERT(SPROP_IS_FREE(*spp)); + *spp = sprop; + } + oldsize--; + } + + /* Finally, free the old table storage. */ + JS_free(cx, oldtable); + return JS_TRUE; +} + +/* + * Take care to exclude the mark and duplicate bits, in case we're called from + * the GC, or we are searching for a property that has not yet been flagged as + * a duplicate when making a duplicate formal parameter. + */ +#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE) + +JS_STATIC_DLL_CALLBACK(JSDHashNumber) +js_HashScopeProperty(JSDHashTable *table, const void *key) +{ + const JSScopeProperty *sprop = (const JSScopeProperty *)key; + JSDHashNumber hash; + JSPropertyOp gsop; + + /* Accumulate from least to most random so the low bits are most random. */ + hash = 0; + gsop = sprop->getter; + if (gsop) + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; + gsop = sprop->setter; + if (gsop) + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; + + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) + ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); + + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->attrs; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->shortid; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->slot; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->id; + return hash; +} + +#define SPROP_MATCH(sprop, child) \ + SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \ + (child)->slot, (child)->attrs, (child)->flags, \ + (child)->shortid) + +#define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \ + aflags, ashortid) \ + ((sprop)->id == (aid) && \ + SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ + aflags, ashortid)) + +#define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ + aflags, ashortid) \ + ((sprop)->getter == (agetter) && \ + (sprop)->setter == (asetter) && \ + (sprop)->slot == (aslot) && \ + (sprop)->attrs == (aattrs) && \ + (((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \ + (sprop)->shortid == (ashortid)) + +JS_STATIC_DLL_CALLBACK(JSBool) +js_MatchScopeProperty(JSDHashTable *table, + const JSDHashEntryHdr *hdr, + const void *key) +{ + const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr; + const JSScopeProperty *sprop = entry->child; + const JSScopeProperty *kprop = (const JSScopeProperty *)key; + + return SPROP_MATCH(sprop, kprop); +} + +static const JSDHashTableOps PropertyTreeHashOps = { + JS_DHashAllocTable, + JS_DHashFreeTable, + JS_DHashGetKeyStub, + js_HashScopeProperty, + js_MatchScopeProperty, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +/* + * A property tree node on rt->propertyFreeList overlays the following prefix + * struct on JSScopeProperty. + */ +typedef struct FreeNode { + jsid id; + JSScopeProperty *next; + JSScopeProperty **prevp; +} FreeNode; + +#define FREENODE(sprop) ((FreeNode *) (sprop)) + +#define FREENODE_INSERT(list, sprop) \ + JS_BEGIN_MACRO \ + FREENODE(sprop)->next = (list); \ + FREENODE(sprop)->prevp = &(list); \ + if (list) \ + FREENODE(list)->prevp = &FREENODE(sprop)->next; \ + (list) = (sprop); \ + JS_END_MACRO + +#define FREENODE_REMOVE(sprop) \ + JS_BEGIN_MACRO \ + *FREENODE(sprop)->prevp = FREENODE(sprop)->next; \ + if (FREENODE(sprop)->next) \ + FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \ + JS_END_MACRO + +/* NB: Called with the runtime lock held. */ +static JSScopeProperty * +NewScopeProperty(JSRuntime *rt) +{ + JSScopeProperty *sprop; + + sprop = rt->propertyFreeList; + if (sprop) { + FREENODE_REMOVE(sprop); + } else { + JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *, + &rt->propertyArenaPool, + sizeof(JSScopeProperty)); + if (!sprop) + return NULL; + } + + JS_RUNTIME_METER(rt, livePropTreeNodes); + JS_RUNTIME_METER(rt, totalPropTreeNodes); + return sprop; +} + +#define CHUNKY_KIDS_TAG ((jsuword)1) +#define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG) +#define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \ + ((jsuword)(kids) & ~CHUNKY_KIDS_TAG)) +#define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \ + ((jsuword)(chunk) | CHUNKY_KIDS_TAG)) +#define MAX_KIDS_PER_CHUNK 10 + +typedef struct PropTreeKidsChunk PropTreeKidsChunk; + +struct PropTreeKidsChunk { + JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; + PropTreeKidsChunk *next; +}; + +static PropTreeKidsChunk * +NewPropTreeKidsChunk(JSRuntime *rt) +{ + PropTreeKidsChunk *chunk; + + chunk = calloc(1, sizeof *chunk); + if (!chunk) + return NULL; + JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0); + JS_RUNTIME_METER(rt, propTreeKidsChunks); + return chunk; +} + +static void +DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) +{ + JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); + free(chunk); +} + +/* NB: Called with the runtime lock held. */ +static JSBool +InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, + JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) +{ + JSPropertyTreeEntry *entry; + JSScopeProperty **childp, *kids, *sprop; + PropTreeKidsChunk *chunk, **chunkp; + uintN i; + + JS_ASSERT(!parent || child->parent != parent); + + if (!parent) { + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + if (!entry) + return JS_FALSE; + childp = &entry->child; + sprop = *childp; + if (!sprop) { + *childp = child; + } else { + /* + * A "Duplicate child" case. + * + * We can't do away with child, as at least one live scope entry + * still points at it. What's more, that scope's lastProp chains + * through an ancestor line to reach child, and js_Enumerate and + * others count on this linkage. We must leave child out of the + * hash table, and not require it to be there when we eventually + * GC it (see RemovePropertyTreeChild, below). + * + * It is necessary to leave the duplicate child out of the hash + * table to preserve entry uniqueness. It is safe to leave the + * child out of the hash table (unlike the duplicate child cases + * below), because the child's parent link will be null, which + * can't dangle. + */ + JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child)); + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + } else { + childp = &parent->kids; + kids = *childp; + if (kids) { + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + childp = &chunk->kids[i]; + sprop = *childp; + if (!sprop) + goto insert; + + JS_ASSERT(sprop != child); + if (SPROP_MATCH(sprop, child)) { + /* + * Duplicate child, see comment above. In this + * case, we must let the duplicate be inserted at + * this level in the tree, so we keep iterating, + * looking for an empty slot in which to insert. + */ + JS_ASSERT(sprop != child); + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + } + chunkp = &chunk->next; + } while ((chunk = *chunkp) != NULL); + + if (sweptChunk) { + chunk = sweptChunk; + } else { + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_FALSE; + } + *chunkp = chunk; + childp = &chunk->kids[0]; + } else { + sprop = kids; + JS_ASSERT(sprop != child); + if (SPROP_MATCH(sprop, child)) { + /* + * Duplicate child, see comment above. Once again, we + * must let duplicates created by deletion pile up in a + * kids-chunk-list, in order to find them when sweeping + * and thereby avoid dangling parent pointers. + */ + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + if (sweptChunk) { + chunk = sweptChunk; + } else { + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_FALSE; + } + parent->kids = CHUNK_TO_KIDS(chunk); + chunk->kids[0] = sprop; + childp = &chunk->kids[1]; + } + } + insert: + *childp = child; + } + + child->parent = parent; + return JS_TRUE; +} + +/* NB: Called with the runtime lock held. */ +static PropTreeKidsChunk * +RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) +{ + JSPropertyTreeEntry *entry; + JSScopeProperty *parent, *kids, *kid; + PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; + uintN i, j; + + parent = child->parent; + if (!parent) { + /* + * Don't remove child if it is not in rt->propertyTreeHash, but only + * matches a root child in the table that has compatible members. See + * the "Duplicate child" comments in InsertPropertyTreeChild, above. + */ + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP); + + if (entry->child == child) + JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr); + } else { + kids = parent->kids; + if (KIDS_IS_CHUNKY(kids)) { + list = chunk = KIDS_TO_CHUNK(kids); + chunkp = &list; + + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + if (chunk->kids[i] == child) { + lastChunk = chunk; + if (!lastChunk->next) { + j = i + 1; + } else { + j = 0; + do { + chunkp = &lastChunk->next; + lastChunk = *chunkp; + } while (lastChunk->next); + } + for (; j < MAX_KIDS_PER_CHUNK; j++) { + if (!lastChunk->kids[j]) + break; + } + --j; + if (chunk != lastChunk || j > i) + chunk->kids[i] = lastChunk->kids[j]; + lastChunk->kids[j] = NULL; + if (j == 0) { + *chunkp = NULL; + if (!list) + parent->kids = NULL; + return lastChunk; + } + return NULL; + } + } + + chunkp = &chunk->next; + } while ((chunk = *chunkp) != NULL); + } else { + kid = kids; + if (kid == child) + parent->kids = NULL; + } + } + return NULL; +} + +/* + * Called *without* the runtime lock held, this function acquires that lock + * only when inserting a new child. Thus there may be races to find or add + * a node that result in duplicates. We expect such races to be rare! + */ +static JSScopeProperty * +GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, + JSScopeProperty *child) +{ + JSRuntime *rt; + JSPropertyTreeEntry *entry; + JSScopeProperty *sprop; + PropTreeKidsChunk *chunk; + uintN i; + + rt = cx->runtime; + if (!parent) { + JS_LOCK_RUNTIME(rt); + + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + if (!entry) + goto out_of_memory; + + sprop = entry->child; + if (sprop) + goto out; + } else { + /* + * Because chunks are appended at the end and never deleted except by + * the GC, we can search without taking the runtime lock. We may miss + * a matching sprop added by another thread, and make a duplicate one, + * but that is an unlikely, therefore small, cost. The property tree + * has extremely low fan-out below its root in popular embeddings with + * real-world workloads. + * + * If workload changes so as to increase fan-out significantly below + * the property tree root, we'll want to add another tag bit stored in + * parent->kids that indicates a JSDHashTable pointer. + */ + entry = NULL; + sprop = parent->kids; + if (sprop) { + if (KIDS_IS_CHUNKY(sprop)) { + chunk = KIDS_TO_CHUNK(sprop); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + sprop = chunk->kids[i]; + if (!sprop) + goto not_found; + + if (SPROP_MATCH(sprop, child)) + return sprop; + } + } while ((chunk = chunk->next) != NULL); + } else { + if (SPROP_MATCH(sprop, child)) + return sprop; + } + } + + not_found: + JS_LOCK_RUNTIME(rt); + } + + sprop = NewScopeProperty(rt); + if (!sprop) + goto out_of_memory; + + sprop->id = child->id; + sprop->getter = child->getter; + sprop->setter = child->setter; + sprop->slot = child->slot; + sprop->attrs = child->attrs; + sprop->flags = child->flags; + sprop->shortid = child->shortid; + sprop->parent = sprop->kids = NULL; + if (!parent) { + entry->child = sprop; + } else { + if (!InsertPropertyTreeChild(rt, parent, sprop, NULL)) + goto out_of_memory; + } + +out: + JS_UNLOCK_RUNTIME(rt); + return sprop; + +out_of_memory: + JS_UNLOCK_RUNTIME(rt); + JS_ReportOutOfMemory(cx); + return NULL; +} + +#ifdef DEBUG_notbrendan +#define CHECK_ANCESTOR_LINE(scope, sparse) \ + JS_BEGIN_MACRO \ + if ((scope)->table) CheckAncestorLine(scope, sparse); \ + JS_END_MACRO + +static void +CheckAncestorLine(JSScope *scope, JSBool sparse) +{ + uint32 size; + JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; + uint32 entryCount, ancestorCount; + + ancestorLine = SCOPE_LAST_PROP(scope); + if (ancestorLine) + JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine)); + + entryCount = 0; + size = SCOPE_CAPACITY(scope); + start = scope->table; + for (spp = start, end = start + size; spp < end; spp++) { + sprop = SPROP_FETCH(spp); + if (sprop) { + entryCount++; + for (aprop = ancestorLine; aprop; aprop = aprop->parent) { + if (aprop == sprop) + break; + } + JS_ASSERT(aprop); + } + } + JS_ASSERT(entryCount == scope->entryCount); + + ancestorCount = 0; + for (sprop = ancestorLine; sprop; sprop = sprop->parent) { + if (SCOPE_HAD_MIDDLE_DELETE(scope) && + !SCOPE_HAS_PROPERTY(scope, sprop)) { + JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); + continue; + } + ancestorCount++; + } + JS_ASSERT(ancestorCount == scope->entryCount); +} +#else +#define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */ +#endif + +static void +ReportReadOnlyScope(JSContext *cx, JSScope *scope) +{ + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object)); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, + str + ? JS_GetStringBytes(str) + : LOCKED_OBJ_GET_CLASS(scope->object)->name); +} + +JSScopeProperty * +js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid) +{ + JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; + uint32 size, splen, i; + int change; + JSTempValueRooter tvr; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* + * You can't add properties to a sealed scope. But note well that you can + * change property attributes in a sealed scope, even though that replaces + * a JSScopeProperty * in the scope's hash table -- but no id is added, so + * the scope remains sealed. + */ + if (SCOPE_IS_SEALED(scope)) { + ReportReadOnlyScope(cx, scope); + return NULL; + } + + /* + * Normalize stub getter and setter values for faster is-stub testing in + * the SPROP_CALL_[GS]ETTER macros. + */ + if (getter == JS_PropertyStub) + getter = NULL; + if (setter == JS_PropertyStub) + setter = NULL; + + /* + * Search for id in order to claim its entry, allocating a property tree + * node if one doesn't already exist for our parameters. + */ + spp = js_SearchScope(scope, id, JS_TRUE); + sprop = overwriting = SPROP_FETCH(spp); + if (!sprop) { + /* Check whether we need to grow, if the load factor is >= .75. */ + size = SCOPE_CAPACITY(scope); + if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { + if (scope->removedCount >= size >> 2) { + METER(compresses); + change = 0; + } else { + METER(grows); + change = 1; + } + if (!ChangeScope(cx, scope, change) && + scope->entryCount + scope->removedCount == size - 1) { + METER(addFailures); + return NULL; + } + spp = js_SearchScope(scope, id, JS_TRUE); + JS_ASSERT(!SPROP_FETCH(spp)); + } + } else { + /* Property exists: js_SearchScope must have returned a valid entry. */ + JS_ASSERT(!SPROP_IS_REMOVED(*spp)); + + /* + * If all property members match, this is a redundant add and we can + * return early. If the caller wants to allocate a slot, but doesn't + * care which slot, copy sprop->slot into slot so we can match sprop, + * if all other members match. + */ + if (!(attrs & JSPROP_SHARED) && + slot == SPROP_INVALID_SLOT && + SPROP_HAS_VALID_SLOT(sprop, scope)) { + slot = sprop->slot; + } + if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, + flags, shortid)) { + METER(redundantAdds); + return sprop; + } + + /* + * Duplicate formal parameters require us to leave the old property + * on the ancestor line, so the decompiler can find it, even though + * its entry in scope->table is overwritten to point at a new property + * descending from the old one. The SPROP_IS_DUPLICATE flag helps us + * cope with the consequent disparity between ancestor line height and + * scope->entryCount. + */ + if (flags & SPROP_IS_DUPLICATE) { + sprop->flags |= SPROP_IS_DUPLICATE; + } else { + /* + * If we are clearing sprop to force an existing property to be + * overwritten (apart from a duplicate formal parameter), we must + * unlink it from the ancestor line at scope->lastProp, lazily if + * sprop is not lastProp. And we must remove the entry at *spp, + * precisely so the lazy "middle delete" fixup code further below + * won't find sprop in scope->table, in spite of sprop being on + * the ancestor line. + * + * When we finally succeed in finding or creating a new sprop + * and storing its pointer at *spp, we'll use the |overwriting| + * local saved when we first looked up id to decide whether we're + * indeed creating a new entry, or merely overwriting an existing + * property. + */ + if (sprop == SCOPE_LAST_PROP(scope)) { + do { + SCOPE_REMOVE_LAST_PROP(scope); + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + sprop = SCOPE_LAST_PROP(scope); + } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); + } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { + /* + * If we have no hash table yet, we need one now. The middle + * delete code is simple-minded that way! + */ + if (!scope->table) { + if (!CreateScopeTable(cx, scope, JS_TRUE)) + return NULL; + spp = js_SearchScope(scope, id, JS_TRUE); + sprop = overwriting = SPROP_FETCH(spp); + } + SCOPE_SET_MIDDLE_DELETE(scope); + } + } + + /* + * If we fail later on trying to find or create a new sprop, we will + * goto fail_overwrite and restore *spp from |overwriting|. Note that + * we don't bother to keep scope->removedCount in sync, because we'll + * fix up *spp and scope->entryCount shortly, no matter how control + * flow returns from this function. + */ + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, NULL); + scope->entryCount--; + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + sprop = NULL; + } + + if (!sprop) { + /* + * If properties were deleted from the middle of the list starting at + * scope->lastProp, we may need to fork the property tree and squeeze + * all deleted properties out of scope's ancestor line. Otherwise we + * risk adding a node with the same id as a "middle" node, violating + * the rule that properties along an ancestor line have distinct ids + * (unless flagged SPROP_IS_DUPLICATE). + */ + if (SCOPE_HAD_MIDDLE_DELETE(scope)) { + JS_ASSERT(scope->table); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + splen = scope->entryCount; + if (splen == 0) { + JS_ASSERT(scope->lastProp == NULL); + } else { + /* + * Enumerate live entries in scope->table using a temporary + * vector, by walking the (possibly sparse, due to deletions) + * ancestor line from scope->lastProp. + */ + spvec = (JSScopeProperty **) + JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); + if (!spvec) + goto fail_overwrite; + i = splen; + sprop = SCOPE_LAST_PROP(scope); + JS_ASSERT(sprop); + do { + /* + * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- + * the latter insists that sprop->id maps to sprop, while + * the former simply tests whether sprop->id is bound in + * scope. We must allow for duplicate formal parameters + * along the ancestor line, and fork them as needed. + */ + if (!SCOPE_GET_PROPERTY(scope, sprop->id)) + continue; + + JS_ASSERT(sprop != overwriting); + if (i == 0) { + /* + * If our original splen estimate, scope->entryCount, + * is less than the ancestor line height, there must + * be duplicate formal parameters in this (function + * object) scope. Count remaining ancestors in order + * to realloc spvec. + */ + JSScopeProperty *tmp = sprop; + do { + if (SCOPE_GET_PROPERTY(scope, tmp->id)) + i++; + } while ((tmp = tmp->parent) != NULL); + spp2 = (JSScopeProperty **) + JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i)); + if (!spp2) { + JS_free(cx, spvec); + goto fail_overwrite; + } + + spvec = spp2; + memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); + splen += i; + } + + spvec[--i] = sprop; + } while ((sprop = sprop->parent) != NULL); + JS_ASSERT(i == 0); + + /* + * Now loop forward through spvec, forking the property tree + * whenever we see a "parent gap" due to deletions from scope. + * NB: sprop is null on first entry to the loop body. + */ + do { + if (spvec[i]->parent == sprop) { + sprop = spvec[i]; + } else { + sprop = GetPropertyTreeChild(cx, sprop, spvec[i]); + if (!sprop) { + JS_free(cx, spvec); + goto fail_overwrite; + } + + spp2 = js_SearchScope(scope, sprop->id, JS_FALSE); + JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]); + SPROP_STORE_PRESERVING_COLLISION(spp2, sprop); + } + } while (++i < splen); + JS_free(cx, spvec); + + /* + * Now sprop points to the last property in scope, where the + * ancestor line from sprop to the root is dense w.r.t. scope: + * it contains no nodes not mapped by scope->table, apart from + * any stinking ECMA-mandated duplicate formal parameters. + */ + scope->lastProp = sprop; + CHECK_ANCESTOR_LINE(scope, JS_FALSE); + JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); + } + + SCOPE_CLR_MIDDLE_DELETE(scope); + } + + /* + * Aliases share another property's slot, passed in the |slot| param. + * Shared properties have no slot. Unshared properties that do not + * alias another property's slot get one here, but may lose it due to + * a JS_ClearScope call. + */ + if (!(flags & SPROP_IS_ALIAS)) { + if (attrs & JSPROP_SHARED) { + slot = SPROP_INVALID_SLOT; + } else { + /* + * We may have set slot from a nearly-matching sprop, above. + * If so, we're overwriting that nearly-matching sprop, so we + * can reuse its slot -- we don't need to allocate a new one. + * Callers should therefore pass SPROP_INVALID_SLOT for all + * non-alias, unshared property adds. + */ + if (slot != SPROP_INVALID_SLOT) + JS_ASSERT(overwriting); + else if (!js_AllocSlot(cx, scope->object, &slot)) + goto fail_overwrite; + } + } + + /* + * Check for a watchpoint on a deleted property; if one exists, change + * setter to js_watch_set. + * XXXbe this could get expensive with lots of watchpoints... + */ + if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && + js_FindWatchPoint(cx->runtime, scope, id)) { + JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); + setter = js_WrapWatchedSetter(cx, id, attrs, setter); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!setter) + goto fail_overwrite; + } + + /* Find or create a property tree node labeled by our arguments. */ + child.id = id; + child.getter = getter; + child.setter = setter; + child.slot = slot; + child.attrs = attrs; + child.flags = flags; + child.shortid = shortid; + sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); + if (!sprop) + goto fail_overwrite; + + /* Store the tree node pointer in the table entry for id. */ + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + scope->entryCount++; + scope->lastProp = sprop; + CHECK_ANCESTOR_LINE(scope, JS_FALSE); + if (!overwriting) { + JS_RUNTIME_METER(cx->runtime, liveScopeProps); + JS_RUNTIME_METER(cx->runtime, totalScopeProps); + } + + /* + * If we reach the hashing threshold, try to allocate scope->table. + * If we can't (a rare event, preceded by swapping to death on most + * modern OSes), stick with linear search rather than whining about + * this little set-back. Therefore we must test !scope->table and + * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the + * entry count just reached the threshold. + */ + if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) + (void) CreateScopeTable(cx, scope, JS_FALSE); + } + + METER(adds); + return sprop; + +fail_overwrite: + if (overwriting) { + /* + * We may or may not have forked overwriting out of scope's ancestor + * line, so we must check (the alternative is to set a flag above, but + * that hurts the common, non-error case). If we did fork overwriting + * out, we'll add it back at scope->lastProp. This means enumeration + * order can change due to a failure to overwrite an id. + * XXXbe very minor incompatibility + */ + for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) { + if (!sprop) { + sprop = SCOPE_LAST_PROP(scope); + if (overwriting->parent == sprop) { + scope->lastProp = overwriting; + } else { + sprop = GetPropertyTreeChild(cx, sprop, overwriting); + if (sprop) { + JS_ASSERT(sprop != overwriting); + scope->lastProp = sprop; + } + overwriting = sprop; + } + break; + } + if (sprop == overwriting) + break; + } + if (overwriting) { + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); + scope->entryCount++; + } + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + } + METER(addFailures); + return NULL; +} + +JSScopeProperty * +js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter) +{ + JSScopeProperty child, *newsprop, **spp; + + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* Allow only shared (slot-less) => unshared (slot-full) transition. */ + attrs |= sprop->attrs & mask; + JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || + !(attrs & JSPROP_SHARED)); + if (getter == JS_PropertyStub) + getter = NULL; + if (setter == JS_PropertyStub) + setter = NULL; + if (sprop->attrs == attrs && + sprop->getter == getter && + sprop->setter == setter) { + return sprop; + } + + child.id = sprop->id; + child.getter = getter; + child.setter = setter; + child.slot = sprop->slot; + child.attrs = attrs; + child.flags = sprop->flags; + child.shortid = sprop->shortid; + + if (SCOPE_LAST_PROP(scope) == sprop) { + /* + * Optimize the case where the last property added to scope is changed + * to have a different attrs, getter, or setter. In the last property + * case, we need not fork the property tree. But since we do not call + * js_AddScopeProperty, we may need to allocate a new slot directly. + */ + if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { + JS_ASSERT(child.slot == SPROP_INVALID_SLOT); + if (!js_AllocSlot(cx, scope->object, &child.slot)) + return NULL; + } + + newsprop = GetPropertyTreeChild(cx, sprop->parent, &child); + if (newsprop) { + spp = js_SearchScope(scope, sprop->id, JS_FALSE); + JS_ASSERT(SPROP_FETCH(spp) == sprop); + + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); + scope->lastProp = newsprop; + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + } + } else { + /* + * Let js_AddScopeProperty handle this |overwriting| case, including + * the conservation of sprop->slot (if it's valid). We must not call + * js_RemoveScopeProperty here, it will free a valid sprop->slot and + * js_AddScopeProperty won't re-allocate it. + */ + newsprop = js_AddScopeProperty(cx, scope, child.id, + child.getter, child.setter, child.slot, + child.attrs, child.flags, child.shortid); + } + +#ifdef DUMP_SCOPE_STATS + if (!newsprop) + METER(changeFailures); +#endif + return newsprop; +} + +JSBool +js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) +{ + JSScopeProperty **spp, *stored, *sprop; + uint32 size; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + if (SCOPE_IS_SEALED(scope)) { + ReportReadOnlyScope(cx, scope); + return JS_FALSE; + } + METER(removes); + + spp = js_SearchScope(scope, id, JS_FALSE); + stored = *spp; + sprop = SPROP_CLEAR_COLLISION(stored); + if (!sprop) { + METER(uselessRemoves); + return JS_TRUE; + } + + /* Convert from a list to a hash so we can handle "middle deletes". */ + if (!scope->table && sprop != scope->lastProp) { + if (!CreateScopeTable(cx, scope, JS_TRUE)) + return JS_FALSE; + spp = js_SearchScope(scope, id, JS_FALSE); + stored = *spp; + sprop = SPROP_CLEAR_COLLISION(stored); + } + + /* First, if sprop is unshared and not cleared, free its slot number. */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + js_FreeSlot(cx, scope->object, sprop->slot); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); + } + + /* Next, remove id by setting its entry to a removed or free sentinel. */ + if (SPROP_HAD_COLLISION(stored)) { + JS_ASSERT(scope->table); + *spp = SPROP_REMOVED; + scope->removedCount++; + } else { + METER(removeFrees); + if (scope->table) + *spp = NULL; + } + scope->entryCount--; + JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps); + + /* Update scope->lastProp directly, or set its deferred update flag. */ + if (sprop == SCOPE_LAST_PROP(scope)) { + do { + SCOPE_REMOVE_LAST_PROP(scope); + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + sprop = SCOPE_LAST_PROP(scope); + } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); + } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { + SCOPE_SET_MIDDLE_DELETE(scope); + } + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* Last, consider shrinking scope's table if its load factor is <= .25. */ + size = SCOPE_CAPACITY(scope); + if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) { + METER(shrinks); + (void) ChangeScope(cx, scope, -1); + } + + return JS_TRUE; +} + +void +js_ClearScope(JSContext *cx, JSScope *scope) +{ + CHECK_ANCESTOR_LINE(scope, JS_TRUE); +#ifdef DEBUG + JS_LOCK_RUNTIME_VOID(cx->runtime, + cx->runtime->liveScopeProps -= scope->entryCount); +#endif + + if (scope->table) + free(scope->table); + SCOPE_CLR_MIDDLE_DELETE(scope); + InitMinimalScope(scope); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); +} + +void +js_MarkId(JSContext *cx, jsid id) +{ + if (JSID_IS_ATOM(id)) + GC_MARK_ATOM(cx, JSID_TO_ATOM(id)); + else if (JSID_IS_OBJECT(id)) + GC_MARK(cx, JSID_TO_OBJECT(id), "id"); + else + JS_ASSERT(JSID_IS_INT(id)); +} + +#if defined GC_MARK_DEBUG || defined DUMP_SCOPE_STATS +# include "jsprf.h" +#endif + +void +js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop) +{ + sprop->flags |= SPROP_MARK; + MARK_ID(cx, sprop->id); + +#if JS_HAS_GETTER_SETTER + if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { +#ifdef GC_MARK_DEBUG + char buf[64]; + char buf2[11]; + const char *id; + + if (JSID_IS_ATOM(sprop->id)) { + JSAtom *atom = JSID_TO_ATOM(sprop->id); + + id = (atom && ATOM_IS_STRING(atom)) + ? JS_GetStringBytes(ATOM_TO_STRING(atom)) + : "unknown"; + } else if (JSID_IS_INT(sprop->id)) { + JS_snprintf(buf2, sizeof buf2, "%d", JSID_TO_INT(sprop->id)); + id = buf2; + } else { + id = ""; + } +#endif + + if (sprop->attrs & JSPROP_GETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_getter_str); +#endif + GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->getter), buf); + } + if (sprop->attrs & JSPROP_SETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_setter_str); +#endif + GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->setter), buf); + } + } +#endif /* JS_HAS_GETTER_SETTER */ +} + +#ifdef DUMP_SCOPE_STATS + +#include +#include + +uint32 js_nkids_max; +uint32 js_nkids_sum; +double js_nkids_sqsum; +uint32 js_nkids_hist[11]; + +static void +MeterKidCount(uintN nkids) +{ + if (nkids) { + js_nkids_sum += nkids; + js_nkids_sqsum += (double)nkids * nkids; + if (nkids > js_nkids_max) + js_nkids_max = nkids; + } + js_nkids_hist[JS_MIN(nkids, 10)]++; +} + +static void +MeterPropertyTree(JSScopeProperty *node) +{ + uintN i, nkids; + JSScopeProperty *kids, *kid; + PropTreeKidsChunk *chunk; + + nkids = 0; + kids = node->kids; + if (kids) { + if (KIDS_IS_CHUNKY(kids)) { + for (chunk = KIDS_TO_CHUNK(kids); chunk; chunk = chunk->next) { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + MeterPropertyTree(kid); + nkids++; + } + } + } else { + MeterPropertyTree(kids); + nkids = 1; + } + } + + MeterKidCount(nkids); +} + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr; + + MeterPropertyTree(entry->child); + return JS_DHASH_NEXT; +} + +static void +DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) +{ + char buf[10]; + JSScopeProperty *kids, *kid; + PropTreeKidsChunk *chunk; + uintN i; + + fprintf(fp, "%*sid %s g/s %p/%p slot %lu attrs %x flags %x shortid %d\n", + level, "", + JSID_IS_ATOM(sprop->id) + ? JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))) + : JSID_IS_OBJECT(sprop->id) + ? js_ValueToPrintableString(cx, OBJECT_JSID_TO_JSVAL(sprop->id)) + : (JS_snprintf(buf, sizeof buf, "%ld", JSVAL_TO_INT(sprop->id)), + buf) + (void *) sprop->getter, (void *) sprop->setter, + (unsigned long) sprop->slot, sprop->attrs, sprop->flags, + sprop->shortid); + kids = sprop->kids; + if (kids) { + ++level; + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + JS_ASSERT(kid->parent == sprop); + DumpSubtree(kid, level, fp); + } + } while ((chunk = chunk->next) != NULL); + } else { + kid = kids; + DumpSubtree(kid, level, fp); + } + } +} + +#endif /* DUMP_SCOPE_STATS */ + +void +js_SweepScopeProperties(JSRuntime *rt) +{ + JSArena **ap, *a; + JSScopeProperty *limit, *sprop, *parent, *kids, *kid; + uintN liveCount; + PropTreeKidsChunk *chunk, *nextChunk, *freeChunk; + uintN i; + +#ifdef DUMP_SCOPE_STATS + uint32 livePropCapacity = 0, totalLiveCount = 0; + static FILE *logfp; + if (!logfp) + logfp = fopen("/tmp/proptree.stats", "a"); + + MeterKidCount(rt->propertyTreeHash.entryCount); + JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL); + + { + double mean = 0.0, var = 0.0, sigma = 0.0; + double nodesum = rt->livePropTreeNodes; + double kidsum = js_nkids_sum; + if (nodesum > 0 && kidsum >= 0) { + mean = kidsum / nodesum; + var = nodesum * js_nkids_sqsum - kidsum * kidsum; + if (var < 0.0 || nodesum <= 1) + var = 0.0; + else + var /= nodesum * (nodesum - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.0) ? sqrt(var) : 0.0; + } + + fprintf(logfp, + "props %u nodes %g beta %g meankids %g sigma %g max %u", + rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps, + mean, sigma, js_nkids_max); + } + + fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u", + js_nkids_hist[0], js_nkids_hist[1], + js_nkids_hist[2], js_nkids_hist[3], + js_nkids_hist[4], js_nkids_hist[5], + js_nkids_hist[6], js_nkids_hist[7], + js_nkids_hist[8], js_nkids_hist[9], + js_nkids_hist[10]); + js_nkids_sum = js_nkids_max = 0; + js_nkids_sqsum = 0; + memset(js_nkids_hist, 0, sizeof js_nkids_hist); +#endif + + ap = &rt->propertyArenaPool.first.next; + while ((a = *ap) != NULL) { + limit = (JSScopeProperty *) a->avail; + liveCount = 0; + for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) { + /* If the id is null, sprop is already on the freelist. */ + if (sprop->id == JSVAL_NULL) + continue; + + /* If the mark bit is set, sprop is alive, so we skip it. */ + if (sprop->flags & SPROP_MARK) { + sprop->flags &= ~SPROP_MARK; + liveCount++; + continue; + } + + /* Ok, sprop is garbage to collect: unlink it from its parent. */ + freeChunk = RemovePropertyTreeChild(rt, sprop); + + /* + * Take care to reparent all sprop's kids to their grandparent. + * InsertPropertyTreeChild can potentially fail for two reasons: + * + * 1. If parent is null, insertion into the root property hash + * table may fail. We are forced to leave the kid out of the + * table (as can already happen with duplicates) but ensure + * that the kid's parent pointer is set to null. + * + * 2. If parent is non-null, allocation of a new KidsChunk can + * fail. To prevent this from happening, we allow sprops's own + * chunks to be reused by the grandparent, which removes the + * need for InsertPropertyTreeChild to malloc a new KidsChunk. + * + * If sprop does not have chunky kids, then we rely on the + * RemovePropertyTreeChild call above (which removed sprop from + * its parent) either leaving one free entry, or else returning + * the now-unused chunk to us so we can reuse it. + * + * We also require the grandparent to have either no kids or else + * chunky kids. A single non-chunky kid would force a new chunk to + * be malloced in some cases (if sprop had a single non-chunky + * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that + * RemovePropertyTreeChild never converts a single-entry chunky + * kid back to a non-chunky kid, so we are assured of correct + * behaviour. + */ + kids = sprop->kids; + if (kids) { + sprop->kids = NULL; + parent = sprop->parent; + /* Validate that grandparent has no kids or chunky kids. */ + JS_ASSERT(!parent || !parent->kids || + KIDS_IS_CHUNKY(parent->kids)); + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + nextChunk = chunk->next; + chunk->next = NULL; + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + JS_ASSERT(kid->parent == sprop); + + /* + * Clear a space in the kids array for possible + * re-use by InsertPropertyTreeChild. + */ + chunk->kids[i] = NULL; + if (!InsertPropertyTreeChild(rt, parent, kid, + chunk)) { + /* + * This can happen only if we failed to add an + * entry to the root property hash table. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } + } + if (!chunk->kids[0]) { + /* The chunk wasn't reused, so we must free it. */ + DestroyPropTreeKidsChunk(rt, chunk); + } + } while ((chunk = nextChunk) != NULL); + } else { + kid = kids; + if (!InsertPropertyTreeChild(rt, parent, kid, freeChunk)) { + /* + * This can happen only if we failed to add an entry + * to the root property hash table. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } + } + } + + if (freeChunk && !freeChunk->kids[0]) { + /* The chunk wasn't reused, so we must free it. */ + DestroyPropTreeKidsChunk(rt, freeChunk); + } + + /* Clear id so we know (above) that sprop is on the freelist. */ + sprop->id = JSVAL_NULL; + FREENODE_INSERT(rt->propertyFreeList, sprop); + JS_RUNTIME_UNMETER(rt, livePropTreeNodes); + } + + /* If a contains no live properties, return it to the malloc heap. */ + if (liveCount == 0) { + for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) + FREENODE_REMOVE(sprop); + JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap); + } else { +#ifdef DUMP_SCOPE_STATS + livePropCapacity += limit - (JSScopeProperty *) a->base; + totalLiveCount += liveCount; +#endif + ap = &a->next; + } + } + +#ifdef DUMP_SCOPE_STATS + fprintf(logfp, " arenautil %g%%\n", + (totalLiveCount * 100.0) / livePropCapacity); + fflush(logfp); +#endif + +#ifdef DUMP_PROPERTY_TREE + { + FILE *dumpfp = fopen("/tmp/proptree.dump", "w"); + if (dumpfp) { + JSPropertyTreeEntry *pte, *end; + + pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore; + end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash); + while (pte < end) { + if (pte->child) + DumpSubtree(pte->child, 0, dumpfp); + pte++; + } + fclose(dumpfp); + } + } +#endif +} + +JSBool +js_InitPropertyTree(JSRuntime *rt) +{ + if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL, + sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) { + rt->propertyTreeHash.ops = NULL; + return JS_FALSE; + } + JS_InitArenaPool(&rt->propertyArenaPool, "properties", + 256 * sizeof(JSScopeProperty), sizeof(void *)); + return JS_TRUE; +} + +void +js_FinishPropertyTree(JSRuntime *rt) +{ + if (rt->propertyTreeHash.ops) { + JS_DHashTableFinish(&rt->propertyTreeHash); + rt->propertyTreeHash.ops = NULL; + } + JS_FinishArenaPool(&rt->propertyArenaPool); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscope.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscope.h new file mode 100644 index 0000000..0565d4d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscope.h @@ -0,0 +1,407 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscope_h___ +#define jsscope_h___ +/* + * JS symbol tables. + */ +#include "jstypes.h" +#include "jsobj.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +#ifdef JS_THREADSAFE +# include "jslock.h" +#endif + +/* + * Given P independent, non-unique properties each of size S words mapped by + * all scopes in a runtime, construct a property tree of N nodes each of size + * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child + * and right-sibling links. We hope that the N < P by enough that the space + * overhead of L, and the overhead of scope entries pointing at property tree + * nodes, is worth it. + * + * The tree construction goes as follows. If any empty scope in the runtime + * has a property X added to it, find or create a node under the tree root + * labeled X, and set scope->lastProp to point at that node. If any non-empty + * scope whose most recently added property is labeled Y has another property + * labeled Z added, find or create a node for Z under the node that was added + * for Y, and set scope->lastProp to point at that node. + * + * A property is labeled by its members' values: id, getter, setter, slot, + * attributes, tiny or short id, and a field telling for..in order. Note that + * labels are not unique in the tree, but they are unique among a node's kids + * (barring rare and benign multi-threaded race condition outcomes, see below) + * and along any ancestor line from the tree root to a given leaf node (except + * for the hard case of duplicate formal parameters to a function). + * + * Thus the root of the tree represents all empty scopes, and the first ply + * of the tree represents all scopes containing one property, etc. Each node + * in the tree can stand for any number of scopes having the same ordered set + * of properties, where that node was the last added to the scope. (We need + * not store the root of the tree as a node, and do not -- all we need are + * links to its kids.) + * + * Sidebar on for..in loop order: ECMA requires no particular order, but this + * implementation has promised and delivered property definition order, and + * compatibility is king. We could use an order number per property, which + * would require a sort in js_Enumerate, and an entry order generation number + * per scope. An order number beats a list, which should be doubly-linked for + * O(1) delete. An even better scheme is to use a parent link in the property + * tree, so that the ancestor line can be iterated from scope->lastProp when + * filling in a JSIdArray from back to front. This parent link also helps the + * GC to sweep properties iteratively. + * + * What if a property Y is deleted from a scope? If Y is the last property in + * the scope, we simply adjust the scope's lastProp member after we remove the + * scope's hash-table entry pointing at that property node. The parent link + * mentioned in the for..in sidebar above makes this adjustment O(1). But if + * Y comes between X and Z in the scope, then we might have to "fork" the tree + * at X, leaving X->Y->Z in case other scopes have those properties added in + * that order; and to finish the fork, we'd add a node labeled Z with the path + * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to + * O(n^2) growth when deleting lots of properties. + * + * Rather, for O(1) growth all around, we should share the path X->Y->Z among + * scopes having those three properties added in that order, and among scopes + * having only X->Z where Y was deleted. All such scopes have a lastProp that + * points to the Z child of Y. But a scope in which Y was deleted does not + * have a table entry for Y, and when iterating that scope by traversing the + * ancestor line from Z, we will have to test for a table entry for each node, + * skipping nodes that lack entries. + * + * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice. + * Therefore we must fork in such a case, if not earlier. Because delete is + * "bursty", we should not fork eagerly. Delaying a fork till we are at risk + * of adding Y after it was deleted already requires a flag in the JSScope, to + * wit, SCOPE_MIDDLE_DELETE. + * + * What about thread safety? If the property tree operations done by requests + * are find-node and insert-node, then the only hazard is duplicate insertion. + * This is harmless except for minor bloat. When all requests have ended or + * been suspended, the GC is free to sweep the tree after marking all nodes + * reachable from scopes, performing remove-node operations as needed. Note + * also that the stable storage of the property nodes during active requests + * permits the property cache (see jsinterp.h) to dereference JSScopeProperty + * weak references safely. + * + * Is the property tree worth it compared to property storage in each table's + * entries? To decide, we must find the relation <> between the words used + * with a property tree and the words required without a tree. + * + * Model all scopes as one super-scope of capacity T entries (T a power of 2). + * Let alpha be the load factor of this double hash-table. With the property + * tree, each entry in the table is a word-sized pointer to a node that can be + * shared by many scopes. But all such pointers are overhead compared to the + * situation without the property tree, where the table stores property nodes + * directly, as entries each of size S words. With the property tree, we need + * L=2 extra words per node for siblings and kids pointers. Without the tree, + * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required + * by double hashing. + * + * Therefore, + * + * (property tree) <> (no property tree) + * N*(S+L) + T <> S*T + * N*(S+L) + T <> P*S + (1-alpha)*S*T + * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T + * + * Note that P is alpha*T by definition, so + * + * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T + * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T + * N*(S+L) <> (P + (1-alpha)*T) * (S-1) + * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1) + * N*(S+L) <> P * (1/alpha) * (S-1) + * + * Let N = P*beta for a compression ratio beta, beta <= 1: + * + * P*beta*(S+L) <> P * (1/alpha) * (S-1) + * beta*(S+L) <> (S-1)/alpha + * beta <> (S-1)/((S+L)*alpha) + * + * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff + * + * beta < 5/(8*alpha) + * + * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An + * average beta from recent Mozilla browser startups was around .6. + * + * Can we reduce L? Observe that the property tree degenerates into a list of + * lists if at most one property Y follows X in all scopes. In or near such a + * case, we waste a word on the right-sibling link outside of the root ply of + * the tree. Note also that the root ply tends to be large, so O(n^2) growth + * searching it is likely, indicating the need for hashing (but with increased + * thread safety costs). + * + * If only K out of N nodes in the property tree have more than one child, we + * could eliminate the sibling link and overlay a children list or hash-table + * pointer on the leftmost-child link (which would then be either null or an + * only-child link; the overlay could be tagged in the low bit of the pointer, + * or flagged elsewhere in the property tree node, although such a flag must + * not be considered when comparing node labels during tree search). + * + * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2. + * If K << N, L approaches 1 and the property tree wins if beta < .95. + * + * We observe that fan-out below the root ply of the property tree appears to + * have extremely low degree (see the MeterPropertyTree code that histograms + * child-counts in jsscope.c), so instead of a hash-table we use a linked list + * of child node pointer arrays ("kid chunks"). The details are isolated in + * jsscope.c; others must treat JSScopeProperty.kids as opaque. We leave it + * strongly typed for debug-ability of the common (null or one-kid) cases. + * + * One final twist (can you stand it?): the mean number of entries per scope + * in Mozilla is < 5, with a large standard deviation (~8). Instead of always + * allocating scope->table, we leave it null while initializing all the other + * scope members as if it were non-null and minimal-length. Until a property + * is added that crosses the threshold of 6 or more entries for hashing, or + * until a "middle delete" occurs, we use linear search from scope->lastProp + * to find a given id, and save on the space overhead of a hash table. + */ + +struct JSScope { + JSObjectMap map; /* base class state */ + JSObject *object; /* object that owns this scope */ + uint8 flags; /* flags, see below */ + int8 hashShift; /* multiplicative hash shift */ + uint16 spare; /* reserved */ + uint32 entryCount; /* number of entries in table */ + uint32 removedCount; /* removed entry sentinels in table */ + JSScopeProperty **table; /* table of ptrs to shared tree nodes */ + JSScopeProperty *lastProp; /* pointer to last property added */ +#ifdef JS_THREADSAFE + JSContext *ownercx; /* creating context, NULL if shared */ + JSThinLock lock; /* binary semaphore protecting scope */ + union { /* union lockful and lock-free state: */ + jsrefcount count; /* lock entry count for reentrancy */ + JSScope *link; /* next link in rt->scopeSharingTodo */ + } u; +#ifdef DEBUG + const char *file[4]; /* file where lock was (re-)taken */ + unsigned int line[4]; /* line where lock was (re-)taken */ +#endif +#endif +}; + +#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) + +/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ +#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift) + +/* Scope flags and some macros to hide them from other files than jsscope.c. */ +#define SCOPE_MIDDLE_DELETE 0x0001 +#define SCOPE_SEALED 0x0002 + +#define SCOPE_HAD_MIDDLE_DELETE(scope) ((scope)->flags & SCOPE_MIDDLE_DELETE) +#define SCOPE_SET_MIDDLE_DELETE(scope) ((scope)->flags |= SCOPE_MIDDLE_DELETE) +#define SCOPE_CLR_MIDDLE_DELETE(scope) ((scope)->flags &= ~SCOPE_MIDDLE_DELETE) + +#define SCOPE_IS_SEALED(scope) ((scope)->flags & SCOPE_SEALED) +#define SCOPE_SET_SEALED(scope) ((scope)->flags |= SCOPE_SEALED) +#if 0 +/* + * Don't define this, it can't be done safely because JS_LOCK_OBJ will avoid + * taking the lock if the object owns its scope and the scope is sealed. + */ +#define SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED) +#endif + +/* + * A little information hiding for scope->lastProp, in case it ever becomes + * a tagged pointer again. + */ +#define SCOPE_LAST_PROP(scope) ((scope)->lastProp) +#define SCOPE_REMOVE_LAST_PROP(scope) ((scope)->lastProp = \ + (scope)->lastProp->parent) + +struct JSScopeProperty { + jsid id; /* int-tagged jsval/untagged JSAtom* */ + JSPropertyOp getter; /* getter and setter hooks or objects */ + JSPropertyOp setter; + uint32 slot; /* index in obj->slots vector */ + uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ + uint8 flags; /* flags, see below for defines */ + int16 shortid; /* tinyid, or local arg/var index */ + JSScopeProperty *parent; /* parent node, reverse for..in order */ + JSScopeProperty *kids; /* null, single child, or a tagged ptr + to many-kids data structure */ +}; + +/* JSScopeProperty pointer tag bit indicating a collision. */ +#define SPROP_COLLISION ((jsuword)1) +#define SPROP_REMOVED ((JSScopeProperty *) SPROP_COLLISION) + +/* Macros to get and set sprop pointer values and collision flags. */ +#define SPROP_IS_FREE(sprop) ((sprop) == NULL) +#define SPROP_IS_REMOVED(sprop) ((sprop) == SPROP_REMOVED) +#define SPROP_IS_LIVE(sprop) ((sprop) > SPROP_REMOVED) +#define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *) \ + ((jsuword)(sprop) | SPROP_COLLISION)) +#define SPROP_HAD_COLLISION(sprop) ((jsuword)(sprop) & SPROP_COLLISION) +#define SPROP_FETCH(spp) SPROP_CLEAR_COLLISION(*(spp)) + +#define SPROP_CLEAR_COLLISION(sprop) \ + ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION)) + +#define SPROP_STORE_PRESERVING_COLLISION(spp, sprop) \ + (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \ + | SPROP_HAD_COLLISION(*(spp)))) + +/* Bits stored in sprop->flags. */ +#define SPROP_MARK 0x01 +#define SPROP_IS_DUPLICATE 0x02 +#define SPROP_IS_ALIAS 0x04 +#define SPROP_HAS_SHORTID 0x08 +#define SPROP_IS_HIDDEN 0x10 /* a normally-hidden property, + e.g., function arg or var */ + +/* + * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather + * than id when calling sprop's getter or setter. + */ +#define SPROP_USERID(sprop) \ + (((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid) \ + : ID_TO_VALUE((sprop)->id)) + +#define SPROP_INVALID_SLOT 0xffffffff + +#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) +#define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) + +#define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) +#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) + +/* + * NB: SPROP_GET must not be called if SPROP_HAS_STUB_GETTER(sprop). + */ +#define SPROP_GET(cx,sprop,obj,obj2,vp) \ + (((sprop)->attrs & JSPROP_GETTER) \ + ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ + OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ, \ + 0, 0, vp) \ + : (sprop)->getter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + +/* + * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) && + * !(sprop->attrs & JSPROP_GETTER)). + */ +#define SPROP_SET(cx,sprop,obj,obj2,vp) \ + (((sprop)->attrs & JSPROP_SETTER) \ + ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ + OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE, \ + 1, vp, vp) \ + : ((sprop)->attrs & JSPROP_GETTER) \ + ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, \ + JSMSG_GETTER_ONLY, NULL), JS_FALSE) \ + : (sprop)->setter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + +/* Macro for common expression to test for shared permanent attributes. */ +#define SPROP_IS_SHARED_PERMANENT(sprop) \ + ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0) + +extern JSScope * +js_GetMutableScope(JSContext *cx, JSObject *obj); + +extern JSScope * +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj); + +extern void +js_DestroyScope(JSContext *cx, JSScope *scope); + +#define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_JSID_TO_JSVAL(id) : \ + JSID_IS_OBJECT(id) ? OBJECT_JSID_TO_JSVAL(id) : \ + (jsval)(id)) +#define HASH_ID(id) (JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->number : \ + JSID_IS_OBJECT(id) ? (jsatomid) JSID_CLRTAG(id) : \ + (jsatomid) JSID_TO_INT(id)) + +extern JS_FRIEND_API(JSScopeProperty **) +js_SearchScope(JSScope *scope, jsid id, JSBool adding); + +#define SCOPE_GET_PROPERTY(scope, id) \ + SPROP_FETCH(js_SearchScope(scope, id, JS_FALSE)) + +#define SCOPE_HAS_PROPERTY(scope, sprop) \ + (SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop)) + +extern JSScopeProperty * +js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid); + +extern JSScopeProperty * +js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter); + +extern JSBool +js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id); + +extern void +js_ClearScope(JSContext *cx, JSScope *scope); + +/* + * These macros used to inline short code sequences, but they grew over time. + * We retain them for internal backward compatibility, and in case one or both + * ever shrink to inline-able size. + */ +#define MARK_ID(cx,id) js_MarkId(cx, id) +#define MARK_SCOPE_PROPERTY(cx,sprop) js_MarkScopeProperty(cx, sprop) + +extern void +js_MarkId(JSContext *cx, jsid id); + +extern void +js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop); + +extern void +js_SweepScopeProperties(JSRuntime *rt); + +extern JSBool +js_InitPropertyTree(JSRuntime *rt); + +extern void +js_FinishPropertyTree(JSRuntime *rt); + +#endif /* jsscope_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscript.c b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscript.c new file mode 100644 index 0000000..73298a4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscript.c @@ -0,0 +1,1717 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS script operations. + */ +#include "jsstddef.h" +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsscript.h" +#if JS_HAS_XDR +#include "jsxdrapi.h" +#endif + +#if JS_HAS_SCRIPT_OBJECT + +static const char js_script_exec[] = "Script.prototype.exec"; +static const char js_script_compile[] = "Script.prototype.compile"; + +/* + * This routine requires that obj has been locked previously. + */ +static jsint +GetScriptExecDepth(JSContext *cx, JSObject *obj) +{ + jsval v; + + JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); + v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); + return JSVAL_TO_INT(v); +} + +static void +AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) +{ + jsint execDepth; + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), + INT_TO_JSVAL(execDepth + delta)); + JS_UNLOCK_OBJ(cx, obj); +} + +#if JS_HAS_TOSOURCE +static JSBool +script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + uint32 indent; + JSScript *script; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + + script = (JSScript *) JS_GetPrivate(cx, obj); + + /* Let n count the source string length, j the "front porch" length. */ + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); + n = j + 2; + if (!script) { + /* Let k count the constructor argument string length. */ + k = 0; + s = NULL; /* quell GCC overwarning */ + } else { + str = JS_DecompileScript(cx, script, "Script.prototype.toSource", + (uintN)indent); + if (!str) + return JS_FALSE; + str = js_QuoteString(cx, str, '\''); + if (!str) + return JS_FALSE; + s = JSSTRING_CHARS(str); + k = JSSTRING_LENGTH(str); + n += k; + } + + /* Allocate the source string and copy into it. */ + t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + + /* Create and return a JS string for t. */ + str = JS_NewUCString(cx, t, n); + if (!str) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + uint32 indent; + JSScript *script; + JSString *str; + + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + str = JS_DecompileScript(cx, script, "Script.prototype.toString", + (uintN)indent); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + JSObject *scopeobj; + jsval v; + JSScript *script, *oldscript; + JSStackFrame *fp, *caller; + const char *file; + uintN line; + JSPrincipals *principals; + jsint execDepth; + + /* Make sure obj is a Script object. */ + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + /* If no args, leave private undefined and return early. */ + if (argc == 0) + goto out; + + /* Otherwise, the first arg is the script source to compile. */ + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + + /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ + fp = cx->fp; + caller = JS_GetScriptedCaller(cx, fp); + JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); + + if (caller) { + if (!scopeobj) { + scopeobj = js_GetScopeChain(cx, caller); + if (!scopeobj) + return JS_FALSE; + fp->scopeChain = scopeobj; /* for the compiler's benefit */ + } + + principals = JS_EvalFramePrincipals(cx, fp, caller); + if (principals == caller->script->principals) { + file = caller->script->filename; + line = js_PCToLineNumber(cx, caller->script, caller->pc); + } else { + file = principals->codebase; + line = 0; + } + } else { + file = NULL; + line = 0; + principals = NULL; + } + + /* Ensure we compile this script with the right (inner) principals. */ + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile); + if (!scopeobj) + return JS_FALSE; + + /* + * Compile the new script using the caller's scope chain, a la eval(). + * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's + * flags, because compilation is here separated from execution, and the + * run-time scope chain may not match the compile-time. JSFRAME_EVAL is + * tested in jsemit.c and jsscan.c to optimize based on identity of run- + * and compile-time scope. + */ + fp->flags |= JSFRAME_SCRIPT_OBJECT; + script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, + JSSTRING_CHARS(str), + JSSTRING_LENGTH(str), + file, line); + if (!script) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); + return JS_FALSE; + } + + /* Swap script for obj's old script, if any. */ + v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); + oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); + JS_UNLOCK_OBJ(cx, obj); + + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->object = obj; + js_CallNewScriptHook(cx, script, NULL); + +out: + /* Return the object. */ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *scopeobj, *parent; + JSStackFrame *fp, *caller; + JSScript *script; + JSBool ok; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + scopeobj = NULL; + if (argc) { + if (!js_ValueToObject(cx, argv[0], &scopeobj)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(scopeobj); + } + + /* + * Emulate eval() by using caller's this, var object, sharp array, etc., + * all propagated by js_Execute via a non-null fourth (down) argument to + * js_Execute. If there is no scripted caller, js_Execute uses its second + * (chain) argument to set the exec frame's varobj, thisp, and scopeChain. + * + * Unlike eval, which the compiler detects, Script.prototype.exec may be + * called from a lightweight function, or even from native code (in which + * case fp->varobj and fp->scopeChain are null). If exec is called from + * a lightweight function, we will need to get a Call object representing + * its frame, to act as the var object and scope chain head. + */ + fp = cx->fp; + caller = JS_GetScriptedCaller(cx, fp); + if (caller && !caller->varobj) { + /* Called from a lightweight function. */ + JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); + + /* Scope chain links from Call object to callee's parent. */ + parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2])); + if (!js_GetCallObject(cx, caller, parent)) + return JS_FALSE; + } + + if (!scopeobj) { + /* No scope object passed in: try to use the caller's scope chain. */ + if (caller) { + /* + * Load caller->scopeChain after the conditional js_GetCallObject + * call above, which resets scopeChain as well as varobj. + */ + scopeobj = js_GetScopeChain(cx, caller); + if (!scopeobj) + return JS_FALSE; + } else { + /* + * Called from native code, so we don't know what scope object to + * use. We could use parent (see above), but Script.prototype.exec + * might be a shared/sealed "superglobal" method. A more general + * approach would use cx->globalObject, which will be the same as + * exec.__parent__ in the non-superglobal case. In the superglobal + * case it's the right object: the global, not the superglobal. + */ + scopeobj = cx->globalObject; + } + } + + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec); + if (!scopeobj) + return JS_FALSE; + + /* Keep track of nesting depth for the script. */ + AdjustScriptExecDepth(cx, obj, 1); + + /* Must get to out label after this */ + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) { + ok = JS_FALSE; + goto out; + } + + /* Belt-and-braces: check that this script object has access to scopeobj. */ + ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals, + CLASS_ATOM(cx, Script)); + if (!ok) + goto out; + + ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); + +out: + AdjustScriptExecDepth(cx, obj, -1); + return ok; +} + +#if JS_HAS_XDR + +static JSBool +XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) +{ + JSContext *cx; + uint32 natoms, i, index; + JSAtom **atoms; + + cx = xdr->cx; + + if (xdr->mode == JSXDR_ENCODE) + natoms = (uint32)map->length; + + if (!JS_XDRUint32(xdr, &natoms)) + return JS_FALSE; + + if (xdr->mode == JSXDR_ENCODE) { + atoms = map->vector; + } else { + if (natoms == 0) { + atoms = NULL; + } else { + atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms); + if (!atoms) + return JS_FALSE; +#ifdef DEBUG + memset(atoms, 0, (size_t)natoms * sizeof *atoms); +#endif + } + + map->vector = atoms; + map->length = natoms; + } + + for (i = 0; i != natoms; ++i) { + if (xdr->mode == JSXDR_ENCODE) + index = i; + if (!JS_XDRUint32(xdr, &index)) + goto bad; + + /* + * Assert that, when decoding, the read index is valid and points to + * an unoccupied element of atoms array. + */ + JS_ASSERT(index < natoms); + JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]); + if (!js_XDRAtom(xdr, &atoms[index])) + goto bad; + } + + return JS_TRUE; + + bad: + if (xdr->mode == JSXDR_DECODE) { + JS_free(cx, atoms); + map->vector = NULL; + map->length = 0; + } + + return JS_FALSE; +} + +JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) +{ + JSContext *cx; + JSScript *script, *newscript, *oldscript; + uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes; + uint32 prologLength, version; + JSBool filenameWasSaved; + jssrcnote *notes, *sn; + + cx = xdr->cx; + script = *scriptp; + nsrcnotes = ntrynotes = 0; + filenameWasSaved = JS_FALSE; + notes = NULL; + + /* + * Encode prologLength and version after script->length (_2 or greater), + * but decode both new (>= _2) and old, prolog&version-free (_1) scripts. + * Version _3 supports principals serialization. Version _4 reorders the + * nsrcnotes and ntrynotes fields to come before everything except magic, + * length, prologLength, and version, so that srcnote and trynote storage + * can be allocated as part of the JSScript (along with bytecode storage). + * + * So far, the magic number has not changed for every jsopcode.tbl change. + * We stipulate forward compatibility by requiring old bytecodes never to + * change or go away (modulo a few exceptions before the XDR interfaces + * evolved, and a few exceptions during active trunk development). With + * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new + * magic number (_5) so that we know to append JSOP_STOP to old scripts + * when deserializing. + */ + if (xdr->mode == JSXDR_ENCODE) + magic = JSXDR_MAGIC_SCRIPT_CURRENT; + if (!JS_XDRUint32(xdr, &magic)) + return JS_FALSE; + JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4); + if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) { + if (!hasMagic) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SCRIPT_MAGIC); + return JS_FALSE; + } + *hasMagic = JS_FALSE; + return JS_TRUE; + } + if (hasMagic) + *hasMagic = JS_TRUE; + + if (xdr->mode == JSXDR_ENCODE) { + length = script->length; + prologLength = PTRDIFF(script->main, script->code, jsbytecode); + JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); + version = (uint32)script->version | (script->numGlobalVars << 16); + lineno = (uint32)script->lineno; + depth = (uint32)script->depth; + + /* Count the srcnotes, keeping notes pointing at the first one. */ + notes = SCRIPT_NOTES(script); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + nsrcnotes = PTRDIFF(sn, notes, jssrcnote); + nsrcnotes++; /* room for the terminator */ + + /* Count the trynotes. */ + if (script->trynotes) { + while (script->trynotes[ntrynotes].catchStart) + ntrynotes++; + ntrynotes++; /* room for the end marker */ + } + } + + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + if (magic >= JSXDR_MAGIC_SCRIPT_2) { + if (!JS_XDRUint32(xdr, &prologLength)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &version)) + return JS_FALSE; + + /* To fuse allocations, we need srcnote and trynote counts early. */ + if (magic >= JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &ntrynotes)) + return JS_FALSE; + } + } + + if (xdr->mode == JSXDR_DECODE) { + size_t alloclength = length; + if (magic < JSXDR_MAGIC_SCRIPT_5) + ++alloclength; /* add a byte for JSOP_STOP */ + + script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes); + if (!script) + return JS_FALSE; + if (magic >= JSXDR_MAGIC_SCRIPT_2) { + script->main += prologLength; + script->version = (JSVersion) (version & 0xffff); + script->numGlobalVars = (uint16) (version >> 16); + + /* If we know nsrcnotes, we allocated space for notes in script. */ + if (magic >= JSXDR_MAGIC_SCRIPT_4) + notes = SCRIPT_NOTES(script); + } + *scriptp = script; + } + + /* + * Control hereafter must goto error on failure, in order for the DECODE + * case to destroy script and conditionally free notes, which if non-null + * in the (DECODE and magic < _4) case must point at a temporary vector + * allocated just below. + */ + oldscript = xdr->script; + xdr->script = script; + if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || + !XDRAtomMap(xdr, &script->atomMap)) { + goto error; + } + + if (magic < JSXDR_MAGIC_SCRIPT_5) { + if (xdr->mode == JSXDR_DECODE) { + /* + * Append JSOP_STOP to old scripts, to relieve the interpreter + * from having to bounds-check pc. Also take care to increment + * length, as it is used below and must count all bytecode. + */ + script->code[length++] = JSOP_STOP; + } + + if (magic < JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) + goto error; + if (xdr->mode == JSXDR_DECODE) { + notes = (jssrcnote *) + JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); + if (!notes) + goto error; + } + } + } + + if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || + !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || + !JS_XDRUint32(xdr, &lineno) || + !JS_XDRUint32(xdr, &depth) || + (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) { + goto error; + } + + /* Script principals transcoding support comes with versions >= _3. */ + if (magic >= JSXDR_MAGIC_SCRIPT_3) { + JSPrincipals *principals; + uint32 encodeable; + + if (xdr->mode == JSXDR_ENCODE) { + principals = script->principals; + encodeable = (cx->runtime->principalsTranscoder != NULL); + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable && + !cx->runtime->principalsTranscoder(xdr, &principals)) { + goto error; + } + } else { + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable) { + if (!cx->runtime->principalsTranscoder) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_DECODE_PRINCIPALS); + goto error; + } + if (!cx->runtime->principalsTranscoder(xdr, &principals)) + goto error; + script->principals = principals; + } + } + } + + if (xdr->mode == JSXDR_DECODE) { + const char *filename = script->filename; + if (filename) { + filename = js_SaveScriptFilename(cx, filename); + if (!filename) + goto error; + JS_free(cx, (void *) script->filename); + script->filename = filename; + filenameWasSaved = JS_TRUE; + } + script->lineno = (uintN)lineno; + script->depth = (uintN)depth; + + if (magic < JSXDR_MAGIC_SCRIPT_4) { + /* + * Argh, we have to reallocate script, copy notes into the extra + * space after the bytecodes, and free the temporary notes vector. + * First, add enough slop to nsrcnotes so we can align the address + * after the srcnotes of the first trynote. + */ + uint32 osrcnotes = nsrcnotes; + + if (ntrynotes) + nsrcnotes += JSTRYNOTE_ALIGNMASK; + newscript = (JSScript *) JS_realloc(cx, script, + sizeof(JSScript) + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote) + + ntrynotes * sizeof(JSTryNote)); + if (!newscript) + goto error; + + *scriptp = script = newscript; + script->code = (jsbytecode *)(script + 1); + script->main = script->code + prologLength; + memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote)); + JS_free(cx, (void *) notes); + notes = NULL; + if (ntrynotes) { + script->trynotes = (JSTryNote *) + ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & + ~(jsword)JSTRYNOTE_ALIGNMASK); + memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); + } + } + } + + while (ntrynotes) { + JSTryNote *tn = &script->trynotes[--ntrynotes]; + uint32 start = (uint32) tn->start, + catchLength = (uint32) tn->length, + catchStart = (uint32) tn->catchStart; + + if (!JS_XDRUint32(xdr, &start) || + !JS_XDRUint32(xdr, &catchLength) || + !JS_XDRUint32(xdr, &catchStart)) { + goto error; + } + tn->start = (ptrdiff_t) start; + tn->length = (ptrdiff_t) catchLength; + tn->catchStart = (ptrdiff_t) catchStart; + } + + xdr->script = oldscript; + return JS_TRUE; + + error: + if (xdr->mode == JSXDR_DECODE) { + if (script->filename && !filenameWasSaved) { + JS_free(cx, (void *) script->filename); + script->filename = NULL; + } + if (notes && magic < JSXDR_MAGIC_SCRIPT_4) + JS_free(cx, (void *) notes); + js_DestroyScript(cx, script); + *scriptp = NULL; + } + return JS_FALSE; +} + +#if JS_HAS_XDR_FREEZE_THAW +/* + * These cannot be exposed to web content, and chrome does not need them, so + * we take them out of the Mozilla client altogether. Fortunately, there is + * no way to serialize a native function (see fun_xdrObject in jsfun.c). + */ + +static JSBool +script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSScript *script; + JSBool ok, hasMagic; + uint32 len; + void *buf; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) + return JS_TRUE; + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); + if (!xdr) + return JS_FALSE; + + /* write */ + ok = js_XDRScript(xdr, &script, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *rval = JSVAL_VOID; + goto out; + } + + buf = JS_XDRMemGetData(xdr, &len); + if (!buf) { + ok = JS_FALSE; + goto out; + } + + JS_ASSERT((jsword)buf % sizeof(jschar) == 0); + len /= sizeof(jschar); + str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); + if (!str) { + ok = JS_FALSE; + goto out; + } + +#if IS_BIG_ENDIAN + { + jschar *chars; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + chars = JS_GetStringChars(str); + for (i = 0; i < len; i++) + chars[i] = JSXDR_SWAB16(chars[i]); + } +#endif + *rval = STRING_TO_JSVAL(str); + +out: + JS_XDRDestroy(xdr); + return ok; +} + +static JSBool +script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSString *str; + void *buf; + uint32 len; + jsval v; + JSScript *script, *oldscript; + JSBool ok, hasMagic; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + if (argc == 0) + return JS_TRUE; + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_DECODE); + if (!xdr) + return JS_FALSE; + + buf = JS_GetStringChars(str); + len = JS_GetStringLength(str); +#if IS_BIG_ENDIAN + { + jschar *from, *to; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + from = (jschar *)buf; + to = (jschar *) JS_malloc(cx, len * sizeof(jschar)); + if (!to) { + JS_XDRDestroy(xdr); + return JS_FALSE; + } + for (i = 0; i < len; i++) + to[i] = JSXDR_SWAB16(from[i]); + buf = (char *)to; + } +#endif + len *= sizeof(jschar); + JS_XDRMemSetData(xdr, buf, len); + + /* XXXbe should magic mismatch be error, or false return value? */ + ok = js_XDRScript(xdr, &script, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *rval = JSVAL_FALSE; + goto out; + } + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); + goto out; + } + + /* Swap script for obj's old script, if any. */ + v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; + LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); + JS_UNLOCK_OBJ(cx, obj); + + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->object = obj; + js_CallNewScriptHook(cx, script, NULL); + +out: + /* + * We reset the buffer to be NULL so that it doesn't free the chars + * memory owned by str (argv[0]). + */ + JS_XDRMemSetData(xdr, NULL, 0); + JS_XDRDestroy(xdr); +#if IS_BIG_ENDIAN + JS_free(cx, buf); +#endif + *rval = JSVAL_TRUE; + return ok; +} + +static const char js_thaw_str[] = "thaw"; + +#endif /* JS_HAS_XDR_FREEZE_THAW */ +#endif /* JS_HAS_XDR */ + +static JSFunctionSpec script_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, script_toSource, 0,0,0}, +#endif + {js_toString_str, script_toString, 0,0,0}, + {"compile", script_compile, 2,0,0}, + {"exec", script_exec, 1,0,0}, +#if JS_HAS_XDR_FREEZE_THAW + {"freeze", script_freeze, 0,0,0}, + {js_thaw_str, script_thaw, 1,0,0}, +#endif /* JS_HAS_XDR_FREEZE_THAW */ + {0,0,0,0,0} +}; + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +static void +script_finalize(JSContext *cx, JSObject *obj) +{ + JSScript *script; + + script = (JSScript *) JS_GetPrivate(cx, obj); + if (script) + js_DestroyScript(cx, script); +} + +static JSBool +script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#if JS_HAS_SCRIPT_OBJECT + return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); +#else + return JS_FALSE; +#endif +} + +static uint32 +script_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSScript *script; + + script = (JSScript *) JS_GetPrivate(cx, obj); + if (script) + js_MarkScript(cx, script); + return 0; +} + +#if !JS_HAS_SCRIPT_OBJECT +const char js_Script_str[] = "Script"; + +#define JSProto_Script JSProto_Object +#endif + +JS_FRIEND_DATA(JSClass) js_ScriptClass = { + js_Script_str, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) | + JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, + NULL, NULL, script_call, NULL,/*XXXbe xdr*/ + NULL, NULL, script_mark, 0 +}; + +#if JS_HAS_SCRIPT_OBJECT + +static JSBool +Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* If not constructing, replace obj with a new Script object. */ + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + + /* + * script_compile does not use rval to root its temporaries + * so we can use it to root obj. + */ + *rval = OBJECT_TO_JSVAL(obj); + } + + if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) + return JS_FALSE; + + return script_compile(cx, obj, argc, argv, rval); +} + +#if JS_HAS_XDR_FREEZE_THAW + +static JSBool +script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + if (!script_thaw(cx, obj, argc, argv, rval)) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec script_static_methods[] = { + {js_thaw_str, script_static_thaw, 1,0,0}, + {0,0,0,0,0} +}; + +#else /* !JS_HAS_XDR_FREEZE_THAW */ + +#define script_static_methods NULL + +#endif /* !JS_HAS_XDR_FREEZE_THAW */ + +JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, + NULL, script_methods, NULL, script_static_methods); +} + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +/* + * Shared script filename management. + */ +JS_STATIC_DLL_CALLBACK(int) +js_compare_strings(const void *k1, const void *k2) +{ + return strcmp(k1, k2) == 0; +} + +/* Shared with jsatom.c to save code space. */ +extern void * JS_DLL_CALLBACK +js_alloc_table_space(void *priv, size_t size); + +extern void JS_DLL_CALLBACK +js_free_table_space(void *priv, void *item); + +/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ +typedef struct ScriptFilenameEntry { + JSHashEntry *next; /* hash chain linkage */ + JSHashNumber keyHash; /* key hash function result */ + const void *key; /* ptr to filename, below */ + uint32 flags; /* user-defined filename prefix flags */ + JSPackedBool mark; /* GC mark flag */ + char filename[3]; /* two or more bytes, NUL-terminated */ +} ScriptFilenameEntry; + +JS_STATIC_DLL_CALLBACK(JSHashEntry *) +js_alloc_sftbl_entry(void *priv, const void *key) +{ + size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1; + + return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); +} + +JS_STATIC_DLL_CALLBACK(void) +js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) +{ + if (flag != HT_FREE_ENTRY) + return; + free(he); +} + +static JSHashAllocOps sftbl_alloc_ops = { + js_alloc_table_space, js_free_table_space, + js_alloc_sftbl_entry, js_free_sftbl_entry +}; + +JSBool +js_InitRuntimeScriptState(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = JS_NEW_LOCK(); + if (!rt->scriptFilenameTableLock) + return JS_FALSE; +#endif + JS_ASSERT(!rt->scriptFilenameTable); + rt->scriptFilenameTable = + JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, + &sftbl_alloc_ops, NULL); + if (!rt->scriptFilenameTable) { + js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */ + return JS_FALSE; + } + JS_INIT_CLIST(&rt->scriptFilenamePrefixes); + return JS_TRUE; +} + +typedef struct ScriptFilenamePrefix { + JSCList links; /* circular list linkage for easy deletion */ + const char *name; /* pointer to pinned ScriptFilenameEntry string */ + size_t length; /* prefix string length, precomputed */ + uint32 flags; /* user-defined flags to inherit from this prefix */ +} ScriptFilenamePrefix; + +void +js_FinishRuntimeScriptState(JSRuntime *rt) +{ + if (rt->scriptFilenameTable) { + JS_HashTableDestroy(rt->scriptFilenameTable); + rt->scriptFilenameTable = NULL; + } +#ifdef JS_THREADSAFE + if (rt->scriptFilenameTableLock) { + JS_DESTROY_LOCK(rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = NULL; + } +#endif +} + +void +js_FreeRuntimeScriptState(JSRuntime *rt) +{ + ScriptFilenamePrefix *sfp; + + if (!rt->scriptFilenameTable) + return; + + while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { + sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; + JS_REMOVE_LINK(&sfp->links); + free(sfp); + } + js_FinishRuntimeScriptState(rt); +} + +#ifdef DEBUG_brendan +#define DEBUG_SFTBL +#endif +#ifdef DEBUG_SFTBL +size_t sftbl_savings = 0; +#endif + +static ScriptFilenameEntry * +SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) +{ + JSHashTable *table; + JSHashNumber hash; + JSHashEntry **hep; + ScriptFilenameEntry *sfe; + size_t length; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + table = rt->scriptFilenameTable; + hash = JS_HashString(filename); + hep = JS_HashTableRawLookup(table, hash, filename); + sfe = (ScriptFilenameEntry *) *hep; +#ifdef DEBUG_SFTBL + if (sfe) + sftbl_savings += strlen(sfe->filename); +#endif + + if (!sfe) { + sfe = (ScriptFilenameEntry *) + JS_HashTableRawAdd(table, hep, hash, filename, NULL); + if (!sfe) + return NULL; + sfe->key = strcpy(sfe->filename, filename); + sfe->flags = 0; + sfe->mark = JS_FALSE; + } + + /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ + if (flags != 0) { + /* Search in case filename was saved already; we must be idempotent. */ + sfp = NULL; + length = strlen(filename); + for (head = link = &rt->scriptFilenamePrefixes; + link->next != head; + link = link->next) { + /* Lag link behind sfp to insert in non-increasing length order. */ + sfp = (ScriptFilenamePrefix *) link->next; + if (!strcmp(sfp->name, filename)) + break; + if (sfp->length <= length) { + sfp = NULL; + break; + } + sfp = NULL; + } + + if (!sfp) { + /* No such prefix: add one now. */ + sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix)); + if (!sfp) + return NULL; + JS_INSERT_AFTER(&sfp->links, link); + sfp->name = sfe->filename; + sfp->length = length; + sfp->flags = 0; + } + + /* + * Accumulate flags in both sfe and sfp: sfe for later access from the + * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer + * filename entries can inherit by prefix. + */ + sfe->flags |= flags; + sfp->flags |= flags; + } + + return sfe; +} + +const char * +js_SaveScriptFilename(JSContext *cx, const char *filename) +{ + JSRuntime *rt; + ScriptFilenameEntry *sfe; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + rt = cx->runtime; + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, 0); + if (!sfe) { + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + JS_ReportOutOfMemory(cx); + return NULL; + } + + /* + * Try to inherit flags by prefix. We assume there won't be more than a + * few (dozen! ;-) prefixes, so linear search is tolerable. + * XXXbe every time I've assumed that in the JS engine, I've been wrong! + */ + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + if (!strncmp(sfp->name, filename, sfp->length)) { + sfe->flags |= sfp->flags; + break; + } + } + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + return sfe->filename; +} + +const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) +{ + ScriptFilenameEntry *sfe; + + /* This may be called very early, via the jsdbgapi.h entry point. */ + if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) + return NULL; + + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, flags); + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + if (!sfe) + return NULL; + + return sfe->filename; +} + +/* + * Back up from a saved filename by its offset within its hash table entry. + */ +#define FILENAME_TO_SFE(fn) \ + ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) + +/* + * The sfe->key member, redundant given sfe->filename but required by the old + * jshash.c code, here gives us a useful sanity check. This assertion will + * very likely botch if someone tries to mark a string that wasn't allocated + * as an sfe->filename. + */ +#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) + +uint32 +js_GetScriptFilenameFlags(const char *filename) +{ + ScriptFilenameEntry *sfe; + + sfe = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); + return sfe->flags; +} + +void +js_MarkScriptFilename(const char *filename) +{ + ScriptFilenameEntry *sfe; + + sfe = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); + sfe->mark = JS_TRUE; +} + +JS_STATIC_DLL_CALLBACK(intN) +js_script_filename_marker(JSHashEntry *he, intN i, void *arg) +{ + ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; + + sfe->mark = JS_TRUE; + return HT_ENUMERATE_NEXT; +} + +void +js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) +{ + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + if (!rt->scriptFilenameTable) + return; + + if (keepAtoms) { + JS_HashTableEnumerateEntries(rt->scriptFilenameTable, + js_script_filename_marker, + rt); + } + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + js_MarkScriptFilename(sfp->name); + } +} + +JS_STATIC_DLL_CALLBACK(intN) +js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) +{ + ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; + + if (!sfe->mark) + return HT_ENUMERATE_REMOVE; + sfe->mark = JS_FALSE; + return HT_ENUMERATE_NEXT; +} + +void +js_SweepScriptFilenames(JSRuntime *rt) +{ + if (!rt->scriptFilenameTable) + return; + + JS_HashTableEnumerateEntries(rt->scriptFilenameTable, + js_script_filename_sweeper, + rt); +#ifdef DEBUG_notme +#ifdef DEBUG_SFTBL + printf("script filename table savings so far: %u\n", sftbl_savings); +#endif +#endif +} + +JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes) +{ + JSScript *script; + + /* Round up source note count to align script->trynotes for its type. */ + if (ntrynotes) + nsrcnotes += JSTRYNOTE_ALIGNMASK; + script = (JSScript *) JS_malloc(cx, + sizeof(JSScript) + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote) + + ntrynotes * sizeof(JSTryNote)); + if (!script) + return NULL; + memset(script, 0, sizeof(JSScript)); + script->code = script->main = (jsbytecode *)(script + 1); + script->length = length; + script->version = cx->version; + if (ntrynotes) { + script->trynotes = (JSTryNote *) + ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & + ~(jsword)JSTRYNOTE_ALIGNMASK); + memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); + } + return script; +} + +JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) +{ + uint32 mainLength, prologLength, nsrcnotes, ntrynotes; + JSScript *script; + const char *filename; + + mainLength = CG_OFFSET(cg); + prologLength = CG_PROLOG_OFFSET(cg); + CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); + CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes); + script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes); + if (!script) + return NULL; + + /* Now that we have script, error control flow must go to label bad. */ + script->main += prologLength; + memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); + memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); + script->numGlobalVars = cg->treeContext.numGlobalVars; + if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) + goto bad; + + filename = cg->filename; + if (filename) { + script->filename = js_SaveScriptFilename(cx, filename); + if (!script->filename) + goto bad; + } + script->lineno = cg->firstLine; + script->depth = cg->maxStackDepth; + if (cg->principals) { + script->principals = cg->principals; + JSPRINCIPALS_HOLD(cx, script->principals); + } + + if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script))) + goto bad; + if (script->trynotes) + js_FinishTakingTryNotes(cx, cg, script->trynotes); + + /* + * We initialize fun->u.script to be the script constructed above + * so that the debugger has a valid FUN_SCRIPT(fun). + */ + if (fun) { + JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); + fun->u.i.script = script; + if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) + fun->flags |= JSFUN_HEAVYWEIGHT; + } + + /* Tell the debugger about this compiled script. */ + js_CallNewScriptHook(cx, script, fun); + return script; + +bad: + js_DestroyScript(cx, script); + return NULL; +} + +JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) +{ + JSRuntime *rt; + JSNewScriptHook hook; + + rt = cx->runtime; + hook = rt->newScriptHook; + if (hook) { + JS_KEEP_ATOMS(rt); + hook(cx, script->filename, script->lineno, script, fun, + rt->newScriptHookData); + JS_UNKEEP_ATOMS(rt); + } +} + +JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script) +{ + JSRuntime *rt; + JSDestroyScriptHook hook; + + rt = cx->runtime; + hook = rt->destroyScriptHook; + if (hook) + hook(cx, script, rt->destroyScriptHookData); +} + +void +js_DestroyScript(JSContext *cx, JSScript *script) +{ + js_CallDestroyScriptHook(cx, script); + + JS_ClearScriptTraps(cx, script); + js_FreeAtomMap(cx, &script->atomMap); + if (script->principals) + JSPRINCIPALS_DROP(cx, script->principals); + if (JS_GSN_CACHE(cx).script == script) + JS_CLEAR_GSN_CACHE(cx); + JS_free(cx, script); +} + +void +js_MarkScript(JSContext *cx, JSScript *script) +{ + JSAtomMap *map; + uintN i, length; + JSAtom **vector; + + map = &script->atomMap; + length = map->length; + vector = map->vector; + for (i = 0; i < length; i++) + GC_MARK_ATOM(cx, vector[i]); + + if (script->filename) + js_MarkScriptFilename(script->filename); +} + +typedef struct GSNCacheEntry { + JSDHashEntryHdr hdr; + jsbytecode *pc; + jssrcnote *sn; +} GSNCacheEntry; + +#define GSN_CACHE_THRESHOLD 100 + +jssrcnote * +js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + ptrdiff_t target, offset; + GSNCacheEntry *entry; + jssrcnote *sn, *result; + uintN nsrcnotes; + + + target = PTRDIFF(pc, script->code, jsbytecode); + if ((uint32)target >= script->length) + return NULL; + + if (JS_GSN_CACHE(cx).script == script) { + JS_METER_GSN_CACHE(cx, hits); + entry = (GSNCacheEntry *) + JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, + JS_DHASH_LOOKUP); + return entry->sn; + } + + JS_METER_GSN_CACHE(cx, misses); + offset = 0; + for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) { + if (SN_IS_TERMINATOR(sn)) { + result = NULL; + break; + } + offset += SN_DELTA(sn); + if (offset == target && SN_IS_GETTABLE(sn)) { + result = sn; + break; + } + } + + if (JS_GSN_CACHE(cx).script != script && + script->length >= GSN_CACHE_THRESHOLD) { + JS_CLEAR_GSN_CACHE(cx); + nsrcnotes = 0; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); + sn = SN_NEXT(sn)) { + if (SN_IS_GETTABLE(sn)) + ++nsrcnotes; + } + if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), + NULL, sizeof(GSNCacheEntry), nsrcnotes)) { + JS_GSN_CACHE(cx).table.ops = NULL; + } else { + pc = script->code; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); + sn = SN_NEXT(sn)) { + pc += SN_DELTA(sn); + if (SN_IS_GETTABLE(sn)) { + entry = (GSNCacheEntry *) + JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, + JS_DHASH_ADD); + entry->pc = pc; + entry->sn = sn; + } + } + JS_GSN_CACHE(cx).script = script; + JS_METER_GSN_CACHE(cx, fills); + } + } + + return result; +} + +uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSAtom *atom; + JSFunction *fun; + uintN lineno; + ptrdiff_t offset, target; + jssrcnote *sn; + JSSrcNoteType type; + + /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ + if (!pc) + return 0; + + /* + * Special case: function definition needs no line number note because + * the function's script contains its starting line number. + */ + if (*pc == JSOP_DEFFUN || + (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) { + atom = js_GetAtom(cx, &script->atomMap, + (*pc == JSOP_DEFFUN) + ? GET_ATOM_INDEX(pc) + : GET_LITERAL_INDEX(pc)); + fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); + JS_ASSERT(FUN_INTERPRETED(fun)); + return fun->u.i.script->lineno; + } + + /* + * General case: walk through source notes accumulating their deltas, + * keeping track of line-number notes, until we pass the note for pc's + * offset within script->code. + */ + lineno = script->lineno; + offset = 0; + target = PTRDIFF(pc, script->code, jsbytecode); + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + offset += SN_DELTA(sn); + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + if (offset <= target) + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + if (offset <= target) + lineno++; + } + if (offset > target) + break; + } + return lineno; +} + +/* The line number limit is the same as the jssrcnote offset limit. */ +#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) + +jsbytecode * +js_LineNumberToPC(JSScript *script, uintN target) +{ + ptrdiff_t offset, best; + uintN lineno, bestdiff, diff; + jssrcnote *sn; + JSSrcNoteType type; + + offset = 0; + best = -1; + lineno = script->lineno; + bestdiff = SN_LINE_LIMIT; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + if (lineno == target) + goto out; + if (lineno > target) { + diff = lineno - target; + if (diff < bestdiff) { + bestdiff = diff; + best = offset; + } + } + offset += SN_DELTA(sn); + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + lineno++; + } + } + if (best >= 0) + offset = best; +out: + return script->code + offset; +} + +JS_FRIEND_API(uintN) +js_GetScriptLineExtent(JSScript *script) +{ + uintN lineno; + jssrcnote *sn; + JSSrcNoteType type; + + lineno = script->lineno; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + lineno++; + } + } + return 1 + lineno - script->lineno; +} + +#if JS_HAS_GENERATORS + +jsbytecode * +js_FindFinallyHandler(JSScript *script, jsbytecode *pc) +{ + JSTryNote *tn; + ptrdiff_t off; + JSOp op2; + + tn = script->trynotes; + if (!tn) + return NULL; + + off = pc - script->main; + if (off < 0) + return NULL; + + JS_ASSERT(tn->catchStart != 0); + do { + if ((jsuword)(off - tn->start) < (jsuword)tn->length) { + /* + * We have a handler: is it the finally one, or a catch handler? + * + * Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK + * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION) + */ + pc = script->main + tn->catchStart; + JS_ASSERT(*pc == JSOP_SETSP); + op2 = pc[JSOP_SETSP_LENGTH]; + if (op2 != JSOP_ENTERBLOCK) { + JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION); + return pc; + } + } + } while ((++tn)->catchStart != 0); + return NULL; +} + +#endif diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscript.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscript.h new file mode 100644 index 0000000..18ad373 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsscript.h @@ -0,0 +1,225 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscript_h___ +#define jsscript_h___ +/* + * JS script descriptor. + */ +#include "jsatom.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* + * Exception handling runtime information. + * + * All fields except length are code offsets relative to the main entry point + * of the script. If script->trynotes is not null, it points to a vector of + * these structs terminated by one with catchStart == 0. + */ +struct JSTryNote { + ptrdiff_t start; /* start of try statement */ + ptrdiff_t length; /* count of try statement bytecodes */ + ptrdiff_t catchStart; /* start of catch block (0 if end) */ +}; + +#define JSTRYNOTE_GRAIN sizeof(ptrdiff_t) +#define JSTRYNOTE_ALIGNMASK (JSTRYNOTE_GRAIN - 1) + +struct JSScript { + jsbytecode *code; /* bytecodes and their immediate operands */ + uint32 length; /* length of code vector */ + jsbytecode *main; /* main entry point, after predef'ing prolog */ + uint16 version; /* JS version under which script was compiled */ + uint16 numGlobalVars; /* declared global var/const/function count */ + JSAtomMap atomMap; /* maps immediate index to literal struct */ + const char *filename; /* source filename or null */ + uintN lineno; /* base line number of script */ + uintN depth; /* maximum stack depth in slots */ + JSTryNote *trynotes; /* exception table for this script */ + JSPrincipals *principals; /* principals for this script */ + JSObject *object; /* optional Script-class object wrapper */ +}; + +/* No need to store script->notes now that it is allocated right after code. */ +#define SCRIPT_NOTES(script) ((jssrcnote*)((script)->code+(script)->length)) + +#define SCRIPT_FIND_CATCH_START(script, pc, catchpc) \ + JS_BEGIN_MACRO \ + JSTryNote *tn_ = (script)->trynotes; \ + jsbytecode *catchpc_ = NULL; \ + if (tn_) { \ + ptrdiff_t off_ = PTRDIFF(pc, (script)->main, jsbytecode); \ + if (off_ >= 0) { \ + while ((jsuword)(off_ - tn_->start) >= (jsuword)tn_->length) \ + ++tn_; \ + if (tn_->catchStart) \ + catchpc_ = (script)->main + tn_->catchStart; \ + } \ + } \ + catchpc = catchpc_; \ + JS_END_MACRO + +/* + * Find the innermost finally block that handles the given pc. This is a + * version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used + * to implement generator.close(). + */ +jsbytecode * +js_FindFinallyHandler(JSScript *script, jsbytecode *pc); + +extern JS_FRIEND_DATA(JSClass) js_ScriptClass; + +extern JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj); + +/* + * On first new context in rt, initialize script runtime state, specifically + * the script filename table and its lock. + */ +extern JSBool +js_InitRuntimeScriptState(JSRuntime *rt); + +/* + * On last context destroy for rt, if script filenames are all GC'd, free the + * script filename table and its lock. + */ +extern void +js_FinishRuntimeScriptState(JSRuntime *rt); + +/* + * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any + * script filename table entries that have not been GC'd, the latter using + * js_FinishRuntimeScriptState. + * + * This allows script filename prefixes to outlive any context in rt. + */ +extern void +js_FreeRuntimeScriptState(JSRuntime *rt); + +extern const char * +js_SaveScriptFilename(JSContext *cx, const char *filename); + +extern const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags); + +extern uint32 +js_GetScriptFilenameFlags(const char *filename); + +extern void +js_MarkScriptFilename(const char *filename); + +extern void +js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms); + +extern void +js_SweepScriptFilenames(JSRuntime *rt); + +/* + * Two successively less primitive ways to make a new JSScript. The first + * does *not* call a non-null cx->runtime->newScriptHook -- only the second, + * js_NewScriptFromCG, calls this optional debugger hook. + * + * The js_NewScript function can't know whether the script it creates belongs + * to a function, or is top-level or eval code, but the debugger wants access + * to the newly made script's function, if any -- so callers of js_NewScript + * are responsible for notifying the debugger after successfully creating any + * kind (function or other) of new JSScript. + */ +extern JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 snlength, uint32 tnlength); + +extern JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); + +/* + * New-script-hook calling is factored from js_NewScriptFromCG so that it + * and callers of js_XDRScript can share this code. In the case of callers + * of js_XDRScript, the hook should be invoked only after successful decode + * of any owning function (the fun parameter) or script object (null fun). + */ +extern JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); + +extern JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script); + +extern void +js_DestroyScript(JSContext *cx, JSScript *script); + +extern void +js_MarkScript(JSContext *cx, JSScript *script); + +/* + * To perturb as little code as possible, we introduce a js_GetSrcNote lookup + * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes + * a macro that uses cx from its calls' lexical environments. + */ +#define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc) + +extern jssrcnote * +js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); + +/* XXX need cx to lock function objects declared by prolog bytecodes. */ +extern uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern jsbytecode * +js_LineNumberToPC(JSScript *script, uintN lineno); + +extern JS_FRIEND_API(uintN) +js_GetScriptLineExtent(JSScript *script); + +/* + * If magic is non-null, js_XDRScript succeeds on magic number mismatch but + * returns false in *magic; it reflects a match via a true *magic out param. + * If magic is null, js_XDRScript returns false on bad magic number errors, + * which it reports. + * + * NB: callers must call js_CallNewScriptHook after successful JSXDR_DECODE + * and subsequent set-up of owning function or script object, if any. + */ +extern JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic); + +JS_END_EXTERN_C + +#endif /* jsscript_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsshell.msg b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsshell.msg new file mode 100644 index 0000000..4b811ac --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsshell.msg @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + Error messages for JSShell. See js.msg for format. +*/ + +MSG_DEF(JSSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") +MSG_DEF(JSSMSG_CANT_OPEN, 1, 2, JSEXN_NONE, "can't open {0}: {1}") +MSG_DEF(JSSMSG_TRAP_USAGE, 2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") +MSG_DEF(JSSMSG_LINE2PC_USAGE, 3, 0, JSEXN_NONE, "usage: line2pc [fun] line") +MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scripts read from files") +MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}") +MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id") diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstddef.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstddef.h new file mode 100644 index 0000000..addaa88 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstddef.h @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * stddef inclusion here to first declare ptrdif as a signed long instead of a + * signed int. + */ + +#ifdef _WINDOWS +# ifndef XP_WIN +# define XP_WIN +# endif +#if defined(_WIN32) || defined(WIN32) +# ifndef XP_WIN32 +# define XP_WIN32 +# endif +#else +# ifndef XP_WIN16 +# define XP_WIN16 +# endif +#endif +#endif + +#ifdef XP_WIN16 +#ifndef _PTRDIFF_T_DEFINED +typedef long ptrdiff_t; + +/* + * The Win16 compiler treats pointer differences as 16-bit signed values. + * This macro allows us to treat them as 17-bit signed values, stored in + * a 32-bit type. + */ +#define PTRDIFF(p1, p2, type) \ + ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type)) + +#define _PTRDIFF_T_DEFINED +#endif /*_PTRDIFF_T_DEFINED*/ +#else /*WIN16*/ + +#define PTRDIFF(p1, p2, type) \ + ((p1) - (p2)) + +#endif + +#include + + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstr.c b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstr.c new file mode 100644 index 0000000..e38f652 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstr.c @@ -0,0 +1,4818 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS string type implementation. + * + * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these + * native methods store strings (possibly newborn) converted from their 'this' + * parameter and arguments on the stack: 'this' conversions at argv[-1], arg + * conversions at their index (argv[0], argv[1]). This is a legitimate method + * of rooting things that might lose their newborn root due to subsequent GC + * allocations in the same native method. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsstr.h" + +#define JSSTRDEP_RECURSION_LIMIT 100 + +size_t +js_MinimizeDependentStrings(JSString *str, int level, JSString **basep) +{ + JSString *base; + size_t start, length; + + JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); + base = JSSTRDEP_BASE(str); + start = JSSTRDEP_START(str); + if (JSSTRING_IS_DEPENDENT(base)) { + if (level < JSSTRDEP_RECURSION_LIMIT) { + start += js_MinimizeDependentStrings(base, level + 1, &base); + } else { + do { + start += JSSTRDEP_START(base); + base = JSSTRDEP_BASE(base); + } while (JSSTRING_IS_DEPENDENT(base)); + } + if (start == 0) { + JS_ASSERT(JSSTRING_IS_PREFIX(str)); + JSPREFIX_SET_BASE(str, base); + } else if (start <= JSSTRDEP_START_MASK) { + length = JSSTRDEP_LENGTH(str); + JSSTRDEP_SET_START_AND_LENGTH(str, start, length); + JSSTRDEP_SET_BASE(str, base); + } + } + *basep = base; + return start; +} + +jschar * +js_GetDependentStringChars(JSString *str) +{ + size_t start; + JSString *base; + + start = js_MinimizeDependentStrings(str, 0, &base); + JS_ASSERT(!JSSTRING_IS_DEPENDENT(base)); + JS_ASSERT(start < base->length); + return base->chars + start; +} + +jschar * +js_GetStringChars(JSString *str) +{ + if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str)) + return NULL; + + *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; + return str->chars; +} + +JSString * +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) +{ + size_t rn, ln, lrdist, n; + jschar *rs, *ls, *s; + JSDependentString *ldep; /* non-null if left should become dependent */ + JSString *str; + + if (JSSTRING_IS_DEPENDENT(right)) { + rn = JSSTRDEP_LENGTH(right); + rs = JSSTRDEP_CHARS(right); + } else { + rn = right->length; + rs = right->chars; + } + if (rn == 0) + return left; + + if (JSSTRING_IS_DEPENDENT(left) || + !(*js_GetGCThingFlags(left) & GCF_MUTABLE)) { + /* We must copy if left does not own a buffer to realloc. */ + ln = JSSTRING_LENGTH(left); + if (ln == 0) + return right; + ls = JSSTRING_CHARS(left); + s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar)); + if (!s) + return NULL; + js_strncpy(s, ls, ln); + ldep = NULL; + } else { + /* We can realloc left's space and make it depend on our result. */ + ln = left->length; + if (ln == 0) + return right; + ls = left->chars; + s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar)); + if (!s) + return NULL; + + /* Take care: right could depend on left! */ + lrdist = (size_t)(rs - ls); + if (lrdist < ln) + rs = s + lrdist; + left->chars = ls = s; + ldep = JSSTRDEP(left); + } + + js_strncpy(s + ln, rs, rn); + n = ln + rn; + s[n] = 0; + str = js_NewString(cx, s, n, GCF_MUTABLE); + if (!str) { + /* Out of memory: clean up any space we (re-)allocated. */ + if (!ldep) { + JS_free(cx, s); + } else { + s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar)); + if (s) + left->chars = s; + } + } else { + /* Morph left into a dependent prefix if we realloc'd its buffer. */ + if (ldep) { + JSPREFIX_SET_LENGTH(ldep, ln); + JSPREFIX_SET_BASE(ldep, str); +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveDependentStrings); + JS_RUNTIME_METER(rt, totalDependentStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum += (double)ln, + rt->strdepLengthSquaredSum += (double)ln * (double)ln)); + } +#endif + } + } + + return str; +} + +/* + * May be called with null cx by js_GetStringChars, above; and by the jslock.c + * MAKE_STRING_IMMUTABLE file-local macro. + */ +const jschar * +js_UndependString(JSContext *cx, JSString *str) +{ + size_t n, size; + jschar *s; + + if (JSSTRING_IS_DEPENDENT(str)) { + n = JSSTRDEP_LENGTH(str); + size = (n + 1) * sizeof(jschar); + s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size)); + if (!s) + return NULL; + + js_strncpy(s, JSSTRDEP_CHARS(str), n); + s[n] = 0; + str->length = n; + str->chars = s; + +#ifdef DEBUG + if (cx) { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_UNMETER(rt, liveDependentStrings); + JS_RUNTIME_UNMETER(rt, totalDependentStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum -= (double)n, + rt->strdepLengthSquaredSum -= (double)n * (double)n)); + } +#endif + } + + return str->chars; +} + +/* + * Forward declarations for URI encode/decode and helper routines + */ +static JSBool +str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static uint32 +Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length); + +/* + * Contributions from the String class to the set of methods defined for the + * global object. escape and unescape used to be defined in the Mocha library, + * but as ECMA decided to spec them, they've been moved to the core engine + * and made ECMA-compliant. (Incomplete escapes are interpreted as literal + * characters by unescape.) + */ + +/* + * Stuff to emulate the old libmocha escape, which took a second argument + * giving the type of escape to perform. Retained for compatibility, and + * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. + */ + +#define URL_XALPHAS ((uint8) 1) +#define URL_XPALPHAS ((uint8) 2) +#define URL_PATH ((uint8) 4) + +static const uint8 urlCharType[256] = +/* Bit 0 xalpha -- the alphas + * Bit 1 xpalpha -- as xalpha but + * converts spaces to plus and plus to %20 + * Bit 2 ... path -- as xalphas but doesn't escape '/' + */ + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ + 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ + 0, }; + +/* This matches the ECMA escape set when mask is 7 (default.) */ + +#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask)) + +/* See ECMA-262 15.1.2.4. */ +JSBool +js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + size_t i, ni, length, newlength; + const jschar *chars; + jschar *newchars; + jschar ch; + jsint mask; + jsdouble d; + const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + if (!JSDOUBLE_IS_FINITE(d) || + (mask = (jsint)d) != d || + mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) + { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_STRING_MASK, numBuf); + return JS_FALSE; + } + } + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + chars = JSSTRING_CHARS(str); + length = newlength = JSSTRING_LENGTH(str); + + /* Take a first pass and see how big the result string will need to be. */ + for (i = 0; i < length; i++) { + if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) + continue; + if (ch < 256) { + if (mask == URL_XPALPHAS && ch == ' ') + continue; /* The character will be encoded as '+' */ + newlength += 2; /* The character will be encoded as %XX */ + } else { + newlength += 5; /* The character will be encoded as %uXXXX */ + } + + /* + * This overflow test works because newlength is incremented by at + * most 5 on each iteration. + */ + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + + if (newlength >= ~(size_t)0 / sizeof(jschar)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!newchars) + return JS_FALSE; + for (i = 0, ni = 0; i < length; i++) { + if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { + newchars[ni++] = ch; + } else if (ch < 256) { + if (mask == URL_XPALPHAS && ch == ' ') { + newchars[ni++] = '+'; /* convert spaces to pluses */ + } else { + newchars[ni++] = '%'; + newchars[ni++] = digits[ch >> 4]; + newchars[ni++] = digits[ch & 0xF]; + } + } else { + newchars[ni++] = '%'; + newchars[ni++] = 'u'; + newchars[ni++] = digits[ch >> 12]; + newchars[ni++] = digits[(ch & 0xF00) >> 8]; + newchars[ni++] = digits[(ch & 0xF0) >> 4]; + newchars[ni++] = digits[ch & 0xF]; + } + } + JS_ASSERT(ni == newlength); + newchars[newlength] = 0; + + str = js_NewString(cx, newchars, newlength, 0); + if (!str) { + JS_free(cx, newchars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#undef IS_OK + +/* See ECMA-262 15.1.2.5 */ +static JSBool +str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + size_t i, ni, length; + const jschar *chars; + jschar *newchars; + jschar ch; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + chars = JSSTRING_CHARS(str); + length = JSSTRING_LENGTH(str); + + /* Don't bother allocating less space for the new string. */ + newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!newchars) + return JS_FALSE; + ni = i = 0; + while (i < length) { + ch = chars[i++]; + if (ch == '%') { + if (i + 1 < length && + JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) + { + ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]); + i += 2; + } else if (i + 4 < length && chars[i] == 'u' && + JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) && + JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4])) + { + ch = (((((JS7_UNHEX(chars[i + 1]) << 4) + + JS7_UNHEX(chars[i + 2])) << 4) + + JS7_UNHEX(chars[i + 3])) << 4) + + JS7_UNHEX(chars[i + 4]); + i += 5; + } + } + newchars[ni++] = ch; + } + newchars[ni] = 0; + + str = js_NewString(cx, newchars, ni, 0); + if (!str) { + JS_free(cx, newchars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#if JS_HAS_UNEVAL +static JSBool +str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = js_ValueToSource(cx, argv[0]); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +const char js_escape_str[] = "escape"; +const char js_unescape_str[] = "unescape"; +#if JS_HAS_UNEVAL +const char js_uneval_str[] = "uneval"; +#endif +const char js_decodeURI_str[] = "decodeURI"; +const char js_encodeURI_str[] = "encodeURI"; +const char js_decodeURIComponent_str[] = "decodeURIComponent"; +const char js_encodeURIComponent_str[] = "encodeURIComponent"; + +static JSFunctionSpec string_functions[] = { + {js_escape_str, js_str_escape, 1,0,0}, + {js_unescape_str, str_unescape, 1,0,0}, +#if JS_HAS_UNEVAL + {js_uneval_str, str_uneval, 1,0,0}, +#endif + {js_decodeURI_str, str_decodeURI, 1,0,0}, + {js_encodeURI_str, str_encodeURI, 1,0,0}, + {js_decodeURIComponent_str, str_decodeURI_Component, 1,0,0}, + {js_encodeURIComponent_str, str_encodeURI_Component, 1,0,0}, + + {0,0,0,0,0} +}; + +jschar js_empty_ucstr[] = {0}; +JSSubString js_EmptySubString = {0, js_empty_ucstr}; + +enum string_tinyid { + STRING_LENGTH = -1 +}; + +static JSPropertySpec string_props[] = { + {js_length_str, STRING_LENGTH, + JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0}, + {0,0,0,0,0} +}; + +static JSBool +str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsval v; + JSString *str; + jsint slot; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + slot = JSVAL_TO_INT(id); + if (slot == STRING_LENGTH) { + if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { + /* Follow ECMA-262 by fetching intrinsic length of our string. */ + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + } else { + /* Preserve compatibility: convert obj to a string primitive. */ + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + } + + *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str)); + } + return JS_TRUE; +} + +#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) + +static JSBool +str_enumerate(JSContext *cx, JSObject *obj) +{ + jsval v; + JSString *str, *str1; + size_t i, length; + + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + + length = JSSTRING_LENGTH(str); + for (i = 0; i < length; i++) { + str1 = js_NewDependentString(cx, str, i, 1, 0); + if (!str1) + return JS_FALSE; + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i), + STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS, NULL)) { + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + jsval v; + JSString *str, *str1; + jsint slot; + + if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) + return JS_TRUE; + + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + + slot = JSVAL_TO_INT(id); + if ((size_t)slot < JSSTRING_LENGTH(str)) { + str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0); + if (!str1) + return JS_FALSE; + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot), + STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS, NULL)) { + return JS_FALSE; + } + *objp = obj; + } + return JS_TRUE; +} + +JSClass js_StringClass = { + js_String_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | + JSCLASS_HAS_CACHED_PROTO(JSProto_String), + JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, + str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#if JS_HAS_TOSOURCE + +/* + * String.prototype.quote is generic (as are most string methods), unlike + * toSource, toString, and valueOf. + */ +static JSBool +str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + str = js_QuoteString(cx, str, '"'); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSString *str; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + + if (JSVAL_IS_STRING((jsval)obj)) { + v = (jsval)obj; + } else { + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); + } + str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (!str) + return JS_FALSE; + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); + s = JSSTRING_CHARS(str); + k = JSSTRING_LENGTH(str); + n = j + k + 2; + t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + str = js_NewString(cx, t, n, 0); + if (!str) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + + if (JSVAL_IS_STRING((jsval)obj)) { + *rval = (jsval)obj; + return JS_TRUE; + } + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toString(cx, obj, argc, argv, rval); + *rval = v; + return JS_TRUE; +} + +static JSBool +str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (JSVAL_IS_STRING((jsval)obj)) { + *rval = (jsval)obj; + return JS_TRUE; + } + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) + return JS_FALSE; + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + return JS_TRUE; +} + +/* + * Java-like string native methods. + */ +static JSBool +str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + begin = js_DoubleToInteger(d); + if (begin < 0) + begin = 0; + else if (begin > length) + begin = length; + + if (argc == 1) { + end = length; + } else { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) + end = 0; + else if (end > length) + end = length; + if (end < begin) { + /* ECMA emulates old JDK1.0 java.lang.String.substring. */ + jsdouble tmp = begin; + begin = end; + end = tmp; + } + } + + str = js_NewDependentString(cx, str, (size_t)begin, + (size_t)(end - begin), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + size_t i, n; + jschar *s, *news; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + n = JSSTRING_LENGTH(str); + news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return JS_FALSE; + s = JSSTRING_CHARS(str); + for (i = 0; i < n; i++) + news[i] = JS_TOLOWER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n, 0); + if (!str) { + JS_free(cx, news); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + /* + * Forcefully ignore the first (or any) argument and return toLowerCase(), + * ECMA has reserved that argument, presumably for defining the locale. + */ + if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + return cx->localeCallbacks->localeToLowerCase(cx, str, rval); + } + return str_toLowerCase(cx, obj, 0, argv, rval); +} + +static JSBool +str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + size_t i, n; + jschar *s, *news; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + n = JSSTRING_LENGTH(str); + news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return JS_FALSE; + s = JSSTRING_CHARS(str); + for (i = 0; i < n; i++) + news[i] = JS_TOUPPER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n, 0); + if (!str) { + JS_free(cx, news); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + /* + * Forcefully ignore the first (or any) argument and return toUpperCase(), + * ECMA has reserved that argument, presumbaly for defining the locale. + */ + if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + return cx->localeCallbacks->localeToUpperCase(cx, str, rval); + } + return str_toUpperCase(cx, obj, 0, argv, rval); +} + +static JSBool +str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str, *thatStr; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + *rval = JSVAL_ZERO; + } else { + thatStr = js_ValueToString(cx, argv[0]); + if (!thatStr) + return JS_FALSE; + if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { + argv[0] = STRING_TO_JSVAL(thatStr); + return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval); + } + *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); + } + return JS_TRUE; +} + +static JSBool +str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + size_t index; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + d = 0.0; + } else { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || JSSTRING_LENGTH(str) <= d) { + *rval = JS_GetEmptyStringValue(cx); + } else { + index = (size_t)d; + str = js_NewDependentString(cx, str, index, 1, 0); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +static JSBool +str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsdouble d; + size_t index; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + d = 0.0; + } else { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || JSSTRING_LENGTH(str) <= d) { + *rval = JS_GetNaNValue(cx); + } else { + index = (size_t)d; + *rval = INT_TO_JSVAL((jsint) JSSTRING_CHARS(str)[index]); + } + return JS_TRUE; +} + +jsint +js_BoyerMooreHorspool(const jschar *text, jsint textlen, + const jschar *pat, jsint patlen, + jsint start) +{ + jsint i, j, k, m; + uint8 skip[BMH_CHARSET_SIZE]; + jschar c; + + JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX); + for (i = 0; i < BMH_CHARSET_SIZE; i++) + skip[i] = (uint8)patlen; + m = patlen - 1; + for (i = 0; i < m; i++) { + c = pat[i]; + if (c >= BMH_CHARSET_SIZE) + return BMH_BAD_PATTERN; + skip[c] = (uint8)(m - i); + } + for (k = start + m; + k < textlen; + k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) { + for (i = k, j = m; ; i--, j--) { + if (j < 0) + return i + 1; + if (text[i] != pat[j]) + break; + } + } + return -1; +} + +static JSBool +str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *str2; + jsint i, j, index, textlen, patlen; + const jschar *text, *pat; + jsdouble d; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + text = JSSTRING_CHARS(str); + textlen = (jsint) JSSTRING_LENGTH(str); + + str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + pat = JSSTRING_CHARS(str2); + patlen = (jsint) JSSTRING_LENGTH(str2); + + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) + i = 0; + else if (d > textlen) + i = textlen; + else + i = (jsint)d; + } else { + i = 0; + } + if (patlen == 0) { + *rval = INT_TO_JSVAL(i); + return JS_TRUE; + } + + /* XXX tune the BMH threshold (512) */ + if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) { + index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i); + if (index != BMH_BAD_PATTERN) + goto out; + } + + index = -1; + j = 0; + while (i + j < textlen) { + if (text[i + j] == pat[j]) { + if (++j == patlen) { + index = i; + break; + } + } else { + i++; + j = 0; + } + } + +out: + *rval = INT_TO_JSVAL(index); + return JS_TRUE; +} + +static JSBool +str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str, *str2; + const jschar *text, *pat; + jsint i, j, textlen, patlen; + jsdouble d; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + text = JSSTRING_CHARS(str); + textlen = (jsint) JSSTRING_LENGTH(str); + + str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + pat = JSSTRING_CHARS(str2); + patlen = (jsint) JSSTRING_LENGTH(str2); + + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(d)) { + i = textlen; + } else { + d = js_DoubleToInteger(d); + if (d < 0) + i = 0; + else if (d > textlen) + i = textlen; + else + i = (jsint)d; + } + } else { + i = textlen; + } + + if (patlen == 0) { + *rval = INT_TO_JSVAL(i); + return JS_TRUE; + } + + j = 0; + while (i >= 0) { + /* Don't assume that text is NUL-terminated: it could be dependent. */ + if (i + j < textlen && text[i + j] == pat[j]) { + if (++j == patlen) + break; + } else { + i--; + j = 0; + } + } + *rval = INT_TO_JSVAL(i); + return JS_TRUE; +} + +/* + * Perl-inspired string functions. + */ +typedef struct GlobData { + uintN flags; /* inout: mode and flag bits, see below */ + uintN optarg; /* in: index of optional flags argument */ + JSString *str; /* out: 'this' parameter object as string */ + JSRegExp *regexp; /* out: regexp parameter object private data */ +} GlobData; + +/* + * Mode and flag bit definitions for match_or_replace's GlobData.flags field. + */ +#define MODE_MATCH 0x00 /* in: return match array on success */ +#define MODE_REPLACE 0x01 /* in: match and replace */ +#define MODE_SEARCH 0x02 /* in: search only, return match index or -1 */ +#define GET_MODE(f) ((f) & 0x03) +#define FORCE_FLAT 0x04 /* in: force flat (non-regexp) string match */ +#define KEEP_REGEXP 0x08 /* inout: keep GlobData.regexp alive for caller + of match_or_replace; if set on input + but clear on output, regexp ownership + does not pass to caller */ +#define GLOBAL_REGEXP 0x10 /* out: regexp had the 'g' flag */ + +static JSBool +match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + JSBool (*glob)(JSContext *cx, jsint count, GlobData *data), + GlobData *data, jsval *rval) +{ + JSString *str, *src, *opt; + JSObject *reobj; + JSRegExp *re; + size_t index, length; + JSBool ok, test; + jsint count; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + data->str = str; + + if (JSVAL_IS_REGEXP(cx, argv[0])) { + reobj = JSVAL_TO_OBJECT(argv[0]); + re = (JSRegExp *) JS_GetPrivate(cx, reobj); + } else { + src = js_ValueToString(cx, argv[0]); + if (!src) + return JS_FALSE; + if (data->optarg < argc) { + argv[0] = STRING_TO_JSVAL(src); + opt = js_ValueToString(cx, argv[data->optarg]); + if (!opt) + return JS_FALSE; + } else { + opt = NULL; + } + re = js_NewRegExpOpt(cx, NULL, src, opt, + (data->flags & FORCE_FLAT) != 0); + if (!re) + return JS_FALSE; + reobj = NULL; + } + /* From here on, all control flow must reach the matching DROP. */ + data->regexp = re; + HOLD_REGEXP(cx, re); + + if (re->flags & JSREG_GLOB) + data->flags |= GLOBAL_REGEXP; + index = 0; + if (GET_MODE(data->flags) == MODE_SEARCH) { + ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); + if (ok) { + *rval = (*rval == JSVAL_TRUE) + ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length) + : INT_TO_JSVAL(-1); + } + } else if (data->flags & GLOBAL_REGEXP) { + if (reobj) { + /* Set the lastIndex property's reserved slot to 0. */ + ok = js_SetLastIndex(cx, reobj, 0); + } else { + ok = JS_TRUE; + } + if (ok) { + length = JSSTRING_LENGTH(str); + for (count = 0; index <= length; count++) { + ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); + if (!ok || *rval != JSVAL_TRUE) + break; + ok = glob(cx, count, data); + if (!ok) + break; + if (cx->regExpStatics.lastMatch.length == 0) { + if (index == length) + break; + index++; + } + } + } + } else { + if (GET_MODE(data->flags) == MODE_REPLACE) { + test = JS_TRUE; + } else { + /* + * MODE_MATCH implies str_match is being called from a script or a + * scripted function. If the caller cares only about testing null + * vs. non-null return value, optimize away the array object that + * would normally be returned in *rval. + */ + JSStackFrame *fp = cx->fp->down; + + /* Skip Function.prototype.call and .apply frames. */ + while (fp && !fp->pc) { + JS_ASSERT(!fp->script); + fp = fp->down; + } + + /* Assume a full array result is required, then prove otherwise. */ + test = JS_FALSE; + if (fp) { + JS_ASSERT(*fp->pc == JSOP_CALL || *fp->pc == JSOP_NEW); + JS_ASSERT(js_CodeSpec[*fp->pc].length == 3); + switch (fp->pc[3]) { + case JSOP_POP: + case JSOP_IFEQ: + case JSOP_IFNE: + case JSOP_IFEQX: + case JSOP_IFNEX: + test = JS_TRUE; + break; + default:; + } + } + } + ok = js_ExecuteRegExp(cx, re, str, &index, test, rval); + } + + DROP_REGEXP(cx, re); + if (reobj) { + /* Tell our caller that it doesn't need to destroy data->regexp. */ + data->flags &= ~KEEP_REGEXP; + } else if (!(data->flags & KEEP_REGEXP)) { + /* Caller didn't want to keep data->regexp, so null and destroy it. */ + data->regexp = NULL; + js_DestroyRegExp(cx, re); + } + + return ok; +} + +typedef struct MatchData { + GlobData base; + jsval *arrayval; /* NB: local root pointer */ +} MatchData; + +static JSBool +match_glob(JSContext *cx, jsint count, GlobData *data) +{ + MatchData *mdata; + JSObject *arrayobj; + JSSubString *matchsub; + JSString *matchstr; + jsval v; + + mdata = (MatchData *)data; + arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval); + if (!arrayobj) { + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj); + } + matchsub = &cx->regExpStatics.lastMatch; + matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0); + if (!matchstr) + return JS_FALSE; + v = STRING_TO_JSVAL(matchstr); + return js_SetProperty(cx, arrayobj, INT_TO_JSID(count), &v); +} + +static JSBool +str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + MatchData mdata; + JSBool ok; + + mdata.base.flags = MODE_MATCH; + mdata.base.optarg = 1; + mdata.arrayval = &argv[2]; + *mdata.arrayval = JSVAL_NULL; + ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval); + if (ok && !JSVAL_IS_NULL(*mdata.arrayval)) + *rval = *mdata.arrayval; + return ok; +} + +static JSBool +str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GlobData data; + + data.flags = MODE_SEARCH; + data.optarg = 1; + return match_or_replace(cx, obj, argc, argv, NULL, &data, rval); +} + +typedef struct ReplaceData { + GlobData base; /* base struct state */ + JSObject *lambda; /* replacement function object or null */ + JSString *repstr; /* replacement string */ + jschar *dollar; /* null or pointer to first $ in repstr */ + jschar *dollarEnd; /* limit pointer for js_strchr_limit */ + jschar *chars; /* result chars, null initially */ + size_t length; /* result length, 0 initially */ + jsint index; /* index in result of next replacement */ + jsint leftIndex; /* left context index in base.str->chars */ + JSSubString dollarStr; /* for "$$" interpret_dollar result */ +} ReplaceData; + +static JSSubString * +interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, + size_t *skip) +{ + JSRegExpStatics *res; + jschar dc, *cp; + uintN num, tmp; + + JS_ASSERT(*dp == '$'); + + /* If there is only a dollar, bail now */ + if (dp + 1 >= ep) + return NULL; + + /* Interpret all Perl match-induced dollar variables. */ + res = &cx->regExpStatics; + dc = dp[1]; + if (JS7_ISDEC(dc)) { + /* ECMA-262 Edition 3: 1-9 or 01-99 */ + num = JS7_UNDEC(dc); + if (num > res->parenCount) + return NULL; + + cp = dp + 2; + if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { + tmp = 10 * num + JS7_UNDEC(dc); + if (tmp <= res->parenCount) { + cp++; + num = tmp; + } + } + if (num == 0) + return NULL; + + /* Adjust num from 1 $n-origin to 0 array-index-origin. */ + num--; + *skip = cp - dp; + return REGEXP_PAREN_SUBSTRING(res, num); + } + + *skip = 2; + switch (dc) { + case '$': + rdata->dollarStr.chars = dp; + rdata->dollarStr.length = 1; + return &rdata->dollarStr; + case '&': + return &res->lastMatch; + case '+': + return &res->lastParen; + case '`': + return &res->leftContext; + case '\'': + return &res->rightContext; + } + return NULL; +} + +static JSBool +find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) +{ + JSString *repstr; + size_t replen, skip; + jschar *dp, *ep; + JSSubString *sub; + JSObject *lambda; + + lambda = rdata->lambda; + if (lambda) { + uintN argc, i, j, m, n, p; + jsval *sp, *oldsp, rval; + void *mark; + JSStackFrame *fp; + JSBool ok; + + /* + * Save the regExpStatics from the current regexp, since they may be + * clobbered by a RegExp usage in the lambda function. Note that all + * members of JSRegExpStatics are JSSubStrings, so not GC roots, save + * input, which is rooted otherwise via argv[-1] in str_replace. + */ + JSRegExpStatics save = cx->regExpStatics; + JSBool freeMoreParens = JS_FALSE; + + /* + * In the lambda case, not only do we find the replacement string's + * length, we compute repstr and return it via rdata for use within + * do_replace. The lambda is called with arguments ($&, $1, $2, ..., + * index, input), i.e., all the properties of a regexp match array. + * For $&, etc., we must create string jsvals from cx->regExpStatics. + * We grab up stack space to keep the newborn strings GC-rooted. + */ + p = rdata->base.regexp->parenCount; + argc = 1 + p + 2; + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) + return JS_FALSE; + + /* Push lambda and its 'this' parameter. */ + *sp++ = OBJECT_TO_JSVAL(lambda); + *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); + +#define PUSH_REGEXP_STATIC(sub) \ + JS_BEGIN_MACRO \ + JSString *str = js_NewStringCopyN(cx, \ + cx->regExpStatics.sub.chars, \ + cx->regExpStatics.sub.length, \ + 0); \ + if (!str) { \ + ok = JS_FALSE; \ + goto lambda_out; \ + } \ + *sp++ = STRING_TO_JSVAL(str); \ + JS_END_MACRO + + /* Push $&, $1, $2, ... */ + PUSH_REGEXP_STATIC(lastMatch); + i = 0; + m = cx->regExpStatics.parenCount; + n = JS_MIN(m, 9); + for (j = 0; i < n; i++, j++) + PUSH_REGEXP_STATIC(parens[j]); + for (j = 0; i < m; i++, j++) + PUSH_REGEXP_STATIC(moreParens[j]); + + /* + * We need to clear moreParens in the top-of-stack cx->regExpStatics + * to it won't be possibly realloc'ed, leaving the bottom-of-stack + * moreParens pointing to freed memory. + */ + cx->regExpStatics.moreParens = NULL; + freeMoreParens = JS_TRUE; + +#undef PUSH_REGEXP_STATIC + + /* Make sure to push undefined for any unmatched parens. */ + for (; i < p; i++) + *sp++ = JSVAL_VOID; + + /* Push match index and input string. */ + *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length); + *sp++ = STRING_TO_JSVAL(rdata->base.str); + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL); + rval = fp->sp[-1]; + fp->sp = oldsp; + + if (ok) { + /* + * NB: we count on the newborn string root to hold any string + * created by this js_ValueToString that would otherwise be GC- + * able, until we use rdata->repstr in do_replace. + */ + repstr = js_ValueToString(cx, rval); + if (!repstr) { + ok = JS_FALSE; + } else { + rdata->repstr = repstr; + *sizep = JSSTRING_LENGTH(repstr); + } + } + + lambda_out: + js_FreeStack(cx, mark); + if (freeMoreParens) + JS_free(cx, cx->regExpStatics.moreParens); + cx->regExpStatics = save; + return ok; + } + + repstr = rdata->repstr; + replen = JSSTRING_LENGTH(repstr); + for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { + sub = interpret_dollar(cx, dp, ep, rdata, &skip); + if (sub) { + replen += sub->length - skip; + dp += skip; + } + else + dp++; + } + *sizep = replen; + return JS_TRUE; +} + +static void +do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) +{ + JSString *repstr; + jschar *bp, *cp, *dp, *ep; + size_t len, skip; + JSSubString *sub; + + repstr = rdata->repstr; + bp = cp = JSSTRING_CHARS(repstr); + for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { + len = dp - cp; + js_strncpy(chars, cp, len); + chars += len; + cp = dp; + sub = interpret_dollar(cx, dp, ep, rdata, &skip); + if (sub) { + len = sub->length; + js_strncpy(chars, sub->chars, len); + chars += len; + cp += skip; + dp += skip; + } else { + dp++; + } + } + js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (cp - bp)); +} + +static JSBool +replace_glob(JSContext *cx, jsint count, GlobData *data) +{ + ReplaceData *rdata; + JSString *str; + size_t leftoff, leftlen, replen, growth; + const jschar *left; + jschar *chars; + + rdata = (ReplaceData *)data; + str = data->str; + leftoff = rdata->leftIndex; + left = JSSTRING_CHARS(str) + leftoff; + leftlen = cx->regExpStatics.lastMatch.chars - left; + rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str); + rdata->leftIndex += cx->regExpStatics.lastMatch.length; + if (!find_replen(cx, rdata, &replen)) + return JS_FALSE; + growth = leftlen + replen; + chars = (jschar *) + (rdata->chars + ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1) + * sizeof(jschar)) + : JS_malloc(cx, (growth + 1) * sizeof(jschar))); + if (!chars) { + JS_free(cx, rdata->chars); + rdata->chars = NULL; + return JS_FALSE; + } + rdata->chars = chars; + rdata->length += growth; + chars += rdata->index; + rdata->index += growth; + js_strncpy(chars, left, leftlen); + chars += leftlen; + do_replace(cx, rdata, chars); + return JS_TRUE; +} + +static JSBool +str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *lambda; + JSString *repstr, *str; + ReplaceData rdata; + JSBool ok; + jschar *chars; + size_t leftlen, rightlen, length; + + if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { + lambda = JSVAL_TO_OBJECT(argv[1]); + repstr = NULL; + } else { + if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) + return JS_FALSE; + repstr = JSVAL_TO_STRING(argv[1]); + lambda = NULL; + } + + /* + * For ECMA Edition 3, the first argument is to be converted to a string + * to match in a "flat" sense (without regular expression metachars having + * special meanings) UNLESS the first arg is a RegExp object. + */ + rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT; + rdata.base.optarg = 2; + + rdata.lambda = lambda; + rdata.repstr = repstr; + if (repstr) { + rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr); + rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$', + rdata.dollarEnd); + } else { + rdata.dollar = rdata.dollarEnd = NULL; + } + rdata.chars = NULL; + rdata.length = 0; + rdata.index = 0; + rdata.leftIndex = 0; + + ok = match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval); + if (!ok) + return JS_FALSE; + + if (!rdata.chars) { + if ((rdata.base.flags & GLOBAL_REGEXP) || *rval != JSVAL_TRUE) { + /* Didn't match even once. */ + *rval = STRING_TO_JSVAL(rdata.base.str); + goto out; + } + leftlen = cx->regExpStatics.leftContext.length; + ok = find_replen(cx, &rdata, &length); + if (!ok) + goto out; + length += leftlen; + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) { + ok = JS_FALSE; + goto out; + } + js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen); + do_replace(cx, &rdata, chars + leftlen); + rdata.chars = chars; + rdata.length = length; + } + + rightlen = cx->regExpStatics.rightContext.length; + length = rdata.length + rightlen; + chars = (jschar *) + JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar)); + if (!chars) { + JS_free(cx, rdata.chars); + ok = JS_FALSE; + goto out; + } + js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars, + rightlen); + chars[length] = 0; + + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + ok = JS_FALSE; + goto out; + } + *rval = STRING_TO_JSVAL(str); + +out: + /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */ + if (rdata.base.flags & KEEP_REGEXP) + js_DestroyRegExp(cx, rdata.base.regexp); + return ok; +} + +/* + * Subroutine used by str_split to find the next split point in str, starting + * at offset *ip and looking either for the separator substring given by sep, + * or for the next re match. In the re case, return the matched separator in + * *sep, and the possibly updated offset in *ip. + * + * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next + * separator occurrence if found, or str->length if no separator is found. + */ +static jsint +find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, + JSSubString *sep) +{ + jsint i, j, k; + size_t length; + jschar *chars; + + /* + * Stop if past end of string. If at end of string, we will compare the + * null char stored there (by js_NewString*) to sep->chars[j] in the while + * loop at the end of this function, so that + * + * "ab,".split(',') => ["ab", ""] + * + * and the resulting array converts back to the string "ab," for symmetry. + * However, we ape Perl and do this only if there is a sufficiently large + * limit argument (see str_split). + */ + i = *ip; + length = JSSTRING_LENGTH(str); + if ((size_t)i > length) + return -1; + + chars = JSSTRING_CHARS(str); + + /* + * Match a regular expression against the separator at or above index i. + * Call js_ExecuteRegExp with true for the test argument. On successful + * match, get the separator from cx->regExpStatics.lastMatch. + */ + if (re) { + size_t index; + jsval rval; + + again: + /* JS1.2 deviated from Perl by never matching at end of string. */ + index = (size_t)i; + if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) + return -2; + if (rval != JSVAL_TRUE) { + /* Mismatch: ensure our caller advances i past end of string. */ + sep->length = 1; + return length; + } + i = (jsint)index; + *sep = cx->regExpStatics.lastMatch; + if (sep->length == 0) { + /* + * Empty string match: never split on an empty match at the start + * of a find_split cycle. Same rule as for an empty global match + * in match_or_replace. + */ + if (i == *ip) { + /* + * "Bump-along" to avoid sticking at an empty match, but don't + * bump past end of string -- our caller must do that by adding + * sep->length to our return value. + */ + if ((size_t)i == length) + return -1; + i++; + goto again; + } + if ((size_t)i == length) { + /* + * If there was a trivial zero-length match at the end of the + * split, then we shouldn't output the matched string at the end + * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15. + */ + sep->chars = NULL; + } + } + JS_ASSERT((size_t)i >= sep->length); + return i - sep->length; + } + + /* + * Deviate from ECMA by never splitting an empty string by any separator + * string into a non-empty array (an array of length 1 that contains the + * empty string). + */ + if (!JS_VERSION_IS_ECMA(cx) && length == 0) + return -1; + + /* + * Special case: if sep is the empty string, split str into one character + * substrings. Let our caller worry about whether to split once at end of + * string into an empty substring. + */ + if (sep->length == 0) + return ((size_t)i == length) ? -1 : i + 1; + + /* + * Now that we know sep is non-empty, search starting at i in str for an + * occurrence of all of sep's chars. If we find them, return the index of + * the first separator char. Otherwise, return length. + */ + j = 0; + while ((size_t)(k = i + j) < length) { + if (chars[k] == sep->chars[j]) { + if ((size_t)++j == sep->length) + return i; + } else { + i++; + j = 0; + } + } + return k; +} + +static JSBool +str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *sub; + JSObject *arrayobj; + jsval v; + JSBool ok, limited; + JSRegExp *re; + JSSubString *sep, tmp; + jsdouble d; + jsint i, j; + uint32 len, limit; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + + if (argc == 0) { + v = STRING_TO_JSVAL(str); + ok = JS_SetElement(cx, arrayobj, 0, &v); + } else { + if (JSVAL_IS_REGEXP(cx, argv[0])) { + re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); + sep = &tmp; + + /* Set a magic value so we can detect a successful re match. */ + sep->chars = NULL; + sep->length = 0; + } else { + JSString *str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + + /* + * Point sep at a local copy of str2's header because find_split + * will modify sep->length. + */ + tmp.length = JSSTRING_LENGTH(str2); + tmp.chars = JSSTRING_CHARS(str2); + sep = &tmp; + re = NULL; + } + + /* Use the second argument as the split limit, if given. */ + limited = (argc > 1) && !JSVAL_IS_VOID(argv[1]); + limit = 0; /* Avoid warning. */ + if (limited) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + + /* Clamp limit between 0 and 1 + string length. */ + if (!js_DoubleToECMAUint32(cx, d, &limit)) + return JS_FALSE; + if (limit > JSSTRING_LENGTH(str)) + limit = 1 + JSSTRING_LENGTH(str); + } + + len = i = 0; + while ((j = find_split(cx, str, re, &i, sep)) >= 0) { + if (limited && len >= limit) + break; + sub = js_NewDependentString(cx, str, i, (size_t)(j - i), 0); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; + + /* + * Imitate perl's feature of including parenthesized substrings + * that matched part of the delimiter in the new array, after the + * split substring that was delimited. + */ + if (re && sep->chars) { + uintN num; + JSSubString *parsub; + + for (num = 0; num < cx->regExpStatics.parenCount; num++) { + if (limited && len >= limit) + break; + parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num); + sub = js_NewStringCopyN(cx, parsub->chars, parsub->length, + 0); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; + } + sep->chars = NULL; + } + + i = j + sep->length; + if (!JS_VERSION_IS_ECMA(cx)) { + /* + * Deviate from ECMA to imitate Perl, which omits a final + * split unless a limit argument is given and big enough. + */ + if (!limited && (size_t)i == JSSTRING_LENGTH(str)) + break; + } + } + ok = (j != -2); + } + return ok; +} + +#if JS_HAS_PERL_SUBSTR +static JSBool +str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + begin = js_DoubleToInteger(d); + if (begin < 0) { + begin += length; + if (begin < 0) + begin = 0; + } else if (begin > length) { + begin = length; + } + + if (argc == 1) { + end = length; + } else { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) + end = 0; + end += begin; + if (end > length) + end = length; + } + + str = js_NewDependentString(cx, str, (size_t)begin, + (size_t)(end - begin), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_PERL_SUBSTR */ + +/* + * Python-esque sequence operations. + */ +static JSBool +str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *str2; + uintN i; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + for (i = 0; i < argc; i++) { + str2 = js_ValueToString(cx, argv[i]); + if (!str2) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str2); + + str = js_ConcatStrings(cx, str, str2); + if (!str) + return JS_FALSE; + } + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + begin = js_DoubleToInteger(d); + if (begin < 0) { + begin += length; + if (begin < 0) + begin = 0; + } else if (begin > length) { + begin = length; + } + + if (argc == 1) { + end = length; + } else { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) { + end += length; + if (end < 0) + end = 0; + } else if (end > length) { + end = length; + } + if (end < begin) + end = begin; + } + + str = js_NewDependentString(cx, str, (size_t)begin, + (size_t)(end - begin), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#if JS_HAS_STR_HTML_HELPERS +/* + * HTML composition aids. + */ +static JSBool +tagify(JSContext *cx, JSObject *obj, jsval *argv, + const char *begin, JSString *param, const char *end, + jsval *rval) +{ + JSString *str; + jschar *tagbuf; + size_t beglen, endlen, parlen, taglen; + size_t i, j; + + if (JSVAL_IS_STRING((jsval)obj)) { + str = JSVAL_TO_STRING((jsval)obj); + } else { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + } + + if (!end) + end = begin; + + beglen = strlen(begin); + taglen = 1 + beglen + 1; /* '' */ + parlen = 0; /* Avoid warning. */ + if (param) { + parlen = JSSTRING_LENGTH(param); + taglen += 2 + parlen + 1; /* '="param"' */ + } + endlen = strlen(end); + taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1; /* 'str' */ + + if (taglen >= ~(size_t)0 / sizeof(jschar)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar)); + if (!tagbuf) + return JS_FALSE; + + j = 0; + tagbuf[j++] = '<'; + for (i = 0; i < beglen; i++) + tagbuf[j++] = (jschar)begin[i]; + if (param) { + tagbuf[j++] = '='; + tagbuf[j++] = '"'; + js_strncpy(&tagbuf[j], JSSTRING_CHARS(param), parlen); + j += parlen; + tagbuf[j++] = '"'; + } + tagbuf[j++] = '>'; + js_strncpy(&tagbuf[j], JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); + j += JSSTRING_LENGTH(str); + tagbuf[j++] = '<'; + tagbuf[j++] = '/'; + for (i = 0; i < endlen; i++) + tagbuf[j++] = (jschar)end[i]; + tagbuf[j++] = '>'; + JS_ASSERT(j == taglen); + tagbuf[j] = 0; + + str = js_NewString(cx, tagbuf, taglen, 0); + if (!str) { + free((char *)tagbuf); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +tagify_value(JSContext *cx, JSObject *obj, jsval *argv, + const char *begin, const char *end, + jsval *rval) +{ + JSString *param; + + param = js_ValueToString(cx, argv[0]); + if (!param) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(param); + return tagify(cx, obj, argv, begin, param, end, rval); +} + +static JSBool +str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "b", NULL, NULL, rval); +} + +static JSBool +str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "i", NULL, NULL, rval); +} + +static JSBool +str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "tt", NULL, NULL, rval); +} + +static JSBool +str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "font size", "font", rval); +} + +static JSBool +str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return tagify_value(cx, obj, argv, "font color", "font", rval); +} + +static JSBool +str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "a href", "a", rval); +} + +static JSBool +str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "a name", "a", rval); +} + +static JSBool +str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "strike", NULL, NULL, rval); +} + +static JSBool +str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "small", NULL, NULL, rval); +} + +static JSBool +str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "big", NULL, NULL, rval); +} + +static JSBool +str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "blink", NULL, NULL, rval); +} + +static JSBool +str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "sup", NULL, NULL, rval); +} + +static JSBool +str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "sub", NULL, NULL, rval); +} +#endif /* JS_HAS_STR_HTML_HELPERS */ + +static JSFunctionSpec string_methods[] = { +#if JS_HAS_TOSOURCE + {"quote", str_quote, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING,0}, +#endif + + /* Java-like methods. */ + {js_toString_str, str_toString, 0,JSFUN_THISP_STRING,0}, + {js_valueOf_str, str_valueOf, 0,JSFUN_THISP_STRING,0}, + {"substring", str_substring, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + + /* Perl-ish methods (search is actually Python-esque). */ + {"match", str_match, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,2}, + {"search", str_search, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"replace", str_replace, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"split", str_split, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, +#if JS_HAS_PERL_SUBSTR + {"substr", str_substr, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, +#endif + + /* Python-esque sequence methods. */ + {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + + /* HTML string methods. */ +#if JS_HAS_STR_HTML_HELPERS + {"bold", str_bold, 0,JSFUN_THISP_PRIMITIVE,0}, + {"italics", str_italics, 0,JSFUN_THISP_PRIMITIVE,0}, + {"fixed", str_fixed, 0,JSFUN_THISP_PRIMITIVE,0}, + {"fontsize", str_fontsize, 1,JSFUN_THISP_PRIMITIVE,0}, + {"fontcolor", str_fontcolor, 1,JSFUN_THISP_PRIMITIVE,0}, + {"link", str_link, 1,JSFUN_THISP_PRIMITIVE,0}, + {"anchor", str_anchor, 1,JSFUN_THISP_PRIMITIVE,0}, + {"strike", str_strike, 0,JSFUN_THISP_PRIMITIVE,0}, + {"small", str_small, 0,JSFUN_THISP_PRIMITIVE,0}, + {"big", str_big, 0,JSFUN_THISP_PRIMITIVE,0}, + {"blink", str_blink, 0,JSFUN_THISP_PRIMITIVE,0}, + {"sup", str_sup, 0,JSFUN_THISP_PRIMITIVE,0}, + {"sub", str_sub, 0,JSFUN_THISP_PRIMITIVE,0}, +#endif + + {0,0,0,0,0} +}; + +static JSBool +String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + if (argc > 0) { + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + } else { + str = cx->runtime->emptyString; + } + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); + return JS_TRUE; +} + +static JSBool +str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jschar *chars; + uintN i; + uint16 code; + JSString *str; + + JS_ASSERT(argc < ARRAY_INIT_LIMIT); + chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + for (i = 0; i < argc; i++) { + if (!js_ValueToUint16(cx, argv[i], &code)) { + JS_free(cx, chars); + return JS_FALSE; + } + chars[i] = (jschar)code; + } + chars[i] = 0; + str = js_NewString(cx, chars, argc, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSFunctionSpec string_static_methods[] = { + {"fromCharCode", str_fromCharCode, 1,0,0}, + {0,0,0,0,0} +}; + +JSBool +js_InitRuntimeStringState(JSContext *cx) +{ + JSRuntime *rt; + JSString *empty; + JSAtom *atom; + + rt = cx->runtime; + + /* Initialize string cache */ +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = JS_NEW_LOCK(); + if (!rt->deflatedStringCacheLock) + return JS_FALSE; +#endif + + /* Make a permanently locked empty string. */ + JS_ASSERT(!rt->emptyString); + empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); + if (!empty) + goto bad; + + /* Atomize it for scripts that use '' + x to convert x to string. */ + atom = js_AtomizeString(cx, empty, ATOM_PINNED); + if (!atom) + goto bad; + + rt->emptyString = empty; + rt->atomState.emptyAtom = atom; + + return JS_TRUE; + + bad: +#ifdef JS_THREADSAFE + JS_DESTROY_LOCK(rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = NULL; +#endif + return JS_FALSE; + +} + +void +js_FinishRuntimeStringState(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + js_UnlockGCThingRT(rt, rt->emptyString); + rt->emptyString = NULL; +} + +void +js_FinishDeflatedStringCache(JSRuntime *rt) +{ + if (rt->deflatedStringCache) { + JS_HashTableDestroy(rt->deflatedStringCache); + rt->deflatedStringCache = NULL; + } +#ifdef JS_THREADSAFE + if (rt->deflatedStringCacheLock) { + JS_DESTROY_LOCK(rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = NULL; + } +#endif +} + +JSObject * +js_InitStringClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + /* Define the escape, unescape functions in the global object. */ + if (!JS_DefineFunctions(cx, obj, string_functions)) + return NULL; + + proto = JS_InitClass(cx, obj, NULL, &js_StringClass, String, 1, + string_props, string_methods, + NULL, string_static_methods); + if (!proto) + return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, + STRING_TO_JSVAL(cx->runtime->emptyString)); + return proto; +} + +JSString * +js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag) +{ + JSString *str; + + if (length > JSSTRING_LENGTH_MASK) { + JS_ReportOutOfMemory(cx); + return NULL; + } + + str = (JSString *) js_NewGCThing(cx, gcflag | GCX_STRING, sizeof(JSString)); + if (!str) + return NULL; + str->length = length; + str->chars = chars; +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveStrings); + JS_RUNTIME_METER(rt, totalStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->lengthSum += (double)length, + rt->lengthSquaredSum += (double)length * (double)length)); + } +#endif + return str; +} + +JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length, uintN gcflag) +{ + JSDependentString *ds; + + if (length == 0) + return cx->runtime->emptyString; + + if (start == 0 && length == JSSTRING_LENGTH(base)) + return base; + + if (start > JSSTRDEP_START_MASK || + (start != 0 && length > JSSTRDEP_LENGTH_MASK)) { + return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length, + gcflag); + } + + ds = (JSDependentString *) + js_NewGCThing(cx, gcflag | GCX_MUTABLE_STRING, sizeof(JSString)); + if (!ds) + return NULL; + if (start == 0) { + JSPREFIX_SET_LENGTH(ds, length); + JSPREFIX_SET_BASE(ds, base); + } else { + JSSTRDEP_SET_START_AND_LENGTH(ds, start, length); + JSSTRDEP_SET_BASE(ds, base); + } +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveDependentStrings); + JS_RUNTIME_METER(rt, totalDependentStrings); + JS_RUNTIME_METER(rt, liveStrings); + JS_RUNTIME_METER(rt, totalStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum += (double)length, + rt->strdepLengthSquaredSum += (double)length * (double)length)); + JS_LOCK_RUNTIME_VOID(rt, + (rt->lengthSum += (double)length, + rt->lengthSquaredSum += (double)length * (double)length)); + } +#endif + return (JSString *)ds; +} + +#ifdef DEBUG +#include + +void printJSStringStats(JSRuntime *rt) { + double mean = 0., var = 0., sigma = 0.; + jsrefcount count = rt->totalStrings; + if (count > 0 && rt->lengthSum >= 0) { + mean = rt->lengthSum / count; + var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum; + if (var < 0.0 || count <= 1) + var = 0.0; + else + var /= count * (count - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", + (unsigned long)count, mean, sigma); + + mean = var = sigma = 0.; + count = rt->totalDependentStrings; + if (count > 0 && rt->strdepLengthSum >= 0) { + mean = rt->strdepLengthSum / count; + var = count * rt->strdepLengthSquaredSum + - rt->strdepLengthSum * rt->strdepLengthSum; + if (var < 0.0 || count <= 1) + var = 0.0; + else + var /= count * (count - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", + (unsigned long)count, mean, sigma); +} +#endif + +JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag) +{ + jschar *news; + JSString *str; + + news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return NULL; + js_strncpy(news, s, n); + news[n] = 0; + str = js_NewString(cx, news, n, gcflag); + if (!str) + JS_free(cx, news); + return str; +} + +JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) +{ + size_t n, m; + jschar *news; + JSString *str; + + n = js_strlen(s); + m = (n + 1) * sizeof(jschar); + news = (jschar *) JS_malloc(cx, m); + if (!news) + return NULL; + memcpy(news, s, m); + str = js_NewString(cx, news, n, gcflag); + if (!str) + JS_free(cx, news); + return str; +} + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_string_pointer(const void *key) +{ + return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; +} + +void +js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str) +{ + JSHashNumber hash; + JSHashEntry *he, **hep; + + if (!rt->deflatedStringCache) + return; + + hash = js_hash_string_pointer(str); + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str); + he = *hep; + if (he) { +#ifdef DEBUG + rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str); +#endif + free(he->value); + JS_HashTableRawRemove(rt->deflatedStringCache, hep, he); + } + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); +} + +void +js_FinalizeString(JSContext *cx, JSString *str) +{ + js_FinalizeStringRT(cx->runtime, str); +} + +void +js_FinalizeStringRT(JSRuntime *rt, JSString *str) +{ + JSBool valid; + + JS_RUNTIME_UNMETER(rt, liveStrings); + if (JSSTRING_IS_DEPENDENT(str)) { + /* If JSSTRFLAG_DEPENDENT is set, this string must be valid. */ + JS_ASSERT(JSSTRDEP_BASE(str)); + JS_RUNTIME_UNMETER(rt, liveDependentStrings); + valid = JS_TRUE; + } else { + /* A stillborn string has null chars, so is not valid. */ + valid = (str->chars != NULL); + if (valid) + free(str->chars); + } + if (valid) { + js_PurgeDeflatedStringCache(rt, str); + str->chars = NULL; + } + str->length = 0; +} + +JSObject * +js_StringToObject(JSContext *cx, JSString *str) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_StringClass, NULL, NULL); + if (!obj) + return NULL; + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); + return obj; +} + +JS_FRIEND_API(const char *) +js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun) +{ + JSString *str; + const char *bytes; + + str = v2sfun(cx, v); + if (!str) + return NULL; + str = js_QuoteString(cx, str, 0); + if (!str) + return NULL; + bytes = js_GetStringBytes(cx->runtime, str); + if (!bytes) + JS_ReportOutOfMemory(cx); + return bytes; +} + +JS_FRIEND_API(JSString *) +js_ValueToString(JSContext *cx, jsval v) +{ + JSObject *obj; + JSString *str; + + if (JSVAL_IS_OBJECT(v)) { + obj = JSVAL_TO_OBJECT(v); + if (!obj) + return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) + return NULL; + } + if (JSVAL_IS_STRING(v)) { + str = JSVAL_TO_STRING(v); + } else if (JSVAL_IS_INT(v)) { + str = js_NumberToString(cx, JSVAL_TO_INT(v)); + } else if (JSVAL_IS_DOUBLE(v)) { + str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v)); + } else if (JSVAL_IS_BOOLEAN(v)) { + str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v)); + } else { + str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + } + return str; +} + +JS_FRIEND_API(JSString *) +js_ValueToSource(JSContext *cx, jsval v) +{ + JSTempValueRooter tvr; + JSString *str; + + if (JSVAL_IS_STRING(v)) + return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (JSVAL_IS_PRIMITIVE(v)) { + /* Special case to preserve negative zero, _contra_ toString. */ + if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v))) { + /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */ + static const jschar js_negzero_ucNstr[] = {'-', '0'}; + + return js_NewStringCopyN(cx, js_negzero_ucNstr, 2, 0); + } + return js_ValueToString(cx, v); + } + + JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); + if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), + cx->runtime->atomState.toSourceAtom, + 0, NULL, &tvr.u.value)) { + str = NULL; + } else { + str = js_ValueToString(cx, tvr.u.value); + } + JS_POP_TEMP_ROOT(cx, &tvr); + return str; +} + +JSHashNumber +js_HashString(JSString *str) +{ + JSHashNumber h; + const jschar *s; + size_t n; + + h = 0; + for (s = JSSTRING_CHARS(str), n = JSSTRING_LENGTH(str); n; s++, n--) + h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; + return h; +} + +intN +js_CompareStrings(JSString *str1, JSString *str2) +{ + size_t l1, l2, n, i; + const jschar *s1, *s2; + intN cmp; + + JS_ASSERT(str1); + JS_ASSERT(str2); + + /* Fast case: pointer equality could be a quick win. */ + if (str1 == str2) + return 0; + + l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); + s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); + n = JS_MIN(l1, l2); + for (i = 0; i < n; i++) { + cmp = s1[i] - s2[i]; + if (cmp != 0) + return cmp; + } + return (intN)(l1 - l2); +} + +JSBool +js_EqualStrings(JSString *str1, JSString *str2) +{ + size_t n; + const jschar *s1, *s2; + + JS_ASSERT(str1); + JS_ASSERT(str2); + + /* Fast case: pointer equality could be a quick win. */ + if (str1 == str2) + return JS_TRUE; + + n = JSSTRING_LENGTH(str1); + if (n != JSSTRING_LENGTH(str2)) + return JS_FALSE; + + if (n == 0) + return JS_TRUE; + + s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); + do { + if (*s1 != *s2) + return JS_FALSE; + ++s1, ++s2; + } while (--n != 0); + + return JS_TRUE; +} + +size_t +js_strlen(const jschar *s) +{ + const jschar *t; + + for (t = s; *t != 0; t++) + continue; + return (size_t)(t - s); +} + +jschar * +js_strchr(const jschar *s, jschar c) +{ + while (*s != 0) { + if (*s == c) + return (jschar *)s; + s++; + } + return NULL; +} + +jschar * +js_strchr_limit(const jschar *s, jschar c, const jschar *limit) +{ + while (s < limit) { + if (*s == c) + return (jschar *)s; + s++; + } + return NULL; +} + +const jschar * +js_SkipWhiteSpace(const jschar *s) +{ + /* JS_ISSPACE is false on a null. */ + while (JS_ISSPACE(*s)) + s++; + return s; +} + +#ifdef JS_C_STRINGS_ARE_UTF8 + +jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *length) +{ + jschar *chars = NULL; + size_t dstlen = 0; + + if (!js_InflateStringToBuffer(cx, bytes, *length, NULL, &dstlen)) + return NULL; + chars = (jschar *) JS_malloc(cx, (dstlen + 1) * sizeof (jschar)); + if (!chars) + return NULL; + js_InflateStringToBuffer(cx, bytes, *length, chars, &dstlen); + chars[dstlen] = 0; + *length = dstlen; + return chars; +} + +/* + * May be called with null cx by js_GetStringBytes, see below. + */ +char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length) +{ + size_t size = 0; + char *bytes = NULL; + if (!js_DeflateStringToBuffer(cx, chars, length, NULL, &size)) + return NULL; + bytes = (char *) (cx ? JS_malloc(cx, size+1) : malloc(size+1)); + if (!bytes) + return NULL; + js_DeflateStringToBuffer(cx, chars, length, bytes, &size); + bytes[size] = 0; + return bytes; +} + +JSBool +js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, + char *dst, size_t *dstlenp) +{ + size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen; + jschar c, c2; + uint32 v; + uint8 utf8buf[6]; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + c = *src++; + srclen--; + if ((c >= 0xDC00) && (c <= 0xDFFF)) + goto badSurrogate; + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + if (srclen < 1) + goto bufferTooSmall; + c2 = *src++; + srclen--; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { + c = c2; + goto badSurrogate; + } + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + if (v < 0x0080) { + /* no encoding necessary - performance hack */ + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (char) v; + utf8Len = 1; + } else { + utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); + if (utf8Len > dstlen) + goto bufferTooSmall; + if (dst) { + for (i = 0; i < utf8Len; i++) + *dst++ = (char) utf8buf[i]; + } + } + dstlen -= utf8Len; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badSurrogate: + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "0x%x", c); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_BAD_SURROGATE_CHAR, + buffer); + } + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +JSBool +js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, + jschar *dst, size_t *dstlenp) +{ + uint32 v; + size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + v = (uint8) *src; + n = 1; + if (v & 0x80) { + while (v & (0x80 >> n)) + n++; + if (n > srclen) + goto bufferTooSmall; + if (n == 1 || n > 6) + goto badCharacter; + for (j = 1; j < n; j++) { + if ((src[j] & 0xC0) != 0x80) + goto badCharacter; + } + v = Utf8ToOneUcs4Char(src, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF || dstlen < 2) { + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "0x%x", v + 0x10000); + JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UTF8_CHAR_TOO_LARGE, + buffer); + } + return JS_FALSE; + } + if (dstlen < 2) + goto bufferTooSmall; + if (dst) { + *dst++ = (jschar)((v >> 10) + 0xD800); + v = (jschar)((v & 0x3FF) + 0xDC00); + } + dstlen--; + } + } + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (jschar) v; + dstlen--; + offset += n; + src += n; + srclen -= n; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badCharacter: + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "%d", offset); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_MALFORMED_UTF8_CHAR, + buffer); + } + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +#else + +JSBool +js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, + jschar *chars, size_t* charsLength) +{ + size_t i; + + if (length > *charsLength) { + for (i = 0; i < *charsLength; i++) + chars[i] = (unsigned char) bytes[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < length; i++) + chars[i] = (unsigned char) bytes[i]; + *charsLength = length; + return JS_TRUE; +} + +jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *bytesLength) +{ + jschar *chars; + size_t i, length = *bytesLength; + + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) { + *bytesLength = 0; + return NULL; + } + for (i = 0; i < length; i++) + chars[i] = (unsigned char) bytes[i]; + chars[length] = 0; + *bytesLength = length; + return chars; +} + +JSBool +js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t length, + char *bytes, size_t* bytesLength) +{ + size_t i; + + if (length > *bytesLength) { + for (i = 0; i < *bytesLength; i++) + bytes[i] = (char) chars[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < length; i++) + bytes[i] = (char) chars[i]; + *bytesLength = length; + return JS_TRUE; +} + +/* + * May be called with null cx by js_GetStringBytes, see below. + */ +char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length) +{ + size_t i, size; + char *bytes; + + size = (length + 1) * sizeof(char); + bytes = (char *) (cx ? JS_malloc(cx, size) : malloc(size)); + if (!bytes) + return NULL; + + for (i = 0; i < length; i++) + bytes[i] = (char) chars[i]; + + bytes[length] = 0; + return bytes; +} + +#endif + +static JSHashTable * +GetDeflatedStringCache(JSRuntime *rt) +{ + JSHashTable *cache; + + cache = rt->deflatedStringCache; + if (!cache) { + cache = JS_NewHashTable(8, js_hash_string_pointer, + JS_CompareValues, JS_CompareValues, + NULL, NULL); + rt->deflatedStringCache = cache; + } + return cache; +} + +JSBool +js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length) +{ + JSHashTable *cache; + JSBool ok; + JSHashNumber hash; + JSHashEntry **hep; + + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + + cache = GetDeflatedStringCache(rt); + if (!cache) { + ok = JS_FALSE; + } else { + hash = js_hash_string_pointer(str); + hep = JS_HashTableRawLookup(cache, hash, str); + JS_ASSERT(*hep == NULL); + ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL; +#ifdef DEBUG + if (ok) + rt->deflatedStringCacheBytes += length; +#endif + } + + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + return ok; +} + +char * +js_GetStringBytes(JSRuntime *rt, JSString *str) +{ + JSHashTable *cache; + char *bytes; + JSHashNumber hash; + JSHashEntry *he, **hep; + + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + + cache = GetDeflatedStringCache(rt); + if (!cache) { + bytes = NULL; + } else { + hash = js_hash_string_pointer(str); + hep = JS_HashTableRawLookup(cache, hash, str); + he = *hep; + if (he) { + bytes = (char *) he->value; + + /* Try to catch failure to JS_ShutDown between runtime epochs. */ + JS_ASSERT((*bytes == '\0' && JSSTRING_LENGTH(str) == 0) || + *bytes == (char) JSSTRING_CHARS(str)[0]); + } else { + bytes = js_DeflateString(NULL, JSSTRING_CHARS(str), + JSSTRING_LENGTH(str)); + if (bytes) { + if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { +#ifdef DEBUG + rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str); +#endif + } else { + free(bytes); + bytes = NULL; + } + } + } + } + + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + return bytes; +} + +/* + * From java.lang.Character.java: + * + * The character properties are currently encoded into 32 bits in the + * following manner: + * + * 10 bits signed offset used for converting case + * 1 bit if 1, adding the signed offset converts the character to + * lowercase + * 1 bit if 1, subtracting the signed offset converts the character to + * uppercase + * 1 bit if 1, character has a titlecase equivalent (possibly itself) + * 3 bits 0 may not be part of an identifier + * 1 ignorable control; may continue a Unicode identifier or JS + * identifier + * 2 may continue a JS identifier but not a Unicode identifier + * (unused) + * 3 may continue a Unicode identifier or JS identifier + * 4 is a JS whitespace character + * 5 may start or continue a JS identifier; + * may continue but not start a Unicode identifier (_) + * 6 may start or continue a JS identifier but not a Unicode + * identifier ($) + * 7 may start or continue a Unicode identifier or JS identifier + * Thus: + * 5, 6, 7 may start a JS identifier + * 1, 2, 3, 5, 6, 7 may continue a JS identifier + * 7 may start a Unicode identifier + * 1, 3, 5, 7 may continue a Unicode identifier + * 1 is ignorable within an identifier + * 4 is JS whitespace + * 2 bits 0 this character has no numeric property + * 1 adding the digit offset to the character code and then + * masking with 0x1F will produce the desired numeric value + * 2 this character has a "strange" numeric value + * 3 a JS supradecimal digit: adding the digit offset to the + * character code, then masking with 0x1F, then adding 10 + * will produce the desired numeric value + * 5 bits digit offset + * 1 bit XML 1.0 name start character + * 1 bit XML 1.0 name character + * 2 bits reserved for future use + * 5 bits character type + */ + +/* The X table has 1024 entries for a total of 1024 bytes. */ + +const uint8 js_X[] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */ + 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */ + 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */ + 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */ + 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */ + 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */ + 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */ + 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */ + 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */ + 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */ + 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */ + 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */ + 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */ + 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */ + 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */ + 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */ + 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */ + 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */ +105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */ +106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */ + 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */ +115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */ +}; + +/* The Y table has 7808 entries for a total of 7808 bytes. */ + +const uint8 js_Y[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */ + 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */ + 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */ + 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */ + 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */ + 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */ + 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */ + 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */ + 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */ + 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */ + 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */ + 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */ + 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */ + 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */ + 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */ + 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */ + 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */ + 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */ + 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */ + 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */ + 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */ + 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */ + 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ + 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */ + 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ + 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */ + 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */ + 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */ + 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */ + 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */ + 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */ + 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */ + 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ + 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */ + 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */ + 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */ + 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ + 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */ + 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ + 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */ + 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */ + 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */ + 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */ + 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */ + 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */ + 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */ + 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */ + 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */ + 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */ + 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */ + 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */ + 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */ + 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */ + 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */ + 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ + 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */ + 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */ + 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */ + 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ + 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */ + 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */ + 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */ + 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */ + 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */ + 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ + 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ + 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */ + 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */ + 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */ + 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */ + 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */ + 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */ + 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */ + 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */ + 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */ + 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ + 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */ + 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */ + 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */ + 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */ + 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */ + 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */ + 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */ + 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */ + 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */ + 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */ + 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */ + 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */ + 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */ + 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */ + 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */ + 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */ + 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */ + 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */ + 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ + 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */ + 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ + 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */ + 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */ + 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */ + 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */ + 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */ + 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */ + 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */ + 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */ + 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */ + 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */ + 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */ + 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ + 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */ + 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */ + 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */ + 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ + 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */ + 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */ + 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */ + 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */ + 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ + 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */ + 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */ + 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */ + 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */ + 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */ + 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */ + 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */ + 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */ + 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */ + 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ + 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */ + 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */ + 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */ + 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */ + 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */ + 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */ + 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */ + 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */ + 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */ + 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */ + 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */ + 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */ + 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */ + 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */ + 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */ + 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */ + 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */ + 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */ + 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */ + 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */ + 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */ + 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */ + 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */ + 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */ + 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */ + 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */ + 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */ + 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */ + 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */ + 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */ + 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */ + 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */ + 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */ + 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */ + 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */ + 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */ + 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */ + 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */ + 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */ + 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */ + 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */ + 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ +102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ + 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ + 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ + 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ + 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ +105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ + 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ + 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ + 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ + 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */ +107, 107, 107, 107, 107, 107, 107, 107, /* 69 */ +107, 107, 7, 7, 7, 5, 6, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */ + 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */ + 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */ + 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */ + 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */ + 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */ + 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */ + 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */ + 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */ + 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */ + 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ + 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */ + 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */ +109, 109, 109, 109, 109, 109, 109, 109, /* 72 */ +109, 109, 109, 109, 110, 110, 110, 110, /* 72 */ +111, 111, 111, 111, 111, 111, 111, 111, /* 72 */ +111, 111, 111, 111, 112, 112, 112, 112, /* 72 */ +113, 113, 113, 46, 46, 46, 46, 46, /* 73 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */ + 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */ + 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */ + 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ +114, 114, 114, 114, 82, 82, 82, 82, /* 80 */ + 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */ + 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */ +115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ +115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ +115, 115, 115, 115, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 81 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 118, 46, 46, 46, 46, 46, /* 82 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */ + 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */ + 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */ + 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */ + 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */ + 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */ +119, 119, 119, 119, 119, 119, 119, 119, /* 88 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 89 */ +114, 114, 83, 83, 83, 83, 83, 83, /* 89 */ + 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */ + 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */ + 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */ + 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */ + 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */ + 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */ +121, 121, 60, 60, 60, 60, 60, 60, /* 90 */ + 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */ + 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */ + 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */ + 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */ + 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */ + 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */ + 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 98 */ +114, 114, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */ + 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ + 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */ + 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */ + 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */ + 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */ + 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */ + 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ + 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ + 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */ + 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */ + 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */ + 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */ + 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */ + 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */ + 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */ + 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */ + 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */ + 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */ + 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */ + 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */ + 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */ + 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */ + 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */ + 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */ + 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */ + 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */ +}; + +/* The A table has 124 entries for a total of 496 bytes. */ + +const uint32 js_A[] = { +0x0001000F, /* 0 Cc, ignorable */ +0x0004000F, /* 1 Cc, whitespace */ +0x0004000C, /* 2 Zs, whitespace */ +0x00000018, /* 3 Po */ +0x0006001A, /* 4 Sc, currency */ +0x00000015, /* 5 Ps */ +0x00000016, /* 6 Pe */ +0x00000019, /* 7 Sm */ +0x00000014, /* 8 Pd */ +0x00036089, /* 9 Nd, identifier part, decimal 16 */ +0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ +0x0000001B, /* 11 Sk */ +0x00050017, /* 12 Pc, underscore */ +0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ +0x0000000C, /* 14 Zs */ +0x0000001C, /* 15 So */ +0x00070182, /* 16 Ll, identifier start */ +0x0000600B, /* 17 No, decimal 16 */ +0x0000500B, /* 18 No, decimal 8 */ +0x0000800B, /* 19 No, strange */ +0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ +0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ +0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ +0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ +0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ +0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ +0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ +0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ +0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ +0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ +0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ +0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ +0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ +0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ +0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ +0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ +0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ +0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ +0x00070181, /* 38 Lu, identifier start */ +0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ +0x00070185, /* 40 Lo, identifier start */ +0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ +0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ +0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ +0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ +0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ +0x00000000, /* 46 unassigned */ +0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ +0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ +0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ +0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ +0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ +0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ +0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ +0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ +0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ +0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ +0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ +0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ +0x00070084, /* 59 Lm, identifier start */ +0x00030086, /* 60 Mn, identifier part */ +0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ +0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ +0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ +0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ +0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ +0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ +0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ +0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ +0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ +0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ +0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ +0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ +0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ +0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ +0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ +0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ +0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ +0x00034089, /* 78 Nd, identifier part, decimal 0 */ +0x00000087, /* 79 Me */ +0x00030088, /* 80 Mc, identifier part */ +0x00037489, /* 81 Nd, identifier part, decimal 26 */ +0x00005A0B, /* 82 No, decimal 13 */ +0x00006E0B, /* 83 No, decimal 23 */ +0x0000740B, /* 84 No, decimal 26 */ +0x0000000B, /* 85 No */ +0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ +0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ +0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ +0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ +0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ +0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ +0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ +0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ +0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ +0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ +0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ +0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ +0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ +0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ +0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ +0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ +0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ +0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ +0x00010010, /* 104 Cf, ignorable */ +0x0004000D, /* 105 Zl, whitespace */ +0x0004000E, /* 106 Zp, whitespace */ +0x0000400B, /* 107 No, decimal 0 */ +0x0000440B, /* 108 No, decimal 2 */ +0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ +0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ +0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ +0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ +0x0007818A, /* 113 Nl, identifier start, strange */ +0x0000420B, /* 114 No, decimal 1 */ +0x0000720B, /* 115 No, decimal 25 */ +0x06A0001C, /* 116 So, hasLower (add 26) */ +0x0690001C, /* 117 So, hasUpper (subtract 26) */ +0x00006C0B, /* 118 No, decimal 22 */ +0x0000560B, /* 119 No, decimal 11 */ +0x0007738A, /* 120 Nl, identifier start, decimal 25 */ +0x0007418A, /* 121 Nl, identifier start, decimal 0 */ +0x00000013, /* 122 Cs */ +0x00000012 /* 123 Co */ +}; + +const jschar js_uriReservedPlusPound_ucstr[] = + {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0}; +const jschar js_uriUnescaped_ucstr[] = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0}; + +#define URI_CHUNK 64U + +/* Concatenate jschars onto an unshared/newborn JSString. */ +static JSBool +AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length) +{ + size_t total; + + JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); + total = str->length + length + 1; + if (!str->chars || + JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(str->length + 1, URI_CHUNK)) { + total = JS_ROUNDUP(total, URI_CHUNK); + str->chars = JS_realloc(cx, str->chars, total * sizeof(jschar)); + if (!str->chars) + return JS_FALSE; + } + js_strncpy(str->chars + str->length, chars, length); + str->length += length; + str->chars[str->length] = 0; + return JS_TRUE; +} + +/* + * ECMA 3, 15.1.3 URI Handling Function Properties + * + * The following are implementations of the algorithms + * given in the ECMA specification for the hidden functions + * 'Encode' and 'Decode'. + */ +static JSBool +Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, + const jschar *unescapedSet2, jsval *rval) +{ + size_t length, j, k, L; + jschar *chars, c, c2; + uint32 v; + uint8 utf8buf[6]; + jschar hexBuf[4]; + static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ + JSString *R; + + length = JSSTRING_LENGTH(str); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + R = js_NewString(cx, NULL, 0, 0); + if (!R) + return JS_FALSE; + + hexBuf[0] = '%'; + hexBuf[3] = 0; + chars = JSSTRING_CHARS(str); + for (k = 0; k < length; k++) { + c = chars[k]; + if (js_strchr(unescapedSet, c) || + (unescapedSet2 && js_strchr(unescapedSet2, c))) { + if (!AddCharsToURI(cx, R, &c, 1)) + return JS_FALSE; + } else { + if ((c >= 0xDC00) && (c <= 0xDFFF)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + k++; + if (k == length) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + c2 = chars[k]; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + L = js_OneUcs4ToUtf8Char(utf8buf, v); + for (j = 0; j < L; j++) { + hexBuf[1] = HexDigits[utf8buf[j] >> 4]; + hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; + if (!AddCharsToURI(cx, R, hexBuf, 3)) + return JS_FALSE; + } + } + } + + /* + * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we + * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 + * more jschars than it needs. + */ + chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); + if (chars) + R->chars = chars; + *rval = STRING_TO_JSVAL(R); + return JS_TRUE; +} + +static JSBool +Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) +{ + size_t length, start, k; + jschar *chars, c, H; + uint32 v; + jsuint B; + uint8 octets[6]; + JSString *R; + intN j, n; + + length = JSSTRING_LENGTH(str); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + R = js_NewString(cx, NULL, 0, 0); + if (!R) + return JS_FALSE; + + chars = JSSTRING_CHARS(str); + for (k = 0; k < length; k++) { + c = chars[k]; + if (c == '%') { + start = k; + if ((k + 2) >= length) + goto bad; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto bad; + B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + k += 2; + if (!(B & 0x80)) { + c = (jschar)B; + } else { + n = 1; + while (B & (0x80 >> n)) + n++; + if (n == 1 || n > 6) + goto bad; + octets[0] = (uint8)B; + if (k + 3 * (n - 1) >= length) + goto bad; + for (j = 1; j < n; j++) { + k++; + if (chars[k] != '%') + goto bad; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto bad; + B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + if ((B & 0xC0) != 0x80) + goto bad; + k += 2; + octets[j] = (char)B; + } + v = Utf8ToOneUcs4Char(octets, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF) + goto bad; + c = (jschar)((v & 0x3FF) + 0xDC00); + H = (jschar)((v >> 10) + 0xD800); + if (!AddCharsToURI(cx, R, &H, 1)) + return JS_FALSE; + } else { + c = (jschar)v; + } + } + if (js_strchr(reservedSet, c)) { + if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1))) + return JS_FALSE; + } else { + if (!AddCharsToURI(cx, R, &c, 1)) + return JS_FALSE; + } + } else { + if (!AddCharsToURI(cx, R, &c, 1)) + return JS_FALSE; + } + } + + /* + * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we + * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 + * more jschars than it needs. + */ + chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); + if (chars) + R->chars = chars; + *rval = STRING_TO_JSVAL(R); + return JS_TRUE; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI); + return JS_FALSE; +} + +static JSBool +str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval); +} + +static JSBool +str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Decode(cx, str, js_empty_ucstr, rval); +} + +static JSBool +str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, + rval); +} + +static JSBool +str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval); +} + +/* + * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at + * least 6 bytes long. Return the number of UTF-8 bytes of data written. + */ +int +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) +{ + int utf8Length = 1; + + JS_ASSERT(ucs4Char <= 0x7FFFFFFF); + if (ucs4Char < 0x80) { + *utf8Buffer = (uint8)ucs4Char; + } else { + int i; + uint32 a = ucs4Char >> 11; + utf8Length = 2; + while (a) { + a >>= 5; + utf8Length++; + } + i = utf8Length; + while (--i) { + utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); + ucs4Char >>= 6; + } + *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); + } + return utf8Length; +} + +/* + * Convert a utf8 character sequence into a UCS-4 character and return that + * character. It is assumed that the caller already checked that the sequence + * is valid. + */ +static uint32 +Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) +{ + uint32 ucs4Char; + uint32 minucs4Char; + /* from Unicode 3.1, non-shortest form is illegal */ + static const uint32 minucs4Table[] = { + 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000 + }; + + JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); + if (utf8Length == 1) { + ucs4Char = *utf8Buffer; + JS_ASSERT(!(ucs4Char & 0x80)); + } else { + JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == + (0x100 - (1 << (8-utf8Length)))); + ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); + minucs4Char = minucs4Table[utf8Length-2]; + while (--utf8Length) { + JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); + ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); + } + if (ucs4Char < minucs4Char || + ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { + ucs4Char = 0xFFFD; + } + } + return ucs4Char; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstr.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstr.h new file mode 100644 index 0000000..708c69a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsstr.h @@ -0,0 +1,500 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsstr_h___ +#define jsstr_h___ +/* + * JS string type implementation. + * + * A JS string is a counted array of unicode characters. To support handoff + * of API client memory, the chars are allocated separately from the length, + * necessitating a pointer after the count, to form a separately allocated + * string descriptor. String descriptors are GC'ed, while their chars are + * allocated from the malloc heap. + * + * When a string is treated as an object (by following it with . or []), the + * runtime wraps it with a JSObject whose valueOf method returns the unwrapped + * string descriptor. + */ +#include +#include "jspubtd.h" +#include "jsprvtd.h" +#include "jshash.h" + +JS_BEGIN_EXTERN_C + +/* + * The original GC-thing "string" type, a flat character string owned by its + * GC-thing descriptor. The chars member points to a vector having byte size + * (length + 1) * sizeof(jschar), terminated at index length by a zero jschar. + * The terminator is purely a backstop, in case the chars pointer flows out to + * native code that requires \u0000 termination. + * + * NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros, + * unless you guard str->member uses with !JSSTRING_IS_DEPENDENT(str). + */ +struct JSString { + size_t length; + jschar *chars; +}; + +/* + * Overlay structure for a string that depends on another string's characters. + * Distinguished by the JSSTRFLAG_DEPENDENT bit being set in length. The base + * member may point to another dependent string if JSSTRING_CHARS has not been + * called yet. The length chars in a dependent string are stored starting at + * base->chars + start, and are not necessarily zero-terminated. If start is + * 0, it is not stored, length is a full size_t (minus the JSSTRFLAG_* bits in + * the high two positions), and the JSSTRFLAG_PREFIX flag is set. + */ +struct JSDependentString { + size_t length; + JSString *base; +}; + +/* Definitions for flags stored in the high order bits of JSString.length. */ +#define JSSTRFLAG_BITS 2 +#define JSSTRFLAG_SHIFT(flg) ((size_t)(flg) << JSSTRING_LENGTH_BITS) +#define JSSTRFLAG_MASK JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS)) +#define JSSTRFLAG_DEPENDENT JSSTRFLAG_SHIFT(1) +#define JSSTRFLAG_PREFIX JSSTRFLAG_SHIFT(2) + +/* Universal JSString type inquiry and accessor macros. */ +#define JSSTRING_BIT(n) ((size_t)1 << (n)) +#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) +#define JSSTRING_HAS_FLAG(str,flg) ((str)->length & (flg)) +#define JSSTRING_IS_DEPENDENT(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT) +#define JSSTRING_IS_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX) +#define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \ + ? JSSTRDEP_CHARS(str) \ + : (str)->chars) +#define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \ + ? JSSTRDEP_LENGTH(str) \ + : (str)->length) +#define JSSTRING_LENGTH_BITS (sizeof(size_t) * JS_BITS_PER_BYTE \ + - JSSTRFLAG_BITS) +#define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS) + +/* Specific JSDependentString shift/mask accessor and mutator macros. */ +#define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS) +#define JSSTRDEP_START_SHIFT JSSTRDEP_LENGTH_BITS +#define JSSTRDEP_START_MASK JSSTRING_BITMASK(JSSTRDEP_START_BITS) +#define JSSTRDEP_LENGTH_BITS (JSSTRING_LENGTH_BITS / 2) +#define JSSTRDEP_LENGTH_MASK JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS) + +#define JSSTRDEP(str) ((JSDependentString *)(str)) +#define JSSTRDEP_START(str) (JSSTRING_IS_PREFIX(str) ? 0 \ + : ((JSSTRDEP(str)->length \ + >> JSSTRDEP_START_SHIFT) \ + & JSSTRDEP_START_MASK)) +#define JSSTRDEP_LENGTH(str) (JSSTRDEP(str)->length \ + & (JSSTRING_IS_PREFIX(str) \ + ? JSSTRING_LENGTH_MASK \ + : JSSTRDEP_LENGTH_MASK)) + +#define JSSTRDEP_SET_START_AND_LENGTH(str,off,len) \ + (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT \ + | ((off) << JSSTRDEP_START_SHIFT) \ + | (len)) +#define JSPREFIX_SET_LENGTH(str,len) \ + (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len)) + +#define JSSTRDEP_BASE(str) (JSSTRDEP(str)->base) +#define JSSTRDEP_SET_BASE(str,bstr) (JSSTRDEP(str)->base = (bstr)) +#define JSPREFIX_BASE(str) JSSTRDEP_BASE(str) +#define JSPREFIX_SET_BASE(str,bstr) JSSTRDEP_SET_BASE(str,bstr) + +#define JSSTRDEP_CHARS(str) \ + (JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \ + ? js_GetDependentStringChars(str) \ + : JSSTRDEP_BASE(str)->chars + JSSTRDEP_START(str)) + +extern size_t +js_MinimizeDependentStrings(JSString *str, int level, JSString **basep); + +extern jschar * +js_GetDependentStringChars(JSString *str); + +extern jschar * +js_GetStringChars(JSString *str); + +extern JSString * +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); + +extern const jschar * +js_UndependString(JSContext *cx, JSString *str); + +struct JSSubString { + size_t length; + const jschar *chars; +}; + +extern jschar js_empty_ucstr[]; +extern JSSubString js_EmptySubString; + +/* Unicode character attribute lookup tables. */ +extern const uint8 js_X[]; +extern const uint8 js_Y[]; +extern const uint32 js_A[]; + +/* Enumerated Unicode general category types. */ +typedef enum JSCharType { + JSCT_UNASSIGNED = 0, + JSCT_UPPERCASE_LETTER = 1, + JSCT_LOWERCASE_LETTER = 2, + JSCT_TITLECASE_LETTER = 3, + JSCT_MODIFIER_LETTER = 4, + JSCT_OTHER_LETTER = 5, + JSCT_NON_SPACING_MARK = 6, + JSCT_ENCLOSING_MARK = 7, + JSCT_COMBINING_SPACING_MARK = 8, + JSCT_DECIMAL_DIGIT_NUMBER = 9, + JSCT_LETTER_NUMBER = 10, + JSCT_OTHER_NUMBER = 11, + JSCT_SPACE_SEPARATOR = 12, + JSCT_LINE_SEPARATOR = 13, + JSCT_PARAGRAPH_SEPARATOR = 14, + JSCT_CONTROL = 15, + JSCT_FORMAT = 16, + JSCT_PRIVATE_USE = 18, + JSCT_SURROGATE = 19, + JSCT_DASH_PUNCTUATION = 20, + JSCT_START_PUNCTUATION = 21, + JSCT_END_PUNCTUATION = 22, + JSCT_CONNECTOR_PUNCTUATION = 23, + JSCT_OTHER_PUNCTUATION = 24, + JSCT_MATH_SYMBOL = 25, + JSCT_CURRENCY_SYMBOL = 26, + JSCT_MODIFIER_SYMBOL = 27, + JSCT_OTHER_SYMBOL = 28 +} JSCharType; + +/* Character classifying and mapping macros, based on java.lang.Character. */ +#define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) +#define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) + +#define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER)) \ + >> JS_CTYPE(c)) & 1) + +#define JS_ISALNUM(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_DECIMAL_DIGIT_NUMBER)) \ + >> JS_CTYPE(c)) & 1) + +/* A unicode letter, suitable for use in an identifier. */ +#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER)) \ + >> JS_CTYPE(c)) & 1) + +/* + * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or + * digit or connector punctuation. + */ +#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER) | \ + (1 << JSCT_NON_SPACING_MARK) | \ + (1 << JSCT_COMBINING_SPACING_MARK) | \ + (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ + (1 << JSCT_CONNECTOR_PUNCTUATION)) \ + >> JS_CTYPE(c)) & 1) + +/* Unicode control-format characters, ignored in input */ +#define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) + +/* + * Per ECMA-262 15.10.2.6, these characters are the only ones that make up a + * "word", as far as a RegExp is concerned. If we want a Unicode-friendlier + * definition of "word", we should rename this macro to something regexp-y. + */ +#define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) + +#define JS_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') +#define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') + +#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ + (c) == '\n') +#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') +#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ + (c) == '-' || (c) == '_') +#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') +#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') + +#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) + +/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ +/* XXXbe fs, etc. ? */ +#define JS_ISSPACE(c) ((JS_CCODE(c) & 0x00070000) == 0x00040000) +#define JS_ISPRINT(c) ((c) < 128 && isprint(c)) + +#define JS_ISUPPER(c) (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER) +#define JS_ISLOWER(c) (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER) + +#define JS_TOUPPER(c) ((jschar) ((JS_CCODE(c) & 0x00100000) \ + ? (c) - ((int32)JS_CCODE(c) >> 22) \ + : (c))) +#define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ + ? (c) + ((int32)JS_CCODE(c) >> 22) \ + : (c))) + +/* + * Shorthands for ASCII (7-bit) decimal and hex conversion. + * Manually inline isdigit for performance; MSVC doesn't do this for us. + */ +#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) +#define JS7_UNDEC(c) ((c) - '0') +#define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) +#define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') +#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) + +/* Initialize per-runtime string state for the first context in the runtime. */ +extern JSBool +js_InitRuntimeStringState(JSContext *cx); + +extern void +js_FinishRuntimeStringState(JSContext *cx); + +extern void +js_FinishDeflatedStringCache(JSRuntime *rt); + +/* Initialize the String class, returning its prototype object. */ +extern JSClass js_StringClass; + +extern JSObject * +js_InitStringClass(JSContext *cx, JSObject *obj); + +extern const char js_escape_str[]; +extern const char js_unescape_str[]; +extern const char js_uneval_str[]; +extern const char js_decodeURI_str[]; +extern const char js_encodeURI_str[]; +extern const char js_decodeURIComponent_str[]; +extern const char js_encodeURIComponent_str[]; + +/* GC-allocate a string descriptor for the given malloc-allocated chars. */ +extern JSString * +js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag); + +extern JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length, uintN gcflag); + +/* Copy a counted string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag); + +/* Copy a C string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag); + +/* Free the chars held by str when it is finalized by the GC. */ +extern void +js_FinalizeString(JSContext *cx, JSString *str); + +extern void +js_FinalizeStringRT(JSRuntime *rt, JSString *str); + +/* Wrap a string value in a String object. */ +extern JSObject * +js_StringToObject(JSContext *cx, JSString *str); + +/* + * Convert a value to a printable C string. + */ +typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v); + +extern JS_FRIEND_API(const char *) +js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun); + +#define js_ValueToPrintableString(cx,v) \ + js_ValueToPrintable(cx, v, js_ValueToString) + +#define js_ValueToPrintableSource(cx,v) \ + js_ValueToPrintable(cx, v, js_ValueToSource) + +/* + * Convert a value to a string, returning null after reporting an error, + * otherwise returning a new string reference. + */ +extern JS_FRIEND_API(JSString *) +js_ValueToString(JSContext *cx, jsval v); + +/* + * Convert a value to its source expression, returning null after reporting + * an error, otherwise returning a new string reference. + */ +extern JS_FRIEND_API(JSString *) +js_ValueToSource(JSContext *cx, jsval v); + +#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ +/* + * Compute a hash function from str. + */ +extern JSHashNumber +js_HashString(JSString *str); +#endif + +/* + * Return less than, equal to, or greater than zero depending on whether + * str1 is less than, equal to, or greater than str2. + */ +extern intN +js_CompareStrings(JSString *str1, JSString *str2); + +/* + * Test if strings are equal. + */ +extern JSBool +js_EqualStrings(JSString *str1, JSString *str2); + +/* + * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. + * The patlen argument must be positive and no greater than BMH_PATLEN_MAX. + * The start argument tells where in text to begin the search. + * + * Return the index of pat in text, or -1 if not found. + */ +#define BMH_CHARSET_SIZE 256 /* ISO-Latin-1 */ +#define BMH_PATLEN_MAX 255 /* skip table element is uint8 */ + +#define BMH_BAD_PATTERN (-2) /* return value if pat is not ISO-Latin-1 */ + +extern jsint +js_BoyerMooreHorspool(const jschar *text, jsint textlen, + const jschar *pat, jsint patlen, + jsint start); + +extern size_t +js_strlen(const jschar *s); + +extern jschar * +js_strchr(const jschar *s, jschar c); + +extern jschar * +js_strchr_limit(const jschar *s, jschar c, const jschar *limit); + +#define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) + +/* + * Return s advanced past any Unicode white space characters. + */ +extern const jschar * +js_SkipWhiteSpace(const jschar *s); + +/* + * Inflate bytes to JS chars and vice versa. Report out of memory via cx + * and return null on error, otherwise return the jschar or byte vector that + * was JS_malloc'ed. length is updated with the length of the new string in jschars. + */ +extern jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *length); + +extern char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length); + +/* + * Inflate bytes to JS chars into a buffer. + * 'chars' must be large enough for 'length' jschars. + * The buffer is NOT null-terminated. + * cx may be NULL, which means no errors are thrown. + * The destination length needs to be initialized with the buffer size, takes + * the number of chars moved. + */ +extern JSBool +js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, + jschar *chars, size_t* charsLength); + +/* + * Deflate JS chars to bytes into a buffer. + * 'bytes' must be large enough for 'length chars. + * The buffer is NOT null-terminated. + * cx may be NULL, which means no errors are thrown. + * The destination length needs to be initialized with the buffer size, takes + * the number of bytes moved. + */ +extern JSBool +js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, + size_t charsLength, char *bytes, size_t* length); + +/* + * Associate bytes with str in the deflated string cache, returning true on + * successful association, false on out of memory. + */ +extern JSBool +js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length); + +/* + * Find or create a deflated string cache entry for str that contains its + * characters chopped from Unicode code points into bytes. + */ +extern char * +js_GetStringBytes(JSRuntime *rt, JSString *str); + +/* Remove a deflated string cache entry associated with str if any. */ +extern void +js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); + +JSBool +js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +/* + * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at + * least 6 bytes long. Return the number of UTF-8 bytes of data written. + */ +extern int +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); + +JS_END_EXTERN_C + +#endif /* jsstr_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jstypes.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jstypes.h new file mode 100644 index 0000000..8aca929 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jstypes.h @@ -0,0 +1,464 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* +** File: jstypes.h +** Description: Definitions of NSPR's basic types +** +** Prototypes and macros used to make up for deficiencies in ANSI environments +** that we have found. +** +** Since we do not wrap and all the other standard headers, authors +** of portable code will not know in general that they need these definitions. +** Instead of requiring these authors to find the dependent uses in their code +** and take the following steps only in those C files, we take steps once here +** for all C files. +**/ + +#ifndef jstypes_h___ +#define jstypes_h___ + +#include + +/*********************************************************************** +** MACROS: JS_EXTERN_API +** JS_EXPORT_API +** DESCRIPTION: +** These are only for externally visible routines and globals. For +** internal routines, just use "extern" for type checking and that +** will not export internal cross-file or forward-declared symbols. +** Define a macro for declaring procedures return types. We use this to +** deal with windoze specific type hackery for DLL definitions. Use +** JS_EXTERN_API when the prototype for the method is declared. Use +** JS_EXPORT_API for the implementation of the method. +** +** Example: +** in dowhim.h +** JS_EXTERN_API( void ) DoWhatIMean( void ); +** in dowhim.c +** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; } +** +** +***********************************************************************/ +#ifdef WIN32 +/* These also work for __MWERKS__ */ +#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_API(__type) __declspec(dllexport) __type +#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#elif defined(XP_OS2) && defined(__declspec) + +#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_API(__type) __declspec(dllexport) __type +#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#elif defined(WIN16) + +#ifdef _WINDLL +#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds +#define JS_EXPORT_API(__type) __type _cdecl _export _loadds +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK + +#else /* this must be .EXE */ +#define JS_EXTERN_API(__type) extern __type _cdecl _export +#define JS_EXPORT_API(__type) __type _cdecl _export +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK +#endif /* _WINDLL */ + +#else /* Unix */ + +#ifdef HAVE_VISIBILITY_ATTRIBUTE +#define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) +#else +#define JS_EXTERNAL_VIS +#endif + +#define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type +#define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#endif + +#ifdef _WIN32 +# if defined(__MWERKS__) || defined(__GNUC__) +# define JS_IMPORT_API(__x) __x +# else +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +# endif +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +#else +# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) +#endif + +#if defined(_WIN32) && !defined(__MWERKS__) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#else +# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) +#endif + +/* + * The linkage of JS API functions differs depending on whether the file is + * used within the JS library or not. Any source file within the JS + * interpreter should define EXPORT_JS_API whereas any client of the library + * should not. + */ +#ifdef EXPORT_JS_API +#define JS_PUBLIC_API(t) JS_EXPORT_API(t) +#define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) +#else +#define JS_PUBLIC_API(t) JS_IMPORT_API(t) +#define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) +#endif + +#define JS_FRIEND_API(t) JS_PUBLIC_API(t) +#define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) + +#ifdef _WIN32 +# define JS_INLINE __inline +#elif defined(__GNUC__) +# define JS_INLINE +#else +# define JS_INLINE +#endif + +/*********************************************************************** +** MACROS: JS_BEGIN_MACRO +** JS_END_MACRO +** DESCRIPTION: +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +***********************************************************************/ +#define JS_BEGIN_MACRO do { +#define JS_END_MACRO } while (0) + +/*********************************************************************** +** MACROS: JS_BEGIN_EXTERN_C +** JS_END_EXTERN_C +** DESCRIPTION: +** Macro shorthands for conditional C++ extern block delimiters. +***********************************************************************/ +#ifdef __cplusplus +#define JS_BEGIN_EXTERN_C extern "C" { +#define JS_END_EXTERN_C } +#else +#define JS_BEGIN_EXTERN_C +#define JS_END_EXTERN_C +#endif + +/*********************************************************************** +** MACROS: JS_BIT +** JS_BITMASK +** DESCRIPTION: +** Bit masking macros. XXX n must be <= 31 to be portable +***********************************************************************/ +#define JS_BIT(n) ((JSUint32)1 << (n)) +#define JS_BITMASK(n) (JS_BIT(n) - 1) + +/*********************************************************************** +** MACROS: JS_PTR_TO_INT32 +** JS_PTR_TO_UINT32 +** JS_INT32_TO_PTR +** JS_UINT32_TO_PTR +** DESCRIPTION: +** Integer to pointer and pointer to integer conversion macros. +***********************************************************************/ +#define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) +#define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) +#define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) +#define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) + +/*********************************************************************** +** MACROS: JS_HOWMANY +** JS_ROUNDUP +** JS_MIN +** JS_MAX +** DESCRIPTION: +** Commonly used macros for operations on compatible types. +***********************************************************************/ +#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y)) +#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y)) +#define JS_MIN(x,y) ((x)<(y)?(x):(y)) +#define JS_MAX(x,y) ((x)>(y)?(x):(y)) + +#if (defined(XP_WIN) && !defined(CROSS_COMPILE)) || defined (WINCE) +# include "jscpucfg.h" /* Use standard Mac or Windows configuration */ +#elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE) +# include "jsautocfg.h" /* Use auto-detected configuration */ +# include "jsosdep.h" /* ...and platform-specific flags */ +#else +# error "Must define one of XP_BEOS, XP_OS2, XP_WIN or XP_UNIX" +#endif + +JS_BEGIN_EXTERN_C + +/************************************************************************ +** TYPES: JSUint8 +** JSInt8 +** DESCRIPTION: +** The int8 types are known to be 8 bits each. There is no type that +** is equivalent to a plain "char". +************************************************************************/ +#if JS_BYTES_PER_BYTE == 1 +typedef unsigned char JSUint8; +typedef signed char JSInt8; +#else +#error No suitable type for JSInt8/JSUint8 +#endif + +/************************************************************************ +** TYPES: JSUint16 +** JSInt16 +** DESCRIPTION: +** The int16 types are known to be 16 bits each. +************************************************************************/ +#if JS_BYTES_PER_SHORT == 2 +typedef unsigned short JSUint16; +typedef short JSInt16; +#else +#error No suitable type for JSInt16/JSUint16 +#endif + +/************************************************************************ +** TYPES: JSUint32 +** JSInt32 +** DESCRIPTION: +** The int32 types are known to be 32 bits each. +************************************************************************/ +#if JS_BYTES_PER_INT == 4 +typedef unsigned int JSUint32; +typedef int JSInt32; +#define JS_INT32(x) x +#define JS_UINT32(x) x ## U +#elif JS_BYTES_PER_LONG == 4 +typedef unsigned long JSUint32; +typedef long JSInt32; +#define JS_INT32(x) x ## L +#define JS_UINT32(x) x ## UL +#else +#error No suitable type for JSInt32/JSUint32 +#endif + +/************************************************************************ +** TYPES: JSUint64 +** JSInt64 +** DESCRIPTION: +** The int64 types are known to be 64 bits each. Care must be used when +** declaring variables of type JSUint64 or JSInt64. Different hardware +** architectures and even different compilers have varying support for +** 64 bit values. The only guaranteed portability requires the use of +** the JSLL_ macros (see jslong.h). +************************************************************************/ +#ifdef JS_HAVE_LONG_LONG +#if JS_BYTES_PER_LONG == 8 +typedef long JSInt64; +typedef unsigned long JSUint64; +#elif defined(WIN16) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#elif defined(WIN32) && !defined(__GNUC__) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#else +typedef long long JSInt64; +typedef unsigned long long JSUint64; +#endif /* JS_BYTES_PER_LONG == 8 */ +#else /* !JS_HAVE_LONG_LONG */ +typedef struct { +#ifdef IS_LITTLE_ENDIAN + JSUint32 lo, hi; +#else + JSUint32 hi, lo; +#endif +} JSInt64; +typedef JSInt64 JSUint64; +#endif /* !JS_HAVE_LONG_LONG */ + +/************************************************************************ +** TYPES: JSUintn +** JSIntn +** DESCRIPTION: +** The JSIntn types are most appropriate for automatic variables. They are +** guaranteed to be at least 16 bits, though various architectures may +** define them to be wider (e.g., 32 or even 64 bits). These types are +** never valid for fields of a structure. +************************************************************************/ +#if JS_BYTES_PER_INT >= 2 +typedef int JSIntn; +typedef unsigned int JSUintn; +#else +#error 'sizeof(int)' not sufficient for platform use +#endif + +/************************************************************************ +** TYPES: JSFloat64 +** DESCRIPTION: +** NSPR's floating point type is always 64 bits. +************************************************************************/ +typedef double JSFloat64; + +/************************************************************************ +** TYPES: JSSize +** DESCRIPTION: +** A type for representing the size of objects. +************************************************************************/ +typedef size_t JSSize; + +/************************************************************************ +** TYPES: JSPtrDiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +typedef ptrdiff_t JSPtrdiff; + +/************************************************************************ +** TYPES: JSUptrdiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 +typedef JSUint64 JSUptrdiff; +#else +typedef unsigned long JSUptrdiff; +#endif + +/************************************************************************ +** TYPES: JSBool +** DESCRIPTION: +** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE +** for clarity of target type in assignments and actual arguments. Use +** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans +** just as you would C int-valued conditions. +************************************************************************/ +typedef JSIntn JSBool; +#define JS_TRUE (JSIntn)1 +#define JS_FALSE (JSIntn)0 + +/************************************************************************ +** TYPES: JSPackedBool +** DESCRIPTION: +** Use JSPackedBool within structs where bitfields are not desireable +** but minimum and consistent overhead matters. +************************************************************************/ +typedef JSUint8 JSPackedBool; + +/* +** A JSWord is an integer that is the same size as a void* +*/ +#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 +typedef JSInt64 JSWord; +typedef JSUint64 JSUword; +#else +typedef long JSWord; +typedef unsigned long JSUword; +#endif + +#include "jsotypes.h" + +/*********************************************************************** +** MACROS: JS_LIKELY +** JS_UNLIKELY +** DESCRIPTION: +** These macros allow you to give a hint to the compiler about branch +** probability so that it can better optimize. Use them like this: +** +** if (JS_LIKELY(v == 1)) { +** ... expected code path ... +** } +** +** if (JS_UNLIKELY(v == 0)) { +** ... non-expected code path ... +** } +** +***********************************************************************/ +#if defined(__GNUC__) && (__GNUC__ > 2) +#define JS_LIKELY(x) (__builtin_expect((x), 1)) +#define JS_UNLIKELY(x) (__builtin_expect((x), 0)) +#else +#define JS_LIKELY(x) (x) +#define JS_UNLIKELY(x) (x) +#endif + +/*********************************************************************** +** MACROS: JS_ARRAY_LENGTH +** JS_ARRAY_END +** DESCRIPTION: +** Macros to get the number of elements and the pointer to one past the +** last element of a C array. Use them like this: +** +** jschar buf[10], *s; +** JSString *str; +** ... +** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; +** ... +** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); +** ... +** +***********************************************************************/ + +#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) +#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) + +JS_END_EXTERN_C + +#endif /* jstypes_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsutil.c b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsutil.c new file mode 100644 index 0000000..1bb9f93 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsutil.c @@ -0,0 +1,198 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsutil.h" + +#ifdef WIN32 +# include +#endif + +JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) +{ + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); +#if defined(WIN32) + DebugBreak(); + exit(3); +#elif defined(XP_OS2) || (defined(__GNUC__) && defined(__i386)) + asm("int $3"); +#endif + abort(); +} + +#if defined DEBUG_notme && defined XP_UNIX + +#define __USE_GNU 1 +#include +#include +#include "jshash.h" +#include "jsprf.h" + +JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; + +static JSCallsite * +CallTree(void **bp) +{ + void **bpup, **bpdown, *pc; + JSCallsite *parent, *site, **csp; + Dl_info info; + int ok, offset; + const char *symbol; + char *method; + + /* Reverse the stack frame list to avoid recursion. */ + bpup = NULL; + for (;;) { + bpdown = (void**) bp[0]; + bp[0] = (void*) bpup; + if ((void**) bpdown[0] < bpdown) + break; + bpup = bp; + bp = bpdown; + } + + /* Reverse the stack again, finding and building a path in the tree. */ + parent = &js_calltree_root; + do { + bpup = (void**) bp[0]; + bp[0] = (void*) bpdown; + pc = bp[1]; + + csp = &parent->kids; + while ((site = *csp) != NULL) { + if (site->pc == pc) { + /* Put the most recently used site at the front of siblings. */ + *csp = site->siblings; + site->siblings = parent->kids; + parent->kids = site; + + /* Site already built -- go up the stack. */ + goto upward; + } + csp = &site->siblings; + } + + /* Check for recursion: see if pc is on our ancestor line. */ + for (site = parent; site; site = site->parent) { + if (site->pc == pc) + goto upward; + } + + /* + * Not in tree at all: let's find our symbolic callsite info. + * XXX static syms are masked by nearest lower global + */ + info.dli_fname = info.dli_sname = NULL; + ok = dladdr(pc, &info); + if (ok < 0) { + fprintf(stderr, "dladdr failed!\n"); + return NULL; + } + +/* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ + symbol = info.dli_sname; + offset = (char*)pc - (char*)info.dli_fbase; + method = symbol + ? strdup(symbol) + : JS_smprintf("%s+%X", + info.dli_fname ? info.dli_fname : "main", + offset); + if (!method) + return NULL; + + /* Create a new callsite record. */ + site = (JSCallsite *) malloc(sizeof(JSCallsite)); + if (!site) + return NULL; + + /* Insert the new site into the tree. */ + site->pc = pc; + site->name = method; + site->library = info.dli_fname; + site->offset = offset; + site->parent = parent; + site->siblings = parent->kids; + parent->kids = site; + site->kids = NULL; + + upward: + parent = site; + bpdown = bp; + bp = bpup; + } while (bp); + + return site; +} + +JSCallsite * +JS_Backtrace(int skip) +{ + void **bp, **bpdown; + + /* Stack walking code adapted from Kipp's "leaky". */ +#if defined(__i386) + __asm__( "movl %%ebp, %0" : "=g"(bp)); +#elif defined(__x86_64__) + __asm__( "movq %%rbp, %0" : "=g"(bp)); +#else + /* + * It would be nice if this worked uniformly, but at least on i386 and + * x86_64, it stopped working with gcc 4.1, because it points to the + * end of the saved registers instead of the start. + */ + bp = (void**) __builtin_frame_address(0); +#endif + while (--skip >= 0) { + bpdown = (void**) *bp++; + if (bpdown < bp) + break; + bp = bpdown; + } + + return CallTree(bp); +} + +#endif /* DEBUG_notme && XP_UNIX */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsutil.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsutil.h new file mode 100644 index 0000000..efcb614 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsutil.h @@ -0,0 +1,106 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ + +#ifndef jsutil_h___ +#define jsutil_h___ + +JS_BEGIN_EXTERN_C + +#ifdef DEBUG + +extern JS_PUBLIC_API(void) +JS_Assert(const char *s, const char *file, JSIntn ln); +#define JS_ASSERT(_expr) \ + ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__)) + +#define JS_NOT_REACHED(_reasonStr) \ + JS_Assert(_reasonStr,__FILE__,__LINE__) + +#else + +#define JS_ASSERT(expr) ((void) 0) +#define JS_NOT_REACHED(reasonStr) + +#endif /* defined(DEBUG) */ + +/* + * Compile-time assert. "condition" must be a constant expression. + * The macro should be used only once per source line in places where + * a "typedef" declaration is allowed. + */ +#define JS_STATIC_ASSERT(condition) \ + JS_STATIC_ASSERT_IMPL(condition, __LINE__) +#define JS_STATIC_ASSERT_IMPL(condition, line) \ + JS_STATIC_ASSERT_IMPL2(condition, line) +#define JS_STATIC_ASSERT_IMPL2(condition, line) \ + typedef int js_static_assert_line_##line[(condition) ? 1 : -1] + +/* +** Abort the process in a non-graceful manner. This will cause a core file, +** call to the debugger or other moral equivalent as well as causing the +** entire process to stop. +*/ +extern JS_PUBLIC_API(void) JS_Abort(void); + +#ifdef XP_UNIX + +typedef struct JSCallsite JSCallsite; + +struct JSCallsite { + uint32 pc; + char *name; + const char *library; + int offset; + JSCallsite *parent; + JSCallsite *siblings; + JSCallsite *kids; + void *handy; +}; + +extern JSCallsite *JS_Backtrace(int skip); + +#endif + +JS_END_EXTERN_C + +#endif /* jsutil_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxdrapi.c b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxdrapi.c new file mode 100644 index 0000000..2855c60 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxdrapi.c @@ -0,0 +1,835 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "jsstddef.h" +#include "jsconfig.h" + +#if JS_HAS_XDR + +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsdhash.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsnum.h" +#include "jsobj.h" /* js_XDRObject */ +#include "jsscript.h" /* js_XDRScript */ +#include "jsstr.h" +#include "jsxdrapi.h" + +#ifdef DEBUG +#define DBG(x) x +#else +#define DBG(x) ((void)0) +#endif + +typedef struct JSXDRMemState { + JSXDRState state; + char *base; + uint32 count; + uint32 limit; +} JSXDRMemState; + +#define MEM_BLOCK 8192 +#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) + +#define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) +#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) +#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) + +#define MEM_LEFT(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_DECODE && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ + JSMSG_END_OF_DATA); \ + return 0; \ + } \ + JS_END_MACRO + +#define MEM_NEED(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_ENCODE) { \ + if (MEM_LIMIT(xdr) && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ + void *data_ = JS_realloc((xdr)->cx, MEM_BASE(xdr), limit_); \ + if (!data_) \ + return 0; \ + MEM_BASE(xdr) = data_; \ + MEM_LIMIT(xdr) = limit_; \ + } \ + } else { \ + MEM_LEFT(xdr, bytes); \ + } \ + JS_END_MACRO + +#define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr))) +#define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) + +static JSBool +mem_get32(JSXDRState *xdr, uint32 *lp) +{ + MEM_LEFT(xdr, 4); + *lp = *(uint32 *)MEM_DATA(xdr); + MEM_INCR(xdr, 4); + return JS_TRUE; +} + +static JSBool +mem_set32(JSXDRState *xdr, uint32 *lp) +{ + MEM_NEED(xdr, 4); + *(uint32 *)MEM_DATA(xdr) = *lp; + MEM_INCR(xdr, 4); + return JS_TRUE; +} + +static JSBool +mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + MEM_LEFT(xdr, len); + memcpy(bytes, MEM_DATA(xdr), len); + MEM_INCR(xdr, len); + return JS_TRUE; +} + +static JSBool +mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + MEM_NEED(xdr, len); + memcpy(MEM_DATA(xdr), bytes, len); + MEM_INCR(xdr, len); + return JS_TRUE; +} + +static void * +mem_raw(JSXDRState *xdr, uint32 len) +{ + void *data; + if (xdr->mode == JSXDR_ENCODE) { + MEM_NEED(xdr, len); + } else if (xdr->mode == JSXDR_DECODE) { + MEM_LEFT(xdr, len); + } + data = MEM_DATA(xdr); + MEM_INCR(xdr, len); + return data; +} + +static JSBool +mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) +{ + switch (whence) { + case JSXDR_SEEK_CUR: + if ((int32)MEM_COUNT(xdr) + offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_START); + return JS_FALSE; + } + if (offset > 0) + MEM_NEED(xdr, offset); + MEM_COUNT(xdr) += offset; + return JS_TRUE; + case JSXDR_SEEK_SET: + if (offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_START); + return JS_FALSE; + } + if (xdr->mode == JSXDR_ENCODE) { + if ((uint32)offset > MEM_COUNT(xdr)) + MEM_NEED(xdr, offset - MEM_COUNT(xdr)); + MEM_COUNT(xdr) = offset; + } else { + if ((uint32)offset > MEM_LIMIT(xdr)) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_END); + return JS_FALSE; + } + MEM_COUNT(xdr) = offset; + } + return JS_TRUE; + case JSXDR_SEEK_END: + if (offset >= 0 || + xdr->mode == JSXDR_ENCODE || + (int32)MEM_LIMIT(xdr) + offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_END_SEEK); + return JS_FALSE; + } + MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset; + return JS_TRUE; + default: { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", whence); + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_WHITHER_WHENCE, numBuf); + return JS_FALSE; + } + } +} + +static uint32 +mem_tell(JSXDRState *xdr) +{ + return MEM_COUNT(xdr); +} + +static void +mem_finalize(JSXDRState *xdr) +{ + JS_free(xdr->cx, MEM_BASE(xdr)); +} + +static JSXDROps xdrmem_ops = { + mem_get32, mem_set32, mem_getbytes, mem_setbytes, + mem_raw, mem_seek, mem_tell, mem_finalize +}; + +JS_PUBLIC_API(void) +JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) +{ + xdr->mode = mode; + xdr->cx = cx; + xdr->registry = NULL; + xdr->numclasses = xdr->maxclasses = 0; + xdr->reghash = NULL; + xdr->userdata = NULL; + xdr->script = NULL; +} + +JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode) +{ + JSXDRState *xdr = (JSXDRState *) JS_malloc(cx, sizeof(JSXDRMemState)); + if (!xdr) + return NULL; + JS_XDRInitBase(xdr, mode, cx); + if (mode == JSXDR_ENCODE) { + if (!(MEM_BASE(xdr) = JS_malloc(cx, MEM_BLOCK))) { + JS_free(cx, xdr); + return NULL; + } + } else { + /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */ + MEM_BASE(xdr) = NULL; + } + xdr->ops = &xdrmem_ops; + MEM_COUNT(xdr) = 0; + MEM_LIMIT(xdr) = MEM_BLOCK; + return xdr; +} + +JS_PUBLIC_API(void *) +JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) +{ + if (xdr->ops != &xdrmem_ops) + return NULL; + *lp = MEM_COUNT(xdr); + return MEM_BASE(xdr); +} + +JS_PUBLIC_API(void) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) +{ + if (xdr->ops != &xdrmem_ops) + return; + MEM_LIMIT(xdr) = len; + MEM_BASE(xdr) = data; + MEM_COUNT(xdr) = 0; +} + +JS_PUBLIC_API(uint32) +JS_XDRMemDataLeft(JSXDRState *xdr) +{ + if (xdr->ops != &xdrmem_ops) + return 0; + return MEM_LIMIT(xdr) - MEM_COUNT(xdr); +} + +JS_PUBLIC_API(void) +JS_XDRMemResetData(JSXDRState *xdr) +{ + if (xdr->ops != &xdrmem_ops) + return; + MEM_COUNT(xdr) = 0; +} + +JS_PUBLIC_API(void) +JS_XDRDestroy(JSXDRState *xdr) +{ + JSContext *cx = xdr->cx; + xdr->ops->finalize(xdr); + if (xdr->registry) { + JS_free(cx, xdr->registry); + if (xdr->reghash) + JS_DHashTableDestroy(xdr->reghash); + } + JS_free(cx, xdr); +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint8(JSXDRState *xdr, uint8 *b) +{ + uint32 l = *b; + if (!JS_XDRUint32(xdr, &l)) + return JS_FALSE; + *b = (uint8) l; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint16(JSXDRState *xdr, uint16 *s) +{ + uint32 l = *s; + if (!JS_XDRUint32(xdr, &l)) + return JS_FALSE; + *s = (uint16) l; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint32(JSXDRState *xdr, uint32 *lp) +{ + JSBool ok = JS_TRUE; + if (xdr->mode == JSXDR_ENCODE) { + uint32 xl = JSXDR_SWAB32(*lp); + ok = xdr->ops->set32(xdr, &xl); + } else if (xdr->mode == JSXDR_DECODE) { + ok = xdr->ops->get32(xdr, lp); + *lp = JSXDR_SWAB32(*lp); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + uint32 padlen; + static char padbuf[JSXDR_ALIGN-1]; + + if (xdr->mode == JSXDR_ENCODE) { + if (!xdr->ops->setbytes(xdr, bytes, len)) + return JS_FALSE; + } else { + if (!xdr->ops->getbytes(xdr, bytes, len)) + return JS_FALSE; + } + len = xdr->ops->tell(xdr); + if (len % JSXDR_ALIGN) { + padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN); + if (xdr->mode == JSXDR_ENCODE) { + if (!xdr->ops->setbytes(xdr, padbuf, padlen)) + return JS_FALSE; + } else { + if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +/** + * Convert between a C string and the XDR representation: + * leading 32-bit count, then counted vector of chars, + * then possibly \0 padding to multiple of 4. + */ +JS_PUBLIC_API(JSBool) +JS_XDRCString(JSXDRState *xdr, char **sp) +{ + uint32 len; + + if (xdr->mode == JSXDR_ENCODE) + len = strlen(*sp); + JS_XDRUint32(xdr, &len); + if (xdr->mode == JSXDR_DECODE) { + if (!(*sp = (char *) JS_malloc(xdr->cx, len + 1))) + return JS_FALSE; + } + if (!JS_XDRBytes(xdr, *sp, len)) { + if (xdr->mode == JSXDR_DECODE) + JS_free(xdr->cx, *sp); + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + (*sp)[len] = '\0'; + } else if (xdr->mode == JSXDR_FREE) { + JS_free(xdr->cx, *sp); + *sp = NULL; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) +{ + uint32 null = (*sp == NULL); + if (!JS_XDRUint32(xdr, &null)) + return JS_FALSE; + if (null) { + *sp = NULL; + return JS_TRUE; + } + return JS_XDRCString(xdr, sp); +} + +static JSBool +XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars) +{ + uint32 i, padlen, nbytes; + jschar *raw; + + nbytes = nchars * sizeof(jschar); + padlen = nbytes % JSXDR_ALIGN; + if (padlen) { + padlen = JSXDR_ALIGN - padlen; + nbytes += padlen; + } + if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) + return JS_FALSE; + if (xdr->mode == JSXDR_ENCODE) { + for (i = 0; i != nchars; i++) + raw[i] = JSXDR_SWAB16(chars[i]); + if (padlen) + memset((char *)raw + nbytes - padlen, 0, padlen); + } else if (xdr->mode == JSXDR_DECODE) { + for (i = 0; i != nchars; i++) + chars[i] = JSXDR_SWAB16(raw[i]); + } + return JS_TRUE; +} + +/* + * Convert between a JS (Unicode) string and the XDR representation. + */ +JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp) +{ + uint32 nchars; + jschar *chars; + + if (xdr->mode == JSXDR_ENCODE) + nchars = JSSTRING_LENGTH(*strp); + if (!JS_XDRUint32(xdr, &nchars)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + chars = (jschar *) JS_malloc(xdr->cx, (nchars + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + } else { + chars = JSSTRING_CHARS(*strp); + } + + if (!XDRChars(xdr, chars, nchars)) + goto bad; + if (xdr->mode == JSXDR_DECODE) { + chars[nchars] = 0; + *strp = JS_NewUCString(xdr->cx, chars, nchars); + if (!*strp) + goto bad; + } + return JS_TRUE; + +bad: + if (xdr->mode == JSXDR_DECODE) + JS_free(xdr->cx, chars); + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) +{ + uint32 null = (*strp == NULL); + if (!JS_XDRUint32(xdr, &null)) + return JS_FALSE; + if (null) { + *strp = NULL; + return JS_TRUE; + } + return JS_XDRString(xdr, strp); +} + +static JSBool +XDRDoubleValue(JSXDRState *xdr, jsdouble *dp) +{ + jsdpun u; + + if (xdr->mode == JSXDR_ENCODE) + u.d = *dp; + if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *dp = u.d; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) +{ + jsdouble d; + + if (xdr->mode == JSXDR_ENCODE) + d = **dpp; + if (!XDRDoubleValue(xdr, &d)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) { + *dpp = JS_NewDouble(xdr->cx, d); + if (!*dpp) + return JS_FALSE; + } + return JS_TRUE; +} + +/* These are magic pseudo-tags: see jsapi.h, near the top, for real tags. */ +#define JSVAL_XDRNULL 0x8 +#define JSVAL_XDRVOID 0xA + +static JSBool +XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) +{ + switch (type) { + case JSVAL_XDRNULL: + *vp = JSVAL_NULL; + break; + case JSVAL_XDRVOID: + *vp = JSVAL_VOID; + break; + case JSVAL_STRING: { + JSString *str; + if (xdr->mode == JSXDR_ENCODE) + str = JSVAL_TO_STRING(*vp); + if (!JS_XDRString(xdr, &str)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = STRING_TO_JSVAL(str); + break; + } + case JSVAL_DOUBLE: { + jsdouble *dp; + if (xdr->mode == JSXDR_ENCODE) + dp = JSVAL_TO_DOUBLE(*vp); + if (!JS_XDRDouble(xdr, &dp)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = DOUBLE_TO_JSVAL(dp); + break; + } + case JSVAL_OBJECT: { + JSObject *obj; + if (xdr->mode == JSXDR_ENCODE) + obj = JSVAL_TO_OBJECT(*vp); + if (!js_XDRObject(xdr, &obj)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = OBJECT_TO_JSVAL(obj); + break; + } + case JSVAL_BOOLEAN: { + uint32 b; + if (xdr->mode == JSXDR_ENCODE) + b = (uint32) JSVAL_TO_BOOLEAN(*vp); + if (!JS_XDRUint32(xdr, &b)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = BOOLEAN_TO_JSVAL((JSBool) b); + break; + } + default: { + uint32 i; + + JS_ASSERT(type & JSVAL_INT); + if (xdr->mode == JSXDR_ENCODE) + i = (uint32) JSVAL_TO_INT(*vp); + if (!JS_XDRUint32(xdr, &i)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = INT_TO_JSVAL((int32) i); + break; + } + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp) +{ + uint32 type; + + if (xdr->mode == JSXDR_ENCODE) { + if (JSVAL_IS_NULL(*vp)) + type = JSVAL_XDRNULL; + else if (JSVAL_IS_VOID(*vp)) + type = JSVAL_XDRVOID; + else + type = JSVAL_TAG(*vp); + } + return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); +} + +JSBool +js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) +{ + jsval v; + uint32 type; + jsdouble d; + JSAtom *atom; + + if (xdr->mode == JSXDR_ENCODE) { + v = ATOM_KEY(*atomp); + return JS_XDRValue(xdr, &v); + } + + /* + * Inline JS_XDRValue when decoding to avoid ceation of GC things when + * then corresponding atom already exists. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &type)) + return JS_FALSE; + if (type == JSVAL_STRING) + return js_XDRStringAtom(xdr, atomp); + + if (type == JSVAL_DOUBLE) { + if (!XDRDoubleValue(xdr, &d)) + return JS_FALSE; + atom = js_AtomizeDouble(xdr->cx, d, 0); + } else { + if (!XDRValueBody(xdr, type, &v)) + return JS_FALSE; + atom = js_AtomizeValue(xdr->cx, v, 0); + } + + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; +} + +extern JSBool +js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp) +{ + JSString *str; + uint32 nchars; + JSAtom *atom; + JSContext *cx; + void *mark; + jschar *chars; + + if (xdr->mode == JSXDR_ENCODE) { + JS_ASSERT(ATOM_IS_STRING(*atomp)); + str = ATOM_TO_STRING(*atomp); + return JS_XDRString(xdr, &str); + } + + /* + * Inline JS_XDRString when decoding to avoid JSString allocation + * for already existing atoms. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &nchars)) + return JS_FALSE; + atom = NULL; + cx = xdr->cx; + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(chars, jschar *, &cx->tempPool, + nchars * sizeof(jschar)); + if (!chars) + JS_ReportOutOfMemory(cx); + else if (XDRChars(xdr, chars, nchars)) + atom = js_AtomizeChars(cx, chars, nchars, 0); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; +} + +/* + * FIXME: This performs lossy conversion and we need to switch to + * js_XDRStringAtom while allowing to read older XDR files. See bug 325202. + */ +JSBool +js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp) +{ + char *bytes; + uint32 nbytes; + JSAtom *atom; + JSContext *cx; + void *mark; + + if (xdr->mode == JSXDR_ENCODE) { + JS_ASSERT(ATOM_IS_STRING(*atomp)); + bytes = JS_GetStringBytes(ATOM_TO_STRING(*atomp)); + return JS_XDRCString(xdr, &bytes); + } + + /* + * Inline JS_XDRCString when decoding not to malloc temporary buffer + * just to free it after atomization. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &nbytes)) + return JS_FALSE; + atom = NULL; + cx = xdr->cx; + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(bytes, char *, &cx->tempPool, + nbytes * sizeof *bytes); + if (!bytes) + JS_ReportOutOfMemory(cx); + else if (JS_XDRBytes(xdr, bytes, nbytes)) + atom = js_Atomize(cx, bytes, nbytes, 0); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) +{ + if (!js_XDRScript(xdr, scriptp, NULL)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + js_CallNewScriptHook(xdr->cx, *scriptp, NULL); + return JS_TRUE; +} + +#define CLASS_REGISTRY_MIN 8 +#define CLASS_INDEX_TO_ID(i) ((i)+1) +#define CLASS_ID_TO_INDEX(id) ((id)-1) + +typedef struct JSRegHashEntry { + JSDHashEntryHdr hdr; + const char *name; + uint32 index; +} JSRegHashEntry; + +JS_PUBLIC_API(JSBool) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) +{ + uintN numclasses, maxclasses; + JSClass **registry; + + numclasses = xdr->numclasses; + maxclasses = xdr->maxclasses; + if (numclasses == maxclasses) { + maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1; + registry = (JSClass **) + JS_realloc(xdr->cx, xdr->registry, maxclasses * sizeof(JSClass *)); + if (!registry) + return JS_FALSE; + xdr->registry = registry; + xdr->maxclasses = maxclasses; + } else { + JS_ASSERT(numclasses && numclasses < maxclasses); + registry = xdr->registry; + } + + registry[numclasses] = clasp; + if (xdr->reghash) { + JSRegHashEntry *entry = (JSRegHashEntry *) + JS_DHashTableOperate(xdr->reghash, clasp->name, JS_DHASH_ADD); + if (!entry) { + JS_ReportOutOfMemory(xdr->cx); + return JS_FALSE; + } + entry->name = clasp->name; + entry->index = numclasses; + } + *idp = CLASS_INDEX_TO_ID(numclasses); + xdr->numclasses = ++numclasses; + return JS_TRUE; +} + +JS_PUBLIC_API(uint32) +JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) +{ + uintN i, numclasses; + + numclasses = xdr->numclasses; + if (numclasses >= 10) { + JSRegHashEntry *entry; + + /* Bootstrap reghash from registry on first overpopulated Find. */ + if (!xdr->reghash) { + xdr->reghash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, + sizeof(JSRegHashEntry), + numclasses); + if (xdr->reghash) { + for (i = 0; i < numclasses; i++) { + JSClass *clasp = xdr->registry[i]; + entry = (JSRegHashEntry *) + JS_DHashTableOperate(xdr->reghash, clasp->name, + JS_DHASH_ADD); + entry->name = clasp->name; + entry->index = i; + } + } + } + + /* If we managed to create reghash, use it for O(1) Find. */ + if (xdr->reghash) { + entry = (JSRegHashEntry *) + JS_DHashTableOperate(xdr->reghash, name, JS_DHASH_LOOKUP); + if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) + return CLASS_INDEX_TO_ID(entry->index); + } + } + + /* Only a few classes, or we couldn't malloc reghash: use linear search. */ + for (i = 0; i < numclasses; i++) { + if (!strcmp(name, xdr->registry[i]->name)) + return CLASS_INDEX_TO_ID(i); + } + return 0; +} + +JS_PUBLIC_API(JSClass *) +JS_XDRFindClassById(JSXDRState *xdr, uint32 id) +{ + uintN i = CLASS_ID_TO_INDEX(id); + + if (i >= xdr->numclasses) + return NULL; + return xdr->registry[i]; +} + +#endif /* JS_HAS_XDR */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxdrapi.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxdrapi.h new file mode 100644 index 0000000..35d9918 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxdrapi.h @@ -0,0 +1,223 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxdrapi_h___ +#define jsxdrapi_h___ + +/* + * JS external data representation interface API. + * + * The XDR system is comprised of three major parts: + * + * - the state serialization/deserialization APIs, which allow consumers + * of the API to serialize JS runtime state (script bytecodes, atom maps, + * object graphs, etc.) for later restoration. These portions + * are implemented in various appropriate files, such as jsscript.c + * for the script portions and jsobj.c for object state. + * - the callback APIs through which the runtime requests an opaque + * representation of a native object, and through which the runtime + * constructs a live native object from an opaque representation. These + * portions are the responsibility of the native object implementor. + * - utility functions for en/decoding of primitive types, such as + * JSStrings. This portion is implemented in jsxdrapi.c. + * + * Spiritually guided by Sun's XDR, where appropriate. + */ + +#include "jspubtd.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* We use little-endian byteorder for all encoded data */ + +#if defined IS_LITTLE_ENDIAN +#define JSXDR_SWAB32(x) x +#define JSXDR_SWAB16(x) x +#elif defined IS_BIG_ENDIAN +#define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ + (((uint32)(x) >> 8) & 0xff00) | \ + (((uint32)(x) << 8) & 0xff0000) | \ + ((uint32)(x) << 24)) +#define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) +#else +#error "unknown byte order" +#endif + +#define JSXDR_ALIGN 4 + +typedef enum JSXDRMode { + JSXDR_ENCODE, + JSXDR_DECODE, + JSXDR_FREE +} JSXDRMode; + +typedef enum JSXDRWhence { + JSXDR_SEEK_SET, + JSXDR_SEEK_CUR, + JSXDR_SEEK_END +} JSXDRWhence; + +typedef struct JSXDROps { + JSBool (*get32)(JSXDRState *, uint32 *); + JSBool (*set32)(JSXDRState *, uint32 *); + JSBool (*getbytes)(JSXDRState *, char *, uint32); + JSBool (*setbytes)(JSXDRState *, char *, uint32); + void * (*raw)(JSXDRState *, uint32); + JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); + uint32 (*tell)(JSXDRState *); + void (*finalize)(JSXDRState *); +} JSXDROps; + +struct JSXDRState { + JSXDRMode mode; + JSXDROps *ops; + JSContext *cx; + JSClass **registry; + uintN numclasses; + uintN maxclasses; + void *reghash; + void *userdata; + JSScript *script; +}; + +extern JS_PUBLIC_API(void) +JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); + +extern JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode); + +extern JS_PUBLIC_API(void *) +JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(void) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); + +extern JS_PUBLIC_API(uint32) +JS_XDRMemDataLeft(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRMemResetData(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRDestroy(JSXDRState *xdr); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint8(JSXDRState *xdr, uint8 *b); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint16(JSXDRState *xdr, uint16 *s); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint32(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCString(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCStringOrNull(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); + +extern JS_PUBLIC_API(uint32) +JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); + +extern JS_PUBLIC_API(JSClass *) +JS_XDRFindClassById(JSXDRState *xdr, uint32 id); + +/* + * Magic numbers. + */ +#define JSXDR_MAGIC_SCRIPT_1 0xdead0001 +#define JSXDR_MAGIC_SCRIPT_2 0xdead0002 +#define JSXDR_MAGIC_SCRIPT_3 0xdead0003 +#define JSXDR_MAGIC_SCRIPT_4 0xdead0004 +#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 +#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 + +/* + * Bytecode version number. Decrement the second term whenever JS bytecode + * changes incompatibly. + * + * This version number should be XDR'ed once near the front of any file or + * larger storage unit containing XDR'ed bytecode and other data, and checked + * before deserialization of bytecode. If the saved version does not match + * the current version, abort deserialization and invalidate the file. + */ +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) + +/* + * Library-private functions. + */ +extern JSBool +js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); + +extern JSBool +js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); + +/* + * FIXME: This is non-unicode version of js_XDRStringAtom that performs lossy + * conversion. Do not use it in the new code! See bug 325202. + */ +extern JSBool +js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp); + +JS_END_EXTERN_C + +#endif /* ! jsxdrapi_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxml.c b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxml.c new file mode 100644 index 0000000..1266255 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxml.c @@ -0,0 +1,8357 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsstddef.h" +#include "jsconfig.h" + +#if JS_HAS_XML_SUPPORT + +#include +#include +#include +#include "jstypes.h" +#include "jsbit.h" +#include "jsprf.h" +#include "jsutil.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsxml.h" + +#ifdef DEBUG +#include /* for #ifdef DEBUG memset calls */ +#endif + +/* + * NOTES + * - in the js shell, you must use the -x command line option, or call + * options('xml') before compiling anything that uses XML literals + * + * TODO + * - XXXbe patrol + * - Fuse objects and their JSXML* private data into single GC-things + * - fix function::foo vs. x.(foo == 42) collision using proper namespacing + * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants + * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! + * - JS_TypeOfValue sure could use a cleaner interface to "types" + */ + +#ifdef DEBUG_brendan +#define METERING 1 +#endif + +#ifdef METERING +static struct { + jsrefcount qname; + jsrefcount qnameobj; + jsrefcount liveqname; + jsrefcount liveqnameobj; + jsrefcount namespace; + jsrefcount namespaceobj; + jsrefcount livenamespace; + jsrefcount livenamespaceobj; + jsrefcount xml; + jsrefcount xmlobj; + jsrefcount livexml; + jsrefcount livexmlobj; +} xml_stats; + +#define METER(x) JS_ATOMIC_INCREMENT(&(x)) +#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) +#else +#define METER(x) /* nothing */ +#define UNMETER(x) /* nothing */ +#endif + +/* + * Random utilities and global functions. + */ +const char js_isXMLName_str[] = "isXMLName"; +const char js_XMLList_str[] = "XMLList"; +const char js_localName_str[] = "localName"; +const char js_xml_parent_str[] = "parent"; +const char js_prefix_str[] = "prefix"; +const char js_toXMLString_str[] = "toXMLString"; +const char js_uri_str[] = "uri"; + +const char js_amp_entity_str[] = "&"; +const char js_gt_entity_str[] = ">"; +const char js_lt_entity_str[] = "<"; +const char js_quot_entity_str[] = """; + +#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) +#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') + +static JSBool +xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); + return JS_TRUE; +} + +/* + * Namespace class and library functions. + */ +enum namespace_tinyid { + NAMESPACE_PREFIX = -1, + NAMESPACE_URI = -2 +}; + +static JSBool +namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLNamespace *ns; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL); + if (!ns) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case NAMESPACE_PREFIX: + *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; + break; + case NAMESPACE_URI: + *vp = STRING_TO_JSVAL(ns->uri); + break; + } + return JS_TRUE; +} + +static void +namespace_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLNamespace *ns; + JSRuntime *rt; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + if (!ns) + return; + JS_ASSERT(ns->object == obj); + ns->object = NULL; + UNMETER(xml_stats.livenamespaceobj); + + rt = cx->runtime; + if (rt->functionNamespaceObject == obj) + rt->functionNamespaceObject = NULL; +} + +static void +namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len) +{ + uint32 i; + JSXMLNamespace *ns; + + for (i = 0; i < len; i++) { + ns = vec[i]; + { +#ifdef GC_MARK_DEBUG + char buf[100]; + + JS_snprintf(buf, sizeof buf, "%s=%s", + ns->prefix ? JS_GetStringBytes(ns->prefix) : "", + JS_GetStringBytes(ns->uri)); +#endif + GC_MARK(cx, ns, buf); + } + } +} + +static uint32 +namespace_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + GC_MARK(cx, ns, "private"); + return 0; +} + +static JSBool +namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXMLNamespace *ns, *ns2; + JSObject *obj2; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) { + *bp = JS_FALSE; + } else { + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); + *bp = js_EqualStrings(ns->uri, ns2->uri); + } + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { + { "Namespace", + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), + JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, namespace_mark, NULL }, + namespace_equality,NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +#define NAMESPACE_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec namespace_props[] = { + {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, + {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv); + if (!ns) + return JS_FALSE; + + *rval = STRING_TO_JSVAL(ns->uri); + return JS_TRUE; +} + +static JSFunctionSpec namespace_methods[] = { + {js_toString_str, namespace_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) + js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace)); + if (!ns) + return NULL; + ns->object = NULL; + ns->prefix = prefix; + ns->uri = uri; + ns->declared = declared; + METER(xml_stats.namespace); + METER(xml_stats.livenamespace); + return ns; +} + +void +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns) +{ + GC_MARK(cx, ns->object, "object"); + GC_MARK(cx, ns->prefix, "prefix"); + GC_MARK(cx, ns->uri, "uri"); +} + +void +js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns) +{ + UNMETER(xml_stats.livenamespace); +} + +JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared) +{ + JSXMLNamespace *ns; + + ns = js_NewXMLNamespace(cx, prefix, uri, declared); + if (!ns) + return NULL; + return js_GetXMLNamespaceObject(cx, ns); +} + +JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) +{ + JSObject *obj; + + obj = ns->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == ns); + return obj; + } + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, ns)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + ns->object = obj; + METER(xml_stats.namespaceobj); + METER(xml_stats.livenamespaceobj); + return obj; +} + +/* + * QName class and library functions. + */ +enum qname_tinyid { + QNAME_URI = -1, + QNAME_LOCALNAME = -2 +}; + +static JSBool +qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLQName *qn; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + qn = (JSXMLQName *) + JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL); + if (!qn) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case QNAME_URI: + *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; + break; + case QNAME_LOCALNAME: + *vp = STRING_TO_JSVAL(qn->localName); + break; + } + return JS_TRUE; +} + +static void +qname_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + if (!qn) + return; + JS_ASSERT(qn->object == obj); + qn->object = NULL; + UNMETER(xml_stats.liveqnameobj); +} + +static void +anyname_finalize(JSContext* cx, JSObject* obj) +{ + JSRuntime *rt; + + /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ + rt = cx->runtime; + if (rt->anynameObject == obj) + rt->anynameObject = NULL; + + qname_finalize(cx, obj); +} + +static uint32 +qname_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + GC_MARK(cx, qn, "private"); + return 0; +} + +static JSBool +qname_identity(JSXMLQName *qna, JSXMLQName *qnb) +{ + if (!qna->uri ^ !qnb->uri) + return JS_FALSE; + if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri)) + return JS_FALSE; + return js_EqualStrings(qna->localName, qnb->localName); +} + +static JSBool +qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXMLQName *qn, *qn2; + JSObject *obj2; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) { + *bp = JS_FALSE; + } else { + qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2); + *bp = qname_identity(qn, qn2); + } + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { + { "QName", + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_CACHED_PROTO(JSProto_QName), + JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL }, + qname_equality, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +/* + * Classes for the ECMA-357-internal types AttributeName and AnyName, which + * are like QName, except that they have no property getters. They share the + * qname_toString method, and therefore are exposed as constructable objects + * in this implementation. + */ +JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { + js_AttributeName_str, + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | + JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +JS_FRIEND_DATA(JSClass) js_AnyNameClass = { + js_AnyName_str, + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | + JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +#define QNAME_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec qname_props[] = { + {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, + {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSClass *clasp; + JSXMLQName *qn; + JSString *str, *qualstr; + size_t length; + jschar *chars; + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) { + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + } else { + qn = (JSXMLQName *) + JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv); + if (!qn) + return JS_FALSE; + } + + if (!qn->uri) { + /* No uri means wildcard qualifier. */ + str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); + } else if (IS_EMPTY(qn->uri)) { + /* Empty string for uri means localName is in no namespace. */ + str = cx->runtime->emptyString; + } else { + qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); + str = js_ConcatStrings(cx, qn->uri, qualstr); + if (!str) + return JS_FALSE; + } + str = js_ConcatStrings(cx, str, qn->localName); + if (!str) + return JS_FALSE; + + if (str && clasp == &js_AttributeNameClass) { + length = JSSTRING_LENGTH(str); + chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + *chars = '@'; + js_strncpy(chars + 1, JSSTRING_CHARS(str), length); + chars[++length] = 0; + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + } + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSFunctionSpec qname_methods[] = { + {js_toString_str, qname_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName)); + if (!qn) + return NULL; + qn->object = NULL; + qn->uri = uri; + qn->prefix = prefix; + qn->localName = localName; + METER(xml_stats.qname); + METER(xml_stats.liveqname); + return qn; +} + +void +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn) +{ + GC_MARK(cx, qn->object, "object"); + GC_MARK(cx, qn->uri, "uri"); + GC_MARK(cx, qn->prefix, "prefix"); + GC_MARK(cx, qn->localName, "localName"); +} + +void +js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn) +{ + UNMETER(xml_stats.liveqname); +} + +JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + + qn = js_NewXMLQName(cx, uri, prefix, localName); + if (!qn) + return NULL; + return js_GetXMLQNameObject(cx, qn); +} + +JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) +{ + JSObject *obj; + + obj = qn->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == qn); + return obj; + } + obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + return obj; +} + +JSObject * +js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) +{ + JSObject *obj; + + obj = qn->object; + if (obj) { + if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass) + return obj; + qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); + if (!qn) + return NULL; + } + + obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + return obj; +} + +JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) +{ + jsval argv[2]; + + /* + * ECMA-357 11.1.2, + * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ + * production, step 2. + */ + if (!JSVAL_IS_PRIMITIVE(nsval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { + nsval = JSVAL_NULL; + } + + argv[0] = nsval; + argv[1] = lnval; + return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); +} + +static JSBool +IsXMLName(const jschar *cp, size_t n) +{ + JSBool rv; + jschar c; + + rv = JS_FALSE; + if (n != 0 && JS_ISXMLNSSTART(*cp)) { + while (--n != 0) { + c = *++cp; + if (!JS_ISXMLNS(c)) + return rv; + } + rv = JS_TRUE; + } + return rv; +} + +JSBool +js_IsXMLName(JSContext *cx, jsval v) +{ + JSClass *clasp; + JSXMLQName *qn; + JSString *name; + JSErrorReporter older; + + /* + * Inline specialization of the QName constructor called with v passed as + * the only argument, to compute the localName for the constructed qname, + * without actually allocating the object or computing its uri and prefix. + * See ECMA-357 13.1.2.1 step 1 and 13.3.2. + */ + if (!JSVAL_IS_PRIMITIVE(v) && + (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)), + clasp == &js_QNameClass.base || + clasp == &js_AttributeNameClass || + clasp == &js_AnyNameClass)) { + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + name = qn->localName; + } else { + older = JS_SetErrorReporter(cx, NULL); + name = js_ValueToString(cx, v); + JS_SetErrorReporter(cx, older); + if (!name) { + JS_ClearPendingException(cx); + return JS_FALSE; + } + } + + return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); +} + +static JSBool +Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval urival, prefixval; + JSObject *uriobj; + JSBool isNamespace, isQName; + JSClass *clasp; + JSString *empty, *prefix; + JSXMLNamespace *ns, *ns2; + JSXMLQName *qn; + + urival = argv[argc > 1]; + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(urival)) { + uriobj = JSVAL_TO_OBJECT(urival); + clasp = OBJ_GET_CLASS(cx, uriobj); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else uriobj = NULL; +#endif + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* Namespace called as function. */ + if (argc == 1 && isNamespace) { + /* Namespace called with one Namespace argument is identity. */ + *rval = urival; + return JS_TRUE; + } + + /* Create and return a new QName object exactly as if constructed. */ + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.namespaceobj); + METER(xml_stats.livenamespaceobj); + + /* + * Create and connect private data to rooted obj early, so we don't have + * to worry about rooting string newborns hanging off of the private data + * further below. + */ + empty = cx->runtime->emptyString; + ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE); + if (!ns) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, ns)) + return JS_FALSE; + ns->object = obj; + + if (argc == 1) { + if (isNamespace) { + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); + ns->uri = ns2->uri; + ns->prefix = ns2->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + ns->prefix = qn->prefix; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + if (!IS_EMPTY(ns->uri)) + ns->prefix = NULL; + } + } else if (argc == 2) { + if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + } + + prefixval = argv[0]; + if (IS_EMPTY(ns->uri)) { + if (!JSVAL_IS_VOID(prefixval)) { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + if (!IS_EMPTY(prefix)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix))); + return JS_FALSE; + } + } + } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { + /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ + ns->prefix = NULL; + } else { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + ns->prefix = prefix; + } + } + + return JS_TRUE; +} + +static JSBool +QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval nameval, nsval; + JSBool isQName, isNamespace; + JSXMLQName *qn; + JSString *uri, *prefix, *name; + JSObject *nsobj; + JSClass *clasp; + JSXMLNamespace *ns; + + nameval = argv[argc > 1]; + isQName = + !JSVAL_IS_PRIMITIVE(nameval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* QName called as function. */ + if (argc == 1 && isQName) { + /* QName called with one QName argument is identity. */ + *rval = nameval; + return JS_TRUE; + } + + /* + * Create and return a new QName object exactly as if constructed. + * Use the constructor's clasp so we can be shared by AttributeName + * (see below after this function). + */ + obj = js_NewObject(cx, + JS_ValueToFunction(cx, argv[-2])->clasp, + NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + + if (isQName) { + /* If namespace is not specified and name is a QName, clone it. */ + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); + if (argc == 1) { + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + goto out; + } + + /* Namespace and qname were passed -- use the qname's localName. */ + nameval = STRING_TO_JSVAL(qn->localName); + } + + if (argc == 0) { + name = cx->runtime->emptyString; + } else { + name = js_ValueToString(cx, nameval); + if (!name) + return JS_FALSE; + + /* Use argv[1] as a local root for name, even if it was not passed. */ + argv[1] = STRING_TO_JSVAL(name); + } + + nsval = argv[0]; + if (argc == 1 || JSVAL_IS_VOID(nsval)) { + if (IS_STAR(name)) { + nsval = JSVAL_NULL; + } else { + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return JS_FALSE; + } + } + + if (JSVAL_IS_NULL(nsval)) { + /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ + uri = prefix = NULL; + } else { + /* + * Inline specialization of the Namespace constructor called with + * nsval passed as the only argument, to compute the uri and prefix + * for the constructed namespace, without actually allocating the + * object or computing other members. See ECMA-357 13.3.2 6(a) and + * 13.2.2. + */ + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(nsval)) { + nsobj = JSVAL_TO_OBJECT(nsval); + clasp = OBJ_GET_CLASS(cx, nsobj); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else nsobj = NULL; +#endif + + if (isNamespace) { + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + uri = ns->uri; + prefix = ns->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { + uri = qn->uri; + prefix = qn->prefix; + } else { + uri = js_ValueToString(cx, nsval); + if (!uri) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(uri); /* local root */ + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; + } + } + +out: + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, qn)) + return JS_FALSE; + qn->object = obj; + return JS_TRUE; +} + +static JSBool +AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + /* + * Since js_AttributeNameClass was initialized, obj will have that as its + * class, not js_QNameClass. + */ + return QName(cx, obj, argc, argv, rval); +} + +/* + * XMLArray library functions. + */ +static JSBool +namespace_identity(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsa->prefix && nsb->prefix) { + if (!js_EqualStrings(nsa->prefix, nsb->prefix)) + return JS_FALSE; + } else { + if (nsa->prefix || nsb->prefix) + return JS_FALSE; + } + return js_EqualStrings(nsa->uri, nsb->uri); +} + +static JSBool +attr_identity(const void *a, const void *b) +{ + const JSXML *xmla = (const JSXML *) a; + const JSXML *xmlb = (const JSXML *) b; + + return qname_identity(xmla->name, xmlb->name); +} + +static void +XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array) +{ + JSXMLArrayCursor *next; + + cursor->array = array; + cursor->index = 0; + next = cursor->next = array->cursors; + if (next) + next->prevp = &cursor->next; + cursor->prevp = &array->cursors; + array->cursors = cursor; + cursor->root = NULL; +} + +static void +XMLArrayCursorFinish(JSXMLArrayCursor *cursor) +{ + JSXMLArrayCursor *next; + + if (!cursor->array) + return; + next = cursor->next; + if (next) + next->prevp = cursor->prevp; + *cursor->prevp = next; + cursor->array = NULL; +} + +static void * +XMLArrayCursorNext(JSXMLArrayCursor *cursor) +{ + JSXMLArray *array; + + array = cursor->array; + if (!array || cursor->index >= array->length) + return NULL; + return cursor->root = array->vector[cursor->index++]; +} + +static void * +XMLArrayCursorItem(JSXMLArrayCursor *cursor) +{ + JSXMLArray *array; + + array = cursor->array; + if (!array || cursor->index >= array->length) + return NULL; + return cursor->root = array->vector[cursor->index]; +} + +static void +XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) +{ + while (cursor) { + GC_MARK(cx, cursor->root, "cursor->root"); + cursor = cursor->next; + } +} + +/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */ +static JSBool +XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + void **vector; + + if (capacity == 0) { + /* We could let realloc(p, 0) free this, but purify gets confused. */ + if (array->vector) + free(array->vector); + vector = NULL; + } else { + if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || + !(vector = (void **) + realloc(array->vector, capacity * sizeof(void *)))) { + if (cx) + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + array->capacity = JSXML_PRESET_CAPACITY | capacity; + array->vector = vector; + return JS_TRUE; +} + +static void +XMLArrayTrim(JSXMLArray *array) +{ + if (array->capacity & JSXML_PRESET_CAPACITY) + return; + if (array->length < array->capacity) + XMLArraySetCapacity(NULL, array, array->length); +} + +static JSBool +XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + array->length = array->capacity = 0; + array->vector = NULL; + array->cursors = NULL; + return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); +} + +static void +XMLArrayFinish(JSContext *cx, JSXMLArray *array) +{ + JSXMLArrayCursor *cursor; + + JS_free(cx, array->vector); + + while ((cursor = array->cursors) != NULL) + XMLArrayCursorFinish(cursor); + +#ifdef DEBUG + memset(array, 0xd5, sizeof *array); +#endif +} + +#define XML_NOT_FOUND ((uint32) -1) + +static uint32 +XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) +{ + void **vector; + uint32 i, n; + + /* The identity op must not reallocate array->vector. */ + vector = array->vector; + if (identity) { + for (i = 0, n = array->length; i < n; i++) { + if (identity(vector[i], elt)) + return i; + } + } else { + for (i = 0, n = array->length; i < n; i++) { + if (vector[i] == elt) + return i; + } + } + return XML_NOT_FOUND; +} + +/* + * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after + * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold + * should be greater than increment. + */ +#define LINEAR_THRESHOLD 256 +#define LINEAR_INCREMENT 32 + +static JSBool +XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) +{ + uint32 capacity, i; + int log2; + void **vector; + + if (index >= array->length) { + if (index >= JSXML_CAPACITY(array)) { + /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ + capacity = index + 1; + if (index >= LINEAR_THRESHOLD) { + capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); + } else { + JS_CEILING_LOG2(log2, capacity); + capacity = JS_BIT(log2); + } + if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || + !(vector = (void **) + realloc(array->vector, capacity * sizeof(void *)))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + array->capacity = capacity; + array->vector = vector; + for (i = array->length; i < index; i++) + vector[i] = NULL; + } + array->length = index + 1; + } + + array->vector[index] = elt; + return JS_TRUE; +} + +static JSBool +XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) +{ + uint32 j; + JSXMLArrayCursor *cursor; + + j = array->length; + JS_ASSERT(i <= j); + if (!XMLArraySetCapacity(cx, array, j + n)) + return JS_FALSE; + + array->length = j + n; + JS_ASSERT(n != (uint32)-1); + while (j != i) { + --j; + array->vector[j + n] = array->vector[j]; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > i) + cursor->index += n; + } + return JS_TRUE; +} + +static void * +XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) +{ + uint32 length; + void **vector, *elt; + JSXMLArrayCursor *cursor; + + length = array->length; + if (index >= length) + return NULL; + + vector = array->vector; + elt = vector[index]; + if (compress) { + while (++index < length) + vector[index-1] = vector[index]; + array->length = length - 1; + array->capacity = JSXML_CAPACITY(array); + } else { + vector[index] = NULL; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > index) + --cursor->index; + } + return elt; +} + +static void +XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) +{ + void **vector; + + JS_ASSERT(!array->cursors); + if (length >= array->length) + return; + + if (length == 0) { + if (array->vector) + free(array->vector); + vector = NULL; + } else { + vector = realloc(array->vector, length * sizeof(void *)); + if (!vector) + return; + } + + if (array->length > length) + array->length = length; + array->capacity = length; + array->vector = vector; +} + +#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) +#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ + XML_NOT_FOUND) +#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ + ? (t *) (a)->vector[i] \ + : NULL) +#define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ + if ((a)->length <= (i)) \ + (a)->length = (i) + 1; \ + ((a)->vector[i] = (void *)(e)); \ + JS_END_MACRO +#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) +#define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) +#define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) +#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) +#define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) + +/* + * Define XML setting property strings and constants early, so everyone can + * use the same names and their magic numbers (tinyids, flags). + */ +static const char js_ignoreComments_str[] = "ignoreComments"; +static const char js_ignoreProcessingInstructions_str[] + = "ignoreProcessingInstructions"; +static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; +static const char js_prettyPrinting_str[] = "prettyPrinting"; +static const char js_prettyIndent_str[] = "prettyIndent"; + +/* + * NB: These XML static property tinyids must + * (a) not collide with the generic negative tinyids at the top of jsfun.c; + * (b) index their corresponding xml_static_props array elements. + * Don't change 'em! + */ +enum xml_static_tinyid { + XML_IGNORE_COMMENTS, + XML_IGNORE_PROCESSING_INSTRUCTIONS, + XML_IGNORE_WHITESPACE, + XML_PRETTY_PRINTING, + XML_PRETTY_INDENT +}; + +static JSBool +xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +static JSBool +xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool b; + uint8 flag; + + JS_ASSERT(JSVAL_IS_INT(id)); + if (!js_ValueToBoolean(cx, *vp, &b)) + return JS_FALSE; + + flag = JS_BIT(JSVAL_TO_INT(id)); + if (b) + cx->xmlSettingFlags |= flag; + else + cx->xmlSettingFlags &= ~flag; + return JS_TRUE; +} + +static JSPropertySpec xml_static_props[] = { + {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreProcessingInstructions_str, + XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, + xml_setting_getter, NULL}, + {0,0,0,0,0} +}; + +/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ +#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) +#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ + JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) +#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) +#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) +#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) + +/* + * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. + * This flag means a couple of things: + * + * - The top JSXML created for a parse tree must have an object owning it. + * + * - That the default namespace normally inherited from the temporary + * tag that wraps a runtime-concatenated XML source + * string must, in the case of a precompiled XML object tree, inherit via + * ad-hoc code in ParseNodeToXML. + * + * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. + */ +#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) + +/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ +#define IS_XML(str) \ + (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str))) + +#define IS_XMLNS(str) \ + (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str))) + +#define IS_XML_CHARS(chars) \ + (JS_TOLOWER((chars)[0]) == 'x' && \ + JS_TOLOWER((chars)[1]) == 'm' && \ + JS_TOLOWER((chars)[2]) == 'l') + +#define HAS_NS_AFTER_XML(chars) \ + (JS_TOLOWER((chars)[3]) == 'n' && \ + JS_TOLOWER((chars)[4]) == 's') + +#define IS_XMLNS_CHARS(chars) \ + (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) + +#define STARTS_WITH_XML(chars,length) \ + (length >= 3 && IS_XML_CHARS(chars)) + +static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; +static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; + +static JSXMLQName * +ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, + JSBool isAttributeName) +{ + JSString *str, *uri, *prefix, *localName; + size_t length, offset; + const jschar *start, *limit, *colon; + uint32 n; + JSXMLNamespace *ns; + + JS_ASSERT(pn->pn_arity == PN_NULLARY); + str = ATOM_TO_STRING(pn->pn_atom); + length = JSSTRING_LENGTH(str); + start = JSSTRING_CHARS(str); + JS_ASSERT(length != 0 && *start != '@'); + JS_ASSERT(length != 1 || *start != '*'); + + uri = cx->runtime->emptyString; + limit = start + length; + colon = js_strchr_limit(start, ':', limit); + if (colon) { + offset = PTRDIFF(colon, start, jschar); + prefix = js_NewDependentString(cx, str, 0, offset, 0); + if (!prefix) + return NULL; + + if (STARTS_WITH_XML(start, offset)) { + if (offset == 3) { + uri = JS_InternString(cx, xml_namespace_str); + if (!uri) + return NULL; + } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { + uri = JS_InternString(cx, xmlns_namespace_str); + if (!uri) + return NULL; + } else { + uri = NULL; + } + } else { + uri = NULL; + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); + if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) { + uri = ns->uri; + break; + } + } + } + + if (!uri) { + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix))); + return NULL; + } + + localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); + if (!localName) + return NULL; + } else { + if (isAttributeName) { + /* + * An unprefixed attribute is not in any namespace, so set prefix + * as well as uri to the empty string. + */ + prefix = uri; + } else { + /* + * Loop from back to front looking for the closest declared default + * namespace. + */ + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); + if (!ns->prefix || IS_EMPTY(ns->prefix)) { + uri = ns->uri; + break; + } + } + prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; + } + localName = str; + } + + return js_NewXMLQName(cx, uri, prefix, localName); +} + +static JSString * +ChompXMLWhitespace(JSContext *cx, JSString *str) +{ + size_t length, newlength, offset; + const jschar *cp, *start, *end; + jschar c; + + length = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (!JS_ISXMLSPACE(c)) + break; + } + while (end > cp) { + c = end[-1]; + if (!JS_ISXMLSPACE(c)) + break; + --end; + } + newlength = PTRDIFF(end, cp, jschar); + if (newlength == length) + return str; + offset = PTRDIFF(cp, start, jschar); + return js_NewDependentString(cx, str, offset, newlength, 0); +} + +static JSXML * +ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, + uintN flags) +{ + JSXML *xml, *kid, *attr, *attrj; + JSString *str; + uint32 length, n, i, j; + JSParseNode *pn2, *pn3, *head, **pnp; + JSXMLNamespace *ns; + JSXMLQName *qn, *attrjqn; + JSXMLClass xml_class; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, + JSMSG_OVER_RECURSED); + return NULL; + } + +#define PN2X_SKIP_CHILD ((JSXML *) 1) + + /* + * Cases return early to avoid common code that gets an outermost xml's + * object, which protects GC-things owned by xml and its descendants from + * garbage collection. + */ + xml = NULL; + if (!js_EnterLocalRootScope(cx)) + return NULL; + switch (pn->pn_type) { + case TOK_XMLELEM: + length = inScopeNSes->length; + pn2 = pn->pn_head; + xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (!xml) + goto fail; + + flags &= ~XSF_PRECOMPILED_ROOT; + n = pn->pn_count; + JS_ASSERT(n >= 2); + n -= 2; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + while ((pn2 = pn2->pn_next) != NULL) { + if (!pn2->pn_next) { + /* Don't append the end tag! */ + JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); + break; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + /* Store kid in xml right away, to protect it from GC. */ + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + kid->parent = xml; + ++i; + + /* XXX where is this documented in an XML spec, or in E4X? */ + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid->xml_value); + if (!str) + goto fail; + kid->xml_value = str; + } + } + + JS_ASSERT(i == n); + if (n < pn->pn_count - 2) + XMLArrayTrim(&xml->xml_kids); + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLLIST: + xml = js_NewXML(cx, JSXML_CLASS_LIST); + if (!xml) + goto fail; + + n = pn->pn_count; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* + * Always ignore insignificant whitespace in lists -- we shouldn't + * condition this on an XML.ignoreWhitespace setting when the list + * constructor is XMLList (note XML/XMLList unification hazard). + */ + if (pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + ++i; + } + + if (n < pn->pn_count) + XMLArrayTrim(&xml->xml_kids); + break; + + case TOK_XMLSTAGO: + case TOK_XMLPTAGC: + length = inScopeNSes->length; + pn2 = pn->pn_head; + JS_ASSERT(pn2->pn_type == TOK_XMLNAME); + if (pn2->pn_arity == PN_LIST) + goto syntax; + + xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); + if (!xml) + goto fail; + + /* First pass: check syntax and process namespace declarations. */ + JS_ASSERT(pn->pn_count >= 1); + n = pn->pn_count - 1; + pnp = &pn2->pn_next; + head = *pnp; + while ((pn2 = *pnp) != NULL) { + size_t length; + const jschar *chars; + + if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) + goto syntax; + + /* Enforce "Well-formedness constraint: Unique Att Spec". */ + for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { + if (pn3->pn_atom == pn2->pn_atom) { + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + str = ATOM_TO_STRING(pn2->pn_atom); + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + if (pn2->pn_type != TOK_XMLATTR) + goto syntax; + + length = JSSTRING_LENGTH(str); + chars = JSSTRING_CHARS(str); + if (length >= 5 && + IS_XMLNS_CHARS(chars) && + (length == 5 || chars[5] == ':')) { + JSString *uri, *prefix; + + uri = ATOM_TO_STRING(pn2->pn_atom); + if (length == 5) { + /* 10.3.2.1. Step 6(h)(i)(1)(a). */ + prefix = cx->runtime->emptyString; + } else { + prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); + if (!prefix) + goto fail; + } + + /* + * Once the new ns is appended to xml->xml_namespaces, it is + * protected from GC by the object that owns xml -- which is + * either xml->object if outermost, or the object owning xml's + * oldest ancestor if !outermost. + */ + ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); + if (!ns) + goto fail; + + /* + * Don't add a namespace that's already in scope. If someone + * extracts a child property from its parent via [[Get]], then + * we enforce the invariant, noted many times in ECMA-357, that + * the child's namespaces form a possibly-improper superset of + * its ancestors' namespaces. + */ + if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || + !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { + goto fail; + } + } + + JS_ASSERT(n >= 2); + n -= 2; + *pnp = pn2->pn_next; + /* XXXbe recycle pn2 */ + continue; + } + + pnp = &pn2->pn_next; + } + + /* + * If called from js_ParseNodeToXMLObject, emulate the effect of the + * ... wrapping done by "ToXML Applied to + * the String Type" (ECMA-357 10.3.1). + */ + if (flags & XSF_PRECOMPILED_ROOT) { + JS_ASSERT(length >= 1); + ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, + namespace_identity)); + ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); + if (!ns) + goto fail; + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + goto fail; + } + XMLArrayTrim(&xml->xml_namespaces); + + /* Second pass: process tag name and attributes, using namespaces. */ + pn2 = pn->pn_head; + qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + xml->name = qn; + + JS_ASSERT((n & 1) == 0); + n >>= 1; + if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) + goto fail; + + for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { + qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); + if (!qn) { + xml->xml_attrs.length = i; + goto fail; + } + + /* + * Enforce "Well-formedness constraint: Unique Att Spec", part 2: + * this time checking local name and namespace URI. + */ + for (j = 0; j < i; j++) { + attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); + attrjqn = attrj->name; + if (js_EqualStrings(attrjqn->uri, qn->uri) && + js_EqualStrings(attrjqn->localName, qn->localName)) { + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + JS_ASSERT(pn2->pn_type == TOK_XMLATTR); + + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); + attr->parent = xml; + attr->name = qn; + attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); + } + + /* Point tag closes its own namespace scope. */ + if (pn->pn_type == TOK_XMLPTAGC) + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: + str = ATOM_TO_STRING(pn->pn_atom); + qn = NULL; + if (pn->pn_type == TOK_XMLCOMMENT) { + if (flags & XSF_IGNORE_COMMENTS) + goto skip_child; + xml_class = JSXML_CLASS_COMMENT; + } else if (pn->pn_type == TOK_XMLPI) { + if (IS_XML(str)) { + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_RESERVED_ID, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(str))); + goto fail; + } + + if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) + goto skip_child; + + qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + + str = pn->pn_atom2 + ? ATOM_TO_STRING(pn->pn_atom2) + : cx->runtime->emptyString; + xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; + } else { + /* CDATA section content, or element text. */ + xml_class = JSXML_CLASS_TEXT; + } + + xml = js_NewXML(cx, xml_class); + if (!xml) + goto fail; + xml->name = qn; + if (pn->pn_type == TOK_XMLSPACE) + xml->xml_flags |= XMLF_WHITESPACE_TEXT; + xml->xml_value = str; + break; + + default: + goto syntax; + } + + js_LeaveLocalRootScopeWithResult(cx, (jsval) xml); + if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) + return NULL; + return xml; + +skip_child: + js_LeaveLocalRootScope(cx); + return PN2X_SKIP_CHILD; + +#undef PN2X_SKIP_CHILD + +syntax: + js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); +fail: + js_LeaveLocalRootScope(cx); + return NULL; +} + +/* + * XML helper, object-ops, and library functions. We start with the helpers, + * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. + */ +static JSBool +GetXMLSetting(JSContext *cx, const char *name, jsval *vp) +{ + jsval v; + + if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v)) + return JS_FALSE; + if (!VALUE_IS_FUNCTION(cx, v)) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); +} + +static JSBool +FillSettingsCache(JSContext *cx) +{ + int i; + const char *name; + jsval v; + JSBool isSet; + + /* Note: XML_PRETTY_INDENT is not a boolean setting. */ + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet)) + return JS_FALSE; + if (isSet) + cx->xmlSettingFlags |= JS_BIT(i); + else + cx->xmlSettingFlags &= ~JS_BIT(i); + } + + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return JS_TRUE; +} + +static JSBool +GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) +{ + int i; + + if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) + return JS_FALSE; + + for (i = 0; xml_static_props[i].name; i++) { + if (!strcmp(xml_static_props[i].name, name)) { + *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; + return JS_TRUE; + } + } + *bp = JS_FALSE; + return JS_TRUE; +} + +static JSBool +GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) +{ + jsval v; + + return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip); +} + +static JSBool +GetXMLSettingFlags(JSContext *cx, uintN *flagsp) +{ + JSBool flag; + + /* Just get the first flag to validate the setting flags cache. */ + if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) + return JS_FALSE; + *flagsp = cx->xmlSettingFlags; + return JS_TRUE; +} + +static JSXML * +ParseXMLSource(JSContext *cx, JSString *src) +{ + jsval nsval; + JSXMLNamespace *ns; + size_t urilen, srclen, length, offset, dstlen; + jschar *chars; + const jschar *srcp, *endp; + void *mark; + JSTokenStream *ts; + uintN lineno; + JSStackFrame *fp; + JSOp op; + JSParseNode *pn; + JSXML *xml; + JSXMLArray nsarray; + uintN flags; + + static const char prefix[] = ""; + static const char suffix[] = ""; + +#define constrlen(constr) (sizeof(constr) - 1) + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + + urilen = JSSTRING_LENGTH(ns->uri); + srclen = JSSTRING_LENGTH(src); + length = constrlen(prefix) + urilen + constrlen(middle) + srclen + + constrlen(suffix); + + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return NULL; + + dstlen = length; + js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); + offset = dstlen; + js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); + offset += urilen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, + &dstlen); + offset += dstlen; + srcp = JSSTRING_CHARS(src); + js_strncpy(chars + offset, srcp, srclen); + offset += srclen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, + &dstlen); + chars [offset + dstlen] = 0; + + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewBufferTokenStream(cx, chars, length); + if (!ts) + return NULL; + for (fp = cx->fp; fp && !fp->pc; fp = fp->down) + continue; + if (fp) { + op = (JSOp) *fp->pc; + if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { + ts->filename = fp->script->filename; + lineno = js_PCToLineNumber(cx, fp->script, fp->pc); + for (endp = srcp + srclen; srcp < endp; srcp++) + if (*srcp == '\n') + --lineno; + ts->lineno = lineno; + } + } + + JS_KEEP_ATOMS(cx->runtime); + pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); + xml = NULL; + if (pn && XMLArrayInit(cx, &nsarray, 1)) { + if (GetXMLSettingFlags(cx, &flags)) + xml = ParseNodeToXML(cx, pn, &nsarray, flags); + + XMLArrayFinish(cx, &nsarray); + } + JS_UNKEEP_ATOMS(cx->runtime); + + JS_ARENA_RELEASE(&cx->tempPool, mark); + JS_free(cx, chars); + return xml; + +#undef constrlen +} + +/* + * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). + * + * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce + * the constraint: + * + * for all x belonging to XML: + * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] + * + * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here + * (in new sub-step 6(a), renumbering the others to (b) and (c)). + * + * Same goes for 10.4.1 Step 7(a). + * + * In order for XML.prototype.namespaceDeclarations() to work correctly, the + * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be + * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such + * undeclared namespaces associated with x not belonging to ancestorNS. + */ +static JSXML * +OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) +{ + JSXMLNamespace *ns; + + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace); + xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!ns || !xml) + return xml; + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return NULL; + ns->declared = JS_FALSE; + } + xml->parent = NULL; + return xml; +} + +static JSObject * +ToXML(JSContext *cx, jsval v) +{ + JSObject *obj; + JSXML *xml; + JSClass *clasp; + JSString *str; + uint32 length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length != 1) + goto bad; + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + return js_GetXMLObject(cx, xml); + } + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + length = 0; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + xml = NULL; +#endif + } else { + xml = ParseXMLSource(cx, str); + if (!xml) + return NULL; + length = JSXML_LENGTH(xml); + } + + if (length == 0) { + obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); + if (!obj) + return NULL; + } else if (length == 1) { + xml = OrphanXMLChild(cx, xml, 0); + if (!xml) + return NULL; + obj = js_GetXMLObject(cx, xml); + if (!obj) + return NULL; + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); + return NULL; + } + return obj; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *kid); + +static JSObject * +ToXMLList(JSContext *cx, jsval v) +{ + JSObject *obj, *listobj; + JSXML *xml, *list, *kid; + JSClass *clasp; + JSString *str; + uint32 i, length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return NULL; + return listobj; + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + xml = NULL; + length = 0; + } else { + if (!js_EnterLocalRootScope(cx)) + return NULL; + xml = ParseXMLSource(cx, str); + if (!xml) { + js_LeaveLocalRootScope(cx); + return NULL; + } + length = JSXML_LENGTH(xml); + } + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + list = (JSXML *) JS_GetPrivate(cx, listobj); + for (i = 0; i < length; i++) { + kid = OrphanXMLChild(cx, xml, i); + if (!kid || !Append(cx, list, kid)) { + listobj = NULL; + break; + } + } + } + + if (xml) + js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); + return listobj; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +/* + * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString + * and their library-public js_* counterparts. The guts of MakeXMLCDataString, + * MakeXMLCommentString, and MakeXMLPIString are further factored into a common + * MakeXMLSpecialString subroutine. + * + * These functions take ownership of sb->base, if sb is non-null, in all cases + * of success or failure. + */ +static JSString * +MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb, + JSString *str, JSString *str2, + const jschar *prefix, size_t prefixlength, + const jschar *suffix, size_t suffixlength) +{ + JSStringBuffer localSB; + size_t length, length2, newlength; + jschar *bp, *base; + + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + + length = JSSTRING_LENGTH(str); + length2 = str2 ? JSSTRING_LENGTH(str2) : 0; + newlength = STRING_BUFFER_OFFSET(sb) + + prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) + + suffixlength; + bp = base = (jschar *) + JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar)); + if (!bp) { + js_FinishStringBuffer(sb); + return NULL; + } + + bp += STRING_BUFFER_OFFSET(sb); + js_strncpy(bp, prefix, prefixlength); + bp += prefixlength; + js_strncpy(bp, JSSTRING_CHARS(str), length); + bp += length; + if (length2 != 0) { + *bp++ = (jschar) ' '; + js_strncpy(bp, JSSTRING_CHARS(str2), length2); + bp += length2; + } + js_strncpy(bp, suffix, suffixlength); + bp[suffixlength] = 0; + + str = js_NewString(cx, base, newlength, 0); + if (!str) + free(base); + return str; +} + +static JSString * +MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', + 'C', 'D', 'A', 'T', 'A', + '['}; + static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; + + return MakeXMLSpecialString(cx, sb, str, NULL, + cdata_prefix_ucNstr, 9, + cdata_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; + static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; + + return MakeXMLSpecialString(cx, sb, str, NULL, + comment_prefix_ucNstr, 4, + comment_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name, + JSString *value) +{ + static const jschar pi_prefix_ucNstr[] = {'<', '?'}; + static const jschar pi_suffix_ucNstr[] = {'?', '>'}; + + return MakeXMLSpecialString(cx, sb, name, value, + pi_prefix_ucNstr, 2, + pi_suffix_ucNstr, 2); +} + +/* + * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends + * equals, a double quote, an attribute value, and a closing double quote. + */ +static void +AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr) +{ + js_AppendCString(sb, "=\""); + valstr = js_EscapeAttributeValue(cx, valstr); + if (!valstr) { + free(sb->base); + sb->base = STRING_BUFFER_ERROR_BASE; + return; + } + js_AppendJSString(sb, valstr); + js_AppendChar(sb, '"'); +} + +/* + * ECMA-357 10.2.1.1 EscapeElementValue helper method. + * + * This function takes ownership of sb->base, if sb is non-null, in all cases + * of success or failure. + */ +static JSString * +EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '<' || c == '>') + newlength += 3; + else if (c == '&') + newlength += 4; + + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '>') + js_AppendCString(sb, js_gt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +/* + * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. + * This function takes ownership of sb->base, if sb is non-null, in all cases. + */ +static JSString * +EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '"') + newlength += 5; + else if (c == '<') + newlength += 3; + else if (c == '&' || c == '\n' || c == '\r' || c == '\t') + newlength += 4; + + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '"') + js_AppendCString(sb, js_quot_entity_str); + else if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else if (c == '\n') + js_AppendCString(sb, " "); + else if (c == '\r') + js_AppendCString(sb, " "); + else if (c == '\t') + js_AppendCString(sb, " "); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ +static JSXMLNamespace * +GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) +{ + JSXMLNamespace *match, *ns; + uint32 i, n; + jsval argv[2]; + JSObject *nsobj; + + JS_ASSERT(qn->uri); + if (!qn->uri) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + qn->prefix + ? js_ValueToPrintableString(cx, + STRING_TO_JSVAL(qn->prefix)) + : js_type_strs[JSTYPE_VOID]); + return NULL; + } + + /* Look for a matching namespace in inScopeNSes, if provided. */ + match = NULL; + if (inScopeNSes) { + for (i = 0, n = inScopeNSes->length; i < n; i++) { + ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); + if (!ns) + continue; + + /* + * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: + * If we preserve prefixes, we must match null qn->prefix against + * an empty ns->prefix, in order to avoid generating redundant + * prefixed and default namespaces for cases such as: + * + * x = + * print(x.toXMLString()); + * + * Per 10.3.2.1, the namespace attribute in t has an empty string + * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): + * + * 1. If the [local name] property of a is "xmlns" + * a. Map ns.prefix to the empty string + * + * But t's name has a null prefix in this implementation, meaning + * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to + * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without + * saying how "no value" maps to an ECMA-357 value -- but it must + * map to the *undefined* prefix value). + * + * Since "" != undefined (or null, in the current implementation) + * the ECMA-357 spec will fail to match in [[GetNamespace]] called + * on t with argument {} U {(prefix="", uri="http://foo.com")}. + * This spec bug leads to ToXMLString results that duplicate the + * declared namespace. + */ + if (js_EqualStrings(ns->uri, qn->uri) && + (ns->prefix == qn->prefix || + ((ns->prefix && qn->prefix) + ? js_EqualStrings(ns->prefix, qn->prefix) + : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { + match = ns; + break; + } + } + } + + /* If we didn't match, make a new namespace from qn. */ + if (!match) { + argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID; + argv[1] = STRING_TO_JSVAL(qn->uri); + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, + 2, argv); + if (!nsobj) + return NULL; + match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + } + return match; +} + +static JSString * +GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) +{ + const jschar *cp, *start, *end; + size_t length, newlength, offset; + uint32 i, n, m, serial; + jschar *bp, *dp; + JSBool done; + JSXMLNamespace *ns; + JSString *prefix; + + JS_ASSERT(!IS_EMPTY(uri)); + + /* + * If there are no *declared* namespaces, skip all collision detection and + * return a short prefix quickly; an example of such a situation: + * + * var x = ; + * var n = new Namespace("http://example.com/"); + * x.@n::att = "val"; + * x.toXMLString(); + * + * This is necessary for various log10 uses below to be valid. + */ + if (decls->length == 0) + return JS_NewStringCopyZ(cx, "a"); + + /* + * Try peeling off the last filename suffix or pathname component till + * we have a valid XML name. This heuristic will prefer "xul" given + * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any + * likely URI of the form ".../xbl2/2005". + */ + start = JSSTRING_CHARS(uri); + cp = end = start + JSSTRING_LENGTH(uri); + while (--cp > start) { + if (*cp == '.' || *cp == '/' || *cp == ':') { + ++cp; + length = PTRDIFF(end, cp, jschar); + if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length)) + break; + end = --cp; + } + } + length = PTRDIFF(end, cp, jschar); + + /* + * If the namespace consisted only of non-XML names or names that begin + * case-insensitively with "xml", arbitrarily create a prefix consisting + * of 'a's of size length (allowing dp-calculating code to work with or + * without this branch executing) plus the space for storing a hyphen and + * the serial number (avoiding reallocation if a collision happens). + */ + bp = (jschar *) cp; + newlength = length; + if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) { + newlength = length + 2 + (size_t) log10(decls->length); + bp = (jschar *) + JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + + bp[newlength] = 0; + for (i = 0; i < newlength; i++) + bp[i] = 'a'; + } + + /* + * Now search through decls looking for a collision. If we collide with + * an existing prefix, start tacking on a hyphen and a serial number. + */ + serial = 0; + do { + done = JS_TRUE; + for (i = 0, n = decls->length; i < n; i++) { + ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); + if (ns && ns->prefix && + JSSTRING_LENGTH(ns->prefix) == newlength && + !memcmp(JSSTRING_CHARS(ns->prefix), bp, + newlength * sizeof(jschar))) { + if (bp == cp) { + newlength = length + 2 + (size_t) log10(n); + bp = (jschar *) + JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + js_strncpy(bp, cp, length); + } + + ++serial; + JS_ASSERT(serial <= n); + dp = bp + length + 2 + (size_t) log10(serial); + *dp = 0; + for (m = serial; m != 0; m /= 10) + *--dp = (jschar)('0' + m % 10); + *--dp = '-'; + JS_ASSERT(dp == bp + length); + + done = JS_FALSE; + break; + } + } + } while (!done); + + if (bp == cp) { + offset = PTRDIFF(cp, start, jschar); + prefix = js_NewDependentString(cx, uri, offset, length, 0); + } else { + prefix = js_NewString(cx, bp, newlength, 0); + if (!prefix) + JS_free(cx, bp); + } + return prefix; +} + +static JSBool +namespace_match(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsb->prefix) + return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix); + return js_EqualStrings(nsa->uri, nsb->uri); +} + +/* ECMA-357 10.2.1 and 10.2.2 */ +static JSString * +XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, + uintN indentLevel) +{ + JSBool pretty, indentKids; + JSStringBuffer sb; + JSString *str, *prefix, *kidstr; + JSXMLArrayCursor cursor; + uint32 i, n; + JSXMLArray empty, decls, ancdecls; + JSXMLNamespace *ns, *ns2; + uintN nextIndentLevel; + JSXML *attr, *kid; + + if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) + return NULL; + + js_InitStringBuffer(&sb); + if (pretty) + js_RepeatChar(&sb, ' ', indentLevel); + str = NULL; + + switch (xml->xml_class) { + case JSXML_CLASS_TEXT: + /* Step 4. */ + if (pretty) { + str = ChompXMLWhitespace(cx, xml->xml_value); + if (!str) + return NULL; + } else { + str = xml->xml_value; + } + return EscapeElementValue(cx, &sb, str); + + case JSXML_CLASS_ATTRIBUTE: + /* Step 5. */ + return EscapeAttributeValue(cx, &sb, xml->xml_value); + + case JSXML_CLASS_COMMENT: + /* Step 6. */ + return MakeXMLCommentString(cx, &sb, xml->xml_value); + + case JSXML_CLASS_PROCESSING_INSTRUCTION: + /* Step 7. */ + return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value); + + case JSXML_CLASS_LIST: + /* ECMA-357 10.2.2. */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + i = 0; + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (pretty && i != 0) + js_AppendChar(&sb, '\n'); + + kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); + if (!kidstr) + break; + + js_AppendJSString(&sb, kidstr); + ++i; + } + XMLArrayCursorFinish(&cursor); + if (kid) + goto list_out; + + if (!sb.base) { + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + return cx->runtime->emptyString; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); + list_out: + if (!str) + js_FinishStringBuffer(&sb); + return str; + + default:; + } + + /* After this point, control must flow through label out: to exit. */ + if (!js_EnterLocalRootScope(cx)) + return NULL; + + /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ + if (!ancestorNSes) { + XMLArrayInit(cx, &empty, 0); + ancestorNSes = ∅ + } + XMLArrayInit(cx, &decls, 0); + ancdecls.capacity = 0; + + /* Clone in-scope namespaces not in ancestorNSes into decls. */ + XMLArrayCursorInit(&cursor, &xml->xml_namespaces); + while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { + if (!ns->declared) + continue; + if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { + /* NOTE: may want to exclude unused namespaces here. */ + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE); + if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) + break; + } + } + XMLArrayCursorFinish(&cursor); + if (ns) + goto out; + + /* + * Union ancestorNSes and decls into ancdecls. Note that ancdecls does + * not own its member references. In the spec, ancdecls has no name, but + * is always written out as (AncestorNamespaces U namespaceDeclarations). + */ + if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) + goto out; + for (i = 0, n = ancestorNSes->length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + for (i = 0, n = decls.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + + /* Step 11, except we don't clone ns unless its prefix is undefined. */ + ns = GetNamespace(cx, xml->name, &ancdecls); + if (!ns) + goto out; + + /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ + if (!ns->prefix) { + /* + * Create a namespace prefix that isn't used by any member of decls. + * Assign the new prefix to a copy of ns. Flag this namespace as if + * it were declared, for assertion-testing's sake later below. + * + * Erratum: if ns->prefix and xml->name are both null (*undefined* in + * ECMA-357), we know that xml was named using the default namespace + * (proof: see GetNamespace and the Namespace constructor called with + * two arguments). So we ought not generate a new prefix here, when + * we can declare ns as the default namespace for xml. + * + * This helps descendants inherit the namespace instead of redundantly + * redeclaring it with generated prefixes in each descendant. + */ + if (!xml->name->prefix) { + prefix = cx->runtime->emptyString; + } else { + prefix = GeneratePrefix(cx, ns->uri, &ancdecls); + if (!prefix) + goto out; + } + ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE); + if (!ns) + goto out; + + /* + * If the xml->name was unprefixed, we must remove any declared default + * namespace from decls before appending ns. How can you get a default + * namespace in decls that doesn't match the one from name? Apparently + * by calling x.setNamespace(ns) where ns has no prefix. The other way + * to fix this is to update x's in-scope namespaces when setNamespace + * is called, but that's not specified by ECMA-357. + * + * Likely Erratum here, depending on whether the lack of update to x's + * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an + * erratum or not. Note that changing setNamespace to update the list + * of in-scope namespaces will change x.namespaceDeclarations(). + */ + if (IS_EMPTY(prefix)) { + i = XMLArrayFindMember(&decls, ns, namespace_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &decls, i, JS_TRUE); + } + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || + !XMLARRAY_APPEND(cx, &decls, ns)) { + goto out; + } + } + + /* Format the element or point-tag into sb. */ + js_AppendChar(&sb, '<'); + + if (ns->prefix && !IS_EMPTY(ns->prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + js_AppendJSString(&sb, xml->name->localName); + + /* + * Step 16 makes a union to avoid writing two loops in step 17, to share + * common attribute value appending spec-code. We prefer two loops for + * faster code and less data overhead. + */ + + /* Step 17(b): append attributes. */ + XMLArrayCursorInit(&cursor, &xml->xml_attrs); + while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + js_AppendChar(&sb, ' '); + ns2 = GetNamespace(cx, attr->name, &ancdecls); + if (!ns2) + break; + + /* 17(b)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + break; + + /* Again, we avoid copying ns2 until we know it's prefix-less. */ + ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE); + if (!ns2) + break; + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || + !XMLARRAY_APPEND(cx, &decls, ns2)) { + break; + } + } + + /* 17(b)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendJSString(&sb, ns2->prefix); + js_AppendChar(&sb, ':'); + } + + /* 17(b)(iv). */ + js_AppendJSString(&sb, attr->name->localName); + + /* 17(d-g). */ + AppendAttributeValue(cx, &sb, attr->xml_value); + } + XMLArrayCursorFinish(&cursor); + if (attr) + goto out; + + /* Step 17(c): append XML namespace declarations. */ + XMLArrayCursorInit(&cursor, &decls); + while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { + JS_ASSERT(ns2->declared); + + js_AppendCString(&sb, " xmlns"); + + /* 17(c)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + break; + ns2->prefix = prefix; + } + + /* 17(c)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendChar(&sb, ':'); + js_AppendJSString(&sb, ns2->prefix); + } + + /* 17(d-g). */ + AppendAttributeValue(cx, &sb, ns2->uri); + } + XMLArrayCursorFinish(&cursor); + if (ns2) + goto out; + + /* Step 18: handle point tags. */ + n = xml->xml_kids.length; + if (n == 0) { + js_AppendCString(&sb, "/>"); + } else { + /* Steps 19 through 25: handle element content, and open the end-tag. */ + js_AppendChar(&sb, '>'); + indentKids = n > 1 || + (n == 1 && + (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && + kid->xml_class != JSXML_CLASS_TEXT); + + if (pretty && indentKids) { + if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) + goto out; + nextIndentLevel = indentLevel + i; + } else { + nextIndentLevel = 0; + } + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (pretty && indentKids) + js_AppendChar(&sb, '\n'); + + kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); + if (!kidstr) + break; + + js_AppendJSString(&sb, kidstr); + } + XMLArrayCursorFinish(&cursor); + if (kid) + goto out; + + if (pretty && indentKids) { + js_AppendChar(&sb, '\n'); + js_RepeatChar(&sb, ' ', indentLevel); + } + js_AppendCString(&sb, "prefix && !IS_EMPTY(ns->prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + + /* Step 27. */ + js_AppendJSString(&sb, xml->name->localName); + js_AppendChar(&sb, '>'); + } + + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + goto out; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); +out: + js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + if (!str && STRING_BUFFER_OK(&sb)) + js_FinishStringBuffer(&sb); + XMLArrayFinish(cx, &decls); + if (ancdecls.capacity != 0) + XMLArrayFinish(cx, &ancdecls); + return str; +} + +/* ECMA-357 10.2 */ +static JSString * +ToXMLString(JSContext *cx, jsval v) +{ + JSObject *obj; + JSString *str; + JSXML *xml; + + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + js_type_strs[JSVAL_IS_NULL(v) + ? JSTYPE_NULL + : JSTYPE_VOID]); + return NULL; + } + + if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) + return js_ValueToString(cx, v); + + if (JSVAL_IS_STRING(v)) + return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); + + obj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, obj)) { + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) + return NULL; + str = js_ValueToString(cx, v); + if (!str) + return NULL; + return EscapeElementValue(cx, NULL, str); + } + + /* Handle non-element cases in this switch, returning from each case. */ + xml = (JSXML *) JS_GetPrivate(cx, obj); + return XMLToXMLString(cx, xml, NULL, 0); +} + +static JSXMLQName * +ToAttributeName(JSContext *cx, jsval v) +{ + JSString *name, *uri, *prefix; + JSObject *obj; + JSClass *clasp; + JSXMLQName *qn; + JSTempValueRooter tvr; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + uri = prefix = cx->runtime->emptyString; + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_ATTR_NAME, + JS_GetStringBytes(name)); + } + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass) + return (JSXMLQName *) JS_GetPrivate(cx, obj); + + if (clasp == &js_QNameClass.base) { + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + } else { + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + } else { + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + uri = prefix = cx->runtime->emptyString; + } + } + + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return NULL; + + JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr); + obj = js_GetAttributeNameObject(cx, qn); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!obj) + return NULL; + return qn; +} + +static JSXMLQName * +ToXMLName(JSContext *cx, jsval v, jsid *funidp) +{ + JSString *name; + JSObject *obj; + JSClass *clasp; + uint32 index; + JSXMLQName *qn; + JSAtom *atom; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) + goto bad; + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) + goto out; + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + goto construct; + } + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + + /* + * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: + * + * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception + * + * First, _P_ should be _s_, to refer to the given string. + * + * Second, why does ToXMLName applied to the string type throw TypeError + * only for numeric literals without any leading or trailing whitespace? + * + * If the idea is to reject uint32 property names, then the check needs to + * be stricter, to exclude hexadecimal and floating point literals. + */ + if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) + goto bad; + + if (*JSSTRING_CHARS(name) == '@') { + name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); + if (!name) + return NULL; + *funidp = 0; + return ToAttributeName(cx, STRING_TO_JSVAL(name)); + } + +construct: + v = STRING_TO_JSVAL(name); + obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); + if (!obj) + return NULL; + +out: + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; + if (qn->uri && atom && + (qn->uri == ATOM_TO_STRING(atom) || + js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) { + if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp)) + return NULL; + } else { + *funidp = 0; + } + return qn; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); + return NULL; +} + +/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ +static JSBool +AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) +{ + JSXMLNamespace *match, *ns2; + uint32 i, n, m; + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ + if (!ns->prefix) { + match = NULL; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) { + match = ns2; + break; + } + } + if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) + return JS_FALSE; + } else { + if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) + return JS_TRUE; + match = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + m = XML_NOT_FOUND; +#endif + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns2 && ns2->prefix && + js_EqualStrings(ns2->prefix, ns->prefix)) { + match = ns2; + m = i; + break; + } + } + if (match && !js_EqualStrings(match->uri, ns->uri)) { + ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, + JSXMLNamespace); + JS_ASSERT(ns2 == match); + match->prefix = NULL; + if (!AddInScopeNamespace(cx, xml, match)) + return JS_FALSE; + } + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return JS_FALSE; + } + + /* OPTION: enforce that descendants have superset namespaces. */ + return JS_TRUE; +} + +/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *xml) +{ + uint32 i, j, k, n; + JSXML *kid; + + JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); + i = list->xml_kids.length; + n = 1; + if (xml->xml_class == JSXML_CLASS_LIST) { + list->xml_target = xml->xml_target; + list->xml_targetprop = xml->xml_targetprop; + n = JSXML_LENGTH(xml); + k = i + n; + if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) + return JS_FALSE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); + if (kid) + XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); + } + return JS_TRUE; + } + + list->xml_target = xml->parent; + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + list->xml_targetprop = NULL; + else + list->xml_targetprop = xml->name; + if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) + return JS_FALSE; + return JS_TRUE; +} + +/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); + +static JSXML * +DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) +{ + JSXML *copy; + JSBool ok; + + /* Our caller may not be protecting newborns with a local root scope. */ + if (!js_EnterLocalRootScope(cx)) + return NULL; + copy = DeepCopyInLRS(cx, xml, flags); + if (copy) { + if (obj) { + /* Caller provided the object for this copy, hook 'em up. */ + ok = JS_SetPrivate(cx, obj, copy); + if (ok) + copy->object = obj; + } else { + ok = js_GetXMLObject(cx, copy) != NULL; + } + if (!ok) + copy = NULL; + } + js_LeaveLocalRootScopeWithResult(cx, (jsval) copy); + return copy; +} + +/* + * (i) We must be in a local root scope (InLRS). + * (ii) parent must have a rooted object. + * (iii) from's owning object must be locked if not thread-local. + */ +static JSBool +DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, + uintN flags) +{ + uint32 j, n; + JSXMLArrayCursor cursor; + JSBool ok; + JSXML *kid, *kid2; + JSString *str; + + JS_ASSERT(cx->localRootStack); + + n = from->length; + if (!XMLArraySetCapacity(cx, to, n)) + return JS_FALSE; + + XMLArrayCursorInit(&cursor, from); + j = 0; + ok = JS_TRUE; + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if ((flags & XSF_IGNORE_COMMENTS) && + kid->xml_class == JSXML_CLASS_COMMENT) { + continue; + } + if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && + kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { + continue; + } + if ((flags & XSF_IGNORE_WHITESPACE) && + (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { + continue; + } + kid2 = DeepCopyInLRS(cx, kid, flags); + if (!kid2) { + to->length = j; + ok = JS_FALSE; + break; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid2->xml_value); + if (!str) { + to->length = j; + ok = JS_FALSE; + break; + } + kid2->xml_value = str; + } + + XMLARRAY_SET_MEMBER(to, j, kid2); + ++j; + if (parent->xml_class != JSXML_CLASS_LIST) + kid2->parent = parent; + } + XMLArrayCursorFinish(&cursor); + if (!ok) + return JS_FALSE; + + if (j < n) + XMLArrayTrim(to); + return JS_TRUE; +} + +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) +{ + JSXML *copy; + JSXMLQName *qn; + JSBool ok; + uint32 i, n; + JSXMLNamespace *ns, *ns2; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + copy = js_NewXML(cx, xml->xml_class); + if (!copy) + return NULL; + qn = xml->name; + if (qn) { + qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); + if (!qn) { + ok = JS_FALSE; + goto out; + } + } + copy->name = qn; + copy->xml_flags = xml->xml_flags; + + if (JSXML_HAS_VALUE(xml)) { + copy->xml_value = xml->xml_value; + ok = JS_TRUE; + } else { + ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); + if (!ok) + goto out; + + if (xml->xml_class == JSXML_CLASS_LIST) { + copy->xml_target = xml->xml_target; + copy->xml_targetprop = xml->xml_targetprop; + } else { + n = xml->xml_namespaces.length; + ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); + if (!ok) + goto out; + for (i = 0; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared); + if (!ns2) { + copy->xml_namespaces.length = i; + ok = JS_FALSE; + goto out; + } + XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); + } + + ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, + 0); + if (!ok) + goto out; + } + } + +out: + if (!ok) + return NULL; + return copy; +} + +static void +ReportBadXMLName(JSContext *cx, jsval id) +{ + JSString *name; + + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + JS_GetStringBytes(name)); + } +} + +/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ +static JSBool +DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) +{ + uint32 index; + JSXML *kid; + + if (!js_IdIsIndex(id, &index)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid) + kid->parent = NULL; + XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); + } + + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml); + +static JSBool +MatchAttrName(JSXMLQName *nameqn, JSXML *attr) +{ + JSXMLQName *attrqn = attr->name; + + return (IS_STAR(nameqn->localName) || + js_EqualStrings(attrqn->localName, nameqn->localName)) && + (!nameqn->uri || + js_EqualStrings(attrqn->uri, nameqn->uri)); +} + +static JSBool +MatchElemName(JSXMLQName *nameqn, JSXML *elem) +{ + return (IS_STAR(nameqn->localName) || + (elem->xml_class == JSXML_CLASS_ELEMENT && + js_EqualStrings(elem->name->localName, nameqn->localName))) && + (!nameqn->uri || + (elem->xml_class == JSXML_CLASS_ELEMENT && + js_EqualStrings(elem->name->uri, nameqn->uri))); +} + +/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ +static JSBool +DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) +{ + uint32 i, n; + JSXML *attr, *kid; + + if (xml->xml_class == JSXML_CLASS_ELEMENT && + OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) { + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (attr && MatchAttrName(nameqn, attr)) { + if (!Append(cx, list, attr)) + return JS_FALSE; + } + } + } + + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass && + MatchElemName(nameqn, kid)) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + if (!DescendantsHelper(cx, kid, nameqn, list)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSXML * +Descendants(JSContext *cx, JSXML *xml, jsval id) +{ + jsid funid; + JSXMLQName *nameqn; + JSObject *listobj; + JSXML *list, *kid; + uint32 i, n; + JSBool ok; + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return NULL; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (funid) + return list; + + /* + * Protect nameqn's object and strings from GC by linking list to it + * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, + * which protects list. Any other object allocations occuring beneath + * DescendantsHelper use local roots. + */ + list->name = nameqn; + if (!js_EnterLocalRootScope(cx)) + return NULL; + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = DescendantsHelper(cx, kid, nameqn, list); + if (!ok) + break; + } + } + } else { + ok = DescendantsHelper(cx, xml, nameqn, list); + } + js_LeaveLocalRootScopeWithResult(cx, (jsval) list); + if (!ok) + return NULL; + list->name = NULL; + return list; +} + +static JSBool +xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +/* Recursive (JSXML *) parameterized version of Equals. */ +static JSBool +XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) +{ + JSXMLQName *qn, *vqn; + uint32 i, j, n; + JSXMLArrayCursor cursor, vcursor; + JSXML *kid, *vkid, *attr, *vattr; + JSBool ok; + JSObject *xobj, *vobj; + +retry: + if (xml->xml_class != vxml->xml_class) { + if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) + goto retry; + } + if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); + if (vxml) + goto retry; + } + *bp = JS_FALSE; + return JS_TRUE; + } + + qn = xml->name; + vqn = vxml->name; + if (qn) { + *bp = vqn && + js_EqualStrings(qn->localName, vqn->localName) && + js_EqualStrings(qn->uri, vqn->uri); + } else { + *bp = vqn == NULL; + } + if (!*bp) + return JS_TRUE; + + if (JSXML_HAS_VALUE(xml)) { + *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); + } else if (xml->xml_kids.length != vxml->xml_kids.length) { + *bp = JS_FALSE; + } else { + XMLArrayCursorInit(&cursor, &xml->xml_kids); + XMLArrayCursorInit(&vcursor, &vxml->xml_kids); + for (;;) { + kid = (JSXML *) XMLArrayCursorNext(&cursor); + vkid = (JSXML *) XMLArrayCursorNext(&vcursor); + if (!kid || !vkid) { + *bp = !kid && !vkid; + ok = JS_TRUE; + break; + } + xobj = js_GetXMLObject(cx, kid); + vobj = js_GetXMLObject(cx, vkid); + ok = xobj && vobj && + xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp); + if (!ok || !*bp) + break; + } + XMLArrayCursorFinish(&vcursor); + XMLArrayCursorFinish(&cursor); + if (!ok) + return JS_FALSE; + + if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { + n = xml->xml_attrs.length; + if (n != vxml->xml_attrs.length) + *bp = JS_FALSE; + for (i = 0; *bp && i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); + if (j == XML_NOT_FOUND) { + *bp = JS_FALSE; + break; + } + vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); + if (!vattr) + continue; + *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); + } + } + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ +static JSBool +Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) +{ + JSObject *vobj; + JSXML *vxml; + + if (JSVAL_IS_PRIMITIVE(v)) { + *bp = JS_FALSE; + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!vxml) + return JS_TRUE; + vobj = js_GetXMLObject(cx, vxml); + if (!vobj) + return JS_FALSE; + return js_XMLObjectOps.equality(cx, vobj, v, bp); + } + if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) + *bp = JS_TRUE; + } + } else { + vobj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, vobj)) { + *bp = JS_FALSE; + } else { + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + if (!XMLEquals(cx, xml, vxml, bp)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) +{ + JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); + + do { + if (xml == kid) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CYCLIC_VALUE, js_XML_str); + return JS_FALSE; + } + } while ((xml = xml->parent) != NULL); + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.11 XML [[Insert]]. */ +static JSBool +Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) +{ + uint32 j, n; + JSXML *vxml, *kid; + JSObject *vobj; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + n = 1; + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + if (vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) + return JS_TRUE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + if (!CheckCycle(cx, xml, kid)) + return JS_FALSE; + } + } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + } + } + } + if (!vxml) { + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + } + + if (i > xml->xml_kids.length) + i = xml->xml_kids.length; + + if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) + return JS_FALSE; + + if (vxml->xml_class == JSXML_CLASS_LIST) { + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + kid->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); + + /* OPTION: enforce that descendants have superset namespaces. */ + } + } else { + vxml->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + return JS_TRUE; +} + +static JSBool +IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) +{ + JSString *str; + + if (index <= JSVAL_INT_MAX) { + *idvp = INT_TO_JSVAL(index); + } else { + str = js_NumberToString(cx, (jsdouble) index); + if (!str) + return JS_FALSE; + *idvp = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +/* ECMA-357 9.1.1.12 XML [[Replace]]. */ +static JSBool +Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) +{ + uint32 i, n; + JSXML *vxml, *kid; + JSObject *vobj; + jsval junk; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (!js_IdIsIndex(id, &i)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + /* + * 9.1.1.12 + * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. + * It should therefore constrain callers to pass in _i <= x.[[Length]]_. + */ + n = xml->xml_kids.length; + if (i >= n) { + if (!IndexToIdVal(cx, n, &id)) + return JS_FALSE; + i = n; + } + + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { + case JSXML_CLASS_ELEMENT: + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + goto do_replace; + + case JSXML_CLASS_LIST: + if (i < n && !DeleteByIndex(cx, xml, id, &junk)) + return JS_FALSE; + if (!Insert(cx, xml, i, v)) + return JS_FALSE; + break; + + default: + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + + do_replace: + vxml->parent = xml; + if (i < n) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid) + kid->parent = NULL; + } + if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) + return JS_FALSE; + break; + } + + return JS_TRUE; +} + +/* Forward declared -- its implementation uses other statics that call it. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result); + +/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ +static JSBool +DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *kid, *parent; + JSBool isIndex; + JSXMLArray *array; + uint32 length, index, kidIndex, deleteCount; + JSXMLQName *nameqn; + jsid funid; + JSObject *nameobj, *kidobj; + JSXMLNameMatcher matcher; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + isIndex = js_IdIsIndex(id, &index); + if (JSXML_HAS_KIDS(xml)) { + array = &xml->xml_kids; + length = array->length; + } else { + array = NULL; + length = 0; + } + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.3. */ + if (isIndex && index < length) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (!kid) + goto out; + parent = kid->parent; + if (parent) { + JS_ASSERT(parent != xml); + JS_ASSERT(JSXML_HAS_KIDS(parent)); + + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameqn = kid->name; + nameobj = js_GetAttributeNameObject(cx, nameqn); + if (!nameobj || !js_GetXMLObject(cx, parent)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(nameobj); + if (!DeleteProperty(cx, parent->object, id, vp)) + return JS_FALSE; + } else { + kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, + NULL); + JS_ASSERT(kidIndex != XML_NOT_FOUND); + if (!IndexToIdVal(cx, kidIndex, &id)) + return JS_FALSE; + if (!DeleteByIndex(cx, parent, id, vp)) + return JS_FALSE; + } + } + + XMLArrayDelete(cx, array, index, JS_TRUE); + } else { + for (index = 0; index < length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !DeleteProperty(cx, kidobj, id, vp)) + return JS_FALSE; + } + } + } + } else { + /* ECMA-357 9.1.1.3. */ + if (isIndex) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + goto out; + nameobj = nameqn->object; + + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + if (xml->xml_class != JSXML_CLASS_ELEMENT) + goto out; + array = &xml->xml_attrs; + length = array->length; + matcher = MatchAttrName; + } else { + matcher = MatchElemName; + } + if (length != 0) { + deleteCount = 0; + for (index = 0; index < length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && matcher(nameqn, kid)) { + kid->parent = NULL; + XMLArrayDelete(cx, array, index, JS_FALSE); + ++deleteCount; + } else if (deleteCount != 0) { + XMLARRAY_SET_MEMBER(array, + index - deleteCount, + array->vector[index]); + } + } + array->length -= deleteCount; + } + } + +out: + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +static JSBool +SyncInScopeNamespaces(JSContext *cx, JSXML *xml) +{ + JSXMLArray *nsarray; + uint32 i, n; + JSXMLNamespace *ns; + + nsarray = &xml->xml_namespaces; + while ((xml = xml->parent) != NULL) { + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +static JSBool +GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, + JSBool attributes, JSXML *list) +{ + JSXMLArray *array; + JSXMLNameMatcher matcher; + JSXMLArrayCursor cursor; + JSXML *kid; + JSBool ok; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (attributes) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + + XMLArrayCursorInit(&cursor, array); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (matcher(nameqn, kid)) { + if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = SyncInScopeNamespaces(cx, kid); + if (!ok) + goto out; + } + ok = Append(cx, list, kid); + if (!ok) + goto out; + } + } + ok = JS_TRUE; + + out: + XMLArrayCursorFinish(&cursor); + return ok; +} + +/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ +static JSBool +GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list, *kid; + uint32 index; + JSObject *kidobj, *listobj; + JSXMLQName *nameqn; + jsid funid; + jsval roots[2]; + JSTempValueRooter tvr; + JSBool attributes; + JSXMLArrayCursor cursor; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + + if (js_IdIsIndex(id, &index)) { + if (xml->xml_class != JSXML_CLASS_LIST) { + *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; + } else { + /* + * ECMA-357 9.2.1.1 starts here. + * + * Erratum: 9.2 is not completely clear that indexed properties + * correspond to kids, but that's what it seems to say, and it's + * what any sane user would want. + */ + if (index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + + *vp = OBJECT_TO_JSVAL(kidobj); + } else { + *vp = JSVAL_VOID; + } + } + return JS_TRUE; + } + + /* + * ECMA-357 9.2.1.1/9.1.1.1 qname case. + */ + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return js_GetXMLFunction(cx, obj, funid, vp); + + roots[0] = OBJECT_TO_JSVAL(nameqn->object); + JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + roots[1] = OBJECT_TO_JSVAL(listobj); + tvr.count++; + + list = (JSXML *) JS_GetPrivate(cx, listobj); + attributes = (OBJ_GET_CLASS(cx, nameqn->object) == + &js_AttributeNameClass); + + if (xml->xml_class == JSXML_CLASS_LIST) { + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT && + !GetNamedProperty(cx, kid, nameqn, attributes, list)) { + listobj = NULL; + break; + } + } + XMLArrayCursorFinish(&cursor); + } else { + if (!GetNamedProperty(cx, xml, nameqn, attributes, list)) + listobj = NULL; + } + + /* + * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given + * list's [[TargetProperty]] to the property that is being appended. + * This means that any use of the internal [[Get]] property returns + * a list which, when used by e.g. [[Insert]] duplicates the last + * element matched by id. + * See bug 336921. + */ + list->xml_target = xml; + list->xml_targetprop = nameqn; + *vp = OBJECT_TO_JSVAL(listobj); + } + + JS_POP_TEMP_ROOT(cx, &tvr); + return listobj != NULL; +} + +static JSXML * +CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) +{ + JS_ASSERT(xml->object != obj); + + xml = DeepCopy(cx, xml, obj, 0); + if (!xml) + return NULL; + + JS_ASSERT(xml->object == obj); + return xml; +} + +#define CHECK_COPY_ON_WRITE(cx,xml,obj) \ + (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) + +static JSString * +KidToString(JSContext *cx, JSXML *xml, uint32 index) +{ + JSXML *kid; + JSObject *kidobj; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) + return cx->runtime->emptyString; + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return NULL; + return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); +} + +/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ +static JSBool +PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool ok, primitiveAssign; + enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; + jsval roots[3]; + JSTempValueRooter tvr; + JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; + JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; + JSXMLQName *targetprop, *nameqn, *attrqn; + uint32 index, i, j, k, n, q; + jsval attrval, nsval, junk; + jsid funid; + JSString *left, *right, *space; + JSXMLNamespace *ns; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(*vp)) { + vobj = JSVAL_TO_OBJECT(*vp); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + /* Control flow after here must exit via label out. */ + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); + roots[ID_ROOT] = id; + roots[VAL_ROOT] = *vp; + JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.2. */ + if (js_IdIsIndex(id, &index)) { + /* Step 1 sets i to the property index. */ + i = index; + + /* 2(a-b). */ + if (xml->xml_target) { + ok = ResolveValue(cx, xml->xml_target, &rxml); + if (!ok) + goto out; + if (!rxml) + goto out; + JS_ASSERT(rxml->object); + } else { + rxml = NULL; + } + + /* 2(c). */ + if (index >= xml->xml_kids.length) { + /* 2(c)(i). */ + if (rxml) { + if (rxml->xml_class == JSXML_CLASS_LIST) { + if (rxml->xml_kids.length != 1) + goto out; + rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); + if (!rxml) + goto out; + ok = js_GetXMLObject(cx, rxml) != NULL; + if (!ok) + goto out; + } + + /* + * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets + * _y.[[Parent]] = r_ where _r_ is the result of + * [[ResolveValue]] called on _x.[[TargetObject]] in + * 2(a)(i). This can result in text parenting text: + * + * var MYXML = new XML(); + * MYXML.appendChild(new XML("Giants")); + * + * (testcase from Werner Sharp ). + * + * To match insertChildAfter, insertChildBefore, + * prependChild, and setChildren, we should silently + * do nothing in this case. + */ + if (!JSXML_HAS_KIDS(rxml)) + goto out; + } + + /* 2(c)(ii) is distributed below as several js_NewXML calls. */ + targetprop = xml->xml_targetprop; + if (!targetprop || IS_STAR(targetprop->localName)) { + /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ + kid = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!kid) + goto bad; + } else { + nameobj = js_GetXMLQNameObject(cx, targetprop); + if (!nameobj) + goto bad; + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* + * 2(c)(iii)(1-3). + * Note that rxml can't be null here, because target + * and targetprop are non-null. + */ + ok = GetProperty(cx, rxml->object, id, &attrval); + if (!ok) + goto out; + if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */ + goto out; + attrobj = JSVAL_TO_OBJECT(attrval); + attr = (JSXML *) JS_GetPrivate(cx, attrobj); + if (JSXML_LENGTH(attr) != 0) + goto out; + + kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + } else { + /* 2(c)(v). */ + kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); + } + if (!kid) + goto bad; + + /* An important bit of 2(c)(ii). */ + kid->name = targetprop; + } + + /* Final important bit of 2(c)(ii). */ + kid->parent = rxml; + + /* 2(c)(vi-vii). */ + i = xml->xml_kids.length; + if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { + /* + * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. + * y.[[Parent]] is here called kid->parent, which we know + * from 2(c)(ii) is _r_, here called rxml. So let's just + * test that! Erratum, the spec should be simpler here. + */ + if (rxml) { + JS_ASSERT(JSXML_HAS_KIDS(rxml)); + n = rxml->xml_kids.length; + j = n - 1; + if (n != 0 && i != 0) { + for (n = j, j = 0; j < n; j++) { + if (rxml->xml_kids.vector[j] == + xml->xml_kids.vector[i-1]) { + break; + } + } + } + + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); + if (!ok) + goto out; + } + + /* + * 2(c)(vii)(2-3). + * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a + * typo for [[TargetProperty]]. + */ + if (vxml) { + kid->name = (vxml->xml_class == JSXML_CLASS_LIST) + ? vxml->xml_targetprop + : vxml->name; + } + } + + /* 2(c)(viii). */ + ok = Append(cx, xml, kid); + if (!ok) + goto out; + } + + /* 2(d). */ + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 2(e). */ + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + goto out; + parent = kid->parent; + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameobj = js_GetAttributeNameObject(cx, kid->name); + if (!nameobj) + goto bad; + id = OBJECT_TO_JSVAL(nameobj); + + if (parent) { + /* 2(e)(i). */ + parentobj = js_GetXMLObject(cx, parent); + if (!parentobj) + goto bad; + ok = PutProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + + /* 2(e)(ii). */ + ok = GetProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); + + /* 2(e)(iii). */ + xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; + } + } + + /* 2(f). */ + else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + /* 2(f)(i) Create a shallow copy _c_ of _V_. */ + copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!copyobj) + goto bad; + copy = (JSXML *) JS_GetPrivate(cx, copyobj); + n = vxml->xml_kids.length; + ok = XMLArraySetCapacity(cx, ©->xml_kids, n); + if (!ok) + goto out; + for (k = 0; k < n; k++) { + kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); + XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2); + } + + JS_ASSERT(parent != xml); + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); + if (!ok) + goto out; + +#ifdef DEBUG + /* Erratum: this loop in the spec is useless. */ + for (j = 0, n = copy->xml_kids.length; j < n; j++) { + kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); + JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) + == kid2); + } +#endif + } + + /* + * 2(f)(iv-vi). + * Erratum: notice the unhandled zero-length V basis case and + * the off-by-one errors for the n != 0 cases in the spec. + */ + if (n == 0) { + XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); + } else { + ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); + if (!ok) + goto out; + + for (j = 0; j < n; j++) + xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; + } + } + + /* 2(g). */ + else if (vxml || JSXML_HAS_VALUE(kid)) { + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, *vp); + if (!ok) + goto out; + + vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); + if (!vxml) + goto out; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); + } + + /* + * 2(g)(iii). + * Erratum: _V_ may not be of type XML, but all index-named + * properties _x[i]_ in an XMLList _x_ must be of type XML, + * according to 9.2.1.1 Overview and other places in the spec. + * + * Thanks to 2(d), we know _V_ (*vp here) is either a string + * or an XML/XMLList object. If *vp is a string, call ToXML + * on it to satisfy the constraint. + */ + if (!vxml) { + JS_ASSERT(JSVAL_IS_STRING(*vp)); + vobj = ToXML(cx, *vp); + if (!vobj) + goto bad; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + + /* 2(h). */ + else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + id = ATOM_KEY(cx->runtime->atomState.starAtom); + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* + * 3. + * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null + * or an r with r.[[Length]] != 1, throw TypeError. + */ + n = JSXML_LENGTH(xml); + if (n > 1) + goto type_error; + if (n == 0) { + ok = ResolveValue(cx, xml, &rxml); + if (!ok) + goto out; + if (!rxml || JSXML_LENGTH(rxml) != 1) + goto type_error; + ok = Append(cx, xml, rxml); + if (!ok) + goto out; + } + JS_ASSERT(JSXML_LENGTH(xml) == 1); + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!kid) + goto out; + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* + * ECMA-357 9.1.1.2. + * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted + * effort in ToString or [[DeepCopy]]. + */ + if (js_IdIsIndex(id, &index)) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + goto bad; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + goto bad; + if (funid) { + ok = js_SetProperty(cx, obj, funid, vp); + goto out; + } + nameobj = nameqn->object; + + if (JSXML_HAS_VALUE(xml)) + goto out; + + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + } else { + rxml = DeepCopyInLRS(cx, vxml, 0); + if (!rxml || !js_GetXMLObject(cx, rxml)) + goto bad; + vxml = rxml; + *vp = OBJECT_TO_JSVAL(vxml->object); + } + roots[VAL_ROOT] = *vp; + + /* + * 6. + * Erratum: why is this done here, so early? use is way later.... + */ + ok = js_GetDefaultXMLNamespace(cx, &nsval); + if (!ok) + goto out; + + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* 7(a). */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) + goto out; + + /* 7(b-c). */ + if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) { + *vp = STRING_TO_JSVAL(cx->runtime->emptyString); + } else { + left = KidToString(cx, vxml, 0); + if (!left) + goto bad; + + space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); + for (i = 1; i < n; i++) { + left = js_ConcatStrings(cx, left, space); + if (!left) + goto bad; + right = KidToString(cx, vxml, i); + if (!right) + goto bad; + left = js_ConcatStrings(cx, left, right); + if (!left) + goto bad; + } + + roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); + } + } else { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 7(d-e). */ + match = NULL; + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrqn = attr->name; + if (js_EqualStrings(attrqn->localName, nameqn->localName) && + (!nameqn->uri || + js_EqualStrings(attrqn->uri, nameqn->uri))) { + if (!match) { + match = attr; + } else { + nameobj = js_GetAttributeNameObject(cx, attrqn); + if (!nameobj) + goto bad; + + id = OBJECT_TO_JSVAL(nameobj); + ok = DeleteProperty(cx, obj, id, &junk); + if (!ok) + goto out; + --i; + } + } + } + + /* 7(f). */ + attr = match; + if (!attr) { + /* 7(f)(i-ii). */ + if (!nameqn->uri) { + left = right = cx->runtime->emptyString; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 7(f)(iii). */ + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto bad; + attr->parent = xml; + attr->name = nameqn; + + /* 7(f)(iv). */ + ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); + if (!ok) + goto out; + + /* 7(f)(v-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = AddInScopeNamespace(cx, xml, ns); + if (!ok) + goto out; + } + + /* 7(g). */ + attr->xml_value = JSVAL_TO_STRING(*vp); + goto out; + } + + /* 8-9. */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && + !IS_STAR(nameqn->localName)) { + goto out; + } + + /* 10-11. */ + id = JSVAL_VOID; + primitiveAssign = !vxml && !IS_STAR(nameqn->localName); + + /* 12. */ + k = n = xml->xml_kids.length; + kid2 = NULL; + while (k != 0) { + --k; + kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (!JSVAL_IS_VOID(id)) { + ok = DeleteByIndex(cx, xml, id, &junk); + if (!ok) + goto out; + } + ok = IndexToIdVal(cx, k, &id); + if (!ok) + goto out; + kid2 = kid; + } + } + + /* + * Erratum: ECMA-357 specified child insertion inconsistently: + * insertChildBefore and insertChildAfter insert an arbitrary XML + * instance, and therefore can create cycles, but appendChild as + * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on + * its argument. But the "Semantics" in 13.4.4.3 do not include + * any [[DeepCopy]] call. + * + * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) + * required adding cycle detection, and allowing duplicate kids to + * be created (see comment 6 in the bug). Allowing duplicate kid + * references means the loop above will delete all but the lowest + * indexed reference, and each [[DeleteByIndex]] nulls the kid's + * parent. Thus the need to restore parent here. This is covered + * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. + */ + if (kid2) { + JS_ASSERT(kid2->parent == xml || !kid2->parent); + if (!kid2->parent) + kid2->parent = xml; + } + + /* 13. */ + if (JSVAL_IS_VOID(id)) { + /* 13(a). */ + ok = IndexToIdVal(cx, n, &id); + if (!ok) + goto out; + + /* 13(b). */ + if (primitiveAssign) { + if (!nameqn->uri) { + ns = (JSXMLNamespace *) + JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + left = ns->uri; + right = ns->prefix; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 13(b)(iii). */ + vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); + if (!vobj) + goto bad; + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + vxml->parent = xml; + vxml->name = nameqn; + + /* 13(b)(iv-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); + if (!ok) + goto out; + ok = AddInScopeNamespace(cx, vxml, ns); + if (!ok) + goto out; + } + } + + /* 14. */ + if (primitiveAssign) { + JSXMLArrayCursor cursor; + + js_IdIsIndex(id, &index); + XMLArrayCursorInit(&cursor, &xml->xml_kids); + cursor.index = index; + kid = (JSXML *) XMLArrayCursorItem(&cursor); + if (JSXML_HAS_KIDS(kid)) { + XMLArrayFinish(cx, &kid->xml_kids); + ok = XMLArrayInit(cx, &kid->xml_kids, 1); + } + + /* 14(b-c). */ + /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ + if (ok) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) { + roots[VAL_ROOT] = *vp; + if ((JSXML *) XMLArrayCursorItem(&cursor) == kid) + ok = Replace(cx, kid, JSVAL_ZERO, *vp); + } + } + XMLArrayCursorFinish(&cursor); + } else { + /* 15(a). */ + ok = Replace(cx, xml, id, *vp); + } + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + js_LeaveLocalRootScope(cx); + return ok; + +type_error: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_PUT, + js_ValueToPrintableString(cx, id)); +bad: + ok = JS_FALSE; + goto out; +} + +/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result) +{ + JSXML *target, *base; + JSXMLQName *targetprop; + JSObject *targetpropobj; + jsval id, tv; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { + if (!js_GetXMLObject(cx, list)) + return JS_FALSE; + *result = list; + return JS_TRUE; + } + + target = list->xml_target; + targetprop = list->xml_targetprop; + if (!target || !targetprop || IS_STAR(targetprop->localName)) { + *result = NULL; + return JS_TRUE; + } + + targetpropobj = js_GetXMLQNameObject(cx, targetprop); + if (!targetpropobj) + return JS_FALSE; + if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) { + *result = NULL; + return JS_TRUE; + } + + if (!ResolveValue(cx, target, &base)) + return JS_FALSE; + if (!base) { + *result = NULL; + return JS_TRUE; + } + if (!js_GetXMLObject(cx, base)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(targetpropobj); + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + + if (JSXML_LENGTH(target) == 0) { + if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { + *result = NULL; + return JS_TRUE; + } + tv = STRING_TO_JSVAL(cx->runtime->emptyString); + if (!PutProperty(cx, base->object, id, &tv)) + return JS_FALSE; + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + } + + *result = target; + return JS_TRUE; +} + +/* + * HasProperty must be able to return a found JSProperty and the object in + * which it was found, if id is of the form function::name. For other ids, + * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp + * and null in *objp. + * + * DROP_PROPERTY helps HasProperty callers drop function properties without + * trying to drop the magic FOUND_XML_PROPERTY cookie. + */ +#define FOUND_XML_PROPERTY ((JSProperty *) 1) +#define DROP_PROPERTY(cx,pobj,prop) (((prop) != FOUND_XML_PROPERTY) \ + ? OBJ_DROP_PROPERTY(cx, pobj, prop) \ + : (void) 0) + +/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ +static JSBool +HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, + JSProperty **propp) +{ + JSXML *xml, *kid; + JSXMLArrayCursor cursor; + JSObject *kidobj; + JSXMLQName *qn; + jsid funid; + JSXMLArray *array; + JSXMLNameMatcher matcher; + uint32 i, n; + + *objp = NULL; + *propp = NULL; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + n = JSXML_LENGTH(xml); + if (js_IdIsIndex(id, &i)) { + if (i < n) + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp)) + break; + if (*propp) + break; + } + } + XMLArrayCursorFinish(&cursor); + if (kid) + return *propp != NULL; + } else { + if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) { + if (i == 0) + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + + qn = ToXMLName(cx, id, &funid); + if (!qn) + return JS_FALSE; + if (funid) + return js_LookupProperty(cx, obj, funid, objp, propp); + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + for (i = 0, n = array->length; i < n; i++) { + kid = XMLARRAY_MEMBER(array, i, JSXML); + if (kid && matcher(qn, kid)) { + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + } + } + + return JS_TRUE; +} + +static void +xml_finalize(JSContext *cx, JSObject *obj) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (!xml) + return; + if (xml->object == obj) + xml->object = NULL; + UNMETER(xml_stats.livexmlobj); +} + +static void +xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) +{ + uint32 i; + JSXML *elt; + + for (i = 0; i < len; i++) { + elt = vec[i]; + { +#ifdef GC_MARK_DEBUG + char buf[120]; + + if (elt->xml_class == JSXML_CLASS_LIST) { + strcpy(buf, js_XMLList_str); + } else if (JSXML_HAS_NAME(elt)) { + JSXMLQName *qn = elt->name; + + JS_snprintf(buf, sizeof buf, "%s::%s", + qn->uri ? JS_GetStringBytes(qn->uri) : "*", + JS_GetStringBytes(qn->localName)); + } else { + JSString *str = elt->xml_value; + size_t srclen = JSSTRING_LENGTH(str); + size_t dstlen = sizeof buf; + + if (srclen >= sizeof buf / 6) + srclen = sizeof buf / 6 - 1; + js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, + buf, &dstlen); + } +#endif + GC_MARK(cx, elt, buf); + } + } +} + +/* + * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to + * be native. Therefore, xml_lookupProperty must return a valid JSProperty + * pointer parameter via *propp to signify "property found". Since the only + * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from + * js_FindXMLProperty (in this file), js_FindProperty (in jsobj.c, called from + * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a + * JSScopeProperty here is when an unqualified name or XML name is being + * accessed or when "name in xml" is called. + * + * This scope property keeps the JSOP_NAME code in js_Interpret happy by + * giving it an sprop with (getter, setter) == (GetProperty, PutProperty). + * + * NB: xml_deleteProperty must take care to remove any property added here. + * + * FIXME This clashes with the function namespace implementation which also + * uses native properties. Effectively after xml_lookupProperty any property + * stored previously using assignments to xml.function::name will be removed. + * We partially workaround the problem in js_GetXMLFunction. There we take + * advantage of the fact that typically function:: is used to access the + * functions from XML.prototype. So when js_GetProperty returns a non-function + * property, we assume that it represents the result of GetProperty setter + * hiding the function and use an extra prototype chain lookup to recover it. + * For a proper solution see bug 355257. + */ +static JSBool +xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + JSScopeProperty *sprop; + + if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp)) + return JS_FALSE; + + if (*propp == FOUND_XML_PROPERTY) { + sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SPROP_INVALID_SLOT, JSPROP_ENUMERATE, + 0, 0); + if (!sprop) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + *objp = obj; + *propp = (JSProperty *) sprop; + } + return JS_TRUE; +} + +static JSBool +xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) +{ + if (VALUE_IS_FUNCTION(cx, value) || getter || setter || + (attrs & JSPROP_ENUMERATE) == 0 || + (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { + return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, + propp); + } + + if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value)) + return JS_FALSE; + if (propp) + *propp = NULL; + return JS_TRUE; +} + +static JSBool +xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + if (id == JS_DEFAULT_XML_NAMESPACE_ID) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + return GetProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return PutProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + JSBool *foundp) +{ + JSObject *pobj; + + if (prop) { + *foundp = JS_TRUE; + } else { + if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop)) + return JS_FALSE; + if (prop) + DROP_PROPERTY(cx, pobj, prop); + *foundp = (prop != NULL); + } + return JS_TRUE; +} + +static JSBool +xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + *attrsp = found ? JSPROP_ENUMERATE : 0; + return JS_TRUE; +} + +static JSBool +xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + if (found) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SET_XML_ATTRS); + } + return !found; +} + +static JSBool +xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + /* + * If this object has its own (mutable) scope, and if id isn't an index, + * then we may have added a property to the scope in xml_lookupProperty + * for it to return to mean "found" and to provide a handle for access + * operations to call the property's getter or setter. The property also + * helps speed up unqualified accesses via the property cache, avoiding + * what amount to two HasProperty searches. + * + * But now it's time to remove any such property, to purge the property + * cache and remove the scope entry. + */ + if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) { + if (!js_DeleteProperty(cx, obj, id, rval)) + return JS_FALSE; + } + + return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval); +} + +static JSBool +xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + JSXML *xml; + + if (hint == JSTYPE_OBJECT) { + /* Called from for..in code in js_Interpret: return an XMLList. */ + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); + if (!obj) + return JS_FALSE; + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); +} + +static JSBool +xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSXML *xml; + uint32 length, index; + JSXMLArrayCursor *cursor; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + length = JSXML_LENGTH(xml); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); + if (!cursor) + return JS_FALSE; + XMLArrayCursorInit(cursor, &xml->xml_kids); + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + break; + + case JSENUMERATE_NEXT: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + *idp = INT_TO_JSID(index); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor) { + XMLArrayCursorFinish(cursor); + JS_free(cx, cursor); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return JS_TRUE; +} + +static uint32 +xml_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + GC_MARK(cx, xml, "private"); + return js_Mark(cx, obj, NULL); +} + +static void +xml_clear(JSContext *cx, JSObject *obj) +{ +} + +static JSBool +HasSimpleContent(JSXML *xml) +{ + JSXML *kid; + JSBool simple; + uint32 i, n; + +again: + switch (xml->xml_class) { + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + return JS_FALSE; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) + return JS_TRUE; + if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + xml = kid; + goto again; + } + } + /* FALL THROUGH */ + default: + simple = JS_TRUE; + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + simple = JS_FALSE; + break; + } + } + return simple; + } +} + +/* + * 11.2.2.1 Step 3(d) onward. + */ +static JSObject * +xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSTempValueRooter tvr; + + JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); + + /* + * As our callers have a bad habit of passing a pointer to an unrooted + * local value as vp, we use a proper root here. + */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); + if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value)) + obj = NULL; + *vp = tvr.u.value; + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; +} + +static JSBool +xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return js_SetProperty(cx, obj, id, vp); +} + +static JSBool +xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp) +{ + JSXML *xml, *kid; + uint32 length, index; + JSXMLArrayCursor *cursor; + JSObject *kidobj; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + length = JSXML_LENGTH(xml); + JS_ASSERT(INT_FITS_IN_JSVAL(length)); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); + if (!cursor) + return JS_FALSE; + XMLArrayCursorInit(cursor, &xml->xml_kids); + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + if (vp) + *vp = JSVAL_VOID; + break; + + case JSENUMERATE_NEXT: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { + if (++index == length) + goto destroy; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + JS_ASSERT(INT_FITS_IN_JSVAL(index)); + *idp = INT_TO_JSID(index); + *vp = OBJECT_TO_JSVAL(kidobj); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor) { + destroy: + XMLArrayCursorFinish(cursor); + JS_free(cx, cursor); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXML *xml, *vxml; + JSObject *vobj; + JSBool ok; + JSString *str, *vstr; + jsdouble d, d2; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, xml, v, bp); + } else if (vxml) { + if (vxml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); + } else { + if (((xml->xml_class == JSXML_CLASS_TEXT || + xml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(vxml)) || + ((vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(xml))) { + ok = js_EnterLocalRootScope(cx); + if (ok) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = js_EqualStrings(str, vstr); + js_LeaveLocalRootScope(cx); + } + } else { + ok = XMLEquals(cx, xml, vxml, bp); + } + } + } else { + ok = js_EnterLocalRootScope(cx); + if (ok) { + if (HasSimpleContent(xml)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = js_EqualStrings(str, vstr); + } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) { + ok = JS_FALSE; + } else if (JSVAL_IS_STRING(v)) { + *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); + } else { + ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); + if (ok) { + d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) + : *JSVAL_TO_DOUBLE(v); + *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); + } + } + } else { + *bp = JS_FALSE; + } + js_LeaveLocalRootScope(cx); + } + } + return ok; +} + +static JSBool +xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) +{ + JSBool ok; + JSObject *listobj, *robj; + JSXML *list, *lxml, *rxml; + + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) { + ok = JS_FALSE; + goto out; + } + + list = (JSXML *) JS_GetPrivate(cx, listobj); + lxml = (JSXML *) JS_GetPrivate(cx, obj); + ok = Append(cx, list, lxml); + if (!ok) + goto out; + + if (VALUE_IS_XML(cx, v)) { + rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + } else { + robj = ToXML(cx, v); + if (!robj) { + ok = JS_FALSE; + goto out; + } + rxml = (JSXML *) JS_GetPrivate(cx, robj); + } + ok = Append(cx, list, rxml); + if (!ok) + goto out; + + *vp = OBJECT_TO_JSVAL(listobj); +out: + js_LeaveLocalRootScopeWithResult(cx, *vp); + return ok; +} + +/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ +JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = { + { js_NewObjectMap, js_DestroyObjectMap, + xml_lookupProperty, xml_defineProperty, + xml_getProperty, xml_setProperty, + xml_getAttributes, xml_setAttributes, + xml_deleteProperty, xml_defaultValue, + xml_enumerate, js_CheckAccess, + NULL, NULL, + NULL, NULL, + NULL, xml_hasInstance, + js_SetProtoOrParent, js_SetProtoOrParent, + xml_mark, xml_clear, + NULL, NULL }, + xml_getMethod, xml_setMethod, + xml_enumerateValues, xml_equality, + xml_concatenate +}; + +static JSObjectOps * +xml_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_XMLObjectOps.base; +} + +JS_FRIEND_DATA(JSClass) js_XMLClass = { + js_XML_str, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, + xml_getObjectOps, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +static JSObject * +CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, + uintN argc, jsval *argv) +{ + JSObject *tmp; + jsval rval; + + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); + return JSVAL_TO_OBJECT(rval); +} + +static JSXML * +StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv) +{ + JSXML *xml; + JSFunction *fun; + + JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2])); + + xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv); + if (!xml || xml->xml_class != JSXML_CLASS_LIST) + return xml; + + if (xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + *objp = js_GetXMLObject(cx, xml); + if (!*objp) + return NULL; + argv[-1] = OBJECT_TO_JSVAL(*objp); + return xml; + } + } + + fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2])); + if (fun) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NON_LIST_XML_METHOD, + JS_GetFunctionName(fun), numBuf); + } + return NULL; +} + +#define XML_METHOD_PROLOG \ + JS_BEGIN_MACRO \ + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ + if (!xml) \ + return JS_FALSE; \ + JS_END_MACRO + +#define NON_LIST_XML_METHOD_PROLOG \ + JS_BEGIN_MACRO \ + xml = StartNonListXMLMethod(cx, &obj, argv); \ + if (!xml) \ + return JS_FALSE; \ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); \ + JS_END_MACRO + +static JSBool +xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSObject *nsobj; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); + if (!nsobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nsobj); + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + if (!AddInScopeNamespace(cx, xml, ns)) + return JS_FALSE; + ns->declared = JS_TRUE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *vxml; + jsval name, v; + JSObject *vobj; + + NON_LIST_XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + if (!js_GetAnyName(cx, &name)) + return JS_FALSE; + + if (!GetProperty(cx, obj, name, &v)) + return JS_FALSE; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vobj = JSVAL_TO_OBJECT(v); + JS_ASSERT(OBJECT_IS_XML(cx, vobj)); + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); + + if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) + return JS_FALSE; + if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0])) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLQName *qn; + + qn = ToAttributeName(cx, argv[0]); + if (!qn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */ + return GetProperty(cx, obj, argv[0], rval); +} + +/* XML and XMLList */ +static JSBool +xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSXMLQName *qn; + JSTempValueRooter tvr; + JSBool ok; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + qn = ToAttributeName(cx, name); + if (!qn) + return JS_FALSE; + name = OBJECT_TO_JSVAL(qn->object); + JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr); + ok = GetProperty(cx, obj, name, rval); + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} + +static JSXML * +xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval) +{ + JSObject *listobj; + JSXML *list; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + return list; +} + +static JSBool +xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, + jsval *rval) +{ + uint32 index; + JSXML *kid; + JSObject *kidobj; + + /* ECMA-357 13.4.4.6 */ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + + if (js_IdIsIndex(name, &index)) { + if (index >= JSXML_LENGTH(xml)) { + *rval = JSVAL_VOID; + } else { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *rval = JSVAL_VOID; + } else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(kidobj); + } + } + return JS_TRUE; + } + + return GetProperty(cx, obj, name, rval); +} + +/* XML and XMLList */ +static JSBool +xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSXMLArrayCursor cursor; + jsval name, v; + JSObject *kidobj; + + XML_METHOD_PROLOG; + name = argv[0]; + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 13.5.4.4 */ + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + break; + if (!xml_child_helper(cx, kidobj, kid, name, &v)) + break; + if (JSVAL_IS_VOID(v)) { + /* The property didn't exist in this kid. */ + continue; + } + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && + !Append(cx, list, vxml)) { + break; + } + } + XMLArrayCursorFinish(&cursor); + return !kid; + } + + /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */ + if (!xml_child_helper(cx, obj, xml, name, rval)) + return JS_FALSE; + if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *parent; + uint32 i, n; + + NON_LIST_XML_METHOD_PROLOG; + parent = xml->parent; + if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { + if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) + break; + } + JS_ASSERT(i < n); + return js_NewNumberValue(cx, i, rval); +} + +/* XML and XMLList */ +static JSBool +xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + return GetProperty(cx, obj, name, rval); +} + +/* XML and XMLList */ +static JSBool +xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSBool ok; + uint32 i, n; + JSObject *kidobj; + jsval v; + + XML_METHOD_PROLOG; + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_comments(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + /* 13.4.4.9 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval value; + JSBool eq; + JSXMLArrayCursor cursor; + JSObject *kidobj; + + XML_METHOD_PROLOG; + value = argv[0]; + if (xml->xml_class == JSXML_CLASS_LIST) { + eq = JS_FALSE; + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !xml_equality(cx, kidobj, value, &eq)) + break; + if (eq) + break; + } + XMLArrayCursorFinish(&cursor); + if (kid && !eq) + return JS_FALSE; + } else { + if (!xml_equality(cx, obj, value, &eq)) + return JS_FALSE; + } + *rval = BOOLEAN_TO_JSVAL(eq); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *copy; + + XML_METHOD_PROLOG; + copy = DeepCopy(cx, xml, NULL, 0); + if (!copy) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(copy->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list; + jsval name; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + list = Descendants(cx, xml, name); + if (!list) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + JSXMLQName *nameqn; + jsid funid; + JSBool ok; + JSXMLArrayCursor cursor; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameqn->object); + + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + if (funid) + return JS_TRUE; + + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_elements(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + XMLArrayCursorFinish(&cursor); + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && + MatchElemName(nameqn, kid)) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSObject *pobj; + JSProperty *prop; + + if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) + return JS_FALSE; + + name = argv[0]; + if (!HasProperty(cx, obj, name, &pobj, &prop)) + return JS_FALSE; + if (!prop) { + return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv, + rval); + } + DROP_PROPERTY(cx, pobj, prop); + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; +again: + switch (xml->xml_class) { + case JSXML_CLASS_ATTRIBUTE: + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + *rval = JSVAL_FALSE; + break; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) { + *rval = JSVAL_TRUE; + } else if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + obj = kidobj; + xml = (JSXML *) JS_GetPrivate(cx, obj); + goto again; + } + } + /* FALL THROUGH */ + default: + *rval = JSVAL_FALSE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + *rval = JSVAL_TRUE; + break; + } + } + break; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); + return JS_TRUE; +} + +typedef struct JSTempRootedNSArray { + JSTempValueRooter tvr; + JSXMLArray array; + jsval value; /* extra root for temporaries */ +} JSTempRootedNSArray; + +JS_STATIC_DLL_CALLBACK(void) +mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) +{ + JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; + + namespace_mark_vector(cx, + (JSXMLNamespace **)tmp->array.vector, + tmp->array.length); + XMLArrayCursorMark(cx, tmp->array.cursors); + if (JSVAL_IS_GCTHING(tmp->value)) + GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value"); +} + +static void +InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + XMLArrayInit(cx, &tmp->array, 0); + tmp->value = JSVAL_NULL; + JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr); +} + +static void +FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array); + JS_POP_TEMP_ROOT(cx, &tmp->tvr); + XMLArrayFinish(cx, &tmp->array); +} + +/* + * Populate a new JS array with elements of JSTempRootedNSArray.array and + * place the result into rval. rval must point to a rooted location. + */ +static JSBool +TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) +{ + JSObject *arrayobj; + uint32 i, n; + JSXMLNamespace *ns; + JSObject *nsobj; + + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + for (i = 0, n = tmp->array.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace); + if (!ns) + continue; + nsobj = js_GetXMLNamespaceObject(cx, ns); + if (!nsobj) + return JS_FALSE; + tmp->value = OBJECT_TO_JSVAL(nsobj); + if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) +{ + uint32 length, i, j, n; + JSXMLNamespace *ns, *ns2; + + length = nsarray->length; + do { + if (xml->xml_class != JSXML_CLASS_ELEMENT) + continue; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + + for (j = 0; j < length; j++) { + ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); + if (ns2 && + ((ns2->prefix && ns->prefix) + ? js_EqualStrings(ns2->prefix, ns->prefix) + : js_EqualStrings(ns2->uri, ns->uri))) { + break; + } + } + + if (j == length) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + ++length; + } + } + } while ((xml = xml->parent) != NULL); + JS_ASSERT(length == nsarray->length); + + return JS_TRUE; +} + +static JSBool +xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSTempRootedNSArray namespaces; + JSBool ok; + + NON_LIST_XML_METHOD_PROLOG; + + InitTempNSArray(cx, &namespaces); + ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && + TempNSArrayToJSArray(cx, &namespaces, rval); + FinishTempNSArray(cx, &namespaces); + return ok; +} + +static JSBool +xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval arg; + uint32 i; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + arg = argv[0]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = 0; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + ++i; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + if (!Insert(cx, xml, i, argv[1])) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval arg; + uint32 i; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + arg = argv[0]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = xml->xml_kids.length; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + if (!Insert(cx, xml, i, argv[1])) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_LIST) { + *rval = JSVAL_ONE; + } else { + if (!js_NewNumberValue(cx, xml->xml_kids.length, rval)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + NON_LIST_XML_METHOD_PROLOG; + *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; + return JS_TRUE; +} + +static JSBool +xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + JSObject *nameobj; + + NON_LIST_XML_METHOD_PROLOG; + if (!xml->name) { + *rval = JSVAL_NULL; + } else { + nameobj = js_GetXMLQNameObject(cx, xml->name); + if (!nameobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(nameobj); + } + return JS_TRUE; +} + +static JSBool +xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *prefix; + JSTempRootedNSArray inScopeNSes; + JSBool ok; + jsuint i, length; + JSXMLNamespace *ns; + JSObject *nsobj; + + NON_LIST_XML_METHOD_PROLOG; + if (argc == 0 && !JSXML_HAS_NAME(xml)) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + if (argc == 0) { + prefix = NULL; + } else { + prefix = js_ValueToString(cx, argv[0]); + if (!prefix) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(prefix); /* local root */ + } + + /* After this point the control must flow through label out. */ + InitTempNSArray(cx, &inScopeNSes); + ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); + if (!ok) + goto out; + + if (!prefix) { + ns = GetNamespace(cx, xml->name, &inScopeNSes.array); + if (!ns) { + ok = JS_FALSE; + goto out; + } + } else { + ns = NULL; + for (i = 0, length = inScopeNSes.array.length; i < length; i++) { + ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); + if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix)) + break; + ns = NULL; + } + } + + if (!ns) { + *rval = JSVAL_VOID; + } else { + nsobj = js_GetXMLNamespaceObject(cx, ns); + if (!nsobj) { + ok = JS_FALSE; + goto out; + } + *rval = OBJECT_TO_JSVAL(nsobj); + } + + out: + FinishTempNSArray(cx, &inScopeNSes); + return JS_TRUE; +} + +static JSBool +xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *yml; + JSBool ok; + JSTempRootedNSArray ancestors, declared; + uint32 i, n; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (JSXML_HAS_VALUE(xml)) + return JS_TRUE; + + /* From here, control flow must goto out to finish these arrays. */ + ok = JS_TRUE; + InitTempNSArray(cx, &ancestors); + InitTempNSArray(cx, &declared); + yml = xml; + + while ((yml = yml->parent) != NULL) { + JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); + for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace); + if (ns && + !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); + if (!ok) + goto out; + } + } + } + + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + if (!ns->declared) + continue; + if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &declared.array, ns); + if (!ok) + goto out; + } + } + + ok = TempNSArrayToJSArray(cx, &declared, rval); + +out: + /* Finishing must be in reverse order of initialization to follow LIFO. */ + FinishTempNSArray(cx, &declared); + FinishTempNSArray(cx, &ancestors); + return ok; +} + +static const char js_attribute_str[] = "attribute"; +static const char js_text_str[] = "text"; + +/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */ +const char *js_xml_class_str[] = { + "list", + "element", + js_attribute_str, + "processing-instruction", + js_text_str, + "comment" +}; + +static JSBool +xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *str; + + NON_LIST_XML_METHOD_PROLOG; + str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id) +{ + jsval junk; + + if (xml->xml_class == JSXML_CLASS_LIST) + return DeleteProperty(cx, obj, id, &junk); + return DeleteByIndex(cx, xml, id, &junk); +} + +/* + * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace + * text between tags to be removed by normalize. + */ +static JSBool +IsXMLSpace(JSString *str) +{ + const jschar *cp, *end; + + cp = JSSTRING_CHARS(str); + end = cp + JSSTRING_LENGTH(str); + while (cp < end) { + if (!JS_ISXMLSPACE(*cp)) + return JS_FALSE; + ++cp; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid, *kid2; + uint32 i, n; + JSObject *kidobj; + JSString *str; + jsval junk; + + XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk)) + return JS_FALSE; + } else if (kid->xml_class == JSXML_CLASS_TEXT) { + while (i + 1 < n && + (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && + kid2->xml_class == JSXML_CLASS_TEXT) { + str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); + if (!str) + return JS_FALSE; + if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1))) + return JS_FALSE; + n = xml->xml_kids.length; + kid->xml_value = str; + } + if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) { + if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i))) + return JS_FALSE; + n = xml->xml_kids.length; + --i; + } + } + } + + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *parent, *kid; + uint32 i, n; + JSObject *parentobj; + + XML_METHOD_PROLOG; + parent = xml->parent; + if (xml->xml_class == JSXML_CLASS_LIST) { + *rval = JSVAL_VOID; + n = xml->xml_kids.length; + if (n == 0) + return JS_TRUE; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!kid) + return JS_TRUE; + parent = kid->parent; + for (i = 1; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->parent != parent) + return JS_TRUE; + } + } + + if (!parent) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + parentobj = js_GetXMLObject(cx, parent); + if (!parentobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(parentobj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + JSXMLQName *nameqn; + jsid funid; + JSBool ok; + JSXMLArrayCursor cursor; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameqn->object); + + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + if (funid) + return JS_TRUE; + + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_processingInstructions(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + XMLArrayCursorFinish(&cursor); + } else { + /* 13.4.4.28 Step 4. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && + (IS_STAR(nameqn->localName) || + js_EqualStrings(nameqn->localName, kid->name->localName))) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +static JSBool +xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + NON_LIST_XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return Insert(cx, xml, 0, argv[0]); +} + +/* XML and XMLList */ +static JSBool +xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + jsval name; + uint32 index; + + XML_METHOD_PROLOG; + name = argv[0]; + *rval = JSVAL_FALSE; + if (js_IdIsIndex(name, &index)) { + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.18. */ + *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); + } else { + /* 13.4.4.30. */ + *rval = BOOLEAN_TO_JSVAL(index == 0); + } + } + return JS_TRUE; +} + +static JSBool +namespace_full_match(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsa->prefix && nsb->prefix && + !js_EqualStrings(nsa->prefix, nsb->prefix)) { + return JS_FALSE; + } + return js_EqualStrings(nsa->uri, nsb->uri); +} + +static JSBool +xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) +{ + JSXMLNamespace *thisns, *attrns; + uint32 i, n; + JSXML *attr, *kid; + + thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); + JS_ASSERT(thisns); + if (thisns == ns) + return JS_TRUE; + + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); + JS_ASSERT(attrns); + if (attrns == ns) + return JS_TRUE; + } + + i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + if (!xml_removeNamespace_helper(cx, kid, ns)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSObject *nsobj; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); + if (!nsobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nsobj); + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + + /* NOTE: remove ns from each ancestor if not used by that ancestor. */ + return xml_removeNamespace_helper(cx, xml, ns); +} + +static JSBool +xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *vxml, *kid; + jsval name, value, id, junk; + uint32 index; + JSObject *nameobj; + JSXMLQName *nameqn; + + NON_LIST_XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + value = argv[1]; + vxml = VALUE_IS_XML(cx, value) + ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value)) + : NULL; + if (!vxml) { + if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1])) + return JS_FALSE; + value = argv[1]; + } else { + vxml = DeepCopy(cx, vxml, NULL, 0); + if (!vxml) + return JS_FALSE; + value = argv[1] = OBJECT_TO_JSVAL(vxml->object); + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + name = argv[0]; + if (js_IdIsIndex(name, &index)) + return Replace(cx, xml, name, value); + + /* Call function QName per spec, not ToXMLName, to avoid attribute names. */ + nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name); + if (!nameobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameobj); + nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); + + id = JSVAL_VOID; + index = xml->xml_kids.length; + while (index != 0) { + --index; + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk)) + return JS_FALSE; + if (!IndexToIdVal(cx, index, &id)) + return JS_FALSE; + } + } + if (JSVAL_IS_VOID(id)) + return JS_TRUE; + return Replace(cx, xml, id, value); +} + +static JSBool +xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + if (!StartNonListXMLMethod(cx, &obj, argv)) + return JS_FALSE; + + if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), + &argv[0])) { + return JS_FALSE; + } + + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + jsval name; + JSXMLQName *nameqn; + JSString *namestr; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + name = argv[0]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { + nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)); + namestr = nameqn->localName; + } else { + if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0])) + return JS_FALSE; + name = argv[0]; + namestr = JSVAL_TO_STRING(name); + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name->localName = namestr; + return JS_TRUE; +} + +static JSBool +xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *nsowner; + jsval name; + JSXMLQName *nameqn; + JSObject *nameobj; + JSXMLArray *nsarray; + uint32 i, n; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + name = argv[0]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && + !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name))) + ->uri) { + name = argv[0] = STRING_TO_JSVAL(nameqn->localName); + } + + nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); + if (!nameobj) + return JS_FALSE; + nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); + + /* ECMA-357 13.4.4.35 Step 4. */ + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + nameqn->uri = cx->runtime->emptyString; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name = nameqn; + + /* + * Erratum: nothing in 13.4.4.35 talks about making the name match the + * in-scope namespaces, either by finding an in-scope namespace with a + * matching uri and setting the new name's prefix to that namespace's + * prefix, or by extending the in-scope namespaces for xml (which are in + * xml->parent if xml is an attribute or a PI). + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + + if (nameqn->prefix) { + /* + * The name being set has a prefix, which originally came from some + * namespace object (which may be the null namespace, where both the + * prefix and uri are the empty string). We must go through a full + * GetNamespace in case that namespace is in-scope in nsowner. + * + * If we find such an in-scope namespace, we return true right away, + * in this block. Otherwise, we fall through to the final return of + * AddInScopeNamespace(cx, nsowner, ns). + */ + ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); + if (!ns) + return JS_FALSE; + + /* XXXbe have to test membership to see whether GetNamespace added */ + if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) + return JS_TRUE; + } else { + /* + * At this point, we know nameqn->prefix is null, so nameqn->uri can't + * be the empty string (the null namespace always uses the empty string + * for both prefix and uri). + * + * This means we must inline GetNamespace and specialize it to match + * uri only, never prefix. If we find a namespace with nameqn's uri + * already in nsowner->xml_namespaces, then all that we need do is set + * nameqn->prefix to that namespace's prefix. + * + * If no such namespace exists, we can create one without going through + * the constructor, because we know nameqn->uri is non-empty (so prefix + * does not need to be converted from null to empty by QName). + */ + JS_ASSERT(!IS_EMPTY(nameqn->uri)); + + nsarray = &nsowner->xml_namespaces; + for (i = 0, n = nsarray->length; i < n; i++) { + ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); + if (ns && js_EqualStrings(ns->uri, nameqn->uri)) { + nameqn->prefix = ns->prefix; + return JS_TRUE; + } + } + + ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE); + if (!ns) + return JS_FALSE; + } + + return AddInScopeNamespace(cx, nsowner, ns); +} + +static JSBool +xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *nsowner; + JSObject *nsobj, *qnobj; + JSXMLNamespace *ns; + jsval qnargv[2]; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml || !js_GetXMLQNameObject(cx, xml->name)) + return JS_FALSE; + + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv); + if (!nsobj) + return JS_FALSE; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + ns->declared = JS_TRUE; + + qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj); + qnargv[1] = OBJECT_TO_JSVAL(xml->name->object); + qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); + if (!qnobj) + return JS_FALSE; + + xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj); + + /* + * Erratum: the spec fails to update the governing in-scope namespaces. + * See the erratum noted in xml_setName, above. + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + return AddInScopeNamespace(cx, nsowner, ns); +} + +/* XML and XMLList */ +static JSBool +xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + uint32 i, n; + JSBool ok; + JSObject *kidobj; + jsval v; + + XML_METHOD_PROLOG; + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_text(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + return JS_FALSE; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) + return JS_FALSE; + } + } + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_TEXT) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = ToXMLString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSString * +xml_toString_helper(JSContext *cx, JSXML *xml) +{ + JSString *str, *kidstr; + JSXML *kid; + JSXMLArrayCursor cursor; + + if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || + xml->xml_class == JSXML_CLASS_TEXT) { + return xml->xml_value; + } + + if (!HasSimpleContent(xml)) + return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); + + str = cx->runtime->emptyString; + js_EnterLocalRootScope(cx); + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class != JSXML_CLASS_COMMENT && + kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { + kidstr = xml_toString_helper(cx, kid); + if (!kidstr) { + str = NULL; + break; + } + str = js_ConcatStrings(cx, str, kidstr); + if (!str) + break; + } + } + XMLArrayCursorFinish(&cursor); + js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + return str; +} + +static JSBool +xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *str; + + XML_METHOD_PROLOG; + str = xml_toString_helper(cx, xml); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec xml_methods[] = { + {"addNamespace", xml_addNamespace, 1,0,0}, + {"appendChild", xml_appendChild, 1,0,0}, + {js_attribute_str, xml_attribute, 1,0,0}, + {"attributes", xml_attributes, 0,0,0}, + {"child", xml_child, 1,0,0}, + {"childIndex", xml_childIndex, 0,0,0}, + {"children", xml_children, 0,0,0}, + {"comments", xml_comments, 0,0,0}, + {"contains", xml_contains, 1,0,0}, + {"copy", xml_copy, 0,0,0}, + {"descendants", xml_descendants, 1,0,0}, + {"elements", xml_elements, 1,0,0}, + {"hasOwnProperty", xml_hasOwnProperty, 1,0,0}, + {"hasComplexContent", xml_hasComplexContent, 1,0,0}, + {"hasSimpleContent", xml_hasSimpleContent, 1,0,0}, + {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,0}, + {"insertChildAfter", xml_insertChildAfter, 2,0,0}, + {"insertChildBefore", xml_insertChildBefore, 2,0,0}, + {js_length_str, xml_length, 0,0,0}, + {js_localName_str, xml_localName, 0,0,0}, + {js_name_str, xml_name, 0,0,0}, + {js_namespace_str, xml_namespace, 1,0,0}, + {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0}, + {"nodeKind", xml_nodeKind, 0,0,0}, + {"normalize", xml_normalize, 0,0,0}, + {js_xml_parent_str, xml_parent, 0,0,0}, + {"processingInstructions",xml_processingInstructions,1,0,0}, + {"prependChild", xml_prependChild, 1,0,0}, + {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,0}, + {"removeNamespace", xml_removeNamespace, 1,0,0}, + {"replace", xml_replace, 2,0,0}, + {"setChildren", xml_setChildren, 1,0,0}, + {"setLocalName", xml_setLocalName, 1,0,0}, + {"setName", xml_setName, 1,0,0}, + {"setNamespace", xml_setNamespace, 1,0,0}, + {js_text_str, xml_text, 0,0,0}, + {js_toString_str, xml_toString, 0,0,0}, + {js_toXMLString_str, xml_toXMLString, 0,0,0}, + {js_toSource_str, xml_toXMLString, 0,0,0}, + {js_valueOf_str, xml_valueOf, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) +{ + int i; + const char *name; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + } + + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +SetDefaultXMLSettings(JSContext *cx, JSObject *obj) +{ + int i; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + v = JSVAL_TRUE; + if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) + return JS_FALSE; + } + v = INT_TO_JSVAL(2); + return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); +} + +static JSBool +xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return CopyXMLSettings(cx, obj, settings); +} + +static JSBool +xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval v; + JSBool ok; + JSObject *settings; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + cx->xmlSettingFlags = 0; + ok = SetDefaultXMLSettings(cx, obj); + } else { + if (JSVAL_IS_PRIMITIVE(v)) + return JS_TRUE; + settings = JSVAL_TO_OBJECT(v); + cx->xmlSettingFlags = 0; + ok = CopyXMLSettings(cx, settings, obj); + } + if (ok) + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return ok; +} + +static JSBool +xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return SetDefaultXMLSettings(cx, settings); +} + +static JSFunctionSpec xml_static_methods[] = { + {"settings", xml_settings, 0,0,0}, + {"setSettings", xml_setSettings, 1,0,0}, + {"defaultSettings", xml_defaultSettings, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSXML *xml, *copy; + JSObject *xobj, *vobj; + JSClass *clasp; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + xobj = ToXML(cx, v); + if (!xobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(xobj); + xml = (JSXML *) JS_GetPrivate(cx, xobj); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, vobj); + if (clasp == &js_XMLClass || + (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { + /* No need to lock obj, it's newly constructed and thread local. */ + copy = DeepCopy(cx, xml, obj, 0); + if (!copy) + return JS_FALSE; + JS_ASSERT(copy->object == obj); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool +XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSObject *vobj, *listobj; + JSXML *xml, *list; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + xml = (JSXML *) JS_GetPrivate(cx, vobj); + if (xml->xml_class == JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return JS_FALSE; + return JS_TRUE; + } + } + } + + /* Toggle on XML support since the script has explicitly requested it. */ + listobj = ToXMLList(cx, v); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + return JS_TRUE; +} + +#define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar)) +#define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLVar)) +#define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) + +static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { + JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ + JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */ + JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */ +}; + +#ifdef DEBUG_notme +JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); +uint32 xml_serial; +#endif + +JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + + xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]); + if (!xml) + return NULL; + + xml->object = NULL; + xml->domnode = NULL; + xml->parent = NULL; + xml->name = NULL; + xml->xml_class = xml_class; + xml->xml_flags = 0; + if (JSXML_CLASS_HAS_VALUE(xml_class)) { + xml->xml_value = cx->runtime->emptyString; + } else { + XMLArrayInit(cx, &xml->xml_kids, 0); + if (xml_class == JSXML_CLASS_LIST) { + xml->xml_target = NULL; + xml->xml_targetprop = NULL; + } else { + XMLArrayInit(cx, &xml->xml_namespaces, 0); + XMLArrayInit(cx, &xml->xml_attrs, 0); + } + } + +#ifdef DEBUG_notme + JS_APPEND_LINK(&xml->links, &xml_leaks); + xml->serial = xml_serial++; +#endif + METER(xml_stats.xml); + METER(xml_stats.livexml); + return xml; +} + +void +js_MarkXML(JSContext *cx, JSXML *xml) +{ + GC_MARK(cx, xml->object, "object"); + GC_MARK(cx, xml->name, "name"); + GC_MARK(cx, xml->parent, "xml_parent"); + + if (JSXML_HAS_VALUE(xml)) { + GC_MARK(cx, xml->xml_value, "value"); + return; + } + + xml_mark_vector(cx, + (JSXML **) xml->xml_kids.vector, + xml->xml_kids.length); + XMLArrayCursorMark(cx, xml->xml_kids.cursors); + XMLArrayTrim(&xml->xml_kids); + + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_target) + GC_MARK(cx, xml->xml_target, "target"); + if (xml->xml_targetprop) + GC_MARK(cx, xml->xml_targetprop, "targetprop"); + } else { + namespace_mark_vector(cx, + (JSXMLNamespace **) xml->xml_namespaces.vector, + xml->xml_namespaces.length); + XMLArrayCursorMark(cx, xml->xml_namespaces.cursors); + XMLArrayTrim(&xml->xml_namespaces); + + xml_mark_vector(cx, + (JSXML **) xml->xml_attrs.vector, + xml->xml_attrs.length); + XMLArrayCursorMark(cx, xml->xml_attrs.cursors); + XMLArrayTrim(&xml->xml_attrs); + } +} + +void +js_FinalizeXML(JSContext *cx, JSXML *xml) +{ + if (JSXML_HAS_KIDS(xml)) { + XMLArrayFinish(cx, &xml->xml_kids); + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + XMLArrayFinish(cx, &xml->xml_namespaces); + XMLArrayFinish(cx, &xml->xml_attrs); + } + } + +#ifdef DEBUG_notme + JS_REMOVE_LINK(&xml->links); +#endif + + UNMETER(xml_stats.livexml); +} + +JSObject * +js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn) +{ + jsval nsval; + JSXMLNamespace *ns; + JSXMLArray nsarray; + JSXML *xml; + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + + if (!XMLArrayInit(cx, &nsarray, 1)) + return NULL; + + XMLARRAY_APPEND(cx, &nsarray, ns); + xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT); + XMLArrayFinish(cx, &nsarray); + if (!xml) + return NULL; + + return xml->object; +} + +JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + JSObject *obj; + JSTempValueRooter tvr; + + xml = js_NewXML(cx, xml_class); + if (!xml) + return NULL; + JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr); + obj = js_GetXMLObject(cx, xml); + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; +} + +static JSObject * +NewXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, xml)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + METER(xml_stats.xmlobj); + METER(xml_stats.livexmlobj); + return obj; +} + +JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = xml->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == xml); + return obj; + } + + /* + * A JSXML cannot be shared among threads unless it has an object. + * A JSXML cannot be given an object unless: + * (a) it has no parent; or + * (b) its parent has no object (therefore is thread-private); or + * (c) its parent's object is locked. + * + * Once given an object, a JSXML is immutable. + */ + JS_ASSERT(!xml->parent || + !xml->parent->object || + JS_IS_OBJ_LOCKED(cx, xml->parent->object)); + + obj = NewXMLObject(cx, xml); + if (!obj) + return NULL; + xml->object = obj; + return obj; +} + +JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, + namespace_props, namespace_methods, NULL, NULL); +} + +JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj) +{ + jsval v; + + if (!js_GetAnyName(cx, &v)) + return NULL; + return JSVAL_TO_OBJECT(v); +} + +JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *pobj, *ctor; + JSFunction *fun; + JSXML *xml; + JSProperty *prop; + JSScopeProperty *sprop; + jsval cval, argv[1], junk; + + /* Define the isXMLName function. */ + if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) + return NULL; + + /* Define the XML class constructor and prototype. */ + proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, + NULL, xml_methods, + xml_static_props, xml_static_methods); + if (!proto) + return NULL; + + xml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!xml || !JS_SetPrivate(cx, proto, xml)) + return NULL; + xml->object = proto; + METER(xml_stats.xmlobj); + METER(xml_stats.livexmlobj); + + /* + * Prepare to set default settings on the XML constructor we just made. + * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, + * which is xml_getProperty, which creates a new XMLList every time! We + * must instead call js_LookupProperty directly. + */ + if (!js_LookupProperty(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), + &pobj, &prop)) { + return NULL; + } + JS_ASSERT(prop); + sprop = (JSScopeProperty *) prop; + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); + OBJ_DROP_PROPERTY(cx, pobj, prop); + JS_ASSERT(VALUE_IS_FUNCTION(cx, cval)); + + /* Set default settings. */ + ctor = JSVAL_TO_OBJECT(cval); + argv[0] = JSVAL_VOID; + if (!xml_setSettings(cx, ctor, 1, argv, &junk)) + return NULL; + + /* Define the XMLList function and give it the same prototype as XML. */ + fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); + if (!fun) + return NULL; + if (!js_SetClassPrototype(cx, fun->object, proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + return NULL; + } + return proto; +} + +JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj) +{ + if (!js_InitNamespaceClass(cx, obj)) + return NULL; + if (!js_InitQNameClass(cx, obj)) + return NULL; + if (!js_InitAttributeNameClass(cx, obj)) + return NULL; + if (!js_InitAnyNameClass(cx, obj)) + return NULL; + return js_InitXMLClass(cx, obj); +} + +JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSAtom *atom; + JSString *prefix, *uri; + + /* An invalid URI, for internal use only, guaranteed not to collide. */ + static const char anti_uri[] = "@mozilla.org/js/function"; + + /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ + rt = cx->runtime; + obj = rt->functionNamespaceObject; + if (!obj) { + JS_LOCK_GC(rt); + obj = rt->functionNamespaceObject; + if (!obj) { + JS_UNLOCK_GC(rt); + atom = js_Atomize(cx, js_function_str, 8, 0); + JS_ASSERT(atom); + prefix = ATOM_TO_STRING(atom); + + /* + * Note that any race to atomize anti_uri here is resolved by + * the atom table code, such that at most one atom for anti_uri + * is created. We store in rt->atomState.lazy unconditionally, + * since we are guaranteed to overwrite either null or the same + * atom pointer. + */ + atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); + if (!atom) + return JS_FALSE; + rt->atomState.lazy.functionNamespaceURIAtom = atom; + + uri = ATOM_TO_STRING(atom); + obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE); + if (!obj) + return JS_FALSE; + + /* + * Avoid entraining any in-scope Object.prototype. The loss of + * Namespace.prototype is not detectable, as there is no way to + * refer to this instance in scripts. When used to qualify method + * names, its prefix and uri references are copied to the QName. + */ + OBJ_SET_PROTO(cx, obj, NULL); + OBJ_SET_PARENT(cx, obj, NULL); + + JS_LOCK_GC(rt); + if (!rt->functionNamespaceObject) + rt->functionNamespaceObject = obj; + else + obj = rt->functionNamespaceObject; + } + JS_UNLOCK_GC(rt); + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* + * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- + * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, + * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a + * lightweight function activation). There's no requirement that fp->varobj + * lie directly on fp->scopeChain, although it should be reachable using the + * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). + * + * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it + * creates a default namespace via 'new Namespace()'. In contrast, Set uses + * its v argument as the uri of a new Namespace, with "" as the prefix. See + * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, + * the default XML namespace will be set to ("", n.uri). So the uri string + * is really the only usefully stored value of the default namespace. + */ +JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) +{ + JSStackFrame *fp; + JSObject *nsobj, *obj, *tmp; + jsval v; + + fp = cx->fp; + nsobj = fp->xmlNamespace; + if (nsobj) { + *vp = OBJECT_TO_JSVAL(nsobj); + return JS_TRUE; + } + + obj = NULL; + for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { + obj = tmp; + if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + *vp = v; + return JS_TRUE; + } + } + + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + if (obj && + !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v, + JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } + fp->xmlNamespace = nsobj; + *vp = v; + return JS_TRUE; +} + +JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v) +{ + jsval argv[2]; + JSObject *nsobj, *varobj; + JSStackFrame *fp; + + argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); + argv[1] = v; + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, + 2, argv); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + + fp = cx->fp; + varobj = fp->varobj; + if (varobj) { + if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v, + JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } + } else { + JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags)); + } + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + return JS_TRUE; +} + +JSBool +js_ToAttributeName(JSContext *cx, jsval *vp) +{ + JSXMLQName *qn; + + qn = ToAttributeName(cx, *vp); + if (!qn) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(qn->object); + return JS_TRUE; +} + +JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str) +{ + return EscapeAttributeValue(cx, NULL, str); +} + +JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) +{ + size_t len, len2, newlen; + jschar *chars; + + if (JSSTRING_IS_DEPENDENT(str) || + !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) { + str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), + 0); + if (!str) + return NULL; + } + + len = str->length; + len2 = JSSTRING_LENGTH(str2); + newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; + chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar)); + if (!chars) + return NULL; + + /* + * Reallocating str (because we know it has no other references) requires + * purging any deflated string cached for it. + */ + js_PurgeDeflatedStringCache(cx->runtime, str); + + str->chars = chars; + str->length = newlen; + chars += len; + if (isName) { + *chars++ = ' '; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + } else { + *chars++ = '='; + *chars++ = '"'; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + *chars++ = '"'; + } + *chars = 0; + return str; +} + +JSString * +js_EscapeElementValue(JSContext *cx, JSString *str) +{ + return EscapeElementValue(cx, NULL, str); +} + +JSString * +js_ValueToXMLString(JSContext *cx, jsval v) +{ + return ToXMLString(cx, v); +} + +static JSBool +anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = ATOM_KEY(cx->runtime->atomState.starAtom); + return JS_TRUE; +} + +JSBool +js_GetAnyName(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSXMLQName *qn; + JSBool ok; + + /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ + rt = cx->runtime; + obj = rt->anynameObject; + if (!obj) { + JS_LOCK_GC(rt); + obj = rt->anynameObject; + if (!obj) { + JS_UNLOCK_GC(rt); + + /* + * Protect multiple newborns created below, in the do-while(0) + * loop used to ensure that we leave this local root scope. + */ + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + do { + qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, + ATOM_TO_STRING(rt->atomState.starAtom)); + if (!qn) { + ok = JS_FALSE; + break; + } + + obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + ok = JS_FALSE; + break; + } + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + + /* + * Avoid entraining any Object.prototype found via cx's scope + * chain or global object. This loses the default toString, + * but no big deal: we want to customize toString anyway for + * clearer diagnostics. + */ + if (!JS_DefineFunction(cx, obj, js_toString_str, + anyname_toString, 0, 0)) { + ok = JS_FALSE; + break; + } + OBJ_SET_PROTO(cx, obj, NULL); + JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); + } while (0); + + js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj)); + if (!ok) + return JS_FALSE; + + JS_LOCK_GC(rt); + if (!rt->anynameObject) + rt->anynameObject = obj; + else + obj = rt->anynameObject; + } + JS_UNLOCK_GC(rt); + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) +{ + JSXMLQName *qn; + jsid funid, id; + JSObject *obj, *pobj, *lastobj; + JSProperty *prop; + const char *printable; + + qn = ToXMLName(cx, name, &funid); + if (!qn) + return JS_FALSE; + id = OBJECT_TO_JSID(qn->object); + + obj = cx->fp->scopeChain; + do { + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + + /* + * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML + * object to carry scope chain linkage in js_FilterXMLList. + */ + pobj = OBJ_THIS_OBJECT(cx, obj); + if (OBJECT_IS_XML(cx, pobj)) { + *objp = pobj; + *namep = ID_TO_VALUE(id); + return JS_TRUE; + } + } + + lastobj = obj; + } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); + + printable = js_ValueToPrintableString(cx, name); + if (printable) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UNDEFINED_XML_NAME, printable); + } + return JS_FALSE; +} + +JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + return GetProperty(cx, obj, name, vp); +} + +JSBool +js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *target; + JSXML *xml; + JSTempValueRooter tvr; + JSBool ok; + + JS_ASSERT(OBJECT_IS_XML(cx, obj)); + + /* After this point, control must flow through label out: to exit. */ + JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); + + /* + * See comments before xml_lookupProperty about the need for the proto + * chain lookup. + */ + target = obj; + for (;;) { + ok = js_GetProperty(cx, target, id, vp); + if (!ok) + goto out; + if (VALUE_IS_FUNCTION(cx, *vp)) { + ok = JS_TRUE; + goto out; + } + target = OBJ_GET_PROTO(cx, target); + if (target == NULL) + break; + tvr.u.object = target; + } + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (HasSimpleContent(xml)) { + /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ + ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), + &tvr.u.object); + if (!ok) + goto out; + JS_ASSERT(tvr.u.object); + ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp); + } + + out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} + +JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + return PutProperty(cx, obj, name, vp); +} + +static JSXML * +GetPrivate(JSContext *cx, JSObject *obj, const char *method) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_METHOD, + js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); + } + return xml; +} + +JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list; + + xml = GetPrivate(cx, obj, "descendants internal method"); + if (!xml) + return JS_FALSE; + + list = Descendants(cx, xml, id); + if (!list) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) +{ + JSXML *list; + uint32 n; + jsval junk; + + list = (JSXML *) JS_GetPrivate(cx, listobj); + for (n = list->xml_kids.length; n != 0; --n) { + if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk)) + return JS_FALSE; + } + return JS_TRUE; +} + +JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) +{ + JSBool ok, match; + JSStackFrame *fp; + uint32 flags; + JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; + JSXML *xml, *list, *result, *kid; + JSXMLArrayCursor cursor; + + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + /* All control flow after this point must exit via label out or bad. */ + *vp = JSVAL_NULL; + fp = cx->fp; + flags = fp->flags; + fp->flags = flags | JSFRAME_FILTERING; + scobj = js_GetScopeChain(cx, fp); + withobj = NULL; + if (!scobj) + goto bad; + xml = GetPrivate(cx, obj, "filtering predicate operator"); + if (!xml) + goto bad; + + if (xml->xml_class == JSXML_CLASS_LIST) { + list = xml; + } else { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + goto bad; + list = (JSXML *) JS_GetPrivate(cx, listobj); + ok = Append(cx, list, xml); + if (!ok) + goto out; + } + + resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!resobj) + goto bad; + result = (JSXML *) JS_GetPrivate(cx, resobj); + + /* Hoist the scope chain update out of the loop over kids. */ + withobj = js_NewWithObject(cx, NULL, scobj, -1); + if (!withobj) + goto bad; + fp->scopeChain = withobj; + + XMLArrayCursorInit(&cursor, &list->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + break; + OBJ_SET_PROTO(cx, withobj, kidobj); + ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match); + if (ok && match) + ok = Append(cx, result, kid); + if (!ok) + break; + } + XMLArrayCursorFinish(&cursor); + if (!ok) + goto out; + if (kid) + goto bad; + + *vp = OBJECT_TO_JSVAL(resobj); + +out: + fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS); + if (withobj) { + fp->scopeChain = scobj; + JS_SetPrivate(cx, withobj, NULL); + } + js_LeaveLocalRootScopeWithResult(cx, *vp); + return ok; +bad: + ok = JS_FALSE; + goto out; +} + +JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v) +{ + return ToXML(cx, v); +} + +JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v) +{ + return ToXMLList(cx, v); +} + +JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj) +{ + uintN flags; + JSXML *xml; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (flags & (XSF_IGNORE_COMMENTS | + XSF_IGNORE_PROCESSING_INSTRUCTIONS | + XSF_IGNORE_WHITESPACE)) { + xml = DeepCopy(cx, xml, NULL, flags); + if (!xml) + return NULL; + return xml->object; + } + return NewXMLObject(cx, xml); +} + +JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value) +{ + uintN flags; + JSObject *obj; + JSXML *xml; + JSXMLQName *qn; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + + if ((xml_class == JSXML_CLASS_COMMENT && + (flags & XSF_IGNORE_COMMENTS)) || + (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && + (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { + return js_NewXMLObject(cx, JSXML_CLASS_TEXT); + } + + obj = js_NewXMLObject(cx, xml_class); + if (!obj) + return NULL; + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (name) { + qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name); + if (!qn) + return NULL; + xml->name = qn; + } + xml->xml_value = value; + return obj; +} + +JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str) +{ + return MakeXMLCDATAString(cx, NULL, str); +} + +JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str) +{ + return MakeXMLCommentString(cx, NULL, str); +} + +JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) +{ + return MakeXMLPIString(cx, NULL, name, str); +} + +#endif /* JS_HAS_XML_SUPPORT */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxml.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxml.h new file mode 100644 index 0000000..71e591a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/jsxml.h @@ -0,0 +1,332 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxml_h___ +#define jsxml_h___ + +#include "jsstddef.h" +#include "jspubtd.h" + +extern const char js_AnyName_str[]; +extern const char js_AttributeName_str[]; +extern const char js_isXMLName_str[]; +extern const char js_XMLList_str[]; + +extern const char js_amp_entity_str[]; +extern const char js_gt_entity_str[]; +extern const char js_lt_entity_str[]; +extern const char js_quot_entity_str[]; + +struct JSXMLNamespace { + JSObject *object; + JSString *prefix; + JSString *uri; + JSBool declared; /* true if declared in its XML tag */ +}; + +extern JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared); + +extern void +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns); + +extern void +js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns); + +extern JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared); + +extern JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns); + +struct JSXMLQName { + JSObject *object; + JSString *uri; + JSString *prefix; + JSString *localName; +}; + +extern JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern void +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn); + +extern void +js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval); + +typedef JSBool +(* JS_DLL_CALLBACK JSIdentityOp)(const void *a, const void *b); + +struct JSXMLArray { + uint32 length; + uint32 capacity; + void **vector; + JSXMLArrayCursor *cursors; +}; + +#define JSXML_PRESET_CAPACITY JS_BIT(31) +#define JSXML_CAPACITY_MASK JS_BITMASK(31) +#define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) + +struct JSXMLArrayCursor { + JSXMLArray *array; + uint32 index; + JSXMLArrayCursor *next; + JSXMLArrayCursor **prevp; + void *root; +}; + +/* + * NB: don't reorder this enum without changing all array initializers that + * depend on it in jsxml.c. + */ +typedef enum JSXMLClass { + JSXML_CLASS_LIST, + JSXML_CLASS_ELEMENT, + JSXML_CLASS_ATTRIBUTE, + JSXML_CLASS_PROCESSING_INSTRUCTION, + JSXML_CLASS_TEXT, + JSXML_CLASS_COMMENT, + JSXML_CLASS_LIMIT +} JSXMLClass; + +#define JSXML_CLASS_HAS_KIDS(class_) ((class_) < JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_VALUE(class_) ((class_) >= JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_NAME(class_) \ + ((uintN)((class_) - JSXML_CLASS_ELEMENT) <= \ + (uintN)(JSXML_CLASS_PROCESSING_INSTRUCTION - JSXML_CLASS_ELEMENT)) + +#ifdef DEBUG_notme +#include "jsclist.h" +#endif + +struct JSXML { +#ifdef DEBUG_notme + JSCList links; + uint32 serial; +#endif + JSObject *object; + void *domnode; /* DOM node if mapped info item */ + JSXML *parent; + JSXMLQName *name; + uint16 xml_class; /* discriminates u, below */ + uint16 xml_flags; /* flags, see below */ + union { + struct JSXMLListVar { + JSXMLArray kids; /* NB: must come first */ + JSXML *target; + JSXMLQName *targetprop; + } list; + struct JSXMLVar { + JSXMLArray kids; /* NB: must come first */ + JSXMLArray namespaces; + JSXMLArray attrs; + } elem; + JSString *value; + } u; + + /* Don't add anything after u -- see js_NewXML for why. */ +}; + +/* union member shorthands */ +#define xml_kids u.list.kids +#define xml_target u.list.target +#define xml_targetprop u.list.targetprop +#define xml_namespaces u.elem.namespaces +#define xml_attrs u.elem.attrs +#define xml_value u.value + +/* xml_flags values */ +#define XMLF_WHITESPACE_TEXT 0x1 + +/* xml_class-testing macros */ +#define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) +#define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) +#define JSXML_HAS_NAME(xml) JSXML_CLASS_HAS_NAME((xml)->xml_class) +#define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ + ? (xml)->xml_kids.length \ + : 0) + +extern JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class); + +extern void +js_MarkXML(JSContext *cx, JSXML *xml); + +extern void +js_FinalizeXML(JSContext *cx, JSXML *xml); + +extern JSObject * +js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn); + +extern JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); + +extern JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml); + +extern JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps; +extern JS_FRIEND_DATA(JSClass) js_XMLClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass; +extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass; +extern JS_FRIEND_DATA(JSClass) js_AnyNameClass; + +/* + * Macros to test whether an object or a value is of type "xml" (per typeof). + * NB: jsapi.h must be included before any call to VALUE_IS_XML. + */ +#define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps.base) +#define VALUE_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ + OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) + +extern JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj); + +extern JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v); + +/* + * Return true if v is a XML QName object, or if it converts to a string that + * contains a valid XML qualified name (one containing no :), false otherwise. + * NB: This function is an infallible predicate, it hides exceptions. + */ +extern JSBool +js_IsXMLName(JSContext *cx, jsval v); + +extern JSBool +js_ToAttributeName(JSContext *cx, jsval *vp); + +extern JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str); + +extern JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, + JSString *str2); + +extern JSString * +js_EscapeElementValue(JSContext *cx, JSString *str); + +extern JSString * +js_ValueToXMLString(JSContext *cx, jsval v); + +extern JSBool +js_GetAnyName(JSContext *cx, jsval *vp); + +extern JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep); + +extern JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); + +extern JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp); + +extern JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v); + +extern JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v); + +extern JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj); + +extern JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value); + +extern JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str); + +#endif /* jsxml_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/perfect.js b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/perfect.js new file mode 100644 index 0000000..aeca121 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/perfect.js @@ -0,0 +1,39 @@ +// Some simple testing of new, eval and some string stuff. + +// constructor -- expression array initialization +function ExprArray(n,v) +{ + // Initializes n values to v coerced to a string. + for (var i = 0; i < n; i++) { + this[i] = "" + v; + } +} + + +// Print the perfect numbers up to n and the sum expression for n's divisors. +function perfect(n) +{ + print("The perfect numbers up to " + n + " are:"); + + // We build sumOfDivisors[i] to hold a string expression for + // the sum of the divisors of i, excluding i itself. + var sumOfDivisors = new ExprArray(n+1,1); + for (var divisor = 2; divisor <= n; divisor++) { + for (var j = divisor + divisor; j <= n; j += divisor) { + sumOfDivisors[j] += " + " + divisor; + } + // At this point everything up to 'divisor' has its sumOfDivisors + // expression calculated, so we can determine whether it's perfect + // already by evaluating. + if (eval(sumOfDivisors[divisor]) == divisor) { + print("" + divisor + " = " + sumOfDivisors[divisor]); + } + } + print("That's all."); +} + + +print("\nA number is 'perfect' if it is equal to the sum of its") +print("divisors (excluding itself).\n"); +perfect(500); + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/plify_jsdhash.sed b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/plify_jsdhash.sed new file mode 100644 index 0000000..eff4901 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/plify_jsdhash.sed @@ -0,0 +1,33 @@ +/ * Double hashing implementation./a\ + * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! +/ * Double hashing, a la Knuth 6./a\ + * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! +s/jsdhash_h___/pldhash_h___/ +s/jsdhash\.bigdump/pldhash.bigdump/ +s/jstypes\.h/nscore.h/ +s/jsbit\.h/prbit.h/ +s/jsdhash\.h/pldhash.h/ +s/jsdhash\.c/pldhash.c/ +s/jsdhash:/pldhash:/ +s/jsutil\.h/nsDebug.h/ +s/JS_DHASH/PL_DHASH/g +s/JS_DHash/PL_DHash/g +s/JSDHash/PLDHash/g +s/JSHash/PLHash/g +s/uint32 /PRUint32/g +s/\([^U]\)int32 /\1PRInt32/g +s/uint16 /PRUint16/g +s/\([^U]\)int16 /\1PRInt16/g +s/uint32/PRUint32/g +s/\([^U]\)int32/\1PRInt32/g +s/uint16/PRUint16/g +s/\([^U]\)int16/\1PRInt16/g +s/JSBool/PRBool/g +s/extern JS_PUBLIC_API(\([^()]*\))/NS_COM_GLUE \1/ +s/JS_PUBLIC_API(\([^()]*\))/\1/ +s/JS_DLL_CALLBACK/PR_CALLBACK/ +s/JS_STATIC_DLL_CALLBACK/PR_STATIC_CALLBACK/ +s/JS_NewDHashTable/PL_NewDHashTable/ +s/JS_ASSERT(0)/NS_NOTREACHED("0")/ +s/\( *\)JS_ASSERT(\(.*\));/\1NS_ASSERTION(\2,\n\1 "\2");/ +s/JS_/PR_/g diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/prmjtime.c b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/prmjtime.c new file mode 100644 index 0000000..6e08423 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/prmjtime.c @@ -0,0 +1,440 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR time code. + */ +#include "jsstddef.h" +#ifdef SOLARIS +#define _REENTRANT 1 +#endif +#include +#include +#include "jstypes.h" +#include "jsutil.h" + +#include "jsprf.h" +#include "prmjtime.h" + +#define PRMJ_DO_MILLISECONDS 1 + +#ifdef XP_OS2 +#include +#endif +#ifdef XP_WIN +#include +#include +#endif + +#if defined(XP_UNIX) || defined(XP_BEOS) + +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ +extern int gettimeofday(struct timeval *tv); +#endif + +#include + +#endif /* XP_UNIX */ + +#define IS_LEAP(year) \ + (year != 0 && ((((year & 0x3) == 0) && \ + ((year - ((year/100) * 100)) != 0)) || \ + (year - ((year/400) * 400)) == 0)) + +#define PRMJ_HOUR_SECONDS 3600L +#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS) +#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L) +#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */ +/* function prototypes */ +static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm); +/* + * get the difference in seconds between this time zone and UTC (GMT) + */ +JSInt32 +PRMJ_LocalGMTDifference() +{ +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) + struct tm ltime; + + /* get the difference between this time zone and GMT */ + memset((char *)<ime,0,sizeof(ltime)); + ltime.tm_mday = 2; + ltime.tm_year = 70; +#ifdef SUNOS4 + ltime.tm_zone = 0; + ltime.tm_gmtoff = 0; + return timelocal(<ime) - (24 * 3600); +#else + return mktime(<ime) - (24L * 3600L); +#endif +#endif +} + +/* Constants for GMT offset from 1970 */ +#define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */ +#define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */ + +#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */ +#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */ + +/* Convert from base time to extended time */ +static JSInt64 +PRMJ_ToExtendedTime(JSInt32 base_time) +{ + JSInt64 exttime; + JSInt64 g1970GMTMicroSeconds; + JSInt64 low; + JSInt32 diff; + JSInt64 tmp; + JSInt64 tmp1; + + diff = PRMJ_LocalGMTDifference(); + JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC); + JSLL_I2L(tmp1,diff); + JSLL_MUL(tmp,tmp,tmp1); + + JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI); + JSLL_UI2L(low,G1970GMTMICROLOW); +#ifndef JS_HAVE_LONG_LONG + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); +#else + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32); +#endif + JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low); + + JSLL_I2L(exttime,base_time); + JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds); + JSLL_SUB(exttime,exttime,tmp); + return exttime; +} + +JSInt64 +PRMJ_Now(void) +{ +#ifdef XP_OS2 + JSInt64 s, us, ms2us, s2us; + struct timeb b; +#endif +#ifdef XP_WIN + JSInt64 s, us, + win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000), + ten = JSLL_INIT(0, 10); + FILETIME time, midnight; +#endif +#if defined(XP_UNIX) || defined(XP_BEOS) + struct timeval tv; + JSInt64 s, us, s2us; +#endif /* XP_UNIX */ + +#ifdef XP_OS2 + ftime(&b); + JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC); + JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); + JSLL_UI2L(s, b.time); + JSLL_UI2L(us, b.millitm); + JSLL_MUL(us, us, ms2us); + JSLL_MUL(s, s, s2us); + JSLL_ADD(s, s, us); + return s; +#endif +#ifdef XP_WIN + /* The windows epoch is around 1600. The unix epoch is around 1970. + win2un is the difference (in windows time units which are 10 times + more precise than the JS time unit) */ + GetSystemTimeAsFileTime(&time); + /* Win9x gets confused at midnight + http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423 + So if the low part (precision <8mins) is 0 then we get the time + again. */ + if (!time.dwLowDateTime) { + GetSystemTimeAsFileTime(&midnight); + time.dwHighDateTime = midnight.dwHighDateTime; + } + JSLL_UI2L(s, time.dwHighDateTime); + JSLL_UI2L(us, time.dwLowDateTime); + JSLL_SHL(s, s, 32); + JSLL_ADD(s, s, us); + JSLL_SUB(s, s, win2un); + JSLL_DIV(s, s, ten); + return s; +#endif + +#if defined(XP_UNIX) || defined(XP_BEOS) +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ + gettimeofday(&tv); +#else + gettimeofday(&tv, 0); +#endif /* _SVID_GETTOD */ + JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); + JSLL_UI2L(s, tv.tv_sec); + JSLL_UI2L(us, tv.tv_usec); + JSLL_MUL(s, s, s2us); + JSLL_ADD(s, s, us); + return s; +#endif /* XP_UNIX */ +} + +/* Get the DST timezone offset for the time passed in */ +JSInt64 +PRMJ_DSTOffset(JSInt64 local_time) +{ + JSInt64 us2s; + time_t local; + JSInt32 diff; + JSInt64 maxtimet; + struct tm tm; + PRMJTime prtm; +#ifndef HAVE_LOCALTIME_R + struct tm *ptm; +#endif + + + JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); + JSLL_DIV(local_time, local_time, us2s); + + /* get the maximum of time_t value */ + JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET); + + if(JSLL_CMP(local_time,>,maxtimet)){ + JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET); + } else if(!JSLL_GE_ZERO(local_time)){ + /*go ahead a day to make localtime work (does not work with 0) */ + JSLL_UI2L(local_time,PRMJ_DAY_SECONDS); + } + JSLL_L2UI(local,local_time); + PRMJ_basetime(local_time,&prtm); +#ifndef HAVE_LOCALTIME_R + ptm = localtime(&local); + if(!ptm){ + return JSLL_ZERO; + } + tm = *ptm; +#else + localtime_r(&local,&tm); /* get dst information */ +#endif + + diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) + + ((tm.tm_min - prtm.tm_min) * 60); + + if(diff < 0){ + diff += PRMJ_DAY_SECONDS; + } + + JSLL_UI2L(local_time,diff); + + JSLL_MUL(local_time,local_time,us2s); + + return(local_time); +} + +/* Format a time value into a buffer. Same semantics as strftime() */ +size_t +PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) +{ +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) + struct tm a; + + /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int + * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets + * confused and dumps core. NSPR20 prtime.c attempts to fill these in by + * calling mktime on the partially filled struct, but this doesn't seem to + * work as well; the result string has "can't get timezone" for ECMA-valid + * years. Might still make sense to use this, but find the range of years + * for which valid tz information exists, and map (per ECMA hint) from the + * given year into that range. + + * N.B. This hasn't been tested with anything that actually _uses_ + * tm_gmtoff; zero might be the wrong thing to set it to if you really need + * to format a time. This fix is for jsdate.c, which only uses + * JS_FormatTime to get a string representing the time zone. */ + memset(&a, 0, sizeof(struct tm)); + + a.tm_sec = prtm->tm_sec; + a.tm_min = prtm->tm_min; + a.tm_hour = prtm->tm_hour; + a.tm_mday = prtm->tm_mday; + a.tm_mon = prtm->tm_mon; + a.tm_wday = prtm->tm_wday; + a.tm_year = prtm->tm_year - 1900; + a.tm_yday = prtm->tm_yday; + a.tm_isdst = prtm->tm_isdst; + + /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff + * are null. This doesn't quite work, though - the timezone is off by + * tzoff + dst. (And mktime seems to return -1 for the exact dst + * changeover time.) + + */ + +#if defined(SUNOS4) + if (mktime(&a) == -1) { + /* Seems to fail whenever the requested date is outside of the 32-bit + * UNIX epoch. We could proceed at this point (setting a.tm_zone to + * "") but then strftime returns a string with a 2-digit field of + * garbage for the year. So we return 0 and hope jsdate.c + * will fall back on toString. + */ + return 0; + } +#endif + + return strftime(buf, buflen, fmt, &a); +#endif +} + +/* table for number of days in a month */ +static int mtab[] = { + /* jan, feb,mar,apr,may,jun */ + 31,28,31,30,31,30, + /* july,aug,sep,oct,nov,dec */ + 31,31,30,31,30,31 +}; + +/* + * basic time calculation functionality for localtime and gmtime + * setups up prtm argument with correct values based upon input number + * of seconds. + */ +static void +PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm) +{ + /* convert tsecs back to year,month,day,hour,secs */ + JSInt32 year = 0; + JSInt32 month = 0; + JSInt32 yday = 0; + JSInt32 mday = 0; + JSInt32 wday = 6; /* start on a Sunday */ + JSInt32 days = 0; + JSInt32 seconds = 0; + JSInt32 minutes = 0; + JSInt32 hours = 0; + JSInt32 isleap = 0; + JSInt64 result; + JSInt64 result1; + JSInt64 result2; + JSInt64 base; + + JSLL_UI2L(result,0); + JSLL_UI2L(result1,0); + JSLL_UI2L(result2,0); + + /* get the base time via UTC */ + base = PRMJ_ToExtendedTime(0); + JSLL_UI2L(result, PRMJ_USEC_PER_SEC); + JSLL_DIV(base,base,result); + JSLL_ADD(tsecs,tsecs,base); + + JSLL_UI2L(result, PRMJ_YEAR_SECONDS); + JSLL_UI2L(result1,PRMJ_DAY_SECONDS); + JSLL_ADD(result2,result,result1); + + /* get the year */ + while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) { + /* subtract a year from tsecs */ + JSLL_SUB(tsecs,tsecs,result); + days += 365; + /* is it a leap year ? */ + if(IS_LEAP(year)){ + JSLL_SUB(tsecs,tsecs,result1); + days++; + } + year++; + isleap = IS_LEAP(year); + } + + JSLL_UI2L(result1,PRMJ_DAY_SECONDS); + + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(mday,result); + + /* let's find the month */ + while(((month == 1 && isleap) ? + (mday >= mtab[month] + 1) : + (mday >= mtab[month]))){ + yday += mtab[month]; + days += mtab[month]; + + mday -= mtab[month]; + + /* it's a Feb, check if this is a leap year */ + if(month == 1 && isleap != 0){ + yday++; + days++; + mday--; + } + month++; + } + + /* now adjust tsecs */ + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + mday++; /* day of month always start with 1 */ + days += mday; + wday = (days + wday) % 7; + + yday += mday; + + /* get the hours */ + JSLL_UI2L(result1,PRMJ_HOUR_SECONDS); + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(hours,result); + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + /* get minutes */ + JSLL_UI2L(result1,60); + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(minutes,result); + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + JSLL_L2I(seconds,tsecs); + + prtm->tm_usec = 0L; + prtm->tm_sec = (JSInt8)seconds; + prtm->tm_min = (JSInt8)minutes; + prtm->tm_hour = (JSInt8)hours; + prtm->tm_mday = (JSInt8)mday; + prtm->tm_mon = (JSInt8)month; + prtm->tm_wday = (JSInt8)wday; + prtm->tm_year = (JSInt16)year; + prtm->tm_yday = (JSInt16)yday; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/prmjtime.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/prmjtime.h new file mode 100644 index 0000000..b74fe84 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/prmjtime.h @@ -0,0 +1,95 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef prmjtime_h___ +#define prmjtime_h___ +/* + * PR date stuff for mocha and java. Placed here temporarily not to break + * Navigator and localize changes to mocha. + */ +#include +#include "jslong.h" +#ifdef MOZILLA_CLIENT +#include "jscompat.h" +#endif + +JS_BEGIN_EXTERN_C + +typedef struct PRMJTime PRMJTime; + +/* + * Broken down form of 64 bit time value. + */ +struct PRMJTime { + JSInt32 tm_usec; /* microseconds of second (0-999999) */ + JSInt8 tm_sec; /* seconds of minute (0-59) */ + JSInt8 tm_min; /* minutes of hour (0-59) */ + JSInt8 tm_hour; /* hour of day (0-23) */ + JSInt8 tm_mday; /* day of month (1-31) */ + JSInt8 tm_mon; /* month of year (0-11) */ + JSInt8 tm_wday; /* 0=sunday, 1=monday, ... */ + JSInt16 tm_year; /* absolute year, AD */ + JSInt16 tm_yday; /* day of year (0 to 365) */ + JSInt8 tm_isdst; /* non-zero if DST in effect */ +}; + +/* Some handy constants */ +#define PRMJ_USEC_PER_SEC 1000000L +#define PRMJ_USEC_PER_MSEC 1000L + +/* Return the current local time in micro-seconds */ +extern JSInt64 +PRMJ_Now(void); + +/* get the difference between this time zone and gmt timezone in seconds */ +extern JSInt32 +PRMJ_LocalGMTDifference(void); + +/* Format a time value into a buffer. Same semantics as strftime() */ +extern size_t +PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *tm); + +/* Get the DST offset for the local time passed in */ +extern JSInt64 +PRMJ_DSTOffset(JSInt64 local_time); + +JS_END_EXTERN_C + +#endif /* prmjtime_h___ */ + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/resource.h b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/resource.h new file mode 100644 index 0000000..9301810 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by js3240.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/win32.order b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/win32.order new file mode 100644 index 0000000..cf4e8c4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/javascriptlint/spidermonkey/src/win32.order @@ -0,0 +1,391 @@ +js_MarkGCThing ; 5893956 +JS_GetPrivate ; 2090130 +JS_HashTableRawLookup ; 1709984 +js_Mark ; 1547496 +js_GetToken ; 1406677 +js_UngetToken ; 1154416 +js_MarkAtom ; 992874 +js_MatchToken ; 980277 +js_CompareStrings ; 662772 +js_Lock ; 628184 +js_Unlock ; 628184 +js_AtomizeString ; 611102 +js_HashString ; 611102 +js_DropScopeProperty ; 546476 +JS_malloc ; 484350 +js_Atomize ; 464433 +js_InflateStringToBuffer ; 460739 +js_HoldScopeProperty ; 442612 +JS_free ; 382991 +js_MarkScript ; 376942 +js_HashId ; 365238 +JS_CompareValues ; 352366 +js_IdToValue ; 337594 +JS_GetClass ; 325296 +js_LookupProperty ; 324680 +js_GetAtom ; 244669 +js_DropProperty ; 223217 +JS_GetParent ; 209680 +js_LiveContext ; 205767 +js_PeekToken ; 200646 +js_GetSlotThreadSafe ; 198839 +JS_GetStringChars ; 190862 +JS_HashTableRawAdd ; 179156 +js_FoldConstants ; 162626 +js_EmitTree ; 145634 +JS_EnumerateStub ; 140640 +js_NewSrcNote ; 136983 +js_GetProperty ; 135639 +js_NewScopeProperty ; 135057 +js_MutateScope ; 135057 +js_GetMutableScope ; 135057 +js_AllocSlot ; 132401 +JS_GetRuntime ; 127316 +JS_FrameIterator ; 121963 +JS_GetFrameFunctionObject ; 120567 +js_AllocGCThing ; 119828 +js_DestroyScopeProperty ; 115989 +js_Emit3 ; 109135 +js_AtomizeChars ; 108038 +JS_HashTableLookup ; 107154 +JS_InstanceOf ; 103905 +js_DefineProperty ; 99514 +js_strncpy ; 88276 +js_PeekTokenSameLine ; 87197 +js_HoldObjectMap ; 79084 +js_DropObjectMap ; 77824 +js_NewObject ; 72421 +js_ValueToString ; 72143 +js_GetClassPrototype ; 66235 +js_UnlockRuntime ; 64699 +js_LockRuntime ; 64699 +js_ContextIterator ; 64586 +JS_ClearWatchPointsForObject ; 64155 +js_FinalizeObject ; 63925 +js_IndexAtom ; 63789 +JS_SetPrivate ; 63702 +JS_GetGlobalObject ; 63546 +js_Emit1 ; 63012 +JS_ContextIterator ; 57847 +JS_GetInstancePrivate ; 57817 +JS_HashTableRawRemove ; 57057 +js_AllocRawStack ; 54181 +js_Invoke ; 53568 +js_FindProperty ; 53150 +JS_GetFrameScript ; 51395 +js_LinkFunctionObject ; 50651 +js_SetSrcNoteOffset ; 47735 +js_InWithStatement ; 47346 +js_NewFunction ; 47074 +js_NewSrcNote2 ; 46165 +JS_HashTableAdd ; 45503 +JS_HashTableRemove ; 45213 +js_InCatchBlock ; 42198 +js_AddRootRT ; 40587 +js_AddRoot ; 40587 +js_SetProperty ; 40558 +JS_AddNamedRoot ; 40462 +js_RemoveRoot ; 40384 +JS_RemoveRootRT ; 38129 +js_NewString ; 37471 +js_DefineFunction ; 36629 +JS_GetContextThread ; 36498 +JS_LookupProperty ; 35137 +JS_ValueToString ; 34072 +JS_realloc ; 33776 +JS_DefineFunction ; 33268 +JS_SetErrorReporter ; 32851 +js_FinalizeString ; 30311 +js_FinalizeStringRT ; 30311 +JS_ArenaAllocate ; 30099 +JS_BeginRequest ; 29323 +JS_EndRequest ; 29323 +JS_GetContextPrivate ; 29189 +JS_CompactArenaPool ; 28874 +js_ValueToStringAtom ; 27934 +JS_ValueToId ; 26517 +js_ValueToBoolean ; 25908 +JS_InternString ; 25467 +js_PopStatement ; 24364 +js_PushStatement ; 24364 +js_NewStringCopyN ; 23911 +js_FlushPropertyCacheByProp ; 23883 +js_GetStringBytes ; 23421 +JS_ArenaRelease ; 23267 +JS_GetStringBytes ; 23106 +js_FreeStack ; 22399 +js_AllocStack ; 22399 +JS_SetProperty ; 21240 +js_InitObjectMap ; 19991 +js_NewScope ; 19991 +js_strlen ; 19070 +JS_GetScriptPrincipals ; 18063 +js_SrcNoteLength ; 17369 +js_DestroyObjectMap ; 17198 +js_DestroyScope ; 17198 +JS_GetStringLength ; 16306 +js_PopStatementCG ; 15418 +JS_GetFrameAnnotation ; 14949 +js_FreeRawStack ; 14032 +js_Interpret ; 14032 +js_TransferScopeLock ; 13899 +JS_ResolveStandardClass ; 13645 +JS_ResumeRequest ; 12837 +JS_SuspendRequest ; 12837 +JS_GetProperty ; 12488 +JS_NewObject ; 11660 +js_AllocTryNotes ; 11418 +js_NewNumberValue ; 10859 +js_InternalInvoke ; 10051 +js_NewDouble ; 9936 +js_SetJumpOffset ; 9886 +js_SkipWhiteSpace ; 9299 +js_NewDoubleValue ; 7474 +JS_GetPendingException ; 7404 +js_NewObjectMap ; 7236 +JS_ClearPendingException ; 7092 +JS_strtod ; 7053 +js_strtod ; 7053 +js_InflateString ; 7004 +JS_GetFunctionName ; 6808 +JS_NewHashTable ; 6794 +JS_NewFunction ; 6575 +js_FreeSlot ; 6476 +js_LockScope ; 6332 +JS_HashTableEnumerateEntries ; 6285 +js_GetLengthProperty ; 6162 +js_LockObj ; 6149 +JS_NewUCStringCopyN ; 5994 +JS_NewNumberValue ; 5904 +js_NewStringCopyZ ; 5809 +JS_NewUCStringCopyZ ; 5809 +js_DeflateString ; 5612 +js_ValueToNumber ; 5456 +JS_SetOptions ; 5322 +js_NewScript ; 4941 +js_InitCodeGenerator ; 4810 +js_FinishTakingSrcNotes ; 4810 +js_NewScriptFromParams ; 4810 +js_InitAtomMap ; 4810 +js_FinishTakingTryNotes ; 4810 +js_NewScriptFromCG ; 4810 +js_FinishCodeGenerator ; 4810 +JS_strdup ; 4534 +JS_HashTableDestroy ; 4119 +js_CheckRedeclaration ; 3965 +JS_DefineFunctions ; 3808 +js_EmitFunctionBody ; 3739 +js_TryMethod ; 3685 +js_DefaultValue ; 3610 +js_CloneFunctionObject ; 3577 +JS_InitClass ; 3546 +js_SetClassPrototype ; 3377 +JS_GetPrototype ; 3268 +JS_DefineProperties ; 3115 +js_FindVariable ; 3093 +js_DestroyScript ; 3041 +JS_ClearScriptTraps ; 3041 +js_FreeAtomMap ; 3041 +JS_NewStringCopyZ ; 2953 +js_AtomizeObject ; 2709 +JS_ValueToBoolean ; 2643 +js_SetLengthProperty ; 2637 +JS_GetOptions ; 2593 +js_ValueToObject ; 2522 +js_ValueToNonNullObject ; 2510 +js_StringToObject ; 2482 +JS_SetElement ; 2448 +js_NumberToString ; 2407 +JS_TypeOfValue ; 2275 +js_NewBufferTokenStream ; 2253 +js_NewTokenStream ; 2253 +js_CloseTokenStream ; 2253 +JS_RemoveRoot ; 2148 +JS_NewDouble ; 2129 +JS_vsnprintf ; 1937 +JS_snprintf ; 1937 +JS_CallFunctionValue ; 1844 +JS_DHashVoidPtrKeyStub ; 1840 +JS_DHashTableOperate ; 1840 +js_SetProtoOrParent ; 1758 +js_DoubleToInteger ; 1729 +JS_SetVersion ; 1531 +js_ValueToFunction ; 1476 +JS_SetPrototype ; 1408 +JS_CeilingLog2 ; 1317 +js_Execute ; 1199 +js_CompileFunctionBody ; 1182 +JS_CompileUCFunctionForPrincipals ; 1182 +js_GetSrcNoteOffset ; 1139 +JS_DHashMatchEntryStub ; 1094 +JS_VersionToString ; 1090 +JS_CompileUCScriptForPrincipals ; 1071 +js_CompileTokenStream ; 1071 +js_CurrentThreadId ; 1058 +JS_IdToValue ; 1046 +js_ConstructObject ; 974 +JS_DestroyScript ; 967 +js_PCToLineNumber ; 967 +JS_DefineProperty ; 930 +JS_GetScriptFilename ; 924 +JS_GetFramePC ; 899 +JS_EvaluateUCScriptForPrincipals ; 892 +JS_PCToLineNumber ; 848 +JS_StringToVersion ; 761 +js_ExecuteRegExp ; 755 +JS_MaybeGC ; 717 +JS_ValueToNumber ; 698 +JS_GetVersion ; 698 +JS_AliasProperty ; 693 +js_AtomizeValue ; 664 +js_BooleanToString ; 664 +js_SetSlotThreadSafe ; 596 +JS_DHashClearEntryStub ; 584 +JS_DHashTableRawRemove ; 584 +JS_DefineObject ; 557 +js_PutCallObject ; 516 +js_GetCallObject ; 516 +js_strchr ; 511 +JS_DefineUCProperty ; 480 +JS_dtostr ; 475 +JS_ValueToInt32 ; 464 +js_ValueToInt32 ; 464 +JS_FinishArenaPool ; 453 +js_NewTryNote ; 441 +js_strtointeger ; 437 +JS_vsmprintf ; 428 +JS_DHashTableInit ; 423 +JS_DHashAllocTable ; 423 +JS_DHashGetStubOps ; 423 +JS_NewDHashTable ; 423 +JS_DHashTableDestroy ; 423 +JS_DHashFreeTable ; 423 +JS_DHashTableFinish ; 423 +js_EmitBreak ; 412 +js_GetAttributes ; 412 +JS_DefineConstDoubles ; 407 +JS_ArenaGrow ; 374 +js_AtomizeInt ; 372 +JS_SetParent ; 345 +JS_CloneFunctionObject ; 343 +JS_IsNativeFrame ; 343 +JS_ReportErrorNumber ; 340 +js_ErrorToException ; 340 +js_ReportErrorNumberVA ; 340 +js_GetErrorMessage ; 340 +js_ExpandErrorArguments ; 340 +js_ReportUncaughtException ; 315 +JS_IsExceptionPending ; 315 +js_ReportErrorAgain ; 315 +js_ErrorFromException ; 315 +JS_LookupUCProperty ; 307 +JS_InitArenaPool ; 293 +PRMJ_Now ; 262 +DllMain@12 ; 235 +JS_ExecuteScript ; 232 +JS_GetFrameFunction ; 226 +PRMJ_LocalGMTDifference ; 175 +JS_GetConstructor ; 175 +JS_SetGlobalObject ; 164 +js_LockGCThing ; 155 +js_NewRegExpObject ; 152 +js_NewRegExp ; 152 +js_InitObjectClass ; 131 +js_InitFunctionClass ; 131 +js_EmitN ; 128 +JS_ArenaFinish ; 124 +js_GC ; 124 +js_SweepAtomState ; 124 +js_MarkAtomState ; 124 +JS_ArenaRealloc ; 124 +js_ForceGC ; 124 +js_FlushPropertyCache ; 122 +js_InitNumberClass ; 114 +JS_smprintf ; 112 +js_DoubleToECMAInt32 ; 112 +js_ValueToECMAInt32 ; 111 +JS_ValueToECMAInt32 ; 111 +JS_SetContextPrivate ; 109 +PRMJ_DSTOffset ; 108 +js_Clear ; 105 +JS_ClearScope ; 105 +JS_NewScriptObject ; 104 +JS_smprintf_free ; 104 +JS_ConvertValue ; 99 +js_GetSrcNote ; 98 +JS_ValueToECMAUint32 ; 93 +js_ValueToECMAUint32 ; 93 +js_printf ; 93 +js_DoubleToECMAUint32 ; 93 +js_DestroyRegExp ; 89 +js_UnlockGCThing ; 89 +js_TryValueOf ; 87 +js_NewSrcNote3 ; 86 +JS_ConvertStub ; 81 +JS_SetPendingException ; 80 +js_InitStringClass ; 79 +JS_GC ; 78 +js_InitArrayClass ; 74 +js_InitDateClass ; 67 +JS_NewContext ; 64 +JS_AddArgumentFormatter ; 64 +js_InitContextForLocking ; 64 +js_NewContext ; 64 +JS_SetBranchCallback ; 64 +JS_ClearRegExpStatics ; 64 +js_InitRegExpStatics ; 64 +js_InitCallClass ; 63 +js_InitRegExpClass ; 61 +js_Enumerate ; 58 +JS_DestroyContext ; 46 +js_DestroyContext ; 46 +js_FreeRegExpStatics ; 46 +js_InitScanner ; 39 +js_NewPrinter ; 36 +js_DestroyPrinter ; 36 +js_GetPrinterOutput ; 36 +JS_FreeArenaPool ; 36 +js_DecompileCode ; 34 +js_EmitContinue ; 33 +js_CheckAccess ; 30 +js_DecompileValueGenerator ; 28 +js_InitMathClass ; 27 +js_InitExceptionClasses ; 25 +js_NewArrayObject ; 24 +js_InitArgumentsClass ; 21 +js_puts ; 20 +js_InitBooleanClass ; 19 +JS_InitStandardClasses ; 19 +js_InitScriptClass ; 19 +js_obj_toString ; 15 +js_GetArgsValue ; 14 +js_GetArgsObject ; 14 +js_AtomizeDouble ; 12 +JS_DestroyIdArray ; 11 +js_NewIdArray ; 11 +JS_GetElement ; 11 +JS_EvaluateScript ; 9 +JS_EvaluateUCScript ; 9 +JS_DecompileFunction ; 8 +js_DecompileFunction ; 8 +JS_NewString ; 8 +js_SetStringBytes ; 8 +JS_GetArrayLength ; 7 +JS_NewArrayObject ; 7 +JS_IsArrayObject ; 7 +JS_ValueToObject ; 7 +JS_DefineElement ; 6 +js_DecompileScript ; 6 +JS_PushArguments ; 4 +JS_PopArguments ; 4 +JS_PushArgumentsVA ; 4 +js_PutArgsObject ; 2 +JS_SetGCCallbackRT ; 2 +JS_Init ; 1 +js_SetupLocks ; 1 +js_InitRuntimeNumberState ; 1 +js_InitRuntimeStringState ; 1 +js_InitLock ; 1 +js_InitGC ; 1 +js_InitAtomState ; 1 +js_InitStringGlobals ; 1 diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/.npmignore new file mode 100644 index 0000000..ceeb05b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/.npmignore @@ -0,0 +1 @@ +/tmp diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/README.md new file mode 100644 index 0000000..44912a1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/README.md @@ -0,0 +1,84 @@ +# jsstyle + +## Overview + +`jsstyle` is a style checker for JavaScript coding style. This tool is derived +from the cstyle tool used to check for the style used in the Solaris kernel, +sometimes known as "Bill Joy Normal Form". This tool is a *little bit* +configurable. However it strives to enforces a single coding style based on +that cstyle. See "Configuration Options" below. + +The original cstyle tool can be found here: + + +The document describing C Style is available here: + + +Examples of conditions checked by this tool include: + +* Strings must be quoted with single quotes. +* Blocks must be indented with tabs, not spaces. +* Continuation lines must be indented with 4 spaces. +* Keywords (for, if, function, etc.) must be followed with a space. +* One line cannot contain multiple keywords. +* Relational operators must be surrounded with spaces. +* There must be no spaces between tabs, nor tabs between spaces. +* Lines must not end with whitespace. +* Multi-line block comments must start and end with a blank line. +* Return expressions must be parenthesized. + + +## Status + +No known bugs. No new features planned. + + +## Usage + + jsstyle [OPTIONS] file1.js [file2.js ...] + + +## Configuration Options + +Configuration options may be specified in a file (one option per line) +with the "-f PATH" switch, or on the command line with the "-o +OPTION1,OPTION2" switch. + +As stated about, `jsstyle` is opinionated and intends to stay that way. +That said, this author was arm twisted under duress to allow the following +configurability. + + doxygen Allow doxygen-style block comments `/** /*!`. + splint Allow splint-style lint comments `/*@ ... @*/`. + This is legacy. Does anyone use this? + indent= An integer number of spaces for indentation, or + 'tab' for tab indentation (the default). + literal-string-quote 'single' (the default) or 'double'. Specifies + the preferred quote character for literal strings. + unparenthesized-return Boolean option, set to 0 to disable the + "unparenthesized return expression" check. + blank-after-start-comment + Boolean option, set to 0 to disable the + "missing blank after start comment" check. + + +## "JSSTYLED"-comments + +When you want `jsstyle` to ignore a line, you can use this: + + /* JSSTYLED */ + ignore = this + line; + +Or for a block: + + /* BEGIN JSSTYLED */ + var here + , be + , some = funky + , style + /* END JSSTYLED */ + + +## License + +CDDL diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/jsstyle b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/jsstyle new file mode 100755 index 0000000..c0df3a9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/deps/jsstyle/jsstyle @@ -0,0 +1,926 @@ +#!/usr/bin/env perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# +# jsstyle - check for some common stylistic errors. +# +# jsstyle is a sort of "lint" for Javascript coding style. This tool is +# derived from the cstyle tool, used to check for the style used in the +# Solaris kernel, sometimes known as "Bill Joy Normal Form". +# +# There's a lot this can't check for, like proper indentation of code +# blocks. There's also a lot more this could check for. +# +# A note to the non perl literate: +# +# perl regular expressions are pretty much like egrep +# regular expressions, with the following special symbols +# +# \s any space character +# \S any non-space character +# \w any "word" character [a-zA-Z0-9_] +# \W any non-word character +# \d a digit [0-9] +# \D a non-digit +# \b word boundary (between \w and \W) +# \B non-word boundary +# + +require 5.0; +use IO::File; +use Getopt::Std; +use strict; + +my $usage = +"Usage: jsstyle [-h?vcC] [-t ] [-f ] [-o ] file ... + +Check your JavaScript file for style. +See for details on config options. +Report bugs to . + +Options: + -h print this help and exit + -v verbose + + -c check continuation indentation inside functions + -t specify tab width for line length calculation + -C don't check anything in header block comments + + -f PATH + path to a jsstyle config file + -o OPTION1,OPTION2 + set config options, e.g. '-o doxygen,indent=2' + +"; + +my %opts; + +if (!getopts("ch?o:t:f:vC", \%opts)) { + print $usage; + exit 2; +} + +if (defined($opts{'h'}) || defined($opts{'?'})) { + print $usage; + exit; +} + +my $check_continuation = $opts{'c'}; +my $verbose = $opts{'v'}; +my $ignore_hdr_comment = $opts{'C'}; +my $tab_width = $opts{'t'}; + +# By default, tabs are 8 characters wide +if (! defined($opts{'t'})) { + $tab_width = 8; +} + + +# Load config +my %config = ( + indent => "tab", + doxygen => 0, # doxygen comments: /** ... */ + splint => 0, # splint comments. Needed? + "unparenthesized-return" => 1, + "literal-string-quote" => "single", # 'single' or 'double' + "blank-after-start-comment" => 1, +); +sub add_config_var ($$) { + my ($scope, $str) = @_; + + if ($str !~ /^([\w-]+)(?:\s*=\s*(.*?))?$/) { + die "$scope: invalid option: '$str'"; + } + my $name = $1; + my $value = ($2 eq '' ? 1 : $2); + #print "scope: '$scope', str: '$str', name: '$name', value: '$value'\n"; + + # Validate config var. + if ($name eq "indent") { + # A number of spaces or "tab". + if ($value !~ /^\d+$/ && $value ne "tab") { + die "$scope: invalid '$name': must be a number (of ". + "spaces) or 'tab'"; + } + } elsif ($name eq "doxygen" || # boolean vars + $name eq "splint" || + $name eq "unparenthesized-return" || + $name eq "blank-after-start-comment") { + if ($value != 1 && $value != 0) { + die "$scope: invalid '$name': don't give a value"; + } + } elsif ($name eq "literal-string-quote") { + if ($value !~ /single|double/) { + die "$scope: invalid '$name': must be 'single' ". + "or 'double'"; + } + } else { + die "$scope: unknown config var: $name"; + } + $config{$name} = $value; +} + +if (defined($opts{'f'})) { + my $path = $opts{'f'}; + my $fh = new IO::File $path, "r"; + if (!defined($fh)) { + die "cannot open config path '$path'"; + } + my $line = 0; + while (<$fh>) { + $line++; + s/^\s*//; # drop leading space + s/\s*$//; # drop trailing space + next if ! $_; # skip empty line + next if /^#/; # skip comments + add_config_var "$path:$line", $_; + } +} + +if (defined($opts{'o'})) { + for my $x (split /,/, $opts{'o'}) { + add_config_var "'-o' option", $x; + } +} + + +my ($filename, $line, $prev); # shared globals + +my $fmt; +my $hdr_comment_start; + +if ($verbose) { + $fmt = "%s: %d: %s\n%s\n"; +} else { + $fmt = "%s: %d: %s\n"; +} + +if ($config{"doxygen"}) { + # doxygen comments look like "/*!" or "/**"; allow them. + $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/; +} else { + $hdr_comment_start = qr/^\s*\/\*$/; +} + +# Note, following must be in single quotes so that \s and \w work right. +my $lint_re = qr/\/\*(?: + jsl:\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*| + CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY| + FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*| + PROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*? + )\*\//x; + +my $splint_re = qr/\/\*@.*?@\*\//x; + +my $err_stat = 0; # exit status + +if ($#ARGV >= 0) { + foreach my $arg (@ARGV) { + my $fh = new IO::File $arg, "r"; + if (!defined($fh)) { + printf "%s: cannot open\n", $arg; + } else { + &jsstyle($arg, $fh); + close $fh; + } + } +} else { + &jsstyle("", *STDIN); +} +exit $err_stat; + +my $no_errs = 0; # set for JSSTYLED-protected lines + +sub err($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $line; + $err_stat = 1; + } +} + +sub err_prefix($$) { + my ($prevline, $error) = @_; + my $out = $prevline."\n".$line; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $out; + $err_stat = 1; + } +} + +sub err_prev($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $. - 1, $error, $prev; + $err_stat = 1; + } +} + +sub jsstyle($$) { + +my ($fn, $filehandle) = @_; +$filename = $fn; # share it globally + +my $in_cpp = 0; +my $next_in_cpp = 0; + +my $in_comment = 0; +my $in_header_comment = 0; +my $comment_done = 0; +my $in_function = 0; +my $in_function_header = 0; +my $in_declaration = 0; +my $note_level = 0; +my $nextok = 0; +my $nocheck = 0; + +my $in_string = 0; + +my ($okmsg, $comment_prefix); + +$line = ''; +$prev = ''; +reset_indent(); + +line: while (<$filehandle>) { + s/\r?\n$//; # strip return and newline + + # save the original line, then remove all text from within + # double or single quotes, we do not want to check such text. + + $line = $_; + + # + # C allows strings to be continued with a backslash at the end of + # the line. We translate that into a quoted string on the previous + # line followed by an initial quote on the next line. + # + # (we assume that no-one will use backslash-continuation with character + # constants) + # + $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); + + # + # normal strings and characters + # + s/'([^\\']|\\.)*'/\'\'/g; + s/"([^\\"]|\\.)*"/\"\"/g; + + # + # detect string continuation + # + if ($nocheck || $in_comment) { + $in_string = 0; + } else { + # + # Now that all full strings are replaced with "", we check + # for unfinished strings continuing onto the next line. + # + $in_string = + (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || + s/^("")*"([^\\"]|\\.)*\\$/""/); + } + + # + # figure out if we are in a cpp directive + # + $in_cpp = $next_in_cpp || /^\s*#/; # continued or started + $next_in_cpp = $in_cpp && /\\$/; # only if continued + + # strip off trailing backslashes, which appear in long macros + s/\s*\\$//; + + # an /* END JSSTYLED */ comment ends a no-check block. + if ($nocheck) { + if (/\/\* *END *JSSTYLED *\*\//) { + $nocheck = 0; + } else { + reset_indent(); + next line; + } + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if ($nextok) { + if ($okmsg) { + err($okmsg); + } + $nextok = 0; + $okmsg = 0; + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + $no_errs = 1; + } elsif ($no_errs) { + $no_errs = 0; + } + + # check length of line. + # first, a quick check to see if there is any chance of being too long. + if ((($line =~ tr/\t/\t/) * ($tab_width - 1)) + length($line) > 80) { + # yes, there is a chance. + # replace tabs with spaces and check again. + my $eline = $line; + 1 while $eline =~ + s/\t+/' ' x + (length($&) * $tab_width - length($`) % $tab_width)/e; + if (length($eline) > 80) { + err("line > 80 characters"); + } + } + + # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). + if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE + s/[^()]//g; # eliminate all non-parens + $note_level += s/\(//g - length; # update paren nest level + next; + } + + # a /* BEGIN JSSTYLED */ comment starts a no-check block. + if (/\/\* *BEGIN *JSSTYLED *\*\//) { + $nocheck = 1; + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + if (/\/\/ *JSSTYLED/) { + /^.*\/\/ *JSSTYLED *(.*)$/; + $okmsg = $1; + $nextok = 1; + } + + # universal checks; apply to everything + if (/\t +\t/) { + err("spaces between tabs"); + } + if (/ \t+ /) { + err("tabs between spaces"); + } + if (/\s$/) { + err("space or tab at end of line"); + } + if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { + err("comment preceded by non-blank"); + } + + # is this the beginning or ending of a function? + # (not if "struct foo\n{\n") + if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { + $in_function = 1; + $in_declaration = 1; + $in_function_header = 0; + $prev = $line; + next line; + } + if (/^}\s*(\/\*.*\*\/\s*)*$/) { + if ($prev =~ /^\s*return\s*;/) { + err_prev("unneeded return at end of function"); + } + $in_function = 0; + reset_indent(); # we don't check between functions + $prev = $line; + next line; + } + if (/^\w*\($/) { + $in_function_header = 1; + } + + # a blank line terminates the declarations within a function. + # XXX - but still a problem in sub-blocks. + if ($in_declaration && /^$/) { + $in_declaration = 0; + } + + if ($comment_done) { + $in_comment = 0; + $in_header_comment = 0; + $comment_done = 0; + } + # does this looks like the start of a block comment? + if (/$hdr_comment_start/) { + if ($config{"indent"} eq "tab") { + if (!/^\t*\/\*/) { + err("block comment not indented by tabs"); + } + } elsif (!/^ *\/\*/) { + err("block comment not indented by spaces"); + } + $in_comment = 1; + /^(\s*)\//; + $comment_prefix = $1; + if ($comment_prefix eq "") { + $in_header_comment = 1; + } + $prev = $line; + next line; + } + # are we still in the block comment? + if ($in_comment) { + if (/^$comment_prefix \*\/$/) { + $comment_done = 1; + } elsif (/\*\//) { + $comment_done = 1; + err("improper block comment close") + unless ($ignore_hdr_comment && $in_header_comment); + } elsif (!/^$comment_prefix \*[ \t]/ && + !/^$comment_prefix \*$/) { + err("improper block comment") + unless ($ignore_hdr_comment && $in_header_comment); + } + } + + if ($in_header_comment && $ignore_hdr_comment) { + $prev = $line; + next line; + } + + # check for errors that might occur in comments and in code. + + # allow spaces to be used to draw pictures in header comments. + #if (/[^ ] / && !/".* .*"/ && !$in_header_comment) { + # err("spaces instead of tabs"); + #} + #if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && + # (!/^ \w/ || $in_function != 0)) { + # err("indent by spaces instead of tabs"); + #} + if ($config{"indent"} eq "tab") { + if (/^ {2,}/ && !/^ [^ ]/) { + err("indent by spaces instead of tabs"); + } + } elsif (/^\t/) { + err("indent by tabs instead of spaces") + } elsif (/^( +)/ && !$in_comment) { + my $indent = $1; + if (length($indent) < $config{"indent"}) { + err("indent of " . length($indent) . + " space(s) instead of " . $config{'indent'}); + } + } + if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { + err("continuation line not indented by 4 spaces"); + } + + # A multi-line block comment must not have content on the first line. + if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) { + err("improper first line of block comment"); + } + + if ($in_comment) { # still in comment, don't do further checks + $prev = $line; + next line; + } + + if ((/[^(]\/\*\S/ || /^\/\*\S/) && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank after open comment"); + } + if (/\S\*\/[^)]|\S\*\/$/ && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank before close comment"); + } + if ($config{"blank-after-start-comment"} && /(?\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\s,=]/ || + (/[^->]>[^,=>\s]/ && !/[^->]>$/) || + (/[^<]<[^,=<\s]/ && !/[^<]<$/) || + /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { + err("missing space around relational operator"); + } + if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || + (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || + (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { + # XXX - should only check this for C++ code + # XXX - there are probably other forms that should be allowed + if (!/\soperator=/) { + err("missing space around assignment operator"); + } + } + if (/[,;]\S/ && !/\bfor \(;;\)/ && + # Allow a comma in a regex quantifier. + !/\/.*?\{\d+,?\d*\}.*?\//) { + err("comma or semicolon followed by non-blank"); + } + # allow "for" statements to have empty "while" clauses + if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { + err("comma or semicolon preceded by blank"); + } + if (/^\s*(&&|\|\|)/) { + err("improper boolean continuation"); + } + if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { + err("more than one space around boolean operator"); + } + if (/\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\(/) { + err("missing space between keyword and paren"); + } + if (/(\b(catch|for|if|with|while|switch|return)\b.*){2,}/) { + # multiple "case" and "sizeof" allowed + err("more than one keyword on line"); + } + if (/\b(delete|typeof|instanceOf|with|throw|catch|new|function|in|for|if|while|switch|return|case)\s\s+\(/ && + !/^#if\s+\(/) { + err("extra space between keyword and paren"); + } + # try to detect "func (x)" but not "if (x)" or + # "#define foo (x)" or "int (*func)();" + if (/\w\s\(/) { + my $s = $_; + # strip off all keywords on the line + s/\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\s\(/XXX(/g; + s/#elif\s\(/XXX(/g; + s/^#define\s+\w+\s+\(/XXX(/; + # do not match things like "void (*f)();" + # or "typedef void (func_t)();" + s/\w\s\(+\*/XXX(*/g; + s/\b(void)\s+\(+/XXX(/og; + if (/\w\s\(/) { + err("extra space between function name and left paren"); + } + $_ = $s; + } + + if ($config{"unparenthesized-return"} && + /^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) { + err("unparenthesized return expression"); + } + if (/\btypeof\b/ && !/\btypeof\s*\(.*\)/) { + err("unparenthesized typeof expression"); + } + if (/\(\s/) { + err("whitespace after left paren"); + } + # allow "for" statements to have empty "continue" clauses + if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) { + err("whitespace before right paren"); + } + if (/^\s*\(void\)[^ ]/) { + err("missing space after (void) cast"); + } + if (/\S{/ && !/({|\(){/ && + # Allow a brace in a regex quantifier. + !/\/.*?\{\d+,?\d*\}.*?\//) { + err("missing space before left brace"); + } + if ($in_function && /^\s+{/ && + ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) { + err("left brace starting a line"); + } + if (/}(else|while)/) { + err("missing space after right brace"); + } + if (/}\s\s+(else|while)/) { + err("extra space after right brace"); + } + if (/^\s+#/) { + err("preprocessor statement not in column 1"); + } + if (/^#\s/) { + err("blank after preprocessor #"); + } + + # + # We completely ignore, for purposes of indentation: + # * lines outside of functions + # * preprocessor lines + # + if ($check_continuation && $in_function && !$in_cpp) { + process_indent($_); + } + + if (/^\s*else\W/) { + if ($prev =~ /^\s*}$/) { + err_prefix($prev, + "else and right brace should be on same line"); + } + } + $prev = $line; +} + +if ($prev eq "") { + err("last line in file is blank"); +} + +} + +# +# Continuation-line checking +# +# The rest of this file contains the code for the continuation checking +# engine. It's a pretty simple state machine which tracks the expression +# depth (unmatched '('s and '['s). +# +# Keep in mind that the argument to process_indent() has already been heavily +# processed; all comments have been replaced by control-A, and the contents of +# strings and character constants have been elided. +# + +my $cont_in; # currently inside of a continuation +my $cont_off; # skipping an initializer or definition +my $cont_noerr; # suppress cascading errors +my $cont_start; # the line being continued +my $cont_base; # the base indentation +my $cont_first; # this is the first line of a statement +my $cont_multiseg; # this continuation has multiple segments + +my $cont_special; # this is a C statement (if, for, etc.) +my $cont_macro; # this is a macro +my $cont_case; # this is a multi-line case + +my @cont_paren; # the stack of unmatched ( and [s we've seen + +sub +reset_indent() +{ + $cont_in = 0; + $cont_off = 0; +} + +sub +delabel($) +{ + # + # replace labels with tabs. Note that there may be multiple + # labels on a line. + # + local $_ = $_[0]; + + while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) { + my ($pre_tabs, $label, $rest) = ($1, $2, $3); + $_ = $pre_tabs; + while ($label =~ s/^([^\t]*)(\t+)//) { + $_ .= "\t" x (length($2) + length($1) / 8); + } + $_ .= ("\t" x (length($label) / 8)).$rest; + } + + return ($_); +} + +sub +process_indent($) +{ + require strict; + local $_ = $_[0]; # preserve the global $_ + + s///g; # No comments + s/\s+$//; # Strip trailing whitespace + + return if (/^$/); # skip empty lines + + # regexps used below; keywords taking (), macros, and continued cases + my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b'; + my $macro = '[A-Z_][A-Z_0-9]*\('; + my $case = 'case\b[^:]*$'; + + # skip over enumerations, array definitions, initializers, etc. + if ($cont_off <= 0 && !/^\s*$special/ && + (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*)){/ || + (/^\s*{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) { + $cont_in = 0; + $cont_off = tr/{/{/ - tr/}/}/; + return; + } + if ($cont_off) { + $cont_off += tr/{/{/ - tr/}/}/; + return; + } + + if (!$cont_in) { + $cont_start = $line; + + if (/^\t* /) { + err("non-continuation indented 4 spaces"); + $cont_noerr = 1; # stop reporting + } + $_ = delabel($_); # replace labels with tabs + + # check if the statement is complete + return if (/^\s*\}?$/); + return if (/^\s*\}?\s*else\s*\{?$/); + return if (/^\s*do\s*\{?$/); + return if (/{$/); + return if (/}[,;]?$/); + + # Allow macros on their own lines + return if (/^\s*[A-Z_][A-Z_0-9]*$/); + + # cases we don't deal with, generally non-kosher + if (/{/) { + err("stuff after {"); + return; + } + + # Get the base line, and set up the state machine + /^(\t*)/; + $cont_base = $1; + $cont_in = 1; + @cont_paren = (); + $cont_first = 1; + $cont_multiseg = 0; + + # certain things need special processing + $cont_special = /^\s*$special/? 1 : 0; + $cont_macro = /^\s*$macro/? 1 : 0; + $cont_case = /^\s*$case/? 1 : 0; + } else { + $cont_first = 0; + + # Strings may be pulled back to an earlier (half-)tabstop + unless ($cont_noerr || /^$cont_base / || + (/^\t*(?: )?(?:gettext\()?\"/ && !/^$cont_base\t/)) { + err_prefix($cont_start, + "continuation should be indented 4 spaces"); + } + } + + my $rest = $_; # keeps the remainder of the line + + # + # The split matches 0 characters, so that each 'special' character + # is processed separately. Parens and brackets are pushed and + # popped off the @cont_paren stack. For normal processing, we wait + # until a ; or { terminates the statement. "special" processing + # (if/for/while/switch) is allowed to stop when the stack empties, + # as is macro processing. Case statements are terminated with a : + # and an empty paren stack. + # + foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) { + next if (length($_) == 0); + + # rest contains the remainder of the line + my $rxp = "[^\Q$_\E]*\Q$_\E"; + $rest =~ s/^$rxp//; + + if (/\(/ || /\[/) { + push @cont_paren, $_; + } elsif (/\)/ || /\]/) { + my $cur = $_; + tr/\)\]/\(\[/; + + my $old = (pop @cont_paren); + if (!defined($old)) { + err("unexpected '$cur'"); + $cont_in = 0; + last; + } elsif ($old ne $_) { + err("'$cur' mismatched with '$old'"); + $cont_in = 0; + last; + } + + # + # If the stack is now empty, do special processing + # for if/for/while/switch and macro statements. + # + next if (@cont_paren != 0); + if ($cont_special) { + if ($rest =~ /^\s*{?$/) { + $cont_in = 0; + last; + } + if ($rest =~ /^\s*;$/) { + err("empty if/for/while body ". + "not on its own line"); + $cont_in = 0; + last; + } + if (!$cont_first && $cont_multiseg == 1) { + err_prefix($cont_start, + "multiple statements continued ". + "over multiple lines"); + $cont_multiseg = 2; + } elsif ($cont_multiseg == 0) { + $cont_multiseg = 1; + } + # We've finished this section, start + # processing the next. + goto section_ended; + } + if ($cont_macro) { + if ($rest =~ /^$/) { + $cont_in = 0; + last; + } + } + } elsif (/\;/) { + if ($cont_case) { + err("unexpected ;"); + } elsif (!$cont_special) { + err("unexpected ;") if (@cont_paren != 0); + if (!$cont_first && $cont_multiseg == 1) { + err_prefix($cont_start, + "multiple statements continued ". + "over multiple lines"); + $cont_multiseg = 2; + } elsif ($cont_multiseg == 0) { + $cont_multiseg = 1; + } + if ($rest =~ /^$/) { + $cont_in = 0; + last; + } + if ($rest =~ /^\s*special/) { + err("if/for/while/switch not started ". + "on its own line"); + } + goto section_ended; + } + } elsif (/\{/) { + err("{ while in parens/brackets") if (@cont_paren != 0); + err("stuff after {") if ($rest =~ /[^\s}]/); + $cont_in = 0; + last; + } elsif (/\}/) { + err("} while in parens/brackets") if (@cont_paren != 0); + if (!$cont_special && $rest !~ /^\s*(while|else)\b/) { + if ($rest =~ /^$/) { + err("unexpected }"); + } else { + err("stuff after }"); + } + $cont_in = 0; + last; + } + } elsif (/\:/ && $cont_case && @cont_paren == 0) { + err("stuff after multi-line case") if ($rest !~ /$^/); + $cont_in = 0; + last; + } + next; +section_ended: + # End of a statement or if/while/for loop. Reset + # cont_special and cont_macro based on the rest of the + # line. + $cont_special = ($rest =~ /^\s*$special/)? 1 : 0; + $cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0; + $cont_case = 0; + next; + } + $cont_noerr = 0 if (!$cont_in); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/footer.html.in b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/footer.html.in new file mode 100644 index 0000000..ee7d55c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/footer.html.in @@ -0,0 +1,55 @@ + + + + + + + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/header.html.in b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/header.html.in new file mode 100644 index 0000000..32c1f85 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/header.html.in @@ -0,0 +1,57 @@ + + + + %(title)s + + + %(doc_style)s + + + + +
      +
      +
      + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/media/css/style.css b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/media/css/style.css new file mode 100644 index 0000000..dc43357 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/media/css/style.css @@ -0,0 +1,406 @@ +/* Trent Mick's blog's CSS. Based heavily (at least initially) on + * and on . + */ + +/* ---- reset + * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline) + * v1.4 2009-07-27 | Authors: Eric Meyer & Richard Clark + * html5doctor.com/html-5-reset-stylesheet/ + */ +html, body, div, span, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +abbr, address, cite, code, +del, dfn, em, img, ins, kbd, q, samp, +small, strong, sub, sup, var, +b, i, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, figure, footer, header, +hgroup, menu, nav, section, +time, mark, audio, video { + margin:0; + padding:0; + border:0; + outline:0; + font-size:100%; + vertical-align:baseline; + background:transparent; +} + +article, aside, figure, footer, header, +hgroup, nav, section { display:block; } + +nav ul { list-style:none; } + +blockquote, q { quotes:none; } + +blockquote:before, blockquote:after, +q:before, q:after { content:''; content:none; } + +a { margin:0; padding:0; font-size:100%; vertical-align:baseline; background:transparent; } +ins { background-color:#ff9; color:#000; text-decoration:none; } +mark { background-color:#ff9; color:#000; font-style:italic; font-weight:bold; } +del { text-decoration: line-through; } +abbr[title], dfn[title] { border-bottom:1px dotted #000; cursor:help; } +sub { vertical-align: sub; } +sup { vertical-align: super; } + +/* tables still need cellspacing="0" in the markup */ +table { border-collapse:collapse; border-spacing:0; } + +input, select { vertical-align:middle; } + + + +/* ---- 24px vertical rhythm + * + * All tags except 'pre' because 24px line-height is way too much, want 18px. + * Tough. + */ +body { + line-height: 24px; + font-family: helvetica, arial, freesans, clean, sans-serif; + font-size: 16px; +} +h1,h2,h3,h4,h5,h6 { + font-family: Georgia,serif; + font-weight: bold; + /* http://www.aestheticallyloyal.com/public/optimize-legibility/ */ + text-rendering: optimizeLegibility; +} +h1 { + font-size: 28px; + line-height: 48px; + padding: 24px 0 24px 0; +} +h2 { + font-size: 21px; + line-height: 24px; + padding: 24px 0 24px 0; +} +h3 { + font-size: 16px; + line-height: 24px; + padding: 24px 0 0 0; +} +h4 { + font-size: 14px; + line-height: 24px; + padding: 24px 0 0 0; +} +h5 { + color: #666; + font-size: 14px; + line-height: 24px; + padding: 24px 0 0 0; +} +h6 { + color: #666; + font-size: 12px; + line-height: 24px; + padding: 0; +} +hr { + padding-bottom: 24px; + margin: 24px 0; + height: 0; + border: none; + text-align: center; + color: #333; +} +hr:after { + content: "\2767"; +} +p + p { + padding-top: 24px; +} +dl { + margin: 24px 0; +} +dt { + font-weight: bold; + margin-top: 24px; +} +dd { + margin: 0 0 0 48px; +} + + + +/* ---- layout */ + +body { + margin: 12px; +} +#wrapper { + margin: 0 auto; + max-width: 700px; + position: relative; +} +#main { + min-height: 300px; +} +#fadeout { + z-index: 50; + position: fixed; + left: 0; + top: 0; + right: 0; + height: 210px; + background: -webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 255, 255)), to(rgba(255, 255, 255, 0))); + background: -moz-linear-gradient(top, rgba(255, 255, 255, 255), rgba(255, 255, 255, 0)); +} +#header { + position: fixed; + z-index: 100; + padding: 48px 0 24px 0; + top: 0; + left: auto; + right: auto; + width: 100%; + background: white; +} +#content { + position: relative; + top: 150px; + overflow: auto; + padding-bottom: 200px; +} +#footer { + position: relative; + padding-top: 60px; +} + +/* ---- base styles */ + +blockquote { + font-family: Georgia,serif; + font-size: 18px; + color: #555; + margin: 24px 15% 24px 15%; + margin: 24px 10% 24px 10%; +} +blockquote cite:before { + content: "\2014 "; +} +blockquote p.hangquotes, blockquote.hangquotes { + text-indent: -0.5em; +} + +code, +pre { + font-family: Consolas, Monaco, "Lucida Console", "Courier New", monospace; + font-size: 12px; + background-color: #f5f5f5; + border: 1px solid #ccc; +} +code { + padding: 0 0.2em; +} +pre { + font-size: 80%; + line-height: 18px; + padding: 5px; + margin: 18px 0; + + /* "overflow:auto" does NOT yield scrollbars in mobile browsers. iPhone + * *does* support two-finger scrolling inside the pre-block, but that + * is undiscoverable. Let's pre-wrap. + */ + white-space: pre; + white-space: -moz-pre-wrap; + white-space: -hp-pre-wrap; + white-space: -o-pre-wrap; + white-space: pre-wrap; + word-wrap: break-word; +} +pre code { + border: medium none; + padding: 0; +} +a code { + text-decoration: underline; +} +h1 + pre, +h2 + pre, +h3 + pre, +h4 + pre, +h5 + pre, +h6 + pre { + margin-top: 0; +} + +table { + margin: 24px 0; +} +th, +td { + border: solid #aaa; + border-width: 1px 0; + line-height: 23px; + padding: 0 12px; + text-align: left; +} +th { + border-collapse: separate; +} +tbody tr:nth-child(odd) { + background-color: #f6f6f6; +} + +ol, ul { + padding: 12px 0; + margin: 0 0 0 24px; +} +ol ol, ul ul, ul ol, ol ul { + margin-left: 24px; +} +ol { list-style-type: decimal; } +ol ol { list-style-type: lower-alpha; } +ol ol ol { list-style-type: upper-roman; } +ol ol ol ol { list-style-type: lower-roman; } +ul { list-style-type: circle; } +ul ul { list-style-type: disc; } +ul ul ul { list-style-type: circle; } + +:link { color: hsl(206, 100%, 23%); } +:visited { color: hsl(240, 20%, 50%); } +:link:hover, :visited:hover { color: hsl(206, 100%, 38%); } + + +/* ---- larger screens */ + +@media only screen and (min-width: 481px) { + pre { + overflow: auto; + white-space: pre; + word-wrap: normal; + } +} + +@media only screen and (min-width: 700px) { + ol, ul { + margin: 0; + } +} + + +/* ---- custom classes */ + +pre.shell, +pre.shell code { + background:#444; + color:#fff; + border-width:0px; +} +pre.shell code::before { + content: '$ '; + display: inline; +} + +.copy { + font-size: 90%; + text-align: center; + color: #777; +} + +/* ----- top header */ +a#homelink:link { + color: #008000; + text-decoration: none; +} +a#homelink:visited { + color: #008000; + text-decoration: none; +} +a#homelink:active { + color: #008000; + text-decoration: none; +} +a#homelink:hover { + color: #008000; + text-decoration: none; +} +#logo { + font-family: Georgia,serif; + font-weight: bold; + font-size: 60px; + top: 55px; +} +#apibox, +#tocbox { + position: relative; +} +#tocbox { + margin-left: 24px; +} +.navbutton { + border: 1px solid #ccc; + position: relative; + display: inline-block; + width: 120px; + text-align: center; + top: -7px; + font-size: 70%; + font-weight: bold; + text-transform: uppercase; + text-decoration: none; + color: black; +} +.navbutton:hover { + cursor: pointer; + background-color: #f3f3f3; +} +.popup { + border: 1px solid #ccc; + padding: 5px 10px; + background-color: white; + font-size: 80%; + line-height: 1.6em; + position: absolute; + top: 16px; + left: 0px; + width: 200px; +} +.popup a { + text-decoration: none; +} +.popup a:hover { + text-decoration: underline; +} +.popup ul { + margin: 0; + padding: 0px; + list-style-type: none; +} +.popup ul ul { + margin-left: 24px; +} +#githubfork { + position: fixed; + top: 0; + right: 0px; +} +#indextagline { + font-size: 24px; + font-style: italic; + text-align: center; + color: green; +} +a#indextaglink:link { + color: #008000; + text-decoration: none; +} +a#indextaglink:visited { + color: #008000; + text-decoration: none; +} +a#indextaglink:active { + color: #008000; + text-decoration: none; +} +a#indextaglink:hover { + color: #008000; + text-decoration: none; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/media/js/jquery-1.4.2.min.js b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/media/js/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/branding/media/js/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
      a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

      ";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
      ";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
      ","
      "];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
      ").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
      "; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/client.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/client.md new file mode 100644 index 0000000..77b325b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/client.md @@ -0,0 +1,297 @@ +--- +title: Client API | ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# ldapjs Client API + +This document covers the ldapjs client API and assumes that you are familiar +with LDAP. If you're not, read the [guide](http://ldapjs.org/guide.html) first. + +# Create a client + +The code to create a new client looks like: + + var ldap = require('ldapjs'); + var client = ldap.createClient({ + url: 'ldap://127.0.0.1:1389' + }); + +You can use `ldap://` or `ldaps://`; the latter would connect over SSL (note +that this will not use the LDAP TLS extended operation, but literally an SSL +connection to port 636, as in LDAP v2). The full set of options to create a +client is: + +||url|| a valid LDAP url.|| +||socketPath|| If you're running an LDAP server over a Unix Domain Socket, use this.|| +||log|| You can optionally pass in a bunyan instance the client will use to acquire a logger. The client logs all messages at the `trace` level.|| +||timeout||How long the client should let operations live for before timing out. Default is Infinity.|| +||connectTimeout||How long the client should wait before timing out on TCP connections. Default is up to the OS.|| +||maxConnections||Whether or not to enable connection pooling, and if so, how many to maintain.|| + +If using connection pooling, you can additionally pass in: + +||bindDN||The DN all connections should be bound as.|| +||bindCredentials||The credentials to use with bindDN.|| +||checkInterval||How often to schedule health checks.|| +||maxIdleTime||How long a client can sit idle before initiating a health check (subject to the frequency set by checkInterval).|| + +## Connection management + +As LDAP is a stateful protocol (as opposed to HTTP), having connections torn +down from underneath you is difficult to deal with. That said, the "raw" +client, which is what you get when maxConnections is either unset or <= 1, does +not do anything for you here; you can handle that however you want. + +More commonly, you probably want to use connection pooling, which performs +health checks, and while you will see occasional errors from a client, those +will be highly transient, as the pooling logic will purge them and create new +ones for you. + +It is highly recommended you just provide bindCredentials initially, as all +clients used will be authenticated, but you can call `bind` at any given time. +This is expensive though, as the pool must first drain, be destroyed, and then +recreated. So try not to do that. + +## Common patterns + +The last two parameters in every API are `controls` and `callback`. `controls` +can be either a single instance of a `Control` or an array of `Control` objects. +You can, and probably will, omit this option. + +Almost every operation has the callback form of `function(err, res)` where err +will be an instance of an `LDAPError` (you can use `instanceof` to switch). +You probably won't need to check the `res` parameter, but it's there if you do. + +# bind +`bind(dn, password, controls,callback)` + +Performs a bind operation against the LDAP server. + +The bind API only allows LDAP 'simple' binds (equivalent to HTTP Basic +Authentication) for now. Note that all client APIs can optionally take an array +of `Control` objects. You probably don't need them though... + +If you have more than 1 connection in the connection pool, you will be called +back after *all* of the connections are bound, not just the first one. + +Example: + + client.bind('cn=root', 'secret', function(err) { + assert.ifError(err); + }); + +# add +`add(dn, entry, controls, callback)` + +Performs an add operation against the LDAP server. + +Allows you to add an entry (which is just a plain JS object), and as always, +controls are optional. + +Example: + + var entry = { + cn: 'foo', + sn: 'bar', + email: ['foo@bar.com', 'foo1@bar.com'], + objectclass: 'fooPerson' + }; + client.add('cn=foo, o=example', entry, function(err) { + assert.ifError(err); + }); + +# compare +`compare(dn, attribute, value, controls, callback)` + +Performs an LDAP compare operation with the given attribute and value against +the entry referenced by dn. + +Example: + + client.compare('cn=foo, o=example', 'sn', 'bar', function(err, matched) { + assert.ifError(err); + + console.log('matched: ' + matched); + }); + +# del +`del(dn, controls, callbak)` + + +Deletes an entry from the LDAP server. + +Example: + + client.del('cn=foo, o=example', function(err) { + assert.ifError(err); + }); + +# exop +`exop(name, value, controls, callback)` + +Performs an LDAP extended operation against an LDAP server. `name` is typically +going to be an OID (well, the RFC says it must be; however, ldapjs has no such +restriction). `value` is completely arbitrary, and is whatever the exop says it +should be. + +Example (performs an LDAP 'whois' extended op): + + client.exop('1.3.6.1.4.1.4203.1.11.3', function(err, value, res) { + assert.ifError(err); + + console.log('whois: ' + value); + }); + +# modify +`modify(name, changes, controls, callback)` + +Performs an LDAP modify operation against the LDAP server. This API requires +you to pass in a `Change` object, which is described below. Note that you can +pass in a single `Change` or an array of `Change` objects. + +Example: + + var change = new ldap.Change({ + operation: 'add', + modification: { + pets: ['cat', 'dog'] + } + }); + + client.modify('cn=foo, o=example', change, function(err) { + assert.ifError(err); + }); + +## Change + +A `Change` object maps to the LDAP protocol of a modify change, and requires you +to set the `operation` and `modification`. The `operation` is a string, and +must be one of: + +||replace||Replaces the attribute referenced in `modification`. If the modification has no values, it is equivalent to a delete.|| +||add||Adds the attribute value(s) referenced in `modification`. The attribute may or may not already exist.|| +||delete||Deletes the attribute (and all values) referenced in `modification`.|| + +`modification` is just a plain old JS object with the values you want. + +# modifyDN +`modifyDN(dn, newDN, controls, callback)` + +Performs an LDAP modifyDN (rename) operation against an entry in the LDAP +server. A couple points with this client API: + +* There is no ability to set "keep old dn." It's always going to flag the old +dn to be purged. +* The client code will automagically figure out if the request is a "new +superior" request ("new superior" means move to a different part of the tree, +as opposed to just renaming the leaf). + +Example: + + client.modifyDN('cn=foo, o=example', 'cn=bar', function(err) { + assert.ifError(err); + }); + +# search +`search(base, options, controls, callback)` + +Performs a search operation against the LDAP server. + +The search operation is more complex than the other operations, so this one +takes an `options` object for all the parameters. However, ldapjs makes some +defaults for you so that if you pass nothing in, it's pretty much equivalent +to an HTTP GET operation (i.e., base search against the DN, filter set to +always match). + +Like every other operation, `base` is a DN string. Options has the following +fields: + +||scope||One of `base`, `one`, or `sub`. Defaults to `base`.|| +||filter||A string version of an LDAP filter (see below), or a programatically constructed `Filter` object. Defaults to `(objectclass=*)`.|| +||attributes||attributes to select and return (if these are set, the server will return *only* these attributes). Defaults to the empty set, which means all attributes.|| +||attrsOnly||boolean on whether you want the server to only return the names of the attributes, and not their values. Borderline useless. Defaults to false.|| +||sizeLimit||the maximum number of entries to return. Defaults to 0 (unlimited).|| +||timeLimit||the maximum amount of time the server should take in responding, in seconds. Defaults to 10. Lots of servers will ignore this.|| + +Responses from the `search` method are an `EventEmitter` where you will get a +notification for each `searchEntry` that comes back from the server. You will +additionally be able to listen for a `searchReference`, `error` and `end` event. +Note that the `error` event will only be for client/TCP errors, not LDAP error +codes like the other APIs. You'll want to check the LDAP status code +(likely for `0`) on the `end` event to assert success. LDAP search results +can give you a lot of status codes, such as time or size exceeded, busy, +inappropriate matching, etc., which is why this method doesn't try to wrap up +the code matching. + +Example: + + var opts = { + filter: '(&(l=Seattle)(email=*@foo.com))', + scope: 'sub' + }; + + client.search('o=example', opts, function(err, res) { + assert.ifError(err); + + res.on('searchEntry', function(entry) { + console.log('entry: ' + JSON.stringify(entry.object)); + }); + res.on('searchReference', function(referral) { + console.log('referral: ' + referral.uris.join()); + }); + res.on('error', function(err) { + console.error('error: ' + err.message); + }); + res.on('end', function(result) { + console.log('status: ' + result.status); + }); + }); + +## Filter Strings + +The easiest way to write search filters is to write them compliant with RFC2254, +which is "The string representation of LDAP search filters." Note that +ldapjs doesn't support extensible matching, since it's one of those features +that almost nobody actually uses in practice. + +Assuming you don't really want to read the RFC, search filters in LDAP are +basically are a "tree" of attribute/value assertions, with the tree specified +in prefix notation. For example, let's start simple, and build up a complicated +filter. The most basic filter is equality, so let's assume you want to search +for an attribute `email` with a value of `foo@bar.com`. The syntax would be: + + (email=foo@bar.com) + +ldapjs requires all filters to be surrounded by '()' blocks. Ok, that was easy. +Let's now assume you want to find all records where the email is actually just +anything in the "@bar.com" domain, and the location attribute is set to Seattle: + + (&(email=*@bar.com)(l=Seattle)) + +Now our filter is actually three LDAP filters. We have an `and` filter, +an `equality` filter (the l=Seattle), and a `substring` filter. Substrings are +wildcard filters. Now, let's say we want to also set our filter to include a +specification that either the employeeType *not* be a manager or a secretary: + + (&(email=*@bar.com)(l=Seattle)(!(|(employeeType=manager)(employeeType=secretary)))) + +It gets a little bit complicated, but it's actually quite powerful, and lets you +find almost anything you're looking for. + +# unbind +`unbind(callback)` + +Performs an unbind operation against the LDAP server. + +The unbind operation takes no parameters other than a callback, and will unbind +(and disconnect) *all* of the connections in the pool. + +Example: + + client.unbind(function(err) { + assert.ifError(err); + }); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/dn.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/dn.md new file mode 100644 index 0000000..ba311ac --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/dn.md @@ -0,0 +1,90 @@ +--- +title: DN API | ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# ldapjs DN API + +This document covers the ldapjs DN API and assumes that you are familiar +with LDAP. If you're not, read the [guide](http://ldapjs.org/guide.html) first. + +DNs are LDAP distinguished names, and are composed of a set of RDNs (relative +distinguished names). [RFC2253](http://www.ietf.org/rfc/rfc2253.txt) has the +complete specification, but basically an RDN is an attribute value assertion +with `=` as the seperator, like: `cn=foo` where 'cn' is 'commonName' and 'foo' +is the value. You can have compound RDNs by using the `+` character: +`cn=foo+sn=bar`. As stated above, DNs are a set of RDNs, typically separated +with the `,` character, like: `cn=foo, ou=people, o=example`. This uniquely +identifies an entry in the tree, and is read "bottom up". + +# parseDN(dnString) + +The `parseDN` API converts a string representation of a DN into an ldapjs DN +object; in most cases this will be handled for you under the covers of the +ldapjs framework, but if you need it, it's there. + + var parseDN = require('ldapjs').parseDN; + + var dn = parseDN('cn=foo+sn=bar, ou=people, o=example'); + console.log(dn.toString()); + +# DN + +The DN object is largely what you'll be interacting with, since all the server +APIs are setup to give you a DN object. + +## childOf(dn) + +Returns a boolean indicating whether 'this' is a child of the passed in dn. The +`dn` argument can be either a string or a DN. + + server.add('o=example', function(req, res, next) { + if (req.dn.childOf('ou=people, o=example')) { + ... + } else { + ... + } + }); + +## parentOf(dn) + +The inverse of `childOf`; returns a boolean on whether or not `this` is a parent +of the passed in dn. Like `childOf`, can take either a string or a DN. + + server.add('o=example', function(req, res, next) { + var dn = parseDN('ou=people, o=example'); + if (dn.parentOf(req.dn)) { + ... + } else { + ... + } + }); + +## equals(dn) + +Returns a boolean indicating whether `this` is equivalent to the passed in `dn` +argument. `dn` can be a string or a DN. + + server.add('o=example', function(req, res, next) { + if (req.dn.equals('cn=foo, ou=people, o=example')) { + ... + } else { + ... + } + }); + +## parent() + +Returns a DN object that is the direct parent of `this`. If there is no parent +this can return `null` (e.g. `parseDN('o=example').parent()` will return null). + +## toString() + +Returns the string representation of `this`. + + server.add('o=example', function(req, res, next) { + console.log(req.dn.toString()); + }); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/errors.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/errors.md new file mode 100644 index 0000000..554db56 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/errors.md @@ -0,0 +1,92 @@ +--- +title: Errors API | ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# ldapjs Errors API + +This document covers the ldapjs errors API and assumes that you are familiar +with LDAP. If you're not, read the [guide](http://ldapjs.org/guide.html) first. + +All errors in the ldapjs framework extend from an abstract error type called +`LDAPError`. In addition to the properties listed below, all errors will have +a `stack` property correctly set. + +In general, you'll be using the errors in ldapjs like: + + var ldap = require('ldapjs'); + + var db = {}; + + server.add('o=example', function(req, res, next) { + var parent = req.dn.parent(); + if (parent) { + if (!db[parent.toString()]) + return next(new ldap.NoSuchObjectError(parent.toString())); + } + if (db[req.dn.toString()]) + return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); + + ... + }); + +I.e., if you just pass them into the `next()` handler, ldapjs will automatically +return the appropriate LDAP error message, and stop the handler chain. + +All errors will have the following properties: + +## code + +Returns the LDAP status code associated with this error. + +## name + +The name of this error. + +## message + +The message that will be returned to the client. + +# Complete list of LDAPError subclasses + +* OperationsError +* ProtocolError +* TimeLimitExceededError +* SizeLimitExceededError +* CompareFalseError +* CompareTrueError +* AuthMethodNotSupportedError +* StrongAuthRequiredError +* ReferralError +* AdminLimitExceededError +* UnavailableCriticalExtensionError +* ConfidentialityRequiredError +* SaslBindInProgressError +* NoSuchAttributeError +* UndefinedAttributeTypeError +* InappropriateMatchingError +* ConstraintViolationError +* AttributeOrValueExistsError +* InvalidAttriubteSyntaxError +* NoSuchObjectError +* AliasProblemError +* InvalidDnSyntaxError +* AliasDerefProblemError +* InappropriateAuthenticationError +* InvalidCredentialsError +* InsufficientAccessRightsError +* BusyError +* UnavailableError +* UnwillingToPerformError +* LoopDetectError +* NamingViolationError +* ObjectclassViolationError +* NotAllowedOnNonLeafError +* NotAllowedOnRdnError +* EntryAlreadyExistsError +* ObjectclassModsProhibitedError +* AffectsMultipleDsasError +* OtherError diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/examples.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/examples.md new file mode 100644 index 0000000..b124fc6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/examples.md @@ -0,0 +1,539 @@ +--- +title: Examples | ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# ldapjs Examples + +This page contains a (hopefully) growing list of sample code to get you started +with ldapjs. + +# In-memory server + + var ldap = require('ldapjs'); + + + ///--- Shared handlers + + function authorize(req, res, next) { + if (!req.connection.ldap.bindDN.equals('cn=root')) + return next(new ldap.InsufficientAccessRightsError()); + + return next(); + } + + + ///--- Globals + + var SUFFIX = 'o=joyent'; + var db = {}; + var server = ldap.createServer(); + + + + server.bind('cn=root', function(req, res, next) { + if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') + return next(new ldap.InvalidCredentialsError()); + + res.end(); + return next(); + }); + + server.add(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + + if (db[dn]) + return next(new ldap.EntryAlreadyExistsError(dn)); + + db[dn] = req.toObject().attributes; + res.end(); + return next(); + }); + + server.bind(SUFFIX, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + if (!dn[dn].userpassword) + return next(new ldap.NoSuchAttributeError('userPassword')); + + if (db[dn].userpassword !== req.credentials) + return next(new ldap.InvalidCredentialsError()); + + res.end(); + return next(); + }); + + server.compare(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + if (!db[dn][req.attribute]) + return next(new ldap.NoSuchAttributeError(req.attribute)); + + var matches = false; + var vals = db[dn][req.attribute]; + for (var i = 0; i < vals.length; i++) { + if (vals[i] === req.value) { + matches = true; + break; + } + } + + res.end(matches); + return next(); + }); + + server.del(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + delete db[dn]; + + res.end(); + return next(); + }); + + server.modify(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!req.changes.length) + return next(new ldap.ProtocolError('changes required')); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + var entry = db[dn]; + + for (var i = 0; i < req.changes.length; i++) { + mod = req.changes[i].modification; + switch (req.changes[i].operation) { + case 'replace': + if (!entry[mod.type]) + return next(new ldap.NoSuchAttributeError(mod.type)); + + if (!mod.vals || !mod.vals.length) { + delete entry[mod.type]; + } else { + entry[mod.type] = mod.vals; + } + + break; + + case 'add': + if (!entry[mod.type]) { + entry[mod.type] = mod.vals; + } else { + mod.vals.forEach(function(v) { + if (entry[mod.type].indexOf(v) === -1) + entry[mod.type].push(v); + }); + } + + break; + + case 'delete': + if (!entry[mod.type]) + return next(new ldap.NoSuchAttributeError(mod.type)); + + delete entry[mod.type]; + + break; + } + } + + res.end(); + return next(); + }); + + server.search(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + var scopeCheck; + + switch (req.scope) { + case 'base': + if (req.filter.matches(db[dn])) { + res.send({ + dn: dn, + attributes: db[dn] + }); + } + + res.end(); + return next(); + + case 'one': + scopeCheck = function(k) { + if (req.dn.equals(k)) + return true; + + var parent = ldap.parseDN(k).parent(); + return (parent ? parent.equals(req.dn) : false); + }; + break; + + case 'sub': + scopeCheck = function(k) { + return (req.dn.equals(k) || req.dn.parentOf(k)); + }; + + break; + } + + Object.keys(db).forEach(function(key) { + if (!scopeCheck(key)) + return; + + if (req.filter.matches(db[key])) { + res.send({ + dn: key, + attributes: db[key] + }); + } + }); + + res.end(); + return next(); + }); + + + + ///--- Fire it up + + server.listen(1389, function() { + console.log('LDAP server up at: %s', server.url); + }); + +# /etc/passwd server + + var fs = require('fs'); + var ldap = require('ldapjs'); + var spawn = require('child_process').spawn; + + + + ///--- Shared handlers + + function authorize(req, res, next) { + if (!req.connection.ldap.bindDN.equals('cn=root')) + return next(new ldap.InsufficientAccessRightsError()); + + return next(); + } + + + function loadPasswdFile(req, res, next) { + fs.readFile('/etc/passwd', 'utf8', function(err, data) { + if (err) + return next(new ldap.OperationsError(err.message)); + + req.users = {}; + + var lines = data.split('\n'); + for (var i = 0; i < lines.length; i++) { + if (!lines[i] || /^#/.test(lines[i])) + continue; + + var record = lines[i].split(':'); + if (!record || !record.length) + continue; + + req.users[record[0]] = { + dn: 'cn=' + record[0] + ', ou=users, o=myhost', + attributes: { + cn: record[0], + uid: record[2], + gid: record[3], + description: record[4], + homedirectory: record[5], + shell: record[6] || '', + objectclass: 'unixUser' + } + }; + } + + return next(); + }); + } + + + var pre = [authorize, loadPasswdFile]; + + + + ///--- Mainline + + var server = ldap.createServer(); + + server.bind('cn=root', function(req, res, next) { + if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') + return next(new ldap.InvalidCredentialsError()); + + res.end(); + return next(); + }); + + + server.add('ou=users, o=myhost', pre, function(req, res, next) { + if (!req.dn.rdns[0].cn) + return next(new ldap.ConstraintViolationError('cn required')); + + if (req.users[req.dn.rdns[0].cn]) + return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); + + var entry = req.toObject().attributes; + + if (entry.objectclass.indexOf('unixUser') === -1) + return next(new ldap.ConstraintViolation('entry must be a unixUser')); + + var opts = ['-m']; + if (entry.description) { + opts.push('-c'); + opts.push(entry.description[0]); + } + if (entry.homedirectory) { + opts.push('-d'); + opts.push(entry.homedirectory[0]); + } + if (entry.gid) { + opts.push('-g'); + opts.push(entry.gid[0]); + } + if (entry.shell) { + opts.push('-s'); + opts.push(entry.shell[0]); + } + if (entry.uid) { + opts.push('-u'); + opts.push(entry.uid[0]); + } + opts.push(entry.cn[0]); + var useradd = spawn('useradd', opts); + + var messages = []; + + useradd.stdout.on('data', function(data) { + messages.push(data.toString()); + }); + useradd.stderr.on('data', function(data) { + messages.push(data.toString()); + }); + + useradd.on('exit', function(code) { + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } + + res.end(); + return next(); + }); + }); + + + server.modify('ou=users, o=myhost', pre, function(req, res, next) { + if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) + return next(new ldap.NoSuchObjectError(req.dn.toString())); + + if (!req.changes.length) + return next(new ldap.ProtocolError('changes required')); + + var user = req.users[req.dn.rdns[0].cn].attributes; + var mod; + + for (var i = 0; i < req.changes.length; i++) { + mod = req.changes[i].modification; + switch (req.changes[i].operation) { + case 'replace': + if (mod.type !== 'userpassword' || !mod.vals || !mod.vals.length) + return next(new ldap.UnwillingToPerformError('only password updates ' + + 'allowed')); + break; + case 'add': + case 'delete': + return next(new ldap.UnwillingToPerformError('only replace allowed')); + } + } + + var passwd = spawn('chpasswd', ['-c', 'MD5']); + passwd.stdin.end(user.cn + ':' + mod.vals[0], 'utf8'); + + passwd.on('exit', function(code) { + if (code !== 0) + return next(new ldap.OperationsError('' + code)); + + res.end(); + return next(); + }); + }); + + + server.del('ou=users, o=myhost', pre, function(req, res, next) { + if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) + return next(new ldap.NoSuchObjectError(req.dn.toString())); + + var userdel = spawn('userdel', ['-f', req.dn.rdns[0].cn]); + + var messages = []; + userdel.stdout.on('data', function(data) { + messages.push(data.toString()); + }); + userdel.stderr.on('data', function(data) { + messages.push(data.toString()); + }); + + userdel.on('exit', function(code) { + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } + + res.end(); + return next(); + }); + }); + + + server.search('o=myhost', pre, function(req, res, next) { + Object.keys(req.users).forEach(function(k) { + if (req.filter.matches(req.users[k].attributes)) + res.send(req.users[k]); + }); + + res.end(); + return next(); + }); + + + + // LDAP "standard" listens on 389, but whatever. + server.listen(1389, '127.0.0.1', function() { + console.log('/etc/passwd LDAP server up at: %s', server.url); + }); + +# Address Book + +This example is courtesy of [Diogo Resende](https://github.com/dresende) and +illustrates setting up an address book for typical mail clients such as +Thunderbird or Evolution over a MySQL database. + + // MySQL test: (create on database 'abook' with username 'abook' and password 'abook') + // + // CREATE TABLE IF NOT EXISTS `users` ( + // `id` int(5) unsigned NOT NULL AUTO_INCREMENT, + // `username` varchar(50) NOT NULL, + // `password` varchar(50) NOT NULL, + // PRIMARY KEY (`id`), + // KEY `username` (`username`) + // ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + // INSERT INTO `users` (`username`, `password`) VALUES + // ('demo', 'demo'); + // CREATE TABLE IF NOT EXISTS `contacts` ( + // `id` int(5) unsigned NOT NULL AUTO_INCREMENT, + // `user_id` int(5) unsigned NOT NULL, + // `name` varchar(100) NOT NULL, + // `email` varchar(255) NOT NULL, + // PRIMARY KEY (`id`), + // KEY `user_id` (`user_id`) + // ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + // INSERT INTO `contacts` (`user_id`, `name`, `email`) VALUES + // (1, 'John Doe', 'john.doe@example.com'), + // (1, 'Jane Doe', 'jane.doe@example.com'); + // + + var ldap = require('ldapjs'), + mysql = require("mysql"), + server = ldap.createServer(), + addrbooks = {}, userinfo = {}, + ldap_port = 389, + basedn = "dc=example, dc=com", + company = "Example", + db = mysql.createClient({ + user: "abook", + password: "abook", + database: "abook" + }); + + db.query("SELECT c.*,u.username,u.password " + + "FROM contacts c JOIN users u ON c.user_id=u.id", + function(err, contacts) { + if (err) { + console.log("Error fetching contacts", err); + process.exit(1); + } + + for (var i = 0; i < contacts.length; i++) { + if (!addrbooks.hasOwnProperty(contacts[i].username)) { + addrbooks[contacts[i].username] = []; + userinfo["cn=" + contacts[i].username + ", " + basedn] = { + abook: addrbooks[contacts[i].username], + pwd: contacts[i].password + }; + } + + var p = contacts[i].name.indexOf(" "); + if (p != -1) + contacts[i].firstname = contacts[i].name.substr(0, p); + + p = contacts[i].name.lastIndexOf(" "); + if (p != -1) + contacts[i].surname = contacts[i].name.substr(p + 1); + + addrbooks[contacts[i].username].push({ + dn: "cn=" + contacts[i].name + ", " + basedn, + attributes: { + objectclass: [ "top" ], + cn: contacts[i].name, + mail: contacts[i].email, + givenname: contacts[i].firstname, + sn: contacts[i].surname, + ou: company + } + }); + } + + server.bind(basedn, function (req, res, next) { + var username = req.dn.toString(), + password = req.credentials; + + if (!userinfo.hasOwnProperty(username) || + userinfo[username].pwd != password) { + return next(new ldap.InvalidCredentialsError()); + } + + res.end(); + return next(); + }); + + server.search(basedn, function(req, res, next) { + var binddn = req.connection.ldap.bindDN.toString(); + + if (userinfo.hasOwnProperty(binddn)) { + for (var i = 0; i < userinfo[binddn].abook.length; i++) { + if (req.filter.matches(userinfo[binddn].abook[i].attributes)) + res.send(userinfo[binddn].abook[i]); + } + } + res.end(); + }); + + server.listen(ldap_port, function() { + console.log("Addressbook started at %s", server.url); + }); + }); + +To test out this example, try: + + $ ldapsearch -H ldap://localhost:389 -x -D cn=demo,dc=example,dc=com \ + -w demo -b "dc=example,dc=com" objectclass=* diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/filters.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/filters.md new file mode 100644 index 0000000..e4a6e0c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/filters.md @@ -0,0 +1,295 @@ +--- +title: Filters API | ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# ldapjs Filters API + +This document covers the ldapjs filters API and assumes that you are familiar +with LDAP. If you're not, read the [guide](http://ldapjs.org/guide.html) first. + +LDAP search filters are really the backbone of LDAP search operations, and +ldapjs tries to get you in "easy" with them if your dataset is small, and also +lets you introspect them if you want to write a "query planner". For reference, +make sure to read over [RFC2254](http://www.ietf.org/rfc/rfc2254.txt), as this +explains the LDAPv3 text filter representation. + +ldapjs gives you a distinct object type mapping to each filter that is +context-sensitive. However, _all_ filters have a `matches()` method on them, if +that's all you need. Most filters will have an `attribute` property on them, +since "simple" filters all operate on an attribute/value assertion. The +"complex" filters are really aggregations of other filters (i.e. 'and'), and so +these don't provide that property. + +All Filters in the ldapjs framework extend from `Filter`, which wil have the +property `type` available; this will return a string name for the filter, and +will be one of: + +||equal||an `EqualityFilter`|| +||present||a `PresenceFilter`|| +||substring||a `SubstringFilter`|| +||ge||a `GreaterThanEqualsFilter`|| +||le||a `LessThanEqualsFilter`|| +||and||an `AndFilter`|| +||or||an `OrFilter`|| +||not||a `NotFilter`|| +||approx||an `ApproximateMatchFilter` (quasi-supported in ldapjs)|| +||ext||an `ExtensibleMatchFilter` (not supported in ldapjs)|| + +# parseFilter(filterString) + +Parses an [RFC2254](http://www.ietf.org/rfc/rfc2254.txt) filter string into an +ldapjs object(s). If the filter is "complex", it will be a "tree" of objects. +For example: + + var parseFilter = require('ldapjs').parseFilter; + + var f = parseFilter('(objectclass=*)'); + +Is a "simple" filter, and would just return a `PresenceFilter` object. However, + + var f = parseFilter('(&(employeeType=manager)(l=Seattle))'); + +Would return an `AndFilter`, which would have a `filters` array of two +`EqualityFilter` objects. + +`parseFilter` will throw if an invalid string is passed in (that is, a +syntactically invalid string). + +# EqualityFilter + +The equality filter is used to check exact matching of attribute/value +assertions. This object will have an `attribute` and `value` property, and the +`name` proerty will be `equal`. + +The string syntax for an equality filter is `(attr=value)`. + +The `matches()` method will return true IFF the passed in object has a +key matching `attribute` and a value matching `value`. + + var f = new EqualityFilter({ + attribute: 'cn', + value: 'foo' + }); + + f.matches({cn: 'foo'}); => true + f.matches({cn: 'bar'}); => false + +Equality matching uses "strict" type JavaScript comparison, and by default +everything in ldapjs (and LDAP) is a UTF-8 string. If you want comparison +of numbers, or something else, you'll need to use a middleware interceptor +that transforms values of objects. + +# PresenceFilter + +The presence filter is used to check if an object has an attribute at all, with +any value. This object will have an `attribute` property, and the `name` +property will be `present`. + +The string syntax for a presence filter is `(attr=*)`. + +The `matches()` method will return true IFF the passed in object has a +key matching `attribute`. + + var f = new PresenceFilter({ + attribute: 'cn' + }); + + f.matches({cn: 'foo'}); => true + f.matches({sn: 'foo'}); => false + +# SubstringFilter + +The substring filter is used to do wildcard matching of a string value. This +object will have an `attribute` property and then it will have an `initial` +property, which is the prefix match, an `any` which will be an array of strings +that are to be found _somewhere_ in the target string, and a `final` property, +which will be the suffix match of the string. `any` and `final` are both +optional. The `name` property will be `substring`. + +The string syntax for a presence filter is `(attr=foo*bar*cat*dog)`, which would +map to: + + { + initial: 'foo', + any: ['bar', 'cat'], + final: 'dog' + } + +The `matches()` method will return true IFF the passed in object has a +key matching `attribute` and the "regex" matches the value + + var f = new SubstringFilter({ + attribute: 'cn', + initial: 'foo', + any: ['bar'], + final: 'baz' + }); + + f.matches({cn: 'foobigbardogbaz'}); => true + f.matches({sn: 'fobigbardogbaz'}); => false + +# GreaterThanEqualsFilter + +The ge filter is used to do comparisons and ordering based on the value type. As +mentioned elsewhere, by default everything in LDAP and ldapjs is a string, so +this filter's `matches()` would be using lexicographical ordering of strings. +If you wanted `>=` semantics over numeric values, you would need to add some +middleware to convert values before comparison (and the value of the filter). +Note that the ldapjs schema middleware will do this. + +The GreaterThanEqualsFilter will have an `attribute` property, a `value` +property and the `name` property will be `ge`. + +The string syntax for a ge filter is: + + (cn>=foo) + +The `matches()` method will return true IFF the passed in object has a +key matching `attribute` and the value is `>=` this filter's `value`. + + var f = new GreaterThanEqualsFilter({ + attribute: 'cn', + value: 'foo', + }); + + f.matches({cn: 'foobar'}); => true + f.matches({cn: 'abc'}); => false + +# LessThanEqualsFilter + +The le filter is used to do comparisons and ordering based on the value type. As +mentioned elsewhere, by default everything in LDAP and ldapjs is a string, so +this filter's `matches()` would be using lexicographical ordering of strings. +If you wanted `<=` semantics over numeric values, you would need to add some +middleware to convert values before comparison (and the value of the filter). +Note that the ldapjs schema middleware will do this. + +The string syntax for a le filter is: + + (cn<=foo) + +The LessThanEqualsFilter will have an `attribute` property, a `value` +property and the `name` property will be `le`. + +The `matches()` method will return true IFF the passed in object has a +key matching `attribute` and the value is `<=` this filter's `value`. + + var f = new LessThanEqualsFilter({ + attribute: 'cn', + value: 'foo', + }); + + f.matches({cn: 'abc'}); => true + f.matches({cn: 'foobar'}); => false + +# AndFilter + +The and filter is a complex filter that simply contains "child" filters. The +object will have a `filters` property which is an array of `Filter` objects. The +`name` property will be `and`. + +The string syntax for an and filter is (assuming below we're and'ing two +equality filters): + + (&(cn=foo)(sn=bar)) + +The `matches()` method will return true IFF the passed in object matches all +the filters in the `filters` array. + + var f = new AndFilter({ + filters: [ + new EqualityFilter({ + attribute: 'cn', + value: 'foo' + }), + new EqualityFilter({ + attribute: 'sn', + value: 'bar' + }) + ] + }); + + f.matches({cn: 'foo', sn: 'bar'}); => true + f.matches({cn: 'foo', sn: 'baz'}); => false + +# OrFilter + +The or filter is a complex filter that simply contains "child" filters. The +object will have a `filters` property which is an array of `Filter` objects. The +`name` property will be `or`. + +The string syntax for an or filter is (assuming below we're or'ing two +equality filters): + + (|(cn=foo)(sn=bar)) + +The `matches()` method will return true IFF the passed in object matches *any* +of the filters in the `filters` array. + + var f = new OrFilter({ + filters: [ + new EqualityFilter({ + attribute: 'cn', + value: 'foo' + }), + new EqualityFilter({ + attribute: 'sn', + value: 'bar' + }) + ] + }); + + f.matches({cn: 'foo', sn: 'baz'}); => true + f.matches({cn: 'bar', sn: 'baz'}); => false + +# NotFilter + +The not filter is a complex filter that contains a single "child" filter. The +object will have a `filter` property which is an instance of a `Filter` object. +The `name` property will be `not`. + +The string syntax for a not filter is (assuming below we're not'ing an +equality filter): + + (!(cn=foo)) + +The `matches()` method will return true IFF the passed in object does not match +the filter in the `filter` property. + + var f = new NotFilter({ + filter: new EqualityFilter({ + attribute: 'cn', + value: 'foo' + }) + }); + + f.matches({cn: 'bar'}); => true + f.matches({cn: 'foo'}); => false + +# ApproximateFilter + +The approximate filter is used to check "approximate" matching of +attribute/value assertions. This object will have an `attribute` and +`value` property, and the `name` proerty will be `approx`. + +As a side point, this is a useless filter. It's really only here if you have +some whacky client that's sending this. It just does an exact match (which +is what ActiveDirectory does too). + +The string syntax for an equality filter is `(attr~=value)`. + +The `matches()` method will return true IFF the passed in object has a +key matching `attribute` and a value exactly matching `value`. + + var f = new ApproximateFilter({ + attribute: 'cn', + value: 'foo' + }); + + f.matches({cn: 'foo'}); => true + f.matches({cn: 'bar'}); => false + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/guide.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/guide.md new file mode 100644 index 0000000..afd5fff --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/guide.md @@ -0,0 +1,640 @@ +--- +title: LDAP Guide | ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# LDAP Guide + +This guide was written assuming that you (1) don't know anything about ldapjs, +and perhaps more importantly (2) know little, if anything about LDAP. If you're +already an LDAP whiz, please don't read this and feel it's condescending. Most +people don't know how LDAP works, other than that "it's that thing that has my +password." + +By the end of this guide, we'll have a simple LDAP server that accomplishes a +"real" task. + +# What exactly is LDAP? + +If you haven't already read the +[wikipedia](http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol) +entry (which you should go do right now), LDAP is the "Lightweight Directory +Access Protocol". A directory service basically breaks down as follows: + +* A directory is a tree of entries (similar to but different than an FS). +* Every entry has a unique name in the tree. +* An entry is a set of attributes. +* An attribute is a key/value(s) pairing (multivalue is natural). + +It might be helpful to visualize: + + o=example + / \ + ou=users ou=groups + / | | \ + cn=john cn=jane cn=dudes cn=dudettes + / + keyid=foo + + +Let's say we wanted to look at the record cn=john: + + dn: cn=john, ou=users, o=example + cn: john + sn: smith + email: john@example.com + email: john.smith@example.com + objectClass: person + +A few things to note: + +* All names in a directory tree are actually referred to as a _distinguished +name_, or _dn_ for short. A dn is comprised of attributes that lead to that +node in the tree, as shown above (the syntax is foo=bar, ...). +* The root of the tree is at the right of the _dn_, which is inverted from a +filesystem hierarchy. +* Every entry in the tree is an _instance of_ an _objectclass_. +* An _objectclass_ is a schema concept; think of it like a table in a +traditional ORM. +* An _objectclass_ defines what _attributes_ an entry can have (on the ORM +analogy, an _attribute_ would be like a column). + +That's it. LDAP, then, is the protocol for interacting with the directory tree, +and it's comprehensively specified for common operations, like +add/update/delete and importantly, search. Really, the power of LDAP comes +through the search operations defined in the protocol, which are richer +than HTTP query string filtering, but less powerful than full SQL. You can +think of LDAP as a NoSQL/document store with a well-defined query syntax. + +So, why isn't LDAP more popular for a lot of applications? Like anything else +that has "simple" or "lightweight" in the name, it's not really that +lightweight. In particular, almost all of the implementations of LDAP stem +from the original University of Michigan codebase written in 1996. At that +time, the original intention of LDAP was to be an IP-accessible gateway to the +much more complex X.500 directories, which means that a lot of that +baggage has carried through to today. That makes for a high barrier to entry, +when most applications just don't need most of those features. + +## How is ldapjs any different? + +Well, on the one hand, since ldapjs has to be 100% wire compatible with LDAP to +be useful, it's not. On the other hand, there are no forced assumptions about +what you need and don't need for your use of a directory system. For example, +want to run with no-schema in OpenLDAP/389DS/et al? Good luck. Most of the +server implementations support arbitrary "backends" for persistence, but really +you'll be using [BDB](http://www.oracle.com/technetwork/database/berkeleydb/overview/index.html). + +Want to run schema-less in ldapjs, or wire it up with some mongoose models? No +problem. Want to back it to redis? Should be able to get some basics up in a +day or two. + +Basically, the ldapjs philospohy is to deal with the "muck" of LDAP, and then +get out of the way so you can just use the "good parts." + +# Ok, cool. Learn me some LDAP! + +With the initial fluff out of the way, let's do something crazy to teach +you some LDAP. Let's put an LDAP server up over the top of your (Linux) host's +/etc/passwd and /etc/group files. Usually sysadmins "go the other way," and +replace /etc/passwd with a +[PAM](http://en.wikipedia.org/wiki/Pluggable_authentication_module "Pluggable +authentication module") module to LDAP. While this is probably not a super +useful real-world use case, it will teach you some of the basics. If it is +useful to you, then that's gravy. + +## Install + +If you don't already have node.js and npm, clearly you need those, so follow +the steps at [nodejs.org](http://nodejs.org) and [npmjs.org](http://npmjs.org), +respectively. After that, run: + + $ npm install ldapjs + +Rather than overload you with client-side programming for now, we'll use +the OpenLDAP CLI to interact with our server. It's almost certainly already +installed on your system, but if not, you can get it from brew/apt/yum/your +package manager here. + +To get started, open some file, and let's get the library loaded and a server +created: + + var ldap = require('ldapjs'); + + var server = ldap.createServer(); + + server.listen(1389, function() { + console.log('/etc/passwd LDAP server up at: %s', server.url); + }); + +And run that. Doing anything will give you errors (LDAP "No Such Object") +since we haven't added any support in yet, but go ahead and try it anyway: + + $ ldapsearch -H ldap://localhost:1389 -x -b "o=myhost" objectclass=* + +Before we go any further, note that the complete code for the server we are +about to build up is on the [examples](http://ldapjs.org/examples.html) page. + +## Bind + +So, lesson #1 about LDAP: unlike HTTP, it's connection-oriented; that means that +you authenticate (in LDAP nomenclature this is called a _bind_), and all +subsequent operations operate at the level of priviledge you established during +a bind. You can bind any number of times on a single connection and change that +identity. Technically, it's optional, and you can support _anonymous_ +operations from clients, but (1) you probably don't want that, and (2) most +LDAP clients will initiate a bind anyway (OpenLDAP will), so let's add it in +and get it out of our way. + +What we're going to do is add a "root" user to our LDAP server. This root user +has no correspondence to our Unix root user, it's just something we're making up +and going to use for allowing an (LDAP) admin to do anything. To do so, add +this code into your file: + + server.bind('cn=root', function(req, res, next) { + if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') + return next(new ldap.InvalidCredentialsError()); + + res.end(); + return next(); + }); + +Not very secure, but this is a demo. What we did there was "mount" a tree in +the ldapjs server, and add a handler for the _bind_ method. If you've ever used +express, this pattern should be really familiar; you can add any number of +handlers in, as we'll see later. + +On to the meat of the method. What's up with this? + + if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') + +The first part `req.dn.toString() !== 'cn=root'`: you're probably thinking +"WTF?!? Does ldapjs allow something other than cn=root into this handler?" Sort +of. It allows cn=root *and any children* into that handler. So the entries +`cn=root` and `cn=evil, cn=root` would both match and flow into this handler. +Hence that check. The second check `req.credentials` is probably obvious, but +it brings up an important point, and that is the `req`, `res` objects in ldapjs +are not homogenous across server operation types. Unlike HTTP, there's not a +single message format, so each of the operations has fields and functions +appropriate to that type. The LDAP bind operation has `credentials`, which are +a string representation of the client's password. This is logically the same as +HTTP Basic Authentication (there are other mechanisms, but that's out of scope +for a getting started guide). Ok, if either of those checks failed, we pass a +new ldapjs `Error` back into the server, and it will (1) halt the chain, and (2) +send the proper error code back to the client. + +Lastly, assuming that this request was ok, we just end the operation with +`res.end()`. The `return next()` isn't strictly necessary, since here we only +have one handler in the chain, but it's good habit to always do that, so if you +add another handler in later you won't get bit by it not being invoked. + +Blah blah, let's try running the ldap client again, first with a bad password: + + $ ldapsearch -H ldap://localhost:1389 -x -D cn=root -w foo -b "o=myhost" objectclass=* + + ldap_bind: Invalid credentials (49) + matched DN: cn=root + additional info: Invalid Credentials + +And again with the correct one: + + $ ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" objectclass=* + + No such object (32) + Additional information: No tree found for: o=myhost + +Don't worry about all the flags we're passing into OpenLDAP, that's just to make +their CLI less annonyingly noisy. This time, we got another `No such object` +error, but it's for the tree `o=myhost`. That means our bind went through, and +our search failed, since we haven't yet added a search handler. Just one more +small thing to do first. + +Remember earlier I said there were no authorization rules baked into LDAP? Well, +we added a bind route, so the only user that can authenticate is `cn=root`, but +what if the remote end doesn't authenticate at all? Right, nothing says they +*have to* bind, that's just what the common clients do. Let's add a quick +authorization handler that we'll use in all our subsequent routes: + + function authorize(req, res, next) { + if (!req.connection.ldap.bindDN.equals('cn=root')) + return next(new ldap.InsufficientAccessRightsError()); + + return next(); + } + +Should be pretty self-explanatory, but as a reminder, LDAP is connection +oriented, so we check that the connection remote user was indeed our `cn=root` +(by default ldapjs will have a DN of `cn=anonymous` if the client didn't bind). + +## Search + +We said we wanted to allow LDAP operations over /etc/passwd, so let's detour +for a moment to explain an /etc/passwd record. + + jsmith:x:1001:1000:Joe Smith,Room 1007,(234)555-8910,(234)555-0044,email:/home/jsmith:/bin/sh + +The sample record above maps to: + +||jsmith||user name.|| +||x||historically this contained the password hash, but that's usually in /etc/shadow now, so you get an 'x'.|| +||1001||the unix numeric user id.|| +||1000||the unix numeric group id. (primary).|| +||'Joe Smith,...'||the "gecos," which is a description, and is usually a comma separated list of contact details.|| +||/home/jsmith||the user's home directory.|| +||/bin/sh||the user's shell.|| + +Let's write some handlers to parse that and transform it into an LDAP search +record (note, you'll need to add `var fs = require('fs');` at the top of the +source file). + +First, make a handler that just loads the "user database" in a "pre" handler: + + function loadPasswdFile(req, res, next) { + fs.readFile('/etc/passwd', 'utf8', function(err, data) { + if (err) + return next(new ldap.OperationsError(err.message)); + + req.users = {}; + + var lines = data.split('\n'); + for (var i = 0; i < lines.length; i++) { + if (!lines[i] || /^#/.test(lines[i])) + continue; + + var record = lines[i].split(':'); + if (!record || !record.length) + continue; + + req.users[record[0]] = { + dn: 'cn=' + record[0] + ', ou=users, o=myhost', + attributes: { + cn: record[0], + uid: record[2], + gid: record[3], + description: record[4], + homedirectory: record[5], + shell: record[6] || '', + objectclass: 'unixUser' + } + }; + } + + return next(); + }); + } + +Ok, all that did is tack the /etc/passwd records onto req.users so that any +subsequent handler doesn't have to reload the file. Next, let's write a search +handler to process that: + + var pre = [authorize, loadPasswdFile]; + + server.search('o=myhost', pre, function(req, res, next) { + Object.keys(req.users).forEach(function(k) { + if (req.filter.matches(req.users[k].attributes)) + res.send(req.users[k]); + }); + + res.end(); + return next(); + }); + +And try running: + + $ ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" cn=root + dn: cn=root, ou=users, o=myhost + cn: root + uid: 0 + gid: 0 + description: System Administrator + homedirectory: /var/root + shell: /bin/sh + objectclass: unixUser + +Sweet! Try this out too: + + $ ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" objectclass=* + ... + +You should have seen an entry for every record in /etc/passwd with the second. +What all did we do here? A lot. Let's break this down... + +### What did I just do on the command line? + +Let's start with looking at what you even asked for: + + $ ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" cn=root + +We can throw away `ldapsearch -H -x -D -w -LLL`, as those just specify the URL +to connect to, the bind credentials and the `-LLL` just quiets down OpenLDAP. +That leaves us with: `-b "o=myhost" cn=root`. + +The `-b o=myhost` tells our LDAP server where to _start_ looking in +the tree for entries that might match the search filter, which above is +`cn=root`. + +In this little LDAP example, we're mostly throwing out any qualification of the +"tree," since there's not actually a tree in /etc/passwd (we will extend later +with /etc/group). Remember how I said ldapjs gets out of the way and doesn't +force anything on you? Here's an example. If we wanted an LDAP server to run +over the filesystem, we actually would use this, but here, meh. + +Next, `cn=root` is the search "filter". LDAP has a rich specification of +filters, where you can specify `and`, `or`, `not`, `>=`, `<=`, `equal`, +`wildcard`, `present` and a few other esoteric things. Really, `equal`, +`wildcard`, `present` and the boolean operators are all you'll likely ever need. +So, the filter `cn=root` is an "equality" filter, and says to only return +entries that have attributes that match that. In the second invocation, we used +a 'presence' filter, to say 'return any entries that have an objectclass' +attribute, which in LDAP parlance is saying "give me everything." + +### The code + +In the code above, let's ignore the fs and split stuff, since really all we +did was read in /etc/passwd line by line. After that, we looked at each record +and made the cheesiest transform ever, which is making up a "search entry." A +search entry _must_ have a DN so the client knows what record it is, and a set +of attributes. So that's why we did this: + + var entry = { + dn: 'cn=' + record[0] + ', ou=users, o=myhost', + attributes: { + cn: record[0], + uid: record[2], + gid: record[3], + description: record[4], + homedirectory: record[5], + shell: record[6] || '', + objectclass: 'unixUser' + } + }; + +Next, we let ldapjs do all the hard work of figuring out LDAP search filters +for us by calling `req.filter.matches`. If it matched, we return the whole +record with `res.send`. In this little example we're running O(n), so for +something big and/or slow, you'd have to do some work to effectively write a +query planner (or just not support it...). For some reference code, check out +`node-ldapjs-riak`, which takes on the fairly difficult task of writing a 'full' +LDAP server over riak. + +To demonstrate what ldapjs is doing for you, let's find all users who have a +shell set to `/bin/false` and whose name starts with `p` (I'm doing this +on Ubuntu). Then, let's say we only care about their login name and primary +group id. We'd do this: + + $ ldapsearch -H ldap://localhost:1389 -x -D cn=root -w secret -LLL -b "o=myhost" "(&(shell=/bin/false)(cn=p*))" cn gid + dn: cn=proxy, ou=users, o=myhost + cn: proxy + gid: 13 + + dn: cn=pulse, ou=users, o=myhost + cn: pulse + gid: 114 + +## Add + +This is going to be a little bit ghetto, since what we're going to do is just +use node's child process module to spawn calls to `adduser`. Go ahead and add +the following code in as another handler (you'll need a +`var spawn = require('child_process').spawn;` at the top of your file): + + server.add('ou=users, o=myhost', pre, function(req, res, next) { + if (!req.dn.rdns[0].cn) + return next(new ldap.ConstraintViolationError('cn required')); + + if (req.users[req.dn.rdns[0].cn]) + return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); + + var entry = req.toObject().attributes; + + if (entry.objectclass.indexOf('unixUser') === -1) + return next(new ldap.ConstraintViolation('entry must be a unixUser')); + + var opts = ['-m']; + if (entry.description) { + opts.push('-c'); + opts.push(entry.description[0]); + } + if (entry.homedirectory) { + opts.push('-d'); + opts.push(entry.homedirectory[0]); + } + if (entry.gid) { + opts.push('-g'); + opts.push(entry.gid[0]); + } + if (entry.shell) { + opts.push('-s'); + opts.push(entry.shell[0]); + } + if (entry.uid) { + opts.push('-u'); + opts.push(entry.uid[0]); + } + opts.push(entry.cn[0]); + var useradd = spawn('useradd', opts); + + var messages = []; + + useradd.stdout.on('data', function(data) { + messages.push(data.toString()); + }); + useradd.stderr.on('data', function(data) { + messages.push(data.toString()); + }); + + useradd.on('exit', function(code) { + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } + + res.end(); + return next(); + }); + }); + +Then, you'll need to be root to have this running, so start your server with +`sudo` (or be root, whatever). Now, go ahead and create a file called +`user.ldif` with the following contents: + + dn: cn=ldapjs, ou=users, o=myhost + objectClass: unixUser + cn: ldapjs + shell: /bin/bash + description: Created via ldapadd + +Now go ahead and invoke with: + + $ ldapadd -H ldap://localhost:1389 -x -D cn=root -w secret -f ./user.ldif + adding new entry "cn=ldapjs, ou=users, o=myhost" + +Let's confirm he got added with an ldapsearch: + + $ ldapsearch -H ldap://localhost:1389 -LLL -x -D cn=root -w secret -b "ou=users, o=myhost" cn=ldapjs + dn: cn=ldapjs, ou=users, o=myhost + cn: ldapjs + uid: 1001 + gid: 1001 + description: Created via ldapadd + homedirectory: /home/ldapjs + shell: /bin/bash + objectclass: unixUser + +As before, here's a breakdown of the code: + + server.add('ou=users, o=myhost', pre, function(req, res, next) { + if (!req.dn.rdns[0].cn) + return next(new ldap.ConstraintViolationError('cn required')); + + if (req.users[req.dn.rdns[0].cn]) + return next(new ldap.EntryAlreadyExistsError(req.dn.toString())); + + var entry = req.toObject().attributes; + + if (entry.objectclass.indexOf('unixUser') === -1) + return next(new ldap.ConstraintViolation('entry must be a unixUser')); + +A few new things: + +* We mounted this handler at `ou=users, o=myhost`. Why? What if we want to +extend this little project with groups? We probably want those under a +different part of the tree. +* We did some really minimal schema enforcement by: + + Checking that the leaf RDN (relative distinguished name) was a _cn_ +attribute. + + We then did `req.toObject()`. As mentioned before, each of the req/res +objects have special APIs that make sense for that operation. Without getting +into the details, the LDAP add operation on the wire doesn't look like a JS +object, and we want to support both the LDAP nerd that wants to see what +got sent, and the "easy" case. So use `.toObject()`. Note we also filtered +out to the `attributes` portion of the object since that's all we're really +looking at. + + Lastly, we did a super minimal check to see if the entry was of type +`unixUser`. Frankly for this case, it's kind of useless, but it does illustrate +one point: attribute names are case-insensitive, so ldapjs converts them all to +lower case (note the client sent _objectClass_ over the wire). + +After that, we really just delegated off to the _useradd_ command. As far as I +know, there is not a node.js module that wraps up `getpwent` and friends, +otherwise we'd use that. + +Now, what's missing? Oh, right, we need to let you set a password. Well, let's +support that via the _modify_ command. + +## Modify + +Unlike HTTP, "partial" document updates are fully specified as part of the +RFC, so appending, removing, or replacing a single attribute is pretty natural. +Go ahead and add the following code into your source file: + + server.modify('ou=users, o=myhost', pre, function(req, res, next) { + if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) + return next(new ldap.NoSuchObjectError(req.dn.toString())); + + if (!req.changes.length) + return next(new ldap.ProtocolError('changes required')); + + var user = req.users[req.dn.rdns[0].cn].attributes; + var mod; + + for (var i = 0; i < req.changes.length; i++) { + mod = req.changes[i].modification; + switch (req.changes[i].operation) { + case 'replace': + if (mod.type !== 'userpassword' || !mod.vals || !mod.vals.length) + return next(new ldap.UnwillingToPerformError('only password updates ' + + 'allowed')); + break; + case 'add': + case 'delete': + return next(new ldap.UnwillingToPerformError('only replace allowed')); + } + } + + var passwd = spawn('chpasswd', ['-c', 'MD5']); + passwd.stdin.end(user.cn + ':' + mod.vals[0], 'utf8'); + + passwd.on('exit', function(code) { + if (code !== 0) + return next(new ldap.OperationsError(code)); + + res.end(); + return next(); + }); + }); + + +Basically, we made sure the remote client was targeting an entry that exists, +ensuring that they were asking to "replace" the `userPassword` attribute (which +is the 'standard' LDAP attribute for passwords; if you think it's easier to use +'password', knock yourself out), and then just delegating to the `chpasswd` +command (which lets you change a user's password over stdin). Next, go ahead +and create a `passwd.ldif` file: + + dn: cn=ldapjs, ou=users, o=myhost + changetype: modify + replace: userPassword + userPassword: secret + - + +And then run the OpenLDAP CLI: + + $ ldapmodify -H ldap://localhost:1389 -x -D cn=root -w secret -f ./passwd.ldif + +You should now be able to login to your box as the ldapjs user. Let's get +the last "mainline" piece of work out of the way, and delete the user. + +## Delete + +Delete is pretty straightforward. The client gives you a dn to delete, and you +delete it :). Add the following code into your server: + + server.del('ou=users, o=myhost', pre, function(req, res, next) { + if (!req.dn.rdns[0].cn || !req.users[req.dn.rdns[0].cn]) + return next(new ldap.NoSuchObjectError(req.dn.toString())); + + var userdel = spawn('userdel', ['-f', req.dn.rdns[0].cn]); + + var messages = []; + userdel.stdout.on('data', function(data) { + messages.push(data.toString()); + }); + userdel.stderr.on('data', function(data) { + messages.push(data.toString()); + }); + + userdel.on('exit', function(code) { + if (code !== 0) { + var msg = '' + code; + if (messages.length) + msg += ': ' + messages.join(); + return next(new ldap.OperationsError(msg)); + } + + res.end(); + return next(); + }); + }); + +And then run the following command: + + $ ldapdelete -H ldap://localhost:1389 -x -D cn=root -w secret "cn=ldapjs, ou=users, o=myhost" + + +# Where to go from here + +The complete source code for this example server is available in +[examples](/examples.html). Make sure to read up on the [server](/server.html) +and [client](/client.html) APIs. If you're looking for a "drop in" solution, +take a look at [ldapjs-riak](https://github.com/mcavage/node-ldapjs-riak). + +[Mozilla](https://wiki.mozilla.org/Mozilla_LDAP_SDK_Programmer%27s_Guide/Understanding_LDAP) +still maintains some web pages with LDAP overviews if you look around, if you're +looking for more tutorials. After that, you'll need to work your way through +the [RFCs](http://tools.ietf.org/html/rfc4510) as you work through the APIs in +ldapjs. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/index.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/index.md new file mode 100644 index 0000000..bd7a0a0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/index.md @@ -0,0 +1,91 @@ +--- +title: ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +
      +Reimagining LDAP for Node.js +
      + +# Overview + +ldapjs is a pure JavaScript, from-scratch framework for implementing +[LDAP](http://tools.ietf.org/html/rfc4510) clients and servers in +[Node.js](http://nodejs.org). It is intended for developers used to interacting +with HTTP services in node and [express](http://expressjs.com). + + var ldap = require('ldapjs'); + + var server = ldap.createServer(); + + server.search('o=example', function(req, res, next) { + var obj = { + dn: req.dn.toString(), + attributes: { + objectclass: ['organization', 'top'], + o: 'example' + } + }; + + if (req.filter.matches(obj.attributes)) + res.send(obj); + + res.end(); + }); + + server.listen(1389, function() { + console.log('LDAP server listening at %s', server.url); + }); + +Try hitting that with: + + $ ldapsearch -H ldap://localhost:1389 -x -b o=example objectclass=* + +# Features + +ldapjs implements most of the common operations in the LDAP v3 RFC(s), for +both client and server. It is 100% wire-compatible with the LDAP protocol +itself, and is interoperable with [OpenLDAP](http://openldap.org) and any other +LDAPv3-compliant implementation. ldapjs gives you a powerful routing and +"intercepting filter" pattern for implementing server(s). It is intended +that you can build LDAP over anything you want, not just traditional databases. + +# Getting started + + $ npm install ldapjs + +If you're new to LDAP, check out the [guide](/guide.html). Otherwise, the +API documentation is: + +||[server](/server.html)||Reference for implementing LDAP servers.|| +||[client](/client.html)||Reference for implementing LDAP clients.|| +||[dn](/dn.html)||API reference for the DN class.|| +||[filters](/filters.html)||API reference for LDAP search filters.|| +||[errors](/errors.html)||Listing of all ldapjs Error objects.|| +||[examples](/examples.html)||Collection of sample/getting started code.|| + +# More information + +||License||[MIT](http://opensource.org/licenses/mit-license.php)|| +||Code||[mcavage/node-ldapjs](https://github.com/mcavage/node-ldapjs)|| +||node.js version||>=0.6|| +||Twitter||[@mcavage](http://twitter.com/mcavage)|| + +# What's not in the box? + +Since most developers and system(s) adminstrators struggle with some of the +esoteric features of LDAP, not all features in LDAP are implemented here. +Specifically: + +* LDIF +* Aliases +* Attributes by OID +* TLS extended operation (seriously, just use SSL) +* Extensible matching + +There are a few others, but those are the "big" ones. + + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/persistent_search.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/persistent_search.md new file mode 100644 index 0000000..bf236be --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/persistent_search.md @@ -0,0 +1,23 @@ +--- +titile: Persistent Search Cache API| ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# ldapjs Persistent Search Cache API + +This document covers the ldapjs Persistent Search Cache API and assumes you are familiar with LDAP. If you're not, read the [guide](http://ldapjs.org/guide.html) first. + +This document also assumes you are familiar with LDAP persistent search. If you're not, read the [rfc](http://tools.ietf.org/id/draft-ietf-ldapext-psearch-03.txt) first. + +Note this API is a cache used to store all connected persistent search clients, and does not actually implement persistent search. + +# addClient(req, res, callback) + +Adds a client to the cache. + +# removeClient(req, res, callback) + +Removes a client from the cache. \ No newline at end of file diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/docs/server.md b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/server.md new file mode 100644 index 0000000..8c77f2d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/docs/server.md @@ -0,0 +1,555 @@ +--- +title: Server API | ldapjs +markdown2extras: wiki-tables +logo-color: green +logo-font-family: google:Aldrich, Verdana, sans-serif +header-font-family: google:Aldrich, Verdana, sans-serif +--- + +# ldapjs Server API + +This document covers the ldapjs server API and assumes that you are familiar +with LDAP. If you're not, read the [guide](http://ldapjs.org/guide.html) first. + +# Create a server + +The code to create a new server looks like: + + var server = ldap.createServer(); + +The full list of options is: + +||log||You can optionally pass in a bunyan instance the client will use to acquire a logger.|| +||certificate||A PEM-encoded X.509 certificate; will cause this server to run in TLS mode.|| +||key||A PEM-encoded private key that corresponds to _certificate_ for SSL.|| + +## Properties on the server object + +### maxConnections + +Set this property to reject connections when the server's connection count gets +high. + +### connections (getter only) + +The number of concurrent connections on the server. + +### url + +Returns the fully qualified URL this server is listening on. For example: +`ldaps://10.1.2.3:1636`. If you haven't yet called `listen`, it will always +return `ldap://localhost:389`. + +### Event: 'close' +`function() {}` + +Emitted when the server closes. + +## Listening for requests + +The LDAP server API wraps up and mirrors the node +[listen](http://nodejs.org/docs/v0.4.11/api/net.html#server.listen) family of +APIs. + +After calling `listen`, the property `url` on the server object itself will be +available. + +Example: + + server.listen(389, '127.0.0.1', function() { + console.log(LDAP server listening at: ' + server.url); + }); + + +### Port and Host +`listen(port, [host], [callback])` + +Begin accepting connections on the specified port and host. If the host is +omitted, the server will accept connections directed to any IPv4 address +(INADDR_ANY). + +This function is asynchronous. The last parameter callback will be called when +the server has been bound. + +### Unix Domain Socket +`listen(path, [callback])` + +Start a UNIX socket server listening for connections on the given path. + +This function is asynchronous. The last parameter callback will be called when +the server has been bound. + +### File descriptor +`listenFD(fd)` + +Start a server listening for connections on the given file descriptor. + +This file descriptor must have already had the `bind(2)` and `listen(2)` system +calls invoked on it. Additionally, it must be set non-blocking; try +`fcntl(fd, F_SETFL, O_NONBLOCK)`. + +# Routes + +The LDAP server API is meant to be the LDAP-equivalent of the express/sinatra +paradigm of programming. Essentially every method is of the form +`OP(req, res, next)` where OP is one of bind, add, del, etc. You can chain +handlers together by calling `next()` and ordering your functions in the +definition of the route. For example: + + function authorize(req, res, next) { + if (!req.connection.ldap.bindDN.equals('cn=root')) + return next(new ldap.InsufficientAccessRightsError()); + + return next(); + } + + server.search('o=example', authorize, function(req, res, next) { ... }); + +Note that ldapjs is also slightly different, since it's often going to be backed +to a DB-like entity, in that it also has an API where you can pass in a +'backend' object. This is necessary if there are persistent connection pools, +caching, etc. that need to be placed in an object. + +For example [ldapjs-riak](https://github.com/mcavage/node-ldapjs-riak) is a +complete implementation of the LDAP protocol over +[Riak](http://www.basho.com/products_riak_overview.php). Getting an LDAP +server up with riak looks like: + + var ldap = require('ldapjs'); + var ldapRiak = require('ldapjs-riak'); + + var server = ldap.createServer(); + var backend = ldapRiak.createBackend({ + "host": "localhost", + "port": 8098, + "bucket": "example", + "indexes": ["l", "cn"], + "uniqueIndexes": ["uid"], + "numConnections": 5 + }); + + server.add("o=example", + backend, + backend.add()); + ... + +The first parameter to an ldapjs route is always the point in the +tree to mount the handler chain at. The second argument is _optionally_ a +backend object. After that you can pass in an arbitrary combination of +functions in the form `f(req, res, next)` or arrays of functions of the same +signature (ldapjs will unroll them). + +Unlike HTTP, LDAP operations do not have a heterogeneous wire format, so each +operation requires specific methods/fields on the request/response +objects. However, there is a `.use()` method availabe, similar to +that on express/connect, allowing you to chain up "middleware": + + server.use(function(req, res, next) { + console.log('hello world'); + return next(); + }); + +## Common Request Elements + +All request objects have the `dn` getter on it, which is "context-sensitive" +and returns the point in the tree that the operation wants to operate on. The +LDAP protocol itself sadly doesn't define operations this way, and has a unique +name for just about every op. So, ldapjs calls it `dn`. The DN object itself +is documented at [DN](/dn.html). + +All requests have an optional array of `Control` objects. `Control` will have +the properties `type` (string), `criticality` (boolean), and optionally, a +string `value`. + +All request objects will have a `connection` object, which is the `net.Socket` +associated to this request. Off the `connection` object is an `ldap` object. +The most important property to pay attention to is the `bindDN` property +which will be an instance of an `ldap.DN` object. This is what the client +authenticated as on this connection. If the client didn't bind, then a DN object +will be there defaulted to `cn=anonymous`. + +Additionally, request will have a `logId` parameter you can use to uniquely +identify the request/connection pair in logs (includes the LDAP messageID). + +## Common Response Elements + +All response objects will have an `end` method on them. By default, calling +`res.end()` with no arguments will return SUCCESS (0x00) to the client +(with the exception of `compare` which will return COMPARE_TRUE (0x06)). You +can pass in a status code to the `end()` method to return an alternate status +code. + +However, it's more common/easier to use the `return next(new LDAPError())` +pattern, since ldapjs will fill in the extra LDAPResult fields like matchedDN +and error message for you. + +## Errors + +ldapjs includes an exception hierarchy that directly corresponds to the RFC list +of error codes. The complete list is documented in [errors](/errors.html). But +the paradigm is something defined like CONSTRAINT_VIOLATION in the RFC would be +`ConstraintViolationError` in ldapjs. Upon calling `next(new LDAPError())`, +ldapjs will _stop_ calling your handler chain. For example: + + server.search('o=example', + function(req, res, next) { return next(); }, + function(req, res, next) { return next(new ldap.OperationsError()); }, + function(req, res, next) { res.end(); } + ); + +In the code snipped above, the third handler would never get invoked. + +# Bind + +Adds a mount in the tree to perform LDAP binds with. Example: + + server.bind('ou=people, o=example', function(req, res, next) { + console.log('bind DN: ' + req.dn.toString()); + console.log('bind PW: ' + req.credentials); + res.end(); + }); + +## BindRequest + +BindRequest objects have the following properties: + +### version + +The LDAP protocol version the client is requesting to run this connection on. +Note that ldapjs only supports LDAP version 3. + +### name + +The DN the client is attempting to bind as (note this is the same as the `dn` +property). + +### authentication + +The method of authentication. Right now only `simple` is supported. + +### credentials + +The credentials to go with the `name/authentication` pair. For `simple`, this +will be the plain-text password. + +## BindResponse + +No extra methods above an `LDAPResult` API call. + +# Add + +Adds a mount in the tree to perform LDAP adds with. + + server.add('ou=people, o=example', function(req, res, next) { + console.log('DN: ' + req.dn.toString()); + console.log('Entry attributes: ' + req.toObject().attributes); + res.end(); + }); + +## AddRequest + +AddRequest objects have the following properties: + +### entry + +The DN the client is attempting to add (this is the same as the `dn` +property). + +### attributes + +The set of attributes in this entry. This will be an array of +`Attribute` objects (which have a type and an array of values). This directly +maps to how the request came in off the wire. It's likely you'll want to use +`toObject()` and iterate that way, since that will transform an AddRequest into +a standard JavaScript object. + +### toObject() + +This operation will return a plain JavaScript object from the request that looks +like: + + { + dn: 'cn=foo, o=example', // string, not DN object + attributes: { + cn: ['foo'], + sn: ['bar'], + objectclass: ['person', 'top'] + } + } + +## AddResponse + +No extra methods above an `LDAPResult` API call. + +# Search + +Adds a handler for the LDAP search operation. + + server.search('o=example', function(req, res, next) { + console.log('base object: ' + req.dn.toString()); + console.log('scope: ' + req.scope); + console.log('filter: ' + req.filter.toString()); + res.end(); + }); + +## SearchRequest + +SearchRequest objects have the following properties: + +### baseObject + +The DN the client is attempting to start the search at (equivalent to `dn`). + +### scope + +(string) one of: + +* base +* one +* sub + +### derefAliases + +An integer (defined in the LDAP protocol). Defaults to '0' (meaning +never deref). + +### sizeLimit + +The number of entries to return. Defaults to '0' (unlimited). ldapjs doesn't +currently automatically enforce this, but probably will at some point. + +### timeLimit + +Maximum amount of time the server should take in sending search entries. +Defaults to '0' (unlimited). + +### typesOnly + +Whether to return only the names of attributes, and not the values. Defaults to +'false'. ldapjs will take care of this for you. + +### filter + +The [filter](/filters.html) object that the client requested. Notably this has +a `matches()` method on it that you can leverage. For an example of +introspecting a filter, take a look at the ldapjs-riak source. + +### attributes + +An optional list of attributes to restrict the returned result sets to. ldapjs +will automatically handle this for you. + +## SearchResponse + +### send(entry) + +Allows you to send a `SearchEntry` object. You do not need to +explicitly pass in a `SearchEntry` object, and can instead just send a plain +JavaScript object that matches the format used from `AddRequest.toObject()`. + + + server.search('o=example', function(req, res, next) { + var obj = { + dn: 'o=example', + attributes: { + objectclass: ['top', 'organization'], + o: ['example'] + } + }; + + if (req.filter.matches(obj)) + res.send(obj) + + res.end(); + }); + +# modify + +Allows you to handle an LDAP modify operation. + + server.modify('o=example', function(req, res, next) { + console.log('DN: ' + req.dn.toString()); + console.log('changes:'); + req.changes.forEach(function(c) { + console.log(' operation: ' + c.operation); + console.log(' modification: ' + c.modification.toString()); + }); + res.end(); + }); + +## ModifyRequest + +ModifyRequest objects have the following properties: + +### object + +The DN the client is attempting to update (this is the same as the `dn` +property). + +### changes + +An array of `Change` objects the client is attempting to perform. See below for +details on the `Change` object. + +## Change + +The `Change` object will have the following properties: + +### operation + +A string, and will be one of: 'add', 'delete', or 'replace'. + +### modification + +Will be an `Attribute` object, which will have a 'type' (string) field, and +'vals', which will be an array of string values. + +## ModifyResponse + +No extra methods above an `LDAPResult` API call. + +# del + +Allows you to handle an LDAP delete operation. + + server.del('o=example', function(req, res, next) { + console.log('DN: ' + req.dn.toString()); + res.end(); + }); + +## DeleteRequest + +### entry + +The DN the client is attempting to delete (this is the same as the `dn` +property). + +## DeleteResponse + +No extra methods above an `LDAPResult` API call. + +# compare + +Allows you to handle an LDAP compare operation. + + server.compare('o=example', function(req, res, next) { + console.log('DN: ' + req.dn.toString()); + console.log('attribute name: ' + req.attribute); + console.log('attribute value: ' + req.value); + res.end(req.value === 'foo'); + }); + +## CompareRequest + +### entry + +The DN the client is attempting to compare (this is the same as the `dn` +property). + +### attribute + +The string name of the attribute to compare values of. + +### value + +The string value of the attribute to compare. + +## CompareResponse + +The `end()` method for compare takes a boolean, as opposed to a numeric code +(you can still pass in a numeric LDAP status code if you want). Beyond +that, there are no extra methods above an `LDAPResult` API call. + +# modifyDN + +Allows you to handle an LDAP modifyDN operation. + + server.modifyDN('o=example', function(req, res, next) { + console.log('DN: ' + req.dn.toString()); + console.log('new RDN: ' + req.newRdn.toString()); + console.log('deleteOldRDN: ' + req.deleteOldRdn); + console.log('new superior: ' + + (req.newSuperior ? req.newSuperior.toString() : '')); + + res.end(); + }); + +## ModifyDNRequest + +### entry + +The DN the client is attempting to rename (this is the same as the `dn` +property). + +### newRdn + +The leaf RDN the client wants to rename this entry to. This will be a DN object. + +### deleteOldRdn + +Whether or not to delete the old RDN (i.e., rename vs copy). Defaults to 'true'. + +### newSuperior + +Optional (DN). If the modifyDN operation wishes to relocate the entry in the +tree, the `newSuperior` field will contain the new parent. + +## ModifyDNResponse + +No extra methods above an `LDAPResult` API call. + +# exop + +Allows you to handle an LDAP extended operation. Extended operations are pretty +much arbitrary extensions, by definition. Typically the extended 'name' is an +OID, but ldapjs makes no such restrictions; it just needs to be a string. +Unlike the other operations, extended operations don't map to any location in +the tree, so routing here will be exact match, as opposed to subtree. + + // LDAP whoami + server.exop('1.3.6.1.4.1.4203.1.11.3', function(req, res, next) { + console.log('name: ' + req.name); + console.log('value: ' + req.value); + res.value = 'u:xxyyz@EXAMPLE.NET'; + res.end(); + return next(); + }); + +## ExtendedRequest + +### name + +Will always be a match to the route-defined name. Clients must include this +in their requests. + +### value + +Optional string. The arbitrary blob the client sends for this extended +operation. + +## ExtendedResponse + +### name + +The name of the extended operation. ldapjs will automatically set this. + +### value + +The arbitrary (string) value to send back as part of the response. + +# unbind + +ldapjs by default provides an unbind handler that just disconnects the client +and cleans up any internals (in ldapjs core). You can override this handler +if you need to clean up any items in your backend, or perform any other cleanup +tasks you need to. + + server.unbind(function(req, res, next) { + res.end(); + }); + +Note that the LDAP unbind operation actually doesn't send any response (by +definition in the RFC), so the UnbindResponse is really just a stub that +ultimately calls `net.Socket.end()` for you. There are no properties available +on either the request or response objects, except, of course, for `end()` on the +response. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/examples/inmemory.js b/dist/node_modules/ldapauth/node_modules/ldapjs/examples/inmemory.js new file mode 100644 index 0000000..c398ce5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/examples/inmemory.js @@ -0,0 +1,197 @@ +var ldap = require('../lib/index'); + + +///--- Shared handlers + +function authorize(req, res, next) { + if (!req.connection.ldap.bindDN.equals('cn=root')) + return next(new ldap.InsufficientAccessRightsError()); + + return next(); +} + + +///--- Globals + +var SUFFIX = 'o=smartdc'; +var db = {}; +var server = ldap.createServer(); + + + +server.bind('cn=root', function(req, res, next) { + if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') + return next(new ldap.InvalidCredentialsError()); + + res.end(); + return next(); +}); + +server.add(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + + if (db[dn]) + return next(new ldap.EntryAlreadyExistsError(dn)); + + db[dn] = req.toObject().attributes; + res.end(); + return next(); +}); + +server.bind(SUFFIX, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + if (!dn[dn].userpassword) + return next(new ldap.NoSuchAttributeError('userPassword')); + + if (db[dn].userpassword !== req.credentials) + return next(new ldap.InvalidCredentialsError()); + + res.end(); + return next(); +}); + +server.compare(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + if (!db[dn][req.attribute]) + return next(new ldap.NoSuchAttributeError(req.attribute)); + + var matches = false; + var vals = db[dn][req.attribute]; + for (var i = 0; i < vals.length; i++) { + if (vals[i] === req.value) { + matches = true; + break; + } + } + + res.end(matches); + return next(); +}); + +server.del(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + delete db[dn]; + + res.end(); + return next(); +}); + +server.modify(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!req.changes.length) + return next(new ldap.ProtocolError('changes required')); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + var entry = db[dn]; + + for (var i = 0; i < req.changes.length; i++) { + mod = req.changes[i].modification; + switch (req.changes[i].operation) { + case 'replace': + if (!entry[mod.type]) + return next(new ldap.NoSuchAttributeError(mod.type)); + + if (!mod.vals || !mod.vals.length) { + delete entry[mod.type]; + } else { + entry[mod.type] = mod.vals; + } + + break; + + case 'add': + if (!entry[mod.type]) { + entry[mod.type] = mod.vals; + } else { + mod.vals.forEach(function(v) { + if (entry[mod.type].indexOf(v) === -1) + entry[mod.type].push(v); + }); + } + + break; + + case 'delete': + if (!entry[mod.type]) + return next(new ldap.NoSuchAttributeError(mod.type)); + + delete entry[mod.type]; + + break; + } + } + + res.end(); + return next(); +}); + +server.search(SUFFIX, authorize, function(req, res, next) { + var dn = req.dn.toString(); + if (!db[dn]) + return next(new ldap.NoSuchObjectError(dn)); + + var scopeCheck; + + switch (req.scope) { + case 'base': + if (req.filter.matches(db[dn])) { + res.send({ + dn: dn, + attributes: db[dn] + }); + } + + res.end(); + return next(); + + case 'one': + scopeCheck = function(k) { + if (req.dn.equals(k)) + return true; + + var parent = ldap.parseDN(k).parent(); + return (parent ? parent.equals(req.dn) : false); + }; + break; + + case 'sub': + scopeCheck = function(k) { + return (req.dn.equals(k) || req.dn.parentOf(k)); + }; + + break; + } + + Object.keys(db).forEach(function(key) { + if (!scopeCheck(key)) + return; + + if (req.filter.matches(db[key])) { + res.send({ + dn: key, + attributes: db[key] + }); + } + }); + + res.end(); + return next(); +}); + + + +///--- Fire it up + +server.listen(1389, function() { + console.log('LDAP server up at: %s', server.url); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/examples/pooled_client.js b/dist/node_modules/ldapauth/node_modules/ldapjs/examples/pooled_client.js new file mode 100644 index 0000000..a0b04dc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/examples/pooled_client.js @@ -0,0 +1,60 @@ +var Logger = require('bunyan'); + +var ldap = require('../lib/index'); + + +/// +// Run the "inmemory.js" server in the same directory +/// + +function ifError(err) { + if (err) { + console.error(err.stack); + process.exit(1); + } +} + +var LOG = new Logger({ + name: 'ldapjs', + stream: process.stderr, + level: (process.env.LOG_LEVEL || 'info'), + serializers: Logger.stdSerializers +}); +var MAX_CONNS = process.env.LDAP_MAX_CONNS || 10; + +var client = ldap.createClient({ + url: 'ldap://localhost:1389', + maxConnections: MAX_CONNS, + log: LOG +}); + +client.bind('cn=root', 'secret', function (err) { + ifError(err); + + client.add('o=smartdc', {o: 'smartdc'}, function (err) { + ifError(err); + + var finished = 0; + for (var i = 0; i < MAX_CONNS; i++) { + client.search('o=smartdc', function (err, res) { + ifError(err); + res.on('end', function () { + if (++finished === (MAX_CONNS - 1)) { + console.error('Go kill the LDAP server and restart it') + setTimeout(function () { + console.log('readding suffix'); + client.add('o=smartdc', {o: 'smartdc'}, function (err) { + ifError(err); + client.unbind(function () { + console.log('All done'); + process.exit(0); + }); + }); + }, 15000); + } + }); + }); + } + }); + +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/examples/snoopldap.d b/dist/node_modules/ldapauth/node_modules/ldapjs/examples/snoopldap.d new file mode 100755 index 0000000..16a92b0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/examples/snoopldap.d @@ -0,0 +1,24 @@ +#!/usr/sbin/dtrace -s + +#pragma D option quiet + +BEGIN +{ + printf("%-8s %-8s %-16s %-15s %-15s %s\n", + "LATENCY", "OPTYPE", "REMOTE IP", "BIND DN", "REQ DN", + "STATUS"); +} + +ldapjs*:::server-*-start +{ + starts[arg0] = timestamp; +} + +ldapjs*:::server-*-done +/starts[arg0]/ +{ + printf("%6dms %-8s %-16s %-15s %-15s %d\n", + (timestamp - starts[arg0]) / 1000000, strtok(probename + 7, "-"), + copyinstr(arg1), copyinstr(arg2), copyinstr(arg3), arg4); + starts[arg0] = 0; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/attribute.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/attribute.js new file mode 100644 index 0000000..d819b81 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/attribute.js @@ -0,0 +1,168 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); + +var asn1 = require('asn1'); + +var Protocol = require('./protocol'); + + + +///--- API + +function Attribute(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.type && typeof (options.type) !== 'string') + throw new TypeError('options.type must be a string'); + } else { + options = {}; + } + + var self = this; + + this.type = options.type || ''; + this._vals = []; + + this.__defineGetter__('vals', function () { + var _vals = []; + + self._vals.forEach(function (v) { + /* JSSTYLED */ + if (/;binary$/.test(self.type)) { + _vals.push(v.toString('base64')); + } else { + _vals.push(v.toString('utf8')); + } + }); + + return _vals; + }); + + this.__defineSetter__('vals', function (vals) { + if (Array.isArray(vals)) { + vals.forEach(function (v) { + self.addValue(v); + }); + } else { + self.addValue(vals); + } + }); + + this.__defineGetter__('buffers', function () { + return self._vals; + }); + + this.__defineGetter__('json', function () { + return { + type: self.type, + vals: self.vals + }; + }); + + if (options.vals) + this.vals = options.vals; + +} +module.exports = Attribute; + + +Attribute.prototype.addValue = function (val) { + if (Buffer.isBuffer(val)) { + this._vals.push(val); + } else { + var encoding = 'utf8'; + /* JSSTYLED */ + if (/;binary$/.test(this.type)) + encoding = 'base64'; + this._vals.push(new Buffer(val + '', encoding)); + } +}; + + +/* BEGIN JSSTYLED */ +Attribute.compare = function compare(a, b) { + if (!(a instanceof Attribute) || !(b instanceof Attribute)) + throw new TypeError('can only compare Attributes'); + + if (a.type < b.type) return -1; + if (a.type > b.type) return 1; + if (a.vals.length < b.vals.length) return -1; + if (a.vals.length > b.vals.length) return 1; + + for (var i = 0; i < a.vals.length; i++) { + if (a.vals[i] < b.vals[i]) return -1; + if (a.vals[i] > b.vals[i]) return 1; + } + + return 0; +}; +/* END JSSTYLED */ + + +Attribute.prototype.parse = function (ber) { + assert.ok(ber); + + ber.readSequence(); + this.type = ber.readString(); + + if (ber.peek() === Protocol.LBER_SET) { + if (ber.readSequence(Protocol.LBER_SET)) { + var end = ber.offset + ber.length; + while (ber.offset < end) + this._vals.push(ber.readString(asn1.Ber.OctetString, true)); + } + } + + return true; +}; + + +Attribute.prototype.toBer = function (ber) { + assert.ok(ber); + + ber.startSequence(); + ber.writeString(this.type); + ber.startSequence(Protocol.LBER_SET); + if (this._vals.length) { + this._vals.forEach(function (b) { + ber.writeByte(asn1.Ber.OctetString); + ber.writeLength(b.length); + for (var i = 0; i < b.length; i++) + ber.writeByte(b[i]); + }); + } else { + ber.writeStringArray([]); + } + ber.endSequence(); + ber.endSequence(); + + return ber; +}; + +Attribute.toBer = function (attr, ber) { + return Attribute.prototype.toBer.call(attr, ber); +}; + + +/* BEGIN JSSTYLED */ +Attribute.isAttribute = function (attr) { + if (!attr) return false; + if (typeof (attr) !== 'object') return false; + if (attr instanceof Attribute) return true; + if (!attr.type || typeof (attr.type) !== 'string') return false; + if (!attr.vals || !Array.isArray(attr.vals)) return false; + for (var i = 0; i < attr.vals.length; i++) { + if (typeof (attr.vals[i]) !== 'string' && !Buffer.isBuffer(attr.vals[i])) + return false; + } + + return true; +}; +/* END JSSTLYED */ + + +Attribute.prototype.toString = function () { + return JSON.stringify(this.json); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/change.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/change.js new file mode 100644 index 0000000..511483d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/change.js @@ -0,0 +1,124 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); + +var Attribute = require('./attribute'); +var Protocol = require('./protocol'); + + + +///--- API + +function Change(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.operation && typeof (options.operation) !== 'string') + throw new TypeError('options.operation must be a string'); + } else { + options = {}; + } + + var self = this; + this._modification = false; + + this.__defineGetter__('operation', function () { + switch (self._operation) { + case 0x00: return 'add'; + case 0x01: return 'delete'; + case 0x02: return 'replace'; + default: + throw new Error('0x' + self._operation.toString(16) + ' is invalid'); + } + }); + this.__defineSetter__('operation', function (val) { + if (typeof (val) !== 'string') + throw new TypeError('operation must be a string'); + + switch (val.toLowerCase()) { + case 'add': + self._operation = 0x00; + break; + case 'delete': + self._operation = 0x01; + break; + case 'replace': + self._operation = 0x02; + break; + default: + throw new Error('Invalid operation type: 0x' + val.toString(16)); + } + }); + this.__defineGetter__('modification', function () { + return self._modification; + }); + + this.__defineSetter__('modification', function (attr) { + if (Attribute.isAttribute(attr)) { + self._modification = attr; + return; + } + var keys = Object.keys(attr); + if (keys.length > 1) + throw new Error('Only one attribute per Change allowed'); + + keys.forEach(function (k) { + var _attr = new Attribute({type: k}); + if (Array.isArray(attr[k])) { + attr[k].forEach(function (v) { + _attr.addValue(v.toString()); + }); + } else { + _attr.addValue(attr[k].toString()); + } + self._modification = _attr; + }); + }); + this.__defineGetter__('json', function () { + return { + operation: self.operation, + modification: self._modification ? self._modification.json : {} + }; + }); + + this.operation = options.operation || options.type || 'add'; + this.modification = options.modification || {}; +} +module.exports = Change; + + +Change.compare = function (a, b) { + if (!(a instanceof Change) || !(b instanceof Change)) + throw new TypeError('can only compare Changes'); + + if (a.operation < b.operation) + return -1; + if (a.operation > b.operation) + return 1; + + return Attribute.compare(a.modification, b.modification); +}; + + +Change.prototype.parse = function (ber) { + assert.ok(ber); + + ber.readSequence(); + this._operation = ber.readEnumeration(); + this._modification = new Attribute(); + this._modification.parse(ber); + + return true; +}; + + +Change.prototype.toBer = function (ber) { + assert.ok(ber); + + ber.startSequence(); + ber.writeEnumeration(this._operation); + ber = this._modification.toBer(ber); + ber.endSequence(); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/client.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/client.js new file mode 100644 index 0000000..33af458 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/client.js @@ -0,0 +1,901 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var EventEmitter = require('events').EventEmitter; +var net = require('net'); +var tls = require('tls'); +var util = require('util'); + +var assert = require('assert-plus'); + +var Attribute = require('../attribute'); +var Change = require('../change'); +var Control = require('../controls/index').Control; +var PagedResultsControl = require('../controls/index').PagedResultsControl; +var Protocol = require('../protocol'); +var dn = require('../dn'); +var errors = require('../errors'); +var filters = require('../filters'); +var messages = require('../messages'); +var url = require('../url'); + + + +///--- Globals + +var AbandonRequest = messages.AbandonRequest; +var AddRequest = messages.AddRequest; +var BindRequest = messages.BindRequest; +var CompareRequest = messages.CompareRequest; +var DeleteRequest = messages.DeleteRequest; +var ExtendedRequest = messages.ExtendedRequest; +var ModifyRequest = messages.ModifyRequest; +var ModifyDNRequest = messages.ModifyDNRequest; +var SearchRequest = messages.SearchRequest; +var UnbindRequest = messages.UnbindRequest; +var UnbindResponse = messages.UnbindResponse; + +var LDAPResult = messages.LDAPResult; +var SearchEntry = messages.SearchEntry; +var SearchReference = messages.SearchReference; +var SearchResponse = messages.SearchResponse; +var Parser = messages.Parser; + +var Filter = filters.Filter; +var PresenceFilter = filters.PresenceFilter; + +var ConnectionError = errors.ConnectionError; + +var CMP_EXPECT = [errors.LDAP_COMPARE_TRUE, errors.LDAP_COMPARE_FALSE]; +var MAX_MSGID = Math.pow(2, 31) - 1; + +// node 0.6 got rid of FDs, so make up a client id for logging +var CLIENT_ID = 0; + + + +///--- Internal Helpers + +function nextClientId() { + if (++CLIENT_ID === MAX_MSGID) + return 1; + + return CLIENT_ID; +} + +function validateControls(controls) { + if (Array.isArray(controls)) { + controls.forEach(function (c) { + if (!(c instanceof Control)) + throw new TypeError('controls must be [Control]'); + }); + } else if (controls instanceof Control) { + controls = [controls]; + } else { + throw new TypeError('controls must be [Control]'); + } + + return controls; +} + + +function setupSocket(socket, opts) { + var log = opts.log; + + socket.ldap = { + id: opts.url ? opts.url.href : opts.socketPath, + messageID: 0, + messages: {}, + getNextMessageID: function getNextMessageID() { + if (++socket.ldap.messageID >= MAX_MSGID) + socket.ldap.messageID = 1; + + return socket.ldap.messageID; + }, + parser: new Parser({ + log: log + }) + }; + + // This won't be set on TLS. So. Very. Annoying. + if (typeof (socket.setKeepAlive) !== 'function') { + socket.setKeepAlive = function setKeepAlive(enable, delay) { + return socket.socket ? socket.socket.setKeepAlive(enable, delay) : false; + }; + } + + // On close we have to walk the outstanding messages and go invoke their + // callback with an error + socket.on('close', function onClose(had_err) { + socket.removeAllListeners('connect'); + socket.removeAllListeners('close'); + socket.removeAllListeners('data'); + socket.removeAllListeners('drain'); + socket.removeAllListeners('end'); + socket.removeAllListeners('error'); + socket.removeAllListeners('timeout'); + + if (log.trace()) + log.trace('close event had_err=%s', had_err ? 'yes' : 'no'); + + opts.emit('close', had_err); + Object.keys(socket.ldap.messages).forEach(function (msgid) { + var err; + if (socket.unbindMessageID !== parseInt(msgid, 10)) { + err = new ConnectionError(socket.ldap.id + ' closed'); + } else { + err = new UnbindResponse({ + messageID: msgid + }); + err.status = 'unbind'; + } + + if (typeof (socket.ldap.messages[msgid]) === 'function') { + var callback = socket.ldap.messages[msgid]; + delete socket.ldap.messages[msgid]; + return callback(err); + } else if (socket.ldap.messages[msgid]) { + if (err instanceof Error) + socket.ldap.messages[msgid].emit('error', err); + delete socket.ldap.messages[msgid]; + } + + delete socket.ldap.parser; + delete socket.ldap; + return false; + }); + }); + + socket.on('data', function onData(data) { + if (log.trace()) + log.trace('data event: %s', util.inspect(data)); + + socket.ldap.parser.write(data); + }); + + socket.on('end', function onEnd() { + if (log.trace()) + log.trace('end event'); + + opts.emit('end'); + socket.end(); + }); + + socket.on('error', function onError(err) { + if (log.trace()) + log.trace({err: err}, 'error event: %s', new Error().stack); + + if (opts.listeners('error').length) + opts.emit('error', err); + + socket.end(); + }); + + socket.on('timeout', function onTimeout() { + if (log.trace()) + log.trace('timeout event'); + + opts.emit('socketTimeout'); + socket.end(); + }); + + // The "router" + socket.ldap.parser.on('message', function onMessage(message) { + message.connection = socket; + var callback = socket.ldap.messages[message.messageID]; + + if (!callback) { + log.error({message: message.json}, 'unsolicited message'); + return false; + } + + return callback(message); + }); + + socket.ldap.parser.on('error', function onParseError(err) { + log.trace({err: err}, 'parser error event'); + + if (opts.listeners('error').length) + opts.emit('error', err); + + socket.end(); + }); +} + + + +///--- API + +/** + * Constructs a new client. + * + * The options object is required, and must contain either a URL (string) or + * a socketPath (string); the socketPath is only if you want to talk to an LDAP + * server over a Unix Domain Socket. Additionally, you can pass in a bunyan + * option that is the result of `new Logger()`, presumably after you've + * configured it. + * + * @param {Object} options must have either url or socketPath. + * @throws {TypeError} on bad input. + */ +function Client(options) { + assert.ok(options); + + EventEmitter.call(this, options); + + var _url; + if (options.url) + _url = url.parse(options.url); + + this.connectTimeout = parseInt((options.connectTimeout || 0), 10); + this.host = _url ? _url.hostname : undefined; + this.log = options.log.child({clazz: 'Client'}, true); + this.port = _url ? _url.port : false; + this.secure = _url ? _url.secure : false; + this.socketPath = options.socketPath || false; + this.timeout = parseInt((options.timeout || 0), 10); + this.url = _url; + + this.socket = this._connect(); +} +util.inherits(Client, EventEmitter); +module.exports = Client; + + +/** + * Sends an abandon request to the LDAP server. + * + * The callback will be invoked as soon as the data is flushed out to the + * network, as there is never a response from abandon. + * + * @param {Number} messageID the messageID to abandon. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err). + * @throws {TypeError} on invalid input. + */ +Client.prototype.abandon = function abandon(messageID, controls, callback) { + assert.number(messageID, 'messageID'); + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + assert.func(callback, 'callback'); + + var req = new AbandonRequest({ + abandonID: messageID, + controls: controls + }); + + return this._send(req, 'abandon', null, callback); +}; + + +/** + * Adds an entry to the LDAP server. + * + * Entry can be either [Attribute] or a plain JS object where the + * values are either a plain value or an array of values. Any value (that's + * not an array) will get converted to a string, so keep that in mind. + * + * @param {String} name the DN of the entry to add. + * @param {Object} entry an array of Attributes to be added or a JS object. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.add = function add(name, entry, controls, callback) { + assert.string(name, 'name'); + assert.object(entry, 'entry'); + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + assert.func(callback, 'callback'); + + if (Array.isArray(entry)) { + entry.forEach(function (a) { + if (!Attribute.isAttribute(a)) + throw new TypeError('entry must be an Array of Attributes'); + }); + } else { + var save = entry; + + entry = []; + Object.keys(save).forEach(function (k) { + var attr = new Attribute({type: k}); + if (Array.isArray(save[k])) { + save[k].forEach(function (v) { + attr.addValue(v.toString()); + }); + } else { + attr.addValue(save[k].toString()); + } + entry.push(attr); + }); + } + + var req = new AddRequest({ + entry: dn.parse(name), + attributes: entry, + controls: controls + }); + + return this._send(req, [errors.LDAP_SUCCESS], null, callback); +}; + + +/** + * Performs a simple authentication against the server. + * + * @param {String} name the DN to bind as. + * @param {String} credentials the userPassword associated with name. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.bind = function bind(name, credentials, controls, callback) { + if (typeof (name) !== 'string' && !(name instanceof dn.DN)) + throw new TypeError('name (string) required'); + assert.string(credentials, 'credentials'); + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + assert.func(callback, 'callback'); + + var req = new BindRequest({ + name: name || '', + authentication: 'Simple', + credentials: credentials || '', + controls: controls + }); + + return this._send(req, [errors.LDAP_SUCCESS], null, callback); +}; + + +/** + * Compares an attribute/value pair with an entry on the LDAP server. + * + * @param {String} name the DN of the entry to compare attributes with. + * @param {String} attr name of an attribute to check. + * @param {String} value value of an attribute to check. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, boolean, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.compare = function compare(name, + attr, + value, + controls, + callback) { + assert.string(name, 'name'); + assert.string(attr, 'attr'); + assert.string(value, 'value'); + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + assert.func(callback, 'callback'); + + var req = new CompareRequest({ + entry: dn.parse(name), + attribute: attr, + value: value, + controls: controls + }); + + return this._send(req, CMP_EXPECT, null, function (err, res) { + if (err) + return callback(err); + + return callback(null, (res.status === errors.LDAP_COMPARE_TRUE), res); + }); +}; + + +/** + * Deletes an entry from the LDAP server. + * + * @param {String} name the DN of the entry to delete. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.del = function del(name, controls, callback) { + assert.string(name, 'name'); + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + assert.func(callback, 'callback'); + + var req = new DeleteRequest({ + entry: dn.parse(name), + controls: controls + }); + + return this._send(req, [errors.LDAP_SUCCESS], null, callback); +}; + + +/** + * Performs an extended operation on the LDAP server. + * + * Pretty much none of the LDAP extended operations return an OID + * (responseName), so I just don't bother giving it back in the callback. + * It's on the third param in `res` if you need it. + * + * @param {String} name the OID of the extended operation to perform. + * @param {String} value value to pass in for this operation. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, value, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.exop = function exop(name, value, controls, callback) { + assert.string(name, 'name'); + if (typeof (value) === 'function') { + callback = value; + controls = []; + value = ''; + } + if (typeof (value) !== 'string') + throw new TypeError('value (string) required'); + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + assert.func(callback, 'callback'); + + var req = new ExtendedRequest({ + requestName: name, + requestValue: value, + controls: controls + }); + + return this._send(req, [errors.LDAP_SUCCESS], null, function (err, res) { + if (err) + return callback(err); + + return callback(null, res.responseValue || '', res); + }); +}; + + +/** + * Performs an LDAP modify against the server. + * + * @param {String} name the DN of the entry to modify. + * @param {Change} change update to perform (can be [Change]). + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.modify = function modify(name, change, controls, callback) { + assert.string(name, 'name'); + assert.object(change, 'change'); + + var changes = []; + + function changeFromObject(change) { + if (!change.operation && !change.type) + throw new Error('change.operation required'); + if (typeof (change.modification) !== 'object') + throw new Error('change.modification (object) required'); + + Object.keys(change.modification).forEach(function (k) { + var mod = {}; + mod[k] = change.modification[k]; + changes.push(new Change({ + operation: change.operation || change.type, + modification: mod + })); + }); + } + + if (change instanceof Change) { + changes.push(change); + } else if (Array.isArray(change)) { + change.forEach(function (c) { + if (c instanceof Change) { + changes.push(c); + } else { + changeFromObject(c); + } + }); + } else { + changeFromObject(change); + } + + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + assert.func(callback, 'callback'); + + var req = new ModifyRequest({ + object: dn.parse(name), + changes: changes, + controls: controls + }); + + return this._send(req, [errors.LDAP_SUCCESS], null, callback); +}; + + +/** + * Performs an LDAP modifyDN against the server. + * + * This does not allow you to keep the old DN, as while the LDAP protocol + * has a facility for that, it's stupid. Just Search/Add. + * + * This will automatically deal with "new superior" logic. + * + * @param {String} name the DN of the entry to modify. + * @param {String} newName the new DN to move this entry to. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.modifyDN = function modifyDN(name, + newName, + controls, + callback) { + if (typeof (name) !== 'string') + throw new TypeError('name (string) required'); + if (typeof (newName) !== 'string') + throw new TypeError('newName (string) required'); + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + if (typeof (callback) !== 'function') + throw new TypeError('callback (function) required'); + + var DN = dn.parse(name); + var newDN = dn.parse(newName); + + var req = new ModifyDNRequest({ + entry: DN, + deleteOldRdn: true, + controls: controls + }); + + if (newDN.length !== 1) { + req.newRdn = dn.parse(newDN.rdns.shift().toString()); + req.newSuperior = newDN; + } else { + req.newRdn = newDN; + } + + return this._send(req, [errors.LDAP_SUCCESS], null, callback); +}; + + +/** + * Performs an LDAP search against the server. + * + * Note that the defaults for options are a 'base' search, if that's what + * you want you can just pass in a string for options and it will be treated + * as the search filter. Also, you can either pass in programatic Filter + * objects or a filter string as the filter option. + * + * Note that this method is 'special' in that the callback 'res' param will + * have two important events on it, namely 'entry' and 'end' that you can hook + * to. The former will emit a SearchEntry object for each record that comes + * back, and the latter will emit a normal LDAPResult object. + * + * @param {String} base the DN in the tree to start searching at. + * @param {Object} options parameters: + * - {String} scope default of 'base'. + * - {String} filter default of '(objectclass=*)'. + * - {Array} attributes [string] to return. + * - {Boolean} attrsOnly whether to return values. + * @param {Control} controls (optional) either a Control or [Control]. + * @param {Function} callback of the form f(err, res). + * @throws {TypeError} on invalid input. + */ +Client.prototype.search = function search(base, options, controls, callback) { + if (typeof (base) !== 'string' && !(base instanceof dn.DN)) + throw new TypeError('base (string) required'); + if (Array.isArray(options) || (options instanceof Control)) { + controls = options; + options = {}; + } else if (typeof (options) === 'function') { + callback = options; + controls = []; + options = { + filter: new PresenceFilter({attribute: 'objectclass'}) + }; + } else if (typeof (options) === 'string') { + options = {filter: filters.parseString(options)}; + } else if (typeof (options) !== 'object') { + throw new TypeError('options (object) required'); + } + if (typeof (options.filter) === 'string') { + options.filter = filters.parseString(options.filter); + } else if (!options.filter) { + options.filter = new PresenceFilter({attribute: 'objectclass'}); + } else if (!(options.filter instanceof Filter)) { + throw new TypeError('options.filter (Filter) required'); + } + + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } else { + controls = validateControls(controls); + } + if (typeof (callback) !== 'function') + throw new TypeError('callback (function) required'); + + if (options.attributes) { + if (!Array.isArray(options.attributes)) { + if (typeof (options.attributes) === 'string') { + options.attributes = [options.attributes]; + } else { + throw new TypeError('options.attributes must be an Array of Strings'); + } + } + } + + var req = new SearchRequest({ + baseObject: typeof (base) === 'string' ? dn.parse(base) : base, + scope: options.scope || 'base', + filter: options.filter, + derefAliases: Protocol.NEVER_DEREF_ALIASES, + sizeLimit: options.sizeLimit || 0, + timeLimit: options.timeLimit || 10, + typesOnly: options.typesOnly || false, + attributes: options.attributes || [], + controls: controls + }); + + return this._send(req, [errors.LDAP_SUCCESS], new EventEmitter(), callback); +}; + + +/** + * Unbinds this client from the LDAP server. + * + * Note that unbind does not have a response, so this callback is actually + * optional; either way, the client is disconnected. + * + * @param {Function} callback of the form f(err). + * @throws {TypeError} if you pass in callback as not a function. + */ +Client.prototype.unbind = function unbind(callback) { + if (!callback) + callback = function () {}; + + if (typeof (callback) !== 'function') + throw new TypeError('callback must be a function'); + + if (!this.socket) + return callback(); + + var req = new UnbindRequest(); + return this._send(req, 'unbind', null, callback); +}; + + + +///--- Private API + +Client.prototype._connect = function _connect() { + var log = this.log; + var proto = this.secure ? tls : net; + var self = this; + var socket = null; + var timer = false; + + function onConnect() { + if (timer) + clearTimeout(timer); + + socket.removeListener('connect', onConnect); + socket.removeListener('secureConnect', onConnect); + assert.ok(socket.ldap); + + socket.ldap.id = nextClientId() + '__' + socket.ldap.id; + self.log = self.log.child({ldap_id: socket.ldap.id}, true); + + log.trace('connect event'); + + self.socket = socket; + self.emit('connect', socket); + } + + socket = proto.connect((this.port || this.socketPath), this.host); + + socket.once('connect', onConnect); + socket.once('secureConnect', onConnect); + setupSocket(socket, this); + + if (this.connectTimeout) { + timer = setTimeout(function onConnectTimeout() { + if (!socket || !socket.readable || !socket.writeable) { + socket.destroy(); + + self.emit('connectTimeout'); + } + }, this.connectTimeout); + } + + return socket; +}; + + +Client.prototype._send = function _send(message, expect, emitter, callback) { + assert.ok(message); + assert.ok(expect); + assert.ok(typeof (emitter) !== undefined); + assert.ok(callback); + + var conn = this.socket; + var log = this.log; + var self = this; + var timer = false; + + if (!conn) + return callback(new ConnectionError('no socket')); + + function _done(event, obj) { + if (emitter) { + if (event === 'error') + emitter.removeAllListeners('end'); + if (event === 'end') + emitter.removeAllListeners('error'); + + return emitter.emit(event, obj); + } + + if (event === 'error') + return callback(obj); + + return callback(null, obj); + } // end function _done(event, obj) + + function _continuePagedSearch(msg) { + // this function looks for a paged control in the response msg + // and continue searching or not according to RFC 2696: + // http://www.ietf.org/rfc/rfc2696.txt + if (Array.isArray(msg.controls) && msg.controls.length > 0) { + log.trace('message has %d controls', msg.controls.length); + + for (var i = 0; i < msg.controls.length; i++) { + var resControl = msg.controls[i]; + + // check paged control in response + if (resControl instanceof PagedResultsControl) { + log.debug('paged search: end of page'); + if (resControl.value.cookie && resControl.value.cookie.length > 0) { + log.trace('paged search: received cookie in response'); + + if (Array.isArray(message.controls) && + message.controls.length > 0) { + for (var j = 0; j < message.controls.length; j++) { + var reqControl = message.controls[j]; + + if (reqControl instanceof PagedResultsControl) { + // update request cookie and re-send + reqControl.value.cookie = resControl.value.cookie; + + try { + log.debug('paged search: continuing'); + conn.write(message.toBer()); + return true; + } catch (e) { + if (timer) + clearTimeout(timer); + + log.trace({err: e}, 'Error writing message to socket'); + callback(e); + return false; + } + } + } + } + } else { + log.debug('paged search done'); + } + } + } + } + + // not a paged search or all pages received + return false; + } // end function _continuePagedSearch(msg) + + function messageCallback(msg) { + if (timer) + clearTimeout(timer); + + if (log.trace()) + log.trace({msg: msg ? msg.json : null}, 'response received'); + + if (expect === 'abandon') + return _done('end', null); + + if (msg instanceof SearchEntry || msg instanceof SearchReference) { + var event = msg.constructor.name; + event = event[0].toLowerCase() + event.slice(1); + return _done(event, msg); + } else if (_continuePagedSearch(msg)) { + // page search continued, just return for now + return undefined; + } else { + delete conn.ldap.messages[message.messageID]; + + if (msg instanceof LDAPResult) { + if (expect.indexOf(msg.status) === -1) + return _done('error', errors.getError(msg)); + + return _done('end', msg); + } else if (msg instanceof Error) { + return _done('error', msg); + } else { + return _done('error', new errors.ProtocolError(msg.type)); + } + } + } // end function messageCallback(msg) + + function onRequestTimeout() { + self.emit('timeout', message); + if (conn.ldap.messages[message.messageID]) { + conn.ldap.messages[message.messageID](new LDAPResult({ + status: 80, // LDAP_OTHER + errorMessage: 'request timeout (client interrupt)' + })); + } + } // end function onRequestTimeout() + + function writeCallback() { + if (expect === 'abandon') { + return callback(null); + } else if (expect === 'unbind') { + conn.unbindMessageID = message.id; + conn.end(); + } else if (emitter) { + return callback(null, emitter); + } + return false; + } // end writeCallback() + + // Start actually doing something... + message.messageID = conn.ldap.getNextMessageID(); + conn.ldap.messages[message.messageID] = messageCallback; + + if (self.timeout) { + log.trace('Setting timeout to %d', self.timeout); + timer = setTimeout(onRequestTimeout, self.timeout); + } + + if (log.trace()) + log.trace('sending request %j', message.json); + + try { + return conn.write(message.toBer(), writeCallback); + } catch (e) { + if (timer) + clearTimeout(timer); + + log.trace({err: e}, 'Error writing message to socket'); + return callback(e); + } +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/index.js new file mode 100644 index 0000000..5f8fae3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/index.js @@ -0,0 +1,63 @@ +// Copyright 2012 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); + +var Logger = require('bunyan'); + +var Client = require('./client'); +var ClientPool = require('./pool'); + + + +///--- Globals + +var DEF_LOG = new Logger({ + name: 'ldapjs', + component: 'client', + stream: process.stderr, + serializers: Logger.stdSerializers +}); + + + +///--- Functions + +function xor() { + var b = false; + for (var i = 0; i < arguments.length; i++) { + if (arguments[i] && !b) { + b = true; + } else if (arguments[i] && b) { + return false; + } + } + return b; +} + + + +///--- Exports + +module.exports = { + + createClient: function createClient(options) { + if (typeof (options) !== 'object') + throw new TypeError('options (object) required'); + if (options.url && typeof (options.url) !== 'string') + throw new TypeError('options.url (string) required'); + if (options.socketPath && typeof (options.socketPath) !== 'string') + throw new TypeError('options.socketPath must be a string'); + if (!xor(options.url, options.socketPath)) + throw new TypeError('options.url ^ options.socketPath (String) required'); + if (!options.log) + options.log = DEF_LOG; + if (typeof (options.log) !== 'object') + throw new TypeError('options.log must be an object'); + + if (options.maxConnections > 1) + return new ClientPool(options); + + return new Client(options); + } + +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/pool.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/pool.js new file mode 100644 index 0000000..3866473 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/client/pool.js @@ -0,0 +1,268 @@ +// Copyright 2012 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var EventEmitter = require('events').EventEmitter; +var util = require('util'); + +var pooling = require('pooling'); + +var ConnectionError = require('../errors').ConnectionError; +var BindResponse = require('../messages').BindResponse; + +var Client = require('./client'); + + + +///--- Globals + +var STD_OPS = [ + 'add', + 'del', + 'modify', + 'modifyDN' +]; + +var RETURN_VAL_OPS = [ + 'compare', + 'exop' +]; + + + +///--- Internal Functions + +function createPool(options) { + assert.ok(options); + + return pooling.createPool({ + checkInterval: options.checkInterval, + log: options.log, + name: 'ldapjs_' + (options.url || options.socketPath), + max: options.maxConnections, + maxIdleTime: options.maxIdleTime, + + create: function createClient(callback) { + var client = new Client(options); + + client.on('error', function (err) { + client.removeAllListeners('connect'); + return callback(err); + }); + + client.once('connect', function onConnect() { + client.removeAllListeners('error'); + + if (!options.bindDN || !options.bindCredentials) + return callback(null, client); + + function bindCallback(err, res) { + if (err) + return callback(err, null); + + return callback(null, client); + } + + return client.bind(options.bindDN, + options.bindCredentials, + options.bindControls || [], + bindCallback); + }); + }, + + check: function check(client, callback) { + // just do a root dse search + client.search('', '(objectclass=*)', function (err, res) { + if (err) + return callback(err); + + res.on('error', function (e) { + client.removeAllListeners('end'); + callback(e); + }); + + res.on('end', function () { + client.removeAllListeners('error'); + callback(null); + }); + + return undefined; + }); + }, + + destroy: function destroy(client) { + client.unbind(function () {}); + } + }); +} + + + +///--- API + +function ClientPool(options) { + assert.ok(options); + EventEmitter.call(this, options); + + this.log = options.log.child({clazz: 'ClientPool'}, true); + this.options = { + bindDN: options.bindDN, + bindCredentials: options.bindCredentials, + bindControls: options.bindControls || [], + checkInterval: options.checkInterval, + connectTimeout: (options.connectTimeout || 0), + maxIdleTime: options.maxIdleTime, + maxConnections: options.maxConnections, + log: options.log, + socketPath: options.socketPath, + timeout: (options.timeout || 0), + url: options.url + }; + this.pool = createPool(options); +} +util.inherits(ClientPool, EventEmitter); +module.exports = ClientPool; + + + +STD_OPS.forEach(function (op) { + ClientPool.prototype[op] = function clientProxy() { + var args = Array.prototype.slice.call(arguments); + var cb = args.pop(); + if (typeof (cb) !== 'function') + throw new TypeError('callback (Function) required'); + var self = this; + + return this.pool.acquire(function onAcquire(err, client) { + if (err) + return cb(err); + + args.push(function proxyCallback(err, res) { + self.pool.release(client); + return cb(err, res); + }); + + try { + return Client.prototype[op].apply(client, args); + } catch (e) { + self.pool.release(client); + return cb(e); + } + }); + }; +}); + + +RETURN_VAL_OPS.forEach(function (op) { + ClientPool.prototype[op] = function clientProxy() { + var args = Array.prototype.slice.call(arguments); + var cb = args.pop(); + if (typeof (cb) !== 'function') + throw new TypeError('callback (Function) required'); + var self = this; + + return this.pool.acquire(function onAcquire(poolErr, client) { + if (poolErr) + return cb(poolErr); + + args.push(function proxyCallback(err, val, res) { + self.pool.release(client); + return cb(err, val, res); + }); + + try { + return Client.prototype[op].apply(client, args); + } catch (e) { + self.pool.release(client); + return cb(e); + } + }); + }; +}); + + +ClientPool.prototype.search = function search(base, opts, controls, callback) { + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } + + var self = this; + + return this.pool.acquire(function onAcquire(err, client) { + if (err) + return callback(err); + + // This is largely in existence for search requests + client.timeout = self.timeout || client.timeout; + + + return client.search(base, opts, controls, function (err, res) { + function cleanup() { + self.pool.release(client); + } + + if (err) { + cleanup(); + return callback(err, res); + } + res.on('error', cleanup); + res.on('end', cleanup); + + return callback(null, res); + }); + }); +}; + + +ClientPool.prototype.abandon = function abandon(msgid, controls, callback) { + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } + + this.log.error({ + messageID: msgid + }, 'Abandon is not supported with connection pooling. Ignoring.'); + return callback(null); +}; + + +ClientPool.prototype.bind = function bind(dn, creds, controls, callback) { + if (typeof (controls) === 'function') { + callback = controls; + controls = []; + } + + var self = this; + + self.options.bindDN = null; + self.options.bindCredentials = null; + self.options.bindControls = null; + + return this.pool.shutdown(function () { + self.pool = createPool(self.options); + + return self.pool.acquire(function onAcquire(err, client) { + if (err) + return callback(err); + + return client.bind(dn, creds, controls, function (err, res) { + self.pool.release(client); + + if (err) + return callback(err, res); + + self.options.bindDN = dn; + self.options.bindCredentials = creds; + self.options.bindControls = controls; + + return callback(null, res); + }); + }); + }); +}; + + +ClientPool.prototype.unbind = function unbind(callback) { + return this.pool.shutdown(callback); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/control.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/control.js new file mode 100644 index 0000000..e5d723d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/control.js @@ -0,0 +1,72 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var Protocol = require('../protocol'); + + + +///--- Globals + +var Ber = asn1.Ber; + + + +///--- API + +function Control(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.type && typeof (options.type) !== 'string') + throw new TypeError('options.type must be a string'); + if (options.criticality !== undefined && + typeof (options.criticality) !== 'boolean') + throw new TypeError('options.criticality must be a boolean'); + if (options.value && typeof (options.value) !== 'string') + throw new TypeError('options.value must be a string'); + } else { + options = {}; + } + + this.type = options.type || ''; + this.criticality = options.critical || options.criticality || false; + this.value = options.value || null; + + var self = this; + this.__defineGetter__('json', function () { + var obj = { + controlType: self.type, + criticality: self.criticality, + controlValue: self.value + }; + return (typeof (self._json) === 'function' ? self._json(obj) : obj); + }); +} +module.exports = Control; + + +Control.prototype.toBer = function (ber) { + assert.ok(ber); + + ber.startSequence(); + ber.writeString(this.type || ''); + ber.writeBoolean(this.criticality); + if (typeof (this._toBer) === 'function') { + this._toBer(ber); + } else { + if (this.value) + ber.writeString(this.value); + } + + ber.endSequence(); + return; +}; + + +Control.prototype.toString = function () { + return this.json; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/entry_change_notification_control.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/entry_change_notification_control.js new file mode 100644 index 0000000..4b1205a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/entry_change_notification_control.js @@ -0,0 +1,93 @@ +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var Control = require('./control'); + + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- API + +function EntryChangeNotificationControl(options) { + if (!options) + options = {}; + + options.type = EntryChangeNotificationControl.OID; + if (options.value) { + if (Buffer.isBuffer(options.value)) { + this.parse(options.value); + } else if (typeof (options.value) === 'object') { + this._value = options.value; + } else { + throw new TypeError('options.value must be a Buffer or Object'); + } + options.value = null; + } + Control.call(this, options); + + var self = this; + this.__defineGetter__('value', function () { + return self._value || {}; + }); +} +util.inherits(EntryChangeNotificationControl, Control); +module.exports = EntryChangeNotificationControl; + + +EntryChangeNotificationControl.prototype.parse = function parse(buffer) { + assert.ok(buffer); + + var ber = new BerReader(buffer); + if (ber.readSequence()) { + this._value = { + changeType: ber.readInt() + }; + + // if the operation was moddn, then parse the optional previousDN attr + if (this._value.changeType === 8) + this._value.previousDN = ber.readString(); + + this._value.changeNumber = ber.readInt(); + + return true; + } + + return false; +}; + + +EntryChangeNotificationControl.prototype._toBer = function (ber) { + assert.ok(ber); + + if (!this._value) + return; + + var writer = new BerWriter(); + writer.startSequence(); + writer.writeInt(this.value.changeType); + if (this.value.previousDN) + writer.writeString(this.value.previousDN); + + writer.writeInt(parseInt(this.value.changeNumber, 10)); + writer.endSequence(); + + ber.writeBuffer(writer.buffer, 0x04); +}; + + +EntryChangeNotificationControl.prototype._json = function (obj) { + obj.controlValue = this.value; + return obj; +}; + + + +EntryChangeNotificationControl.OID = '2.16.840.1.113730.3.4.7'; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/index.js new file mode 100644 index 0000000..d2d7f23 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/index.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); + +var Control = require('./control'); +var EntryChangeNotificationControl = + require('./entry_change_notification_control'); +var PersistentSearchControl = require('./persistent_search_control'); +var PagedResultsControl = require('./paged_results_control'); + + + +///--- API + +module.exports = { + + getControl: function getControl(ber) { + assert.ok(ber); + + if (ber.readSequence() === null) + return null; + + var type; + var critical = false; + var value; + + if (ber.length) { + var end = ber.offset + ber.length; + + type = ber.readString(); + if (ber.offset < end) { + if (ber.peek() === 0x01) + critical = ber.readBoolean(); + } + + if (ber.offset < end) + value = ber.readString(0x04, true); + } + + var control; + switch (type) { + case PersistentSearchControl.OID: + control = new PersistentSearchControl({ + critical: critical, + value: value + }); + break; + case EntryChangeNotificationControl.OID: + control = new EntryChangeNotificationControl({ + critical: critical, + value: value + }); + break; + case PagedResultsControl.OID: + control = new PagedResultsControl({ + critical: critical, + value: value + }); + break; + default: + control = new Control({ + type: type, + critical: critical, + value: value ? value.toString('utf8') : null + }); + break; + } + + return control; + }, + + Control: Control, + EntryChangeNotificationControl: EntryChangeNotificationControl, + PagedResultsControl: PagedResultsControl, + PersistentSearchControl: PersistentSearchControl +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/paged_results_control.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/paged_results_control.js new file mode 100644 index 0000000..7cdbdf7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/paged_results_control.js @@ -0,0 +1,91 @@ +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var Control = require('./control'); + + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- API + +function PagedResultsControl(options) { + if (!options) + options = {}; + + options.type = PagedResultsControl.OID; + if (options.value) { + if (Buffer.isBuffer(options.value)) { + this.parse(options.value); + } else if (typeof (options.value) === 'object') { + this._value = options.value; + } else { + throw new TypeError('options.value must be a Buffer or Object'); + } + options.value = null; + } + Control.call(this, options); + + var self = this; + this.__defineGetter__('value', function () { + return self._value || {}; + }); +} +util.inherits(PagedResultsControl, Control); +module.exports = PagedResultsControl; + + +PagedResultsControl.prototype.parse = function parse(buffer) { + assert.ok(buffer); + + var ber = new BerReader(buffer); + if (ber.readSequence()) { + this._value = {}; + this._value.size = ber.readInt(); + this._value.cookie = ber.readString(asn1.Ber.OctetString, true); + //readString returns '' instead of a zero-length buffer + if (!this._value.cookie) + this._value.cookie = new Buffer(0); + + return true; + } + + return false; +}; + + +PagedResultsControl.prototype._toBer = function (ber) { + assert.ok(ber); + + if (!this._value) + return; + + var writer = new BerWriter(); + writer.startSequence(); + writer.writeInt(this.value.size); + if (this.value.cookie && this.value.cookie.length > 0) { + writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString); + } else { + writer.writeString(''); //writeBuffer rejects zero-length buffers + } + writer.endSequence(); + + ber.writeBuffer(writer.buffer, 0x04); +}; + + +PagedResultsControl.prototype._json = function (obj) { + obj.controlValue = this.value; + return obj; +}; + + + +PagedResultsControl.OID = '1.2.840.113556.1.4.319'; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/persistent_search_control.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/persistent_search_control.js new file mode 100644 index 0000000..33f3471 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/controls/persistent_search_control.js @@ -0,0 +1,89 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var Control = require('./control'); + + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- API + +function PersistentSearchControl(options) { + if (!options) + options = {}; + + options.type = PersistentSearchControl.OID; + if (options.value) { + if (Buffer.isBuffer(options.value)) { + this.parse(options.value); + } else if (typeof (options.value) === 'object') { + this._value = options.value; + } else { + throw new TypeError('options.value must be a Buffer or Object'); + } + options.value = null; + } + Control.call(this, options); + + var self = this; + this.__defineGetter__('value', function () { + return self._value || {}; + }); +} +util.inherits(PersistentSearchControl, Control); +module.exports = PersistentSearchControl; + + +PersistentSearchControl.prototype.parse = function parse(buffer) { + assert.ok(buffer); + + var ber = new BerReader(buffer); + if (ber.readSequence()) { + this._value = { + changeTypes: ber.readInt(), + changesOnly: ber.readBoolean(), + returnECs: ber.readBoolean() + }; + + return true; + } + + return false; +}; + + +PersistentSearchControl.prototype._toBer = function (ber) { + assert.ok(ber); + + if (!this._value) + return; + + var writer = new BerWriter(); + writer.startSequence(); + writer.writeInt(this.value.changeTypes); + writer.writeBoolean(this.value.changesOnly); + writer.writeBoolean(this.value.returnECs); + writer.endSequence(); + + ber.writeBuffer(writer.buffer, 0x04); +}; + + +PersistentSearchControl.prototype._json = function (obj) { + obj.controlValue = this.value; + return obj; +}; + + + +PersistentSearchControl.OID = '2.16.840.1.113730.3.4.3'; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/dn.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/dn.js new file mode 100644 index 0000000..bee7199 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/dn.js @@ -0,0 +1,359 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + + + +function invalidDN(name) { + var e = new Error(); + e.name = 'InvalidDistinguishedNameError'; + e.message = name; + return e; +} + + +function isAlphaNumeric(c) { + var re = /[A-Za-z0-9]/; + return re.test(c); +} + + +function isWhitespace(c) { + var re = /\s/; + return re.test(c); +} + +function RDN(obj) { + var self = this; + + if (obj) { + Object.keys(obj).forEach(function (k) { + self[k.toLowerCase()] = obj[k]; + }); + } +} + + +RDN.prototype.toString = function () { + var self = this; + + var str = ''; + Object.keys(this).forEach(function (k) { + if (str.length) + str += '+'; + + str += k + '=' + self[k]; + }); + + return str; +}; + +// Thank you OpenJDK! +function parse(name) { + if (typeof (name) !== 'string') + throw new TypeError('name (string) required'); + + var cur = 0; + var len = name.length; + + function parseRdn() { + var rdn = new RDN(); + while (cur < len) { + trim(); + var attr = parseAttrType(); + trim(); + if (cur >= len || name[cur++] !== '=') + throw invalidDN(name); + + trim(); + var value = parseAttrValue(); + trim(); + rdn[attr.toLowerCase()] = value; + if (cur >= len || name[cur] !== '+') + break; + ++cur; + } + + return rdn; + } + + + function trim() { + while ((cur < len) && isWhitespace(name[cur])) + ++cur; + } + + function parseAttrType() { + var beg = cur; + while (cur < len) { + var c = name[cur]; + if (isAlphaNumeric(c) || + c == '.' || + c == '-' || + c == ' ') { + ++cur; + } else { + break; + } + } + // Back out any trailing spaces. + while ((cur > beg) && (name[cur - 1] == ' ')) + --cur; + + if (beg == cur) + throw invalidDN(name); + + return name.slice(beg, cur); + } + + function parseAttrValue() { + if (cur < len && name[cur] == '#') { + return parseBinaryAttrValue(); + } else if (cur < len && name[cur] == '"') { + return parseQuotedAttrValue(); + } else { + return parseStringAttrValue(); + } + } + + function parseBinaryAttrValue() { + var beg = cur++; + while (cur < len && isAlphaNumeric(name[cur])) + ++cur; + + return name.slice(beg, cur); + } + + function parseQuotedAttrValue() { + var beg = cur++; + + while ((cur < len) && name[cur] != '"') { + if (name[cur] === '\\') + ++cur; // consume backslash, then what follows + + ++cur; + } + if (cur++ >= len) // no closing quote + throw invalidDN(name); + + return name.slice(beg, cur); + } + + function parseStringAttrValue() { + var beg = cur; + var esc = -1; + + while ((cur < len) && !atTerminator()) { + if (name[cur] === '\\') { + ++cur; // consume backslash, then what follows + esc = cur; + } + ++cur; + } + if (cur > len) // backslash followed by nothing + throw invalidDN(name); + + // Trim off (unescaped) trailing whitespace. + var end; + for (end = cur; end > beg; end--) { + if (!isWhitespace(name[end - 1]) || (esc === (end - 1))) + break; + } + return name.slice(beg, end); + } + + function atTerminator() { + return (cur < len && + (name[cur] === ',' || + name[cur] === ';' || + name[cur] === '+')); + } + + var rdns = []; + + rdns.push(parseRdn()); + while (cur < len) { + if (name[cur] === ',' || name[cur] === ';') { + ++cur; + rdns.push(parseRdn()); + } else { + throw invalidDN(name); + } + } + + return new DN(rdns); +} + + + +///--- API + + +function DN(rdns) { + if (!Array.isArray(rdns)) + throw new TypeError('rdns ([object]) required'); + rdns.forEach(function (rdn) { + if (typeof (rdn) !== 'object') + throw new TypeError('rdns ([object]) required'); + }); + + this.rdns = rdns.slice(); + + this.__defineGetter__('length', function () { + return this.rdns.length; + }); +} + + +DN.prototype.toString = function () { + var _dn = []; + this.rdns.forEach(function (rdn) { + _dn.push(rdn.toString()); + }); + return _dn.join(', '); +}; + + +DN.prototype.childOf = function (dn) { + if (typeof (dn) !== 'object') + dn = parse(dn); + + if (this.rdns.length <= dn.rdns.length) + return false; + + var diff = this.rdns.length - dn.rdns.length; + for (var i = dn.rdns.length - 1; i >= 0; i--) { + var rdn = dn.rdns[i]; + + var keys = Object.keys(rdn); + if (!keys.length) + return false; + + for (var j = 0; j < keys.length; j++) { + var k = keys[j]; + var ourRdn = this.rdns[i + diff]; + if (ourRdn[k] !== rdn[k]) + return false; + } + } + + return true; +}; + + +DN.prototype.parentOf = function (dn) { + if (typeof (dn) !== 'object') + dn = parse(dn); + + if (!this.rdns.length || this.rdns.length >= dn.rdns.length) + return false; + + var diff = dn.rdns.length - this.rdns.length; + for (var i = this.rdns.length - 1; i >= 0; i--) { + var rdn = this.rdns[i]; + var keys = Object.keys(rdn); + + if (!keys.length) + return false; + + for (var j = 0; j < keys.length; j++) { + var k = keys[j]; + var theirRdn = dn.rdns[i + diff]; + if (theirRdn[k] !== rdn[k]) + return false; + } + } + + return true; +}; + + +DN.prototype.equals = function (dn) { + if (typeof (dn) !== 'object') + dn = parse(dn); + + if (this.rdns.length !== dn.rdns.length) + return false; + + for (var i = 0; i < this.rdns.length; i++) { + var ours = this.rdns[i]; + var theirs = dn.rdns[i]; + var ourKeys = Object.keys(ours); + var theirKeys = Object.keys(theirs); + if (ourKeys.length !== theirKeys.length) + return false; + + ourKeys.sort(); + theirKeys.sort(); + + for (var j = 0; j < ourKeys.length; j++) { + if (ourKeys[j] !== theirKeys[j]) + return false; + if (ours[ourKeys[j]] !== theirs[ourKeys[j]]) + return false; + } + } + + return true; +}; + + +DN.prototype.parent = function () { + if (this.rdns.length > 1) { + var save = this.rdns.shift(); + var dn = new DN(this.rdns); + this.rdns.unshift(save); + return dn; + } + + return null; +}; + + +DN.prototype.clone = function () { + return new DN(this.rdns); +}; + + +DN.prototype.reverse = function () { + this.rdns.reverse(); + return this; +}; + + +DN.prototype.pop = function () { + return this.rdns.pop(); +}; + + +DN.prototype.push = function (rdn) { + if (typeof (rdn) !== 'object') + throw new TypeError('rdn (RDN) required'); + + return this.rdns.push(rdn); +}; + + +DN.prototype.shift = function () { + return this.rdns.shift(); +}; + + +DN.prototype.unshift = function (rdn) { + if (typeof (rdn) !== 'object') + throw new TypeError('rdn (RDN) required'); + + return this.rdns.unshift(rdn); +}; + + + +///--- Exports + +module.exports = { + + parse: parse, + + DN: DN, + + RDN: RDN + +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/dtrace.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/dtrace.js new file mode 100644 index 0000000..42d6abe --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/dtrace.js @@ -0,0 +1,102 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var dtrace = require('dtrace-provider'); + + + +///--- Globals + +var SERVER_PROVIDER; +var DTRACE_ID = 0; +var MAX_INT = 4294967295; + +/* + * Args: + * server-*-start: + * 0 -> id + * 1 -> remoteIP + * 2 -> bindDN + * 3 -> req.dn + * 4,5 -> op specific + * + * server-*-done: + * 0 -> id + * 1 -> remoteIp + * 2 -> bindDN + * 3 -> requsetDN + * 4 -> status + * 5 -> errorMessage + * + */ +var SERVER_PROBES = { + + // 4: attributes.length + 'server-add-start': ['int', 'char *', 'char *', 'char *', 'int'], + 'server-add-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + + 'server-bind-start': ['int', 'char *', 'char *', 'char *'], + 'server-bind-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + + // 4: attribute, 5: value + 'server-compare-start': ['int', 'char *', 'char *', 'char *', + 'char *', 'char *'], + 'server-compare-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + + 'server-delete-start': ['int', 'char *', 'char *', 'char *'], + 'server-delete-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + + // 4: requestName, 5: requestValue + 'server-exop-start': ['int', 'char *', 'char *', 'char *', 'char *', + 'char *'], + 'server-exop-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + + // 4: changes.length + 'server-modify-start': ['int', 'char *', 'char *', 'char *', 'int'], + 'server-modify-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + + // 4: newRdn, 5: newSuperior + 'server-modifydn-start': ['int', 'char *', 'char *', 'char *', 'char *', + 'char *'], + 'server-modifydn-done': ['int', 'char *', 'char *', 'char *', 'int', + 'char *'], + + // 4: scope, 5: filter + 'server-search-start': ['int', 'char *', 'char *', 'char *', 'char *', + 'char *'], + 'server-search-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + // Last two are searchEntry.DN and seachEntry.attributes.length + 'server-search-entry': ['int', 'char *', 'char *', 'char *', 'char *', 'int'], + + 'server-unbind-start': ['int', 'char *', 'char *', 'char *'], + 'server-unbind-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], + + // remote IP + 'server-connection': ['char *'] +}; + + +///--- API + +module.exports = function () { + if (!SERVER_PROVIDER) { + SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs'); + + Object.keys(SERVER_PROBES).forEach(function (p) { + var args = SERVER_PROBES[p].splice(0); + args.unshift(p); + + dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args); + }); + + SERVER_PROVIDER.enable(); + + SERVER_PROVIDER._nextId = function () { + if (DTRACE_ID === MAX_INT) + DTRACE_ID = 0; + + return ++DTRACE_ID; + }; + } + + return SERVER_PROVIDER; +}(); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/errors/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/errors/index.js new file mode 100644 index 0000000..c835741 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/errors/index.js @@ -0,0 +1,154 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var util = require('util'); + +var LDAPResult = require('../messages').LDAPResult; + + +///--- Globals + +var CODES = { + LDAP_SUCCESS: 0, + LDAP_OPERATIONS_ERROR: 1, + LDAP_PROTOCOL_ERROR: 2, + LDAP_TIME_LIMIT_EXCEEDED: 3, + LDAP_SIZE_LIMIT_EXCEEDED: 4, + LDAP_COMPARE_FALSE: 5, + LDAP_COMPARE_TRUE: 6, + LDAP_AUTH_METHOD_NOT_SUPPORTED: 7, + LDAP_STRONG_AUTH_REQUIRED: 8, + LDAP_REFERRAL: 10, + LDAP_ADMIN_LIMIT_EXCEEDED: 11, + LDAP_UNAVAILABLE_CRITICAL_EXTENSION: 12, + LDAP_CONFIDENTIALITY_REQUIRED: 13, + LDAP_SASL_BIND_IN_PROGRESS: 14, + LDAP_NO_SUCH_ATTRIBUTE: 16, + LDAP_UNDEFINED_ATTRIBUTE_TYPE: 17, + LDAP_INAPPROPRIATE_MATCHING: 18, + LDAP_CONSTRAINT_VIOLATION: 19, + LDAP_ATTRIBUTE_OR_VALUE_EXISTS: 20, + LDAP_INVALID_ATTRIBUTE_SYNTAX: 21, + LDAP_NO_SUCH_OBJECT: 32, + LDAP_ALIAS_PROBLEM: 33, + LDAP_INVALID_DN_SYNTAX: 34, + LDAP_ALIAS_DEREF_PROBLEM: 36, + LDAP_INAPPROPRIATE_AUTHENTICATION: 48, + LDAP_INVALID_CREDENTIALS: 49, + LDAP_INSUFFICIENT_ACCESS_RIGHTS: 50, + LDAP_BUSY: 51, + LDAP_UNAVAILABLE: 52, + LDAP_UNWILLING_TO_PERFORM: 53, + LDAP_LOOP_DETECT: 54, + LDAP_NAMING_VIOLATION: 64, + LDAP_OBJECTCLASS_VIOLATION: 65, + LDAP_NOT_ALLOWED_ON_NON_LEAF: 66, + LDAP_NOT_ALLOWED_ON_RDN: 67, + LDAP_ENTRY_ALREADY_EXISTS: 68, + LDAP_OBJECTCLASS_MODS_PROHIBITED: 69, + LDAP_AFFECTS_MULTIPLE_DSAS: 71, + LDAP_OTHER: 80 +}; + +var ERRORS = []; + + + +///--- Error Base class + +function LDAPError(errorName, errorCode, msg, dn, caller) { + if (Error.captureStackTrace) + Error.captureStackTrace(this, caller || LDAPError); + + this.__defineGetter__('dn', function () { + return (dn ? (dn.toString() || '') : ''); + }); + this.__defineGetter__('code', function () { + return errorCode; + }); + this.__defineGetter__('name', function () { + return errorName; + }); + this.__defineGetter__('message', function () { + return msg || errorName; + }); +} +util.inherits(LDAPError, Error); + + + +///--- Exported API +// Some whacky games here to make sure all the codes are exported + +module.exports = {}; +module.exports.LDAPError = LDAPError; + +Object.keys(CODES).forEach(function (code) { + module.exports[code] = CODES[code]; + if (code === 'LDAP_SUCCESS') + return; + + var err = ''; + var msg = ''; + var pieces = code.split('_').slice(1); + for (var i = 0; i < pieces.length; i++) { + var lc = pieces[i].toLowerCase(); + var key = lc.charAt(0).toUpperCase() + lc.slice(1); + err += key; + msg += key + ((i + 1) < pieces.length ? ' ' : ''); + } + + if (!/\w+Error$/.test(err)) + err += 'Error'; + + // At this point LDAP_OPERATIONS_ERROR is now OperationsError in $err + // and 'Operations Error' in $msg + module.exports[err] = function (message, dn, caller) { + LDAPError.call(this, + err, + CODES[code], + message || msg, + dn || null, + caller || module.exports[err]); + }; + module.exports[err].constructor = module.exports[err]; + util.inherits(module.exports[err], LDAPError); + + ERRORS[CODES[code]] = { + err: err, + message: msg + }; +}); + + +module.exports.getError = function (res) { + if (!(res instanceof LDAPResult)) + throw new TypeError('res (LDAPResult) required'); + + var errObj = ERRORS[res.status]; + var E = module.exports[errObj.err]; + return new E(res.errorMessage || errObj.message, + res.matchedDN || null, + module.exports.getError); +}; + + +module.exports.getMessage = function (code) { + if (typeof (code) !== 'number') + throw new TypeError('code (number) required'); + + var errObj = ERRORS[code]; + return (errObj && errObj.message ? errObj.message : ''); +}; + + + +function ConnectionError(message) { + LDAPError.call(this, + 'ConnectionError', + 0x80, // LDAP_OTHER, + message, + null, + ConnectionError); +} +util.inherits(ConnectionError, LDAPError); +module.exports.ConnectionError = ConnectionError; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/and_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/and_filter.js new file mode 100644 index 0000000..f72524c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/and_filter.js @@ -0,0 +1,83 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function AndFilter(options) { + if (typeof (options) === 'object') { + if (!options.filters || !Array.isArray(options.filters)) + throw new TypeError('options.filters ([Filter]) required'); + this.filters = options.filters.slice(); + } else { + options = {}; + } + + options.type = Protocol.FILTER_AND; + Filter.call(this, options); + + if (!this.filters) + this.filters = []; + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'And', + filters: self.filters || [] + }; + }); +} +util.inherits(AndFilter, Filter); +module.exports = AndFilter; + + +AndFilter.prototype.toString = function () { + var str = '(&'; + this.filters.forEach(function (f) { + str += f.toString(); + }); + str += ')'; + + return str; +}; + + +AndFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + var matches = this.filters.length ? true : false; + + for (var i = 0; i < this.filters.length; i++) { + if (!this.filters[i].matches(target)) + return false; + } + + return matches; +}; + + +AndFilter.prototype.addFilter = function (filter) { + if (!filter || typeof (filter) !== 'object') + throw new TypeError('filter (object) required'); + + this.filters.push(filter); +}; + + +AndFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + this.filters.forEach(function (f) { + ber = f.toBer(ber); + }); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/approx_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/approx_filter.js new file mode 100644 index 0000000..4faff38 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/approx_filter.js @@ -0,0 +1,83 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var escape = require('./escape').escape; + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function ApproximateFilter(options) { + if (typeof (options) === 'object') { + if (!options.attribute || typeof (options.attribute) !== 'string') + throw new TypeError('options.attribute (string) required'); + if (!options.value || typeof (options.value) !== 'string') + throw new TypeError('options.value (string) required'); + this.attribute = escape(options.attribute); + this.value = escape(options.value); + } else { + options = {}; + } + options.type = Protocol.FILTER_APPROX; + Filter.call(this, options); + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'ApproximateMatch', + attribute: self.attribute || undefined, + value: self.value || undefined + }; + }); +} +util.inherits(ApproximateFilter, Filter); +module.exports = ApproximateFilter; + + +ApproximateFilter.prototype.toString = function () { + return '(' + this.attribute + '~=' + this.value + ')'; +}; + + +ApproximateFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + var matches = false; + if (target.hasOwnProperty(this.attribute)) { + var tv = target[this.attribute]; + if (Array.isArray(tv)) { + matches = (tv.indexOf(this.value) != -1); + } else { + matches = (this.value === target[this.attribute]); + } + } + + return matches; +}; + + +ApproximateFilter.prototype.parse = function (ber) { + assert.ok(ber); + + this.attribute = ber.readString().toLowerCase(); + this.value = ber.readString(); + + return true; +}; + + +ApproximateFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.attribute); + ber.writeString(this.value); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/equality_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/equality_filter.js new file mode 100644 index 0000000..9638507 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/equality_filter.js @@ -0,0 +1,89 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var escape = require('./escape').escape; + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function EqualityFilter(options) { + if (typeof (options) === 'object') { + if (!options.attribute || typeof (options.attribute) !== 'string') + throw new TypeError('options.attribute (string) required'); + if (!options.value || typeof (options.value) !== 'string') + throw new TypeError('options.value (string) required'); + this.attribute = escape(options.attribute); + this.value = escape(options.value); + } else { + options = {}; + } + options.type = Protocol.FILTER_EQUALITY; + Filter.call(this, options); + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'EqualityMatch', + attribute: self.attribute || undefined, + value: self.value || undefined + }; + }); +} +util.inherits(EqualityFilter, Filter); +module.exports = EqualityFilter; + + +EqualityFilter.prototype.toString = function () { + return '(' + this.attribute + '=' + this.value + ')'; +}; + + +EqualityFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + var self = this; + + if (target.hasOwnProperty(this.attribute)) { + var value = this.value; + return Filter.multi_test( + function (v) { + if (self.attribute === 'objectclass') + v = v.toLowerCase(); + return value === v; + }, + target[this.attribute]); + } + + return false; +}; + + +EqualityFilter.prototype.parse = function (ber) { + assert.ok(ber); + + this.attribute = ber.readString().toLowerCase(); + this.value = ber.readString(); + + if (this.attribute === 'objectclass') + this.value = this.value.toLowerCase(); + + return true; +}; + + +EqualityFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.attribute); + ber.writeString(this.value); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/escape.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/escape.js new file mode 100644 index 0000000..c9aea4e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/escape.js @@ -0,0 +1,45 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +/** + * RFC 2254 Escaping of filter strings + * + * Raw Escaped + * (o=Parens (R Us)) (o=Parens \28R Us\29) + * (cn=star*) (cn=star\2A) + * (filename=C:\MyFile) (filename=C:\5cMyFile) + * + * Use substr_filter to avoid having * ecsaped. + * + * @author [Austin King](https://github.com/ozten) + */ +exports.escape = function (inp) { + if (typeof (inp) === 'string') { + var esc = ''; + for (var i = 0; i < inp.length; i++) { + switch (inp[i]) { + case '*': + esc += '\\2a'; + break; + case '(': + esc += '\\28'; + break; + case ')': + esc += '\\29'; + break; + case '\\': + esc += '\\5c'; + break; + case '\0': + esc += '\\00'; + break; + default: + esc += inp[i]; + break; + } + } + return esc; + + } else { + return inp; + } +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/ext_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/ext_filter.js new file mode 100644 index 0000000..339fbda --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/ext_filter.js @@ -0,0 +1,144 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function ExtensibleFilter(options) { + if (typeof (options) === 'object') { + if (options.rule && typeof (options.rule) !== 'string') + throw new TypeError('options.rule must be a string'); + if (options.matchType && typeof (options.matchType) !== 'string') + throw new TypeError('options.type must be a string'); + if (options.value && typeof (options.value) !== 'string') + throw new TypeError('options.value (string) required'); + } else { + options = {}; + } + + this.matchType = options.matchType || null; + this.rule = options.rule || null; + this.value = options.value || ''; + this.dnAttributes = options.dnAttributes || false; + options.type = Protocol.FILTER_EXT; + Filter.call(this, options); + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'ExtensibleMatch', + matchRule: self.rule, + matchType: self.matchType, + matchValue: self.value, + dnAttributes: self.dnAttributes + }; + }); + this.__defineGetter__('matchingRule', function () { + return self.rule; + }); + this.__defineGetter__('matchValue', function () { + return self.value; + }); +} +util.inherits(ExtensibleFilter, Filter); +module.exports = ExtensibleFilter; + + +ExtensibleFilter.prototype.toString = function () { + var str = '('; + + if (this.matchType) + str += this.matchType; + + str += ':'; + + if (this.dnAttributes) + str += 'dn:'; + + if (this.rule) + str += this.rule + ':'; + + return (str + '=' + this.value + ')'); +}; + + +/** + * THIS IS A STUB! + * + * ldapjs does not support server side extensible matching. This class exists + * only for the client to send them. + * + * @param {Object} target the target object. + * @return {Boolean} false always. + */ +ExtensibleFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + if (this.dnAttribute) + throw new Error('ExtensibleMatch dnAttributes not supported'); + + if (!this.value) + return false; + + var self = this; + if (this.matchType && target.hasOwnProperty(this.matchType)) { + return Filter.multi_test(function (v) { + if (self.rule === '2.5.13.2') + return self.value.toLowerCase() === v.toLowerCase(); + + return self.value === v; + }, target[this.matchType]); + } + + return false; +}; + + +ExtensibleFilter.prototype.parse = function (ber) { + var end = ber.offset + ber.length; + while (ber.offset < end) { + var tag = ber.peek(); + switch (tag) { + case 0x81: + this.rule = ber.readString(tag); + break; + case 0x82: + this.matchType = ber.readString(tag); + break; + case 0x83: + this.value = ber.readString(tag); + break; + case 0x84: + this.dnAttributes = ber.readBoolean(tag); + break; + default: + throw new Error('Invalid ext_match filter type: 0x' + tag.toString(16)); + } + } + + return true; +}; + + +ExtensibleFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + if (this.rule) + ber.writeString(this.rule, 0x81); + if (this.matchType) + ber.writeString(this.matchType, 0x82); + + ber.writeString(this.value, 0x83); + if (this.dnAttributes) + ber.writeBoolean(this.dnAttributes, 0x84); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/filter.js new file mode 100644 index 0000000..4ebc39b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/filter.js @@ -0,0 +1,75 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); + +var asn1 = require('asn1'); + + +var Protocol = require('../protocol'); + + + +///--- Globals + +var BerWriter = asn1.BerWriter; + + + +///--- API + +function Filter(options) { + if (!options || typeof (options) !== 'object') + throw new TypeError('options (object) required'); + if (typeof (options.type) !== 'number') + throw new TypeError('options.type (number) required'); + + this._type = options.type; + + var self = this; + this.__defineGetter__('type', function () { + switch (self._type) { + case Protocol.FILTER_AND: return 'and'; + case Protocol.FILTER_OR: return 'or'; + case Protocol.FILTER_NOT: return 'not'; + case Protocol.FILTER_EQUALITY: return 'equal'; + case Protocol.FILTER_SUBSTRINGS: return 'substring'; + case Protocol.FILTER_GE: return 'ge'; + case Protocol.FILTER_LE: return 'le'; + case Protocol.FILTER_PRESENT: return 'present'; + case Protocol.FILTER_APPROX: return 'approx'; + case Protocol.FILTER_EXT: return 'ext'; + default: + throw new Error('0x' + self._type.toString(16) + + ' is an invalid search filter'); + } + }); +} +module.exports = Filter; + + +Filter.prototype.toBer = function (ber) { + if (!ber || !(ber instanceof BerWriter)) + throw new TypeError('ber (BerWriter) required'); + + ber.startSequence(this._type); + ber = this._toBer(ber); + ber.endSequence(); + return ber; +}; + + +// Test a rule against one or more values. +Filter.multi_test = function (rule, value) { + if (Array.isArray(value)) { + var response = false; + for (var i = 0; i < value.length; i++) { + if (rule(value[i])) { + response = true; + break; + } + } + return response; + } else { + return rule(value); + } +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/ge_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/ge_filter.js new file mode 100644 index 0000000..8ca7e52 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/ge_filter.js @@ -0,0 +1,81 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var escape = require('./escape').escape; + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function GreaterThanEqualsFilter(options) { + if (typeof (options) === 'object') { + if (!options.attribute || typeof (options.attribute) !== 'string') + throw new TypeError('options.attribute (string) required'); + if (!options.value || typeof (options.value) !== 'string') + throw new TypeError('options.value (string) required'); + this.attribute = escape(options.attribute); + this.value = escape(options.value); + } else { + options = {}; + } + + options.type = Protocol.FILTER_GE; + Filter.call(this, options); + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'GreaterThanEqualsMatch', + attribute: self.attribute || undefined, + value: self.value || undefined + }; + }); +} +util.inherits(GreaterThanEqualsFilter, Filter); +module.exports = GreaterThanEqualsFilter; + + +GreaterThanEqualsFilter.prototype.toString = function () { + return '(' + this.attribute + '>=' + this.value + ')'; +}; + + +GreaterThanEqualsFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + if (target.hasOwnProperty(this.attribute)) { + var value = this.value; + return Filter.multi_test( + function (v) { return value <= v; }, + target[this.attribute]); + } + + return false; +}; + + +GreaterThanEqualsFilter.prototype.parse = function (ber) { + assert.ok(ber); + + this.attribute = ber.readString().toLowerCase(); + this.value = ber.readString(); + + return true; +}; + + +GreaterThanEqualsFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.attribute); + ber.writeString(this.value); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/index.js new file mode 100644 index 0000000..476396f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/index.js @@ -0,0 +1,484 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); + +var asn1 = require('asn1'); + +var Protocol = require('../protocol'); + +var Filter = require('./filter'); +var AndFilter = require('./and_filter'); +var ApproximateFilter = require('./approx_filter'); +var EqualityFilter = require('./equality_filter'); +var ExtensibleFilter = require('./ext_filter'); +var GreaterThanEqualsFilter = require('./ge_filter'); +var LessThanEqualsFilter = require('./le_filter'); +var NotFilter = require('./not_filter'); +var OrFilter = require('./or_filter'); +var PresenceFilter = require('./presence_filter'); +var SubstringFilter = require('./substr_filter'); + + + +///--- Globals + +var BerReader = asn1.BerReader; + + + +///--- Internal Parsers + +// expression parsing +// returns the index of the closing parenthesis matching the open paren +// specified by openParenIndex +function matchParens(str, openParenIndex) { + var stack = []; + var esc = false; + for (var i = openParenIndex || 0; i < str.length; i++) { + var c = str[i]; + + if (c === '\\') { + if (!esc) + esc = true; + continue; + } else if (c === '(' && !esc) { + stack.push(1); + } else if (c === ')' && !esc) { + stack.pop(); + if (stack.length === 0) + return i; + } + + esc = false; + } + + return str.length - 1; +} + + +// recursive function that builds a filter tree from a string expression +// the filter tree is an intermediary step between the incoming expression and +// the outgoing Filter Class structure. +function _buildFilterTree(expr) { + var c; + var child; + var clean = false; + var endParen; + var esc = false; + var i = 0; + var tree = {}; + var split; + var substrNdx = 0; + var val = ''; + + if (expr.length === 0) + return tree; + + // Chop the parens (the call to matchParens below gets rid of the trailer) + if (expr.charAt(0) == '(') + expr = expr.substring(1, expr.length - 1); + + //store prefix operator + if (expr.charAt(0) === '&') { + tree.op = 'and'; + expr = expr.substring(1); + } else if (expr.charAt(0) === '|') { + tree.op = 'or'; + expr = expr.substring(1); + } else if (expr.charAt(0) === '!') { + tree.op = 'not'; + expr = expr.substring(1); + } else { + tree.op = 'expr'; + } + + if (tree.op != 'expr') { + tree.children = []; + + // logical operators are k-ary, so we go until our expression string runs + // out (at least for this recursion level) + while (expr.length !== 0) { + endParen = matchParens(expr); + if (endParen == expr.length - 1) { + tree.children[i] = _buildFilterTree(expr); + expr = ''; + } else { + child = expr.slice(0, endParen + 1); + expr = expr.substring(endParen + 1); + tree.children[i] = _buildFilterTree(child); + } + i++; + } + } else { + //else its some sort of non-logical expression, parse and return as such + var operatorStr = ''; + tree.name = ''; + tree.value = ''; + if (expr.indexOf('~=') !== -1) { + operatorStr = '~='; + tree.tag = 'approxMatch'; + } else if (expr.indexOf('>=') !== -1) { + operatorStr = '>='; + tree.tag = 'greaterOrEqual'; + } else if (expr.indexOf('<=') !== -1) { + operatorStr = '<='; + tree.tag = 'lessOrEqual'; + } else if (expr.indexOf(':=') !== -1) { + operatorStr = ':='; + tree.tag = 'extensibleMatch'; + } else if (expr.indexOf('=') !== -1) { + operatorStr = '='; + tree.tag = 'equalityMatch'; + } else { + tree.tag = 'present'; + } + + if (operatorStr === '') { + tree.name = expr; + } else { + // pull out lhs and rhs of equality operator + var splitAry = expr.split(operatorStr); + tree.name = splitAry.shift(); + tree.value = splitAry.join(operatorStr); + + // substrings fall into the equality bin in the + // switch above so we need more processing here + if (tree.tag === 'equalityMatch') { + if (tree.value === '*') { + tree.tag = 'present'; + } else { + + // Effectively a hand-rolled .shift() to support \* sequences + clean = true; + split = []; + substrNdx = 0; + split[substrNdx] = ''; + for (i = 0; i < tree.value.length; i++) { + c = tree.value[i]; + if (esc) { + split[substrNdx] += c; + esc = false; + } else if (c === '*') { + split[++substrNdx] = ''; + } else if (c === '\\') { + esc = true; + } else { + split[substrNdx] += c; + } + } + + if (split.length > 1) { + tree.tag = 'substrings'; + clean = true; + + // if the value string doesn't start with a * then theres no initial + // value else split will have an empty string in its first array + // index... + // we need to remove that empty string + if (tree.value.indexOf('*') !== 0) { + tree.initial = split.shift(); + } else { + split.shift(); + } + + // if the value string doesn't end with a * then theres no final + // value also same split stuff as the initial stuff above + if (tree.value.lastIndexOf('*') !== tree.value.length - 1) { + tree['final'] = split.pop(); + } else { + split.pop(); + } + tree.any = split; + } else { + tree.value = split[0]; // pick up the cleaned version + } + } + + } else if (tree.tag == 'extensibleMatch') { + split = tree.name.split(':'); + tree.extensible = { + matchType: split[0], + value: tree.value + }; + switch (split.length) { + case 1: + break; + case 2: + if (split[1].toLowerCase() === 'dn') { + tree.extensible.dnAttributes = true; + } else { + tree.extensible.rule = split[1]; + } + break; + case 3: + tree.extensible.dnAttributes = true; + tree.extensible.rule = split[2]; + break; + default: + throw new Error('Invalid extensible filter'); + } + } + } + + // Cleanup any escape sequences + if (!clean) { + + for (i = 0; i < tree.value.length; i++) { + c = tree.value[i]; + if (esc) { + val += c; + esc = false; + } else if (c === '\\') { + esc = true; + } else { + val += c; + } + } + tree.value = val; + } + } + + return tree; +} + + +function serializeTree(tree, filter) { + if (tree === undefined || tree.length === 0) + return; + + // if the current tree object is not an expression then its a logical + // operator (ie an internal node in the tree) + var current = null; + if (tree.op !== 'expr') { + switch (tree.op) { + case 'and': + current = new AndFilter(); + break; + case 'or': + current = new OrFilter(); + break; + case 'not': + current = new NotFilter(); + break; + default: + break; + } + + filter.addFilter(current || filter); + if (current || tree.children.length) { + tree.children.forEach(function (child) { + serializeTree(child, current); + }); + } + } else { + // else its a leaf node in the tree, and represents some type of + // non-logical expression + var tmp; + + // convert the tag name to a filter class type + switch (tree.tag) { + case 'approxMatch': + tmp = new ApproximateFilter({ + attribute: tree.name, + value: tree.value + }); + break; + case 'extensibleMatch': + tmp = new ExtensibleFilter(tree.extensible); + break; + case 'greaterOrEqual': + tmp = new GreaterThanEqualsFilter({ + attribute: tree.name, + value: tree.value + }); + break; + case 'lessOrEqual': + tmp = new LessThanEqualsFilter({ + attribute: tree.name, + value: tree.value + }); + break; + case 'equalityMatch': + tmp = new EqualityFilter({ + attribute: tree.name, + value: tree.value + }); + break; + case 'substrings': + tmp = new SubstringFilter({ + attribute: tree.name, + initial: tree.initial, + any: tree.any, + 'final': tree['final'] + }); + break; + case 'present': + tmp = new PresenceFilter({ + attribute: tree.name + }); + break; + default: + break; + } + if (tmp) + filter.addFilter(tmp); + } +} + + +function _parseString(str) { + assert.ok(str); + + // create a blank object to pass into treeToObjs + // since its recursive we have to prime it ourselves. + // this gets stripped off before the filter structure is returned + // at the bottom of this function. + var filterObj = new AndFilter({ + filters: [] + }); + + serializeTree(_buildFilterTree(str), filterObj); + return filterObj.filters[0]; +} + + +/* + * A filter looks like this coming in: + * Filter ::= CHOICE { + * and [0] SET OF Filter, + * or [1] SET OF Filter, + * not [2] Filter, + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeType, + * approxMatch [8] AttributeValueAssertion, + * extensibleMatch [9] MatchingRuleAssertion --v3 only + * } + * + * SubstringFilter ::= SEQUENCE { + * type AttributeType, + * SEQUENCE OF CHOICE { + * initial [0] IA5String, + * any [1] IA5String, + * final [2] IA5String + * } + * } + * + * The extensibleMatch was added in LDAPv3: + * + * MatchingRuleAssertion ::= SEQUENCE { + * matchingRule [1] MatchingRuleID OPTIONAL, + * type [2] AttributeDescription OPTIONAL, + * matchValue [3] AssertionValue, + * dnAttributes [4] BOOLEAN DEFAULT FALSE + * } + */ +function _parse(ber) { + assert.ok(ber); + + function parseSet(f) { + var end = ber.offset + ber.length; + while (ber.offset < end) + f.addFilter(_parse(ber)); + } + + var f; + + var type = ber.readSequence(); + switch (type) { + + case Protocol.FILTER_AND: + f = new AndFilter(); + parseSet(f); + break; + + case Protocol.FILTER_APPROX: + f = new ApproximateFilter(); + f.parse(ber); + break; + + case Protocol.FILTER_EQUALITY: + f = new EqualityFilter(); + f.parse(ber); + return f; + + case Protocol.FILTER_EXT: + f = new ExtensibleFilter(); + f.parse(ber); + return f; + + case Protocol.FILTER_GE: + f = new GreaterThanEqualsFilter(); + f.parse(ber); + return f; + + case Protocol.FILTER_LE: + f = new LessThanEqualsFilter(); + f.parse(ber); + return f; + + case Protocol.FILTER_NOT: + var _f = _parse(ber); + f = new NotFilter({ + filter: _f + }); + break; + + case Protocol.FILTER_OR: + f = new OrFilter(); + parseSet(f); + break; + + case Protocol.FILTER_PRESENT: + f = new PresenceFilter(); + f.parse(ber); + break; + + case Protocol.FILTER_SUBSTRINGS: + f = new SubstringFilter(); + f.parse(ber); + break; + + default: + throw new Error('Invalid search filter type: 0x' + type.toString(16)); + } + + + assert.ok(f); + return f; +} + + + +///--- API + +module.exports = { + + parse: function (ber) { + if (!ber || !(ber instanceof BerReader)) + throw new TypeError('ber (BerReader) required'); + + return _parse(ber); + }, + + parseString: function (filter) { + if (!filter || typeof (filter) !== 'string') + throw new TypeError('filter (string) required'); + + return _parseString(filter); + }, + + AndFilter: AndFilter, + ApproximateFilter: ApproximateFilter, + EqualityFilter: EqualityFilter, + ExtensibleFilter: ExtensibleFilter, + GreaterThanEqualsFilter: GreaterThanEqualsFilter, + LessThanEqualsFilter: LessThanEqualsFilter, + NotFilter: NotFilter, + OrFilter: OrFilter, + PresenceFilter: PresenceFilter, + SubstringFilter: SubstringFilter, + Filter: Filter +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/le_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/le_filter.js new file mode 100644 index 0000000..c2eab53 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/le_filter.js @@ -0,0 +1,81 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var escape = require('./escape').escape; + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function LessThanEqualsFilter(options) { + if (typeof (options) === 'object') { + if (!options.attribute || typeof (options.attribute) !== 'string') + throw new TypeError('options.attribute (string) required'); + if (!options.value || typeof (options.value) !== 'string') + throw new TypeError('options.value (string) required'); + this.attribute = escape(options.attribute); + this.value = escape(options.value); + } else { + options = {}; + } + + options.type = Protocol.FILTER_LE; + Filter.call(this, options); + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'LessThanEqualsMatch', + attribute: self.attribute || undefined, + value: self.value || undefined + }; + }); +} +util.inherits(LessThanEqualsFilter, Filter); +module.exports = LessThanEqualsFilter; + + +LessThanEqualsFilter.prototype.toString = function () { + return '(' + this.attribute + '<=' + this.value + ')'; +}; + + +LessThanEqualsFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + if (target.hasOwnProperty(this.attribute)) { + var value = this.value; + return Filter.multi_test( + function (v) { return value >= v; }, + target[this.attribute]); + } + + return false; +}; + + +LessThanEqualsFilter.prototype.parse = function (ber) { + assert.ok(ber); + + this.attribute = ber.readString().toLowerCase(); + this.value = ber.readString(); + + return true; +}; + + +LessThanEqualsFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.attribute); + ber.writeString(this.value); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/not_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/not_filter.js new file mode 100644 index 0000000..f4b9038 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/not_filter.js @@ -0,0 +1,59 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function NotFilter(options) { + if (typeof (options) === 'object') { + if (!options.filter || !(options.filter instanceof Filter)) + throw new TypeError('options.filter (Filter) required'); + + } else { + options = {}; + } + + this.filter = options.filter || {}; + options.type = Protocol.FILTER_NOT; + Filter.call(this, options); + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'Not', + filter: self.filter + }; + }); +} +util.inherits(NotFilter, Filter); +module.exports = NotFilter; + + +NotFilter.prototype.addFilter = function (f) { + if (!(f instanceof Filter)) + throw new TypeError('filter (Filter) required'); + this.filter = f; +}; + +NotFilter.prototype.toString = function () { + return '(!' + this.filter.toString() + ')'; +}; + + +NotFilter.prototype.matches = function (target) { + return !this.filter.matches(target); +}; + + +NotFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + return this.filter.toBer(ber); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/or_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/or_filter.js new file mode 100644 index 0000000..a9daa04 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/or_filter.js @@ -0,0 +1,81 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function OrFilter(options) { + if (typeof (options) === 'object') { + if (!options.filters || !Array.isArray(options.filters)) + throw new TypeError('options.filters ([Filter]) required'); + this.filters = options.filters.slice(); + } else { + options = {}; + } + + options.type = Protocol.FILTER_OR; + Filter.call(this, options); + + if (!this.filters) + this.filters = []; + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'Or', + filters: self.filters || [] + }; + }); +} +util.inherits(OrFilter, Filter); +module.exports = OrFilter; + + +OrFilter.prototype.toString = function () { + var str = '(|'; + this.filters.forEach(function (f) { + str += f.toString(); + }); + str += ')'; + + return str; +}; + + +OrFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + for (var i = 0; i < this.filters.length; i++) { + if (this.filters[i].matches(target)) + return true; + } + + return false; +}; + + +OrFilter.prototype.addFilter = function (filter) { + if (!filter || typeof (filter) !== 'object') + throw new TypeError('filter (object) required'); + + this.filters.push(filter); +}; + + +OrFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + this.filters.forEach(function (f) { + ber = f.toBer(ber); + }); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/presence_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/presence_filter.js new file mode 100644 index 0000000..a5ac76c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/presence_filter.js @@ -0,0 +1,70 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var escape = require('./escape').escape; + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + +///--- API + +function PresenceFilter(options) { + if (typeof (options) === 'object') { + if (!options.attribute || typeof (options.attribute) !== 'string') + throw new TypeError('options.attribute (string) required'); + this.attribute = escape(options.attribute); + } else { + options = {}; + } + options.type = Protocol.FILTER_PRESENT; + Filter.call(this, options); + + var self = this; + this.__defineGetter__('json', function () { + return { + type: 'PresenceMatch', + attribute: self.attribute || undefined + }; + }); +} +util.inherits(PresenceFilter, Filter); +module.exports = PresenceFilter; + + +PresenceFilter.prototype.toString = function () { + return '(' + this.attribute + '=*)'; +}; + + +PresenceFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + return target.hasOwnProperty(this.attribute); +}; + + +PresenceFilter.prototype.parse = function (ber) { + assert.ok(ber); + + this.attribute = + ber.buffer.slice(0, ber.length).toString('utf8').toLowerCase(); + + ber._offset += ber.length; + + return true; +}; + + +PresenceFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + for (var i = 0; i < this.attribute.length; i++) + ber.writeByte(this.attribute.charCodeAt(i)); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/substr_filter.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/substr_filter.js new file mode 100644 index 0000000..417a2cc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/filters/substr_filter.js @@ -0,0 +1,161 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var escape = require('./escape').escape; + +var Filter = require('./filter'); + +var Protocol = require('../protocol'); + + + +///--- API + +function SubstringFilter(options) { + var _any; + if (typeof (options) === 'object') { + if (!options.attribute || typeof (options.attribute) !== 'string') + throw new TypeError('options.attribute (string) required'); + this.attribute = escape(options.attribute); + this.initial = options.initial ? escape(options.initial) : null; + _any = options.any ? options.any.slice(0) : []; + this['final'] = escape(options['final']) || null; + } else { + options = {}; + } + + if (!_any) + _any = []; + + this.any = []; + var self = this; + _any.forEach(function (a) { + self.any.push(escape(a)); + }); + + options.type = Protocol.FILTER_SUBSTRINGS; + Filter.call(this, options); + + this.__defineGetter__('json', function () { + return { + type: 'SubstringMatch', + initial: self.initial || undefined, + any: self.any || undefined, + 'final': self['final'] || undefined + }; + }); +} +util.inherits(SubstringFilter, Filter); +module.exports = SubstringFilter; + + +SubstringFilter.prototype.toString = function () { + var str = '(' + this.attribute + '='; + + if (this.initial) + str += this.initial; + + str += '*'; + + this.any.forEach(function (s) { + str += s + '*'; + }); + + if (this['final']) + str += this['final']; + + str += ')'; + + return str; +}; + + +SubstringFilter.prototype.matches = function (target) { + if (typeof (target) !== 'object') + throw new TypeError('target (object) required'); + + if (target.hasOwnProperty(this.attribute)) { + var re = ''; + if (this.initial) + re += '^' + this.initial + '.*'; + + this.any.forEach(function (s) { + re += s + '.*'; + }); + + if (this['final']) + re += this['final'] + '$'; + + var matcher = new RegExp(re); + var self = this; + return Filter.multi_test( + function (v) { + if (self.attribute === 'objectclass') + v = v.toLowerCase(); + return matcher.test(v); + }, + target[this.attribute]); + } + + return false; +}; + + +SubstringFilter.prototype.parse = function (ber) { + assert.ok(ber); + + this.attribute = ber.readString().toLowerCase(); + ber.readSequence(); + var end = ber.offset + ber.length; + + while (ber.offset < end) { + var tag = ber.peek(); + switch (tag) { + case 0x80: // Initial + this.initial = ber.readString(tag); + if (this.attribute === 'objectclass') + this.initial = this.initial.toLowerCase(); + break; + case 0x81: // Any + var anyVal = ber.readString(tag); + if (this.attribute === 'objectclass') + anyVal = anyVal.toLowerCase(); + this.any.push(anyVal); + break; + case 0x82: // Final + this['final'] = ber.readString(tag); + if (this.attribute === 'objectclass') + this['final'] = this['final'].toLowerCase(); + break; + default: + throw new Error('Invalid substrings filter type: 0x' + tag.toString(16)); + } + } + + return true; +}; + + +SubstringFilter.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.attribute); + ber.startSequence(); + + if (this.initial) + ber.writeString(this.initial, 0x80); + + if (this.any && this.any.length) + this.any.forEach(function (s) { + ber.writeString(s, 0x81); + }); + + if (this['final']) + ber.writeString(this['final'], 0x82); + + ber.endSequence(); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/index.js new file mode 100644 index 0000000..06314a7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/index.js @@ -0,0 +1,118 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var Logger = require('bunyan'); + +var client = require('./client'); +var Attribute = require('./attribute'); +var Change = require('./change'); +var Protocol = require('./protocol'); +var Server = require('./server'); + +var assert = require('assert'); +var controls = require('./controls'); +var persistentSearch = require('./persistent_search'); +var dn = require('./dn'); +var errors = require('./errors'); +var filters = require('./filters'); +var messages = require('./messages'); +var url = require('./url'); + + + +/// Hack a few things we need (i.e., "monkey patch" the prototype) + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (str) { + var re = new RegExp('^' + str); + return re.test(this); + }; +} + + +if (!String.prototype.endsWith) { + String.prototype.endsWith = function (str) { + var re = new RegExp(str + '$'); + return re.test(this); + }; +} + + + +///--- API + +module.exports = { + + createClient: client.createClient, + + Server: Server, + createServer: function (options) { + if (options === undefined) + options = {}; + + if (typeof (options) !== 'object') + throw new TypeError('options (object) required'); + + if (!options.log) { + options.log = new Logger({ + name: 'ldapjs', + component: 'client', + stream: process.stderr + }); + } + + return new Server(options); + }, + + Attribute: Attribute, + Change: Change, + + DN: dn.DN, + PersistentSearchCache: persistentSearch.PersistentSearchCache, + RDN: dn.RDN, + + parseDN: dn.parse, + dn: dn, + + persistentSearch: persistentSearch, + + filters: filters, + parseFilter: filters.parseString, + + parseURL: url.parse, + + url: url +}; + + + +///--- Export all the childrenz + +var k; + +for (k in Protocol) { + if (Protocol.hasOwnProperty(k)) + module.exports[k] = Protocol[k]; +} + +for (k in messages) { + if (messages.hasOwnProperty(k)) + module.exports[k] = messages[k]; +} + +for (k in controls) { + if (controls.hasOwnProperty(k)) + module.exports[k] = controls[k]; +} + +for (k in filters) { + if (filters.hasOwnProperty(k)) { + if (k !== 'parse' && k !== 'parseString') + module.exports[k] = filters[k]; + } +} + +for (k in errors) { + if (errors.hasOwnProperty(k)) { + module.exports[k] = errors[k]; + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/abandon_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/abandon_request.js new file mode 100644 index 0000000..22637bd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/abandon_request.js @@ -0,0 +1,101 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Attribute = require('../attribute'); +var Protocol = require('../protocol'); + +///--- Globals + +var Ber = asn1.Ber; + + + +///--- API + +function AbandonRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.abandonID && typeof (options.abandonID) !== 'number') + throw new TypeError('abandonID must be a number'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_ABANDON; + LDAPMessage.call(this, options); + + this.abandonID = options.abandonID || 0; + + this.__defineGetter__('type', function () { return 'AbandonRequest'; }); +} +util.inherits(AbandonRequest, LDAPMessage); +module.exports = AbandonRequest; + + +AbandonRequest.prototype._parse = function (ber, length) { + assert.ok(ber); + assert.ok(length); + + // What a PITA - have to replicate ASN.1 integer logic to work around the + // way abandon is encoded and the way ldapjs framework handles "normal" + // messages + + var buf = ber.buffer; + var offset = 0; + var value = 0; + + var fb = buf[offset++]; + value = fb & 0x7F; + for (var i = 1; i < length; i++) { + value <<= 8; + value |= (buf[offset++] & 0xff); + } + if ((fb & 0x80) == 0x80) + value = -value; + + ber._offset += length; + + this.abandonID = value; + + return true; +}; + + +AbandonRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + var i = this.abandonID; + var sz = 4; + + while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) && + (sz > 1)) { + sz--; + i <<= 8; + } + assert.ok(sz <= 4); + + while (sz-- > 0) { + ber.writeByte((i & 0xff000000) >> 24); + i <<= 8; + } + + return ber; +}; + + +AbandonRequest.prototype._json = function (j) { + assert.ok(j); + + j.abandonID = this.abandonID; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/abandon_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/abandon_response.js new file mode 100644 index 0000000..874d5c7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/abandon_response.js @@ -0,0 +1,32 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var LDAPMessage = require('./result'); +var Protocol = require('../protocol'); + + +///--- API +// Stub this out + +function AbandonResponse(options) { + if (!options) + options = {}; + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + + options.protocolOp = 0; + LDAPMessage.call(this, options); + this.__defineGetter__('type', function () { return 'AbandonResponse'; }); +} +util.inherits(AbandonResponse, LDAPMessage); +module.exports = AbandonResponse; + + +AbandonResponse.prototype.end = function (status) {}; + + +AbandonResponse.prototype._json = function (j) { + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/add_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/add_request.js new file mode 100644 index 0000000..afed4b4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/add_request.js @@ -0,0 +1,190 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Attribute = require('../attribute'); +var Protocol = require('../protocol'); + +///--- Globals + +var Ber = asn1.Ber; + + + +///--- API + +function AddRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.entry && !(options.entry instanceof dn.DN)) + throw new TypeError('options.entry must be a DN'); + if (options.attributes) { + if (!Array.isArray(options.attributes)) + throw new TypeError('options.attributes must be [Attribute]'); + options.attributes.forEach(function (a) { + if (!Attribute.isAttribute(a)) + throw new TypeError('options.attributes must be [Attribute]'); + }); + } + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_ADD; + LDAPMessage.call(this, options); + + this.entry = options.entry || null; + this.attributes = options.attributes ? options.attributes.slice(0) : []; + + var self = this; + this.__defineGetter__('type', function () { return 'AddRequest'; }); + this.__defineGetter__('_dn', function () { return self.entry; }); +} +util.inherits(AddRequest, LDAPMessage); +module.exports = AddRequest; + + +AddRequest.prototype._parse = function (ber) { + assert.ok(ber); + + this.entry = dn.parse(ber.readString()); + + ber.readSequence(); + + var end = ber.offset + ber.length; + while (ber.offset < end) { + var a = new Attribute(); + a.parse(ber); + a.type = a.type.toLowerCase(); + if (a.type === 'objectclass') { + for (var i = 0; i < a.vals.length; i++) + a.vals[i] = a.vals[i].toLowerCase(); + } + this.attributes.push(a); + } + + this.attributes.sort(Attribute.compare); + return true; +}; + + +AddRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.entry.toString()); + ber.startSequence(); + this.attributes.forEach(function (a) { + a.toBer(ber); + }); + ber.endSequence(); + + return ber; +}; + + +AddRequest.prototype._json = function (j) { + assert.ok(j); + + j.entry = this.entry.toString(); + j.attributes = []; + + this.attributes.forEach(function (a) { + j.attributes.push(a.json); + }); + + return j; +}; + + +AddRequest.prototype.indexOf = function (attr) { + if (!attr || typeof (attr) !== 'string') + throw new TypeError('attr (string) required'); + + for (var i = 0; i < this.attributes.length; i++) { + if (this.attributes[i].type === attr) + return i; + } + + return -1; +}; + + +AddRequest.prototype.attributeNames = function () { + var attrs = []; + + for (var i = 0; i < this.attributes.length; i++) + attrs.push(this.attributes[i].type.toLowerCase()); + + return attrs; +}; + + +AddRequest.prototype.getAttribute = function (name) { + if (!name || typeof (name) !== 'string') + throw new TypeError('attribute name (string) required'); + + name = name.toLowerCase(); + + for (var i = 0; i < this.attributes.length; i++) { + if (this.attributes[i].type === name) + return this.attribute[i]; + } + + return null; +}; + + +AddRequest.prototype.addAttribute = function (attr) { + if (!(attr instanceof Attribute)) + throw new TypeError('attribute (Attribute) required'); + + return this.attributes.push(attr); +}; + + +/** + * Returns a "pure" JS representation of this object. + * + * An example object would look like: + * + * { + * "dn": "cn=unit, dc=test", + * "attributes": { + * "cn": ["unit", "foo"], + * "objectclass": ["top", "person"] + * } + * } + * + * @return {Object} that looks like the above. + */ +AddRequest.prototype.toObject = function () { + var self = this; + + var obj = { + dn: self.entry ? self.entry.toString() : '', + attributes: {} + }; + + if (!this.attributes || !this.attributes.length) + return obj; + + this.attributes.forEach(function (a) { + if (!obj.attributes[a.type]) + obj.attributes[a.type] = []; + + a.vals.forEach(function (v) { + if (obj.attributes[a.type].indexOf(v) === -1) + obj.attributes[a.type].push(v); + }); + }); + + return obj; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/add_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/add_response.js new file mode 100644 index 0000000..b5ac773 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/add_response.js @@ -0,0 +1,24 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var util = require('util'); + +var LDAPResult = require('./result'); +var Protocol = require('../protocol'); + + + +///--- API + +function AddResponse(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_ADD; + LDAPResult.call(this, options); +} +util.inherits(AddResponse, LDAPResult); +module.exports = AddResponse; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/bind_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/bind_request.js new file mode 100644 index 0000000..ab31434 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/bind_request.js @@ -0,0 +1,88 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + + +var dn = require('../dn'); +var Protocol = require('../protocol'); + +///--- Globals + +var Ber = asn1.Ber; + +var LDAP_BIND_SIMPLE = 'simple'; +var LDAP_BIND_SASL = 'sasl'; + + + +///--- API + +function BindRequest(options) { + if (options && typeof (options) !== 'object') + throw new TypeError('options must be an object'); + + options = options || {}; + + options.protocolOp = Protocol.LDAP_REQ_BIND; + LDAPMessage.call(this, options); + + this.version = options.version || 0x03; + this.name = options.name || null; + this.authentication = options.authentication || LDAP_BIND_SIMPLE; + this.credentials = options.credentials || ''; + + var self = this; + this.__defineGetter__('type', function () { return 'BindRequest'; }); + this.__defineGetter__('_dn', function () { return self.name; }); +} +util.inherits(BindRequest, LDAPMessage); +module.exports = BindRequest; + + +BindRequest.prototype._parse = function (ber) { + assert.ok(ber); + + this.version = ber.readInt(); + this.name = dn.parse(ber.readString()); + + var t = ber.peek(); + + // TODO add support for SASL et al + if (t !== Ber.Context) + throw new Error('authentication 0x' + t.toString(16) + ' not supported'); + + this.authentication = LDAP_BIND_SIMPLE; + this.credentials = ber.readString(Ber.Context); + + return true; +}; + + +BindRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeInt(this.version); + ber.writeString((this.name || '').toString()); + // TODO add support for SASL et al + ber.writeString((this.credentials || ''), Ber.Context); + + return ber; +}; + + +BindRequest.prototype._json = function (j) { + assert.ok(j); + + j.version = this.version; + j.name = this.name; + j.authenticationType = this.authentication; + j.credentials = this.credentials; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/bind_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/bind_response.js new file mode 100644 index 0000000..edb0a71 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/bind_response.js @@ -0,0 +1,24 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var util = require('util'); + +var LDAPResult = require('./result'); +var Protocol = require('../protocol'); + + + +///--- API + +function BindResponse(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_BIND; + LDAPResult.call(this, options); +} +util.inherits(BindResponse, LDAPResult); +module.exports = BindResponse; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/compare_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/compare_request.js new file mode 100644 index 0000000..8c81253 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/compare_request.js @@ -0,0 +1,81 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Protocol = require('../protocol'); + + + +///--- API + +function CompareRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.entry && !(options.entry instanceof dn.DN)) + throw new TypeError('options.entry must be a DN'); + if (options.attribute && typeof (options.attribute) !== 'string') + throw new TypeError('options.attribute must be a string'); + if (options.value && typeof (options.value) !== 'string') + throw new TypeError('options.value must be a string'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_COMPARE; + LDAPMessage.call(this, options); + + this.entry = options.entry || null; + this.attribute = options.attribute || ''; + this.value = options.value || ''; + + var self = this; + this.__defineGetter__('type', function () { return 'CompareRequest'; }); + this.__defineGetter__('_dn', function () { + return self.entry ? self.entry.toString() : ''; + }); +} +util.inherits(CompareRequest, LDAPMessage); +module.exports = CompareRequest; + + +CompareRequest.prototype._parse = function (ber) { + assert.ok(ber); + + this.entry = dn.parse(ber.readString()); + + ber.readSequence(); + this.attribute = ber.readString().toLowerCase(); + this.value = ber.readString(); + + return true; +}; + + +CompareRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.entry.toString()); + ber.startSequence(); + ber.writeString(this.attribute); + ber.writeString(this.value); + ber.endSequence(); + + return ber; +}; + + +CompareRequest.prototype._json = function (j) { + assert.ok(j); + + j.entry = this.entry.toString(); + j.attribute = this.attribute; + j.value = this.value; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/compare_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/compare_response.js new file mode 100644 index 0000000..33289d0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/compare_response.js @@ -0,0 +1,37 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var util = require('util'); + +var LDAPResult = require('./result'); +var Protocol = require('../protocol'); + + + +///--- API + +function CompareResponse(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_COMPARE; + LDAPResult.call(this, options); +} +util.inherits(CompareResponse, LDAPResult); +module.exports = CompareResponse; + + +CompareResponse.prototype.end = function (matches) { + var status = 0x06; + if (typeof (matches) === 'boolean') { + if (!matches) + status = 0x05; // Compare false + } else { + status = matches; + } + + return LDAPResult.prototype.end.call(this, status); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/del_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/del_request.js new file mode 100644 index 0000000..11cf48c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/del_request.js @@ -0,0 +1,73 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Attribute = require('../attribute'); +var Protocol = require('../protocol'); + +///--- Globals + +var Ber = asn1.Ber; + + + +///--- API + +function DeleteRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.entry && !(options.entry instanceof dn.DN)) + throw new TypeError('options.entry must be a DN'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_DELETE; + LDAPMessage.call(this, options); + + this.entry = options.entry || null; + + var self = this; + this.__defineGetter__('type', function () { return 'DeleteRequest'; }); + this.__defineGetter__('_dn', function () { return self.entry; }); +} +util.inherits(DeleteRequest, LDAPMessage); +module.exports = DeleteRequest; + + +DeleteRequest.prototype._parse = function (ber, length) { + assert.ok(ber); + + this.entry = dn.parse(ber.buffer.slice(0, length).toString('utf8')); + ber._offset += ber.length; + + return true; +}; + + +DeleteRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + var buf = new Buffer(this.entry.toString()); + for (var i = 0; i < buf.length; i++) + ber.writeByte(buf[i]); + + return ber; +}; + + +DeleteRequest.prototype._json = function (j) { + assert.ok(j); + + j.entry = this.entry; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/del_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/del_response.js new file mode 100644 index 0000000..de55aa1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/del_response.js @@ -0,0 +1,23 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var util = require('util'); + +var LDAPResult = require('./result'); +var Protocol = require('../protocol'); + + +///--- API + +function DeleteResponse(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_DELETE; + LDAPResult.call(this, options); +} +util.inherits(DeleteResponse, LDAPResult); +module.exports = DeleteResponse; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/ext_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/ext_request.js new file mode 100644 index 0000000..0329cdc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/ext_request.js @@ -0,0 +1,96 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Protocol = require('../protocol'); + + + +///--- Globals + +var Ber = asn1.Ber; + + + +///--- API + +function ExtendedRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.requestName && typeof (options.requestName) !== 'string') + throw new TypeError('options.requestName must be a string'); + if (options.requestValue && typeof (options.requestValue) !== 'string') + throw new TypeError('options.requestValue must be a string'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_EXTENSION; + LDAPMessage.call(this, options); + + this.requestName = options.requestName || ''; + this.requestValue = options.requestValue || undefined; + + this.__defineGetter__('type', function () { return 'ExtendedRequest'; }); + this.__defineGetter__('_dn', function () { return this.requestName; }); + this.__defineGetter__('name', function () { + return this.requestName; + }); + this.__defineGetter__('value', function () { + return this.requestValue; + }); + this.__defineSetter__('name', function (name) { + if (typeof (name) !== 'string') + throw new TypeError('name must be a string'); + + this.requestName = name; + }); + this.__defineSetter__('value', function (val) { + if (typeof (val) !== 'string') + throw new TypeError('value must be a string'); + + this.requestValue = val; + }); +} +util.inherits(ExtendedRequest, LDAPMessage); +module.exports = ExtendedRequest; + + +ExtendedRequest.prototype._parse = function (ber) { + assert.ok(ber); + + this.requestName = ber.readString(0x80); + if (ber.peek() === 0x81) + this.requestValue = ber.readString(0x81); + + return true; +}; + + +ExtendedRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.requestName, 0x80); + if (this.requestValue) + ber.writeString(this.requestValue, 0x81); + + return ber; +}; + + +ExtendedRequest.prototype._json = function (j) { + assert.ok(j); + + j.requestName = this.requestName; + j.requestValue = this.requestValue; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/ext_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/ext_response.js new file mode 100644 index 0000000..91360c7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/ext_response.js @@ -0,0 +1,92 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var LDAPResult = require('./result'); +var Protocol = require('../protocol'); + + +///--- API + +function ExtendedResponse(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.responseName && typeof (options.responseName) !== 'string') + throw new TypeError('options.responseName must be a string'); + if (options.responseValue && typeof (options.responseValue) !== 'string') + throw new TypeError('options.responseValue must be a string'); + } else { + options = {}; + } + + this.responseName = options.responseName || undefined; + this.responseValue = options.responseValue || undefined; + + options.protocolOp = Protocol.LDAP_REP_EXTENSION; + LDAPResult.call(this, options); + + this.__defineGetter__('name', function () { + return this.responseName; + }); + this.__defineGetter__('value', function () { + return this.responseValue; + }); + this.__defineSetter__('name', function (name) { + if (typeof (name) !== 'string') + throw new TypeError('name must be a string'); + + this.responseName = name; + }); + this.__defineSetter__('value', function (val) { + if (typeof (val) !== 'string') + throw new TypeError('value must be a string'); + + this.responseValue = val; + }); +} +util.inherits(ExtendedResponse, LDAPResult); +module.exports = ExtendedResponse; + + +ExtendedResponse.prototype._parse = function (ber) { + assert.ok(ber); + + if (!LDAPResult.prototype._parse.call(this, ber)) + return false; + + if (ber.peek() === 0x8a) + this.responseName = ber.readString(0x8a); + if (ber.peek() === 0x8b) + this.responseValue = ber.readString(0x8b); + + return true; +}; + + +ExtendedResponse.prototype._toBer = function (ber) { + assert.ok(ber); + + if (!LDAPResult.prototype._toBer.call(this, ber)) + return false; + + if (this.responseName) + ber.writeString(this.responseName, 0x8a); + if (this.responseValue) + ber.writeString(this.responseValue, 0x8b); + + return ber; +}; + + +ExtendedResponse.prototype._json = function (j) { + assert.ok(j); + + j = LDAPResult.prototype._json.call(this, j); + + j.responseName = this.responseName; + j.responseValue = this.responseValue; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/index.js new file mode 100644 index 0000000..899ee6f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/index.js @@ -0,0 +1,63 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); +var Parser = require('./parser'); + +var AbandonRequest = require('./abandon_request'); +var AbandonResponse = require('./abandon_response'); +var AddRequest = require('./add_request'); +var AddResponse = require('./add_response'); +var BindRequest = require('./bind_request'); +var BindResponse = require('./bind_response'); +var CompareRequest = require('./compare_request'); +var CompareResponse = require('./compare_response'); +var DeleteRequest = require('./del_request'); +var DeleteResponse = require('./del_response'); +var ExtendedRequest = require('./ext_request'); +var ExtendedResponse = require('./ext_response'); +var ModifyRequest = require('./modify_request'); +var ModifyResponse = require('./modify_response'); +var ModifyDNRequest = require('./moddn_request'); +var ModifyDNResponse = require('./moddn_response'); +var SearchRequest = require('./search_request'); +var SearchEntry = require('./search_entry'); +var SearchReference = require('./search_reference'); +var SearchResponse = require('./search_response'); +var UnbindRequest = require('./unbind_request'); +var UnbindResponse = require('./unbind_response'); + + + +///--- API + +module.exports = { + + LDAPMessage: LDAPMessage, + LDAPResult: LDAPResult, + Parser: Parser, + + AbandonRequest: AbandonRequest, + AbandonResponse: AbandonResponse, + AddRequest: AddRequest, + AddResponse: AddResponse, + BindRequest: BindRequest, + BindResponse: BindResponse, + CompareRequest: CompareRequest, + CompareResponse: CompareResponse, + DeleteRequest: DeleteRequest, + DeleteResponse: DeleteResponse, + ExtendedRequest: ExtendedRequest, + ExtendedResponse: ExtendedResponse, + ModifyRequest: ModifyRequest, + ModifyResponse: ModifyResponse, + ModifyDNRequest: ModifyDNRequest, + ModifyDNResponse: ModifyDNResponse, + SearchRequest: SearchRequest, + SearchEntry: SearchEntry, + SearchReference: SearchReference, + SearchResponse: SearchResponse, + UnbindRequest: UnbindRequest, + UnbindResponse: UnbindResponse + +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/message.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/message.js new file mode 100644 index 0000000..516f650 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/message.js @@ -0,0 +1,108 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var Control = require('../controls').Control; +var Protocol = require('../protocol'); + + + +///--- Globals + +var Ber = asn1.Ber; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var getControl = require('../controls').getControl; + + + +///--- API + + +/** + * LDAPMessage structure. + * + * @param {Object} options stuff. + */ +function LDAPMessage(options) { + if (!options || typeof (options) !== 'object') + throw new TypeError('options (object) required'); + + this.messageID = options.messageID || 0; + this.protocolOp = options.protocolOp || undefined; + this.controls = options.controls ? options.controls.slice(0) : []; + + this.log = options.log; + + var self = this; + this.__defineGetter__('id', function () { return self.messageID; }); + this.__defineGetter__('dn', function () { return self._dn || ''; }); + this.__defineGetter__('type', function () { return 'LDAPMessage'; }); + this.__defineGetter__('json', function () { + var j = { + messageID: self.messageID, + protocolOp: self.type + }; + j = self._json(j); + j.controls = self.controls; + return j; + }); +} +module.exports = LDAPMessage; + + +LDAPMessage.prototype.toString = function () { + return JSON.stringify(this.json); +}; + + +LDAPMessage.prototype.parse = function (ber) { + assert.ok(ber); + + if (this.log.trace()) + this.log.trace('parse: data=%s', util.inspect(ber.buffer)); + + // Delegate off to the specific type to parse + this._parse(ber, ber.remain); + + // Look for controls + if (ber.peek() === 0xa0) { + ber.readSequence(); + var end = ber.offset + ber.length; + while (ber.offset < end) { + var c = getControl(ber); + if (c) + this.controls.push(c); + } + } + + if (this.log.trace()) + this.log.trace('Parsing done: %j', this.json); + return true; +}; + + +LDAPMessage.prototype.toBer = function () { + var writer = new BerWriter(); + writer.startSequence(); + writer.writeInt(this.messageID); + + writer.startSequence(this.protocolOp); + if (this._toBer) + writer = this._toBer(writer); + writer.endSequence(); + + if (this.controls && this.controls.length) { + writer.startSequence(0xa0); + this.controls.forEach(function (c) { + c.toBer(writer); + }); + writer.endSequence(); + } + + writer.endSequence(); + return writer.buffer; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/moddn_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/moddn_request.js new file mode 100644 index 0000000..76052b5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/moddn_request.js @@ -0,0 +1,92 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Protocol = require('../protocol'); + + + +///--- Globals + +var Ber = asn1.Ber; + + +///--- API + +function ModifyDNRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.entry && !(options.entry instanceof dn.DN)) + throw new TypeError('options.entry must be a DN'); + if (options.newRdn && !(options.newRdn instanceof dn.DN)) + throw new TypeError('options.newRdn must be a DN'); + if (options.deleteOldRdn !== undefined && + typeof (options.deleteOldRdn) !== 'boolean') + throw new TypeError('options.deleteOldRdn must be a boolean'); + if (options.newSuperior && !(options.newSuperior instanceof dn.DN)) + throw new TypeError('options.newSuperior must be a DN'); + + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_MODRDN; + LDAPMessage.call(this, options); + + this.entry = options.entry || null; + this.newRdn = options.newRdn || null; + this.deleteOldRdn = options.deleteOldRdn || true; + this.newSuperior = options.newSuperior || null; + + var self = this; + this.__defineGetter__('type', function () { return 'ModifyDNRequest'; }); + this.__defineGetter__('_dn', function () { return self.entry; }); +} +util.inherits(ModifyDNRequest, LDAPMessage); +module.exports = ModifyDNRequest; + + +ModifyDNRequest.prototype._parse = function (ber) { + assert.ok(ber); + + this.entry = dn.parse(ber.readString()); + this.newRdn = dn.parse(ber.readString()); + this.deleteOldRdn = ber.readBoolean(); + if (ber.peek() === 0x80) + this.newSuperior = dn.parse(ber.readString(0x80)); + + return true; +}; + + +ModifyDNRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.entry.toString()); + ber.writeString(this.newRdn.toString()); + ber.writeBoolean(this.deleteOldRdn); + if (this.newSuperior) + ber.writeString(this.newSuperior.toString()); + + return ber; +}; + + +ModifyDNRequest.prototype._json = function (j) { + assert.ok(j); + + j.entry = this.entry.toString(); + j.newRdn = this.newRdn.toString(); + j.deleteOldRdn = this.deleteOldRdn; + j.newSuperior = this.newSuperior ? this.newSuperior.toString() : ''; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/moddn_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/moddn_response.js new file mode 100644 index 0000000..288cfe8 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/moddn_response.js @@ -0,0 +1,23 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var util = require('util'); + +var LDAPResult = require('./result'); +var Protocol = require('../protocol'); + + +///--- API + +function ModifyDNResponse(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_MODRDN; + LDAPResult.call(this, options); +} +util.inherits(ModifyDNResponse, LDAPResult); +module.exports = ModifyDNResponse; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/modify_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/modify_request.js new file mode 100644 index 0000000..bdd0043 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/modify_request.js @@ -0,0 +1,94 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Attribute = require('../attribute'); +var Change = require('../change'); +var Protocol = require('../protocol'); + + + +///--- API + +function ModifyRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.object && !(options.object instanceof dn.DN)) + throw new TypeError('options.object must be a DN'); + if (options.attributes) { + if (!Array.isArray(options.attributes)) + throw new TypeError('options.attributes must be [Attribute]'); + options.attributes.forEach(function (a) { + if (!(a instanceof Attribute)) + throw new TypeError('options.attributes must be [Attribute]'); + }); + } + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_MODIFY; + LDAPMessage.call(this, options); + + this.object = options.object || null; + this.changes = options.changes ? options.changes.slice(0) : []; + + var self = this; + this.__defineGetter__('type', function () { return 'ModifyRequest'; }); + this.__defineGetter__('_dn', function () { return self.object; }); +} +util.inherits(ModifyRequest, LDAPMessage); +module.exports = ModifyRequest; + + +ModifyRequest.prototype._parse = function (ber) { + assert.ok(ber); + + this.object = dn.parse(ber.readString()); + + ber.readSequence(); + var end = ber.offset + ber.length; + while (ber.offset < end) { + var c = new Change(); + c.parse(ber); + c.modification.type = c.modification.type.toLowerCase(); + this.changes.push(c); + } + + this.changes.sort(Change.compare); + return true; +}; + + +ModifyRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.object.toString()); + ber.startSequence(); + this.changes.forEach(function (c) { + c.toBer(ber); + }); + ber.endSequence(); + + return ber; +}; + + +ModifyRequest.prototype._json = function (j) { + assert.ok(j); + + j.object = this.object; + j.changes = []; + + this.changes.forEach(function (c) { + j.changes.push(c.json); + }); + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/modify_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/modify_response.js new file mode 100644 index 0000000..deecd4c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/modify_response.js @@ -0,0 +1,23 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var util = require('util'); + +var LDAPResult = require('./result'); +var Protocol = require('../protocol'); + + +///--- API + +function ModifyResponse(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_MODIFY; + LDAPResult.call(this, options); +} +util.inherits(ModifyResponse, LDAPResult); +module.exports = ModifyResponse; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/parser.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/parser.js new file mode 100644 index 0000000..0ae0f9b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/parser.js @@ -0,0 +1,223 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var EventEmitter = require('events').EventEmitter; +var util = require('util'); + +var asn1 = require('asn1'); + +var AbandonRequest = require('./abandon_request'); +var AddRequest = require('./add_request'); +var AddResponse = require('./add_response'); +var BindRequest = require('./bind_request'); +var BindResponse = require('./bind_response'); +var CompareRequest = require('./compare_request'); +var CompareResponse = require('./compare_response'); +var DeleteRequest = require('./del_request'); +var DeleteResponse = require('./del_response'); +var ExtendedRequest = require('./ext_request'); +var ExtendedResponse = require('./ext_response'); +var ModifyRequest = require('./modify_request'); +var ModifyResponse = require('./modify_response'); +var ModifyDNRequest = require('./moddn_request'); +var ModifyDNResponse = require('./moddn_response'); +var SearchRequest = require('./search_request'); +var SearchEntry = require('./search_entry'); +var SearchReference = require('./search_reference'); +var SearchResponse = require('./search_response'); +var UnbindRequest = require('./unbind_request'); +var UnbindResponse = require('./unbind_response'); + +var LDAPResult = require('./result'); +var Message = require('./message'); + +var Protocol = require('../protocol'); + +// Just make sure this adds to the prototype +require('buffertools'); + + + +///--- Globals + +var Ber = asn1.Ber; +var BerReader = asn1.BerReader; + + + +///--- API + +function Parser(options) { + if (!options || typeof (options) !== 'object') + throw new TypeError('options (object) required'); + if (typeof (options.log) !== 'object') + throw new TypeError('options.log (object) required'); + + EventEmitter.call(this); + + this.buffer = null; + this.log = options.log; +} +util.inherits(Parser, EventEmitter); +module.exports = Parser; + + +Parser.prototype.write = function (data) { + if (!data || !Buffer.isBuffer(data)) + throw new TypeError('data (buffer) required'); + + var nextMessage = null; + var self = this; + + function end() { + if (nextMessage) + return self.write(nextMessage); + + return true; + } + + self.buffer = (self.buffer ? self.buffer.concat(data) : data); + + var ber = new BerReader(self.buffer); + if (!ber.readSequence()) + return false; + + if (ber.remain < ber.length) { // ENOTENOUGH + return false; + } else if (ber.remain > ber.length) { // ETOOMUCH + // This is sort of ugly, but allows us to make miminal copies + nextMessage = self.buffer.slice(ber.offset + ber.length); + ber._size = ber.offset + ber.length; + assert.equal(ber.remain, ber.length); + } + + // If we're here, ber holds the message, and nextMessage is temporarily + // pointing at the next sequence of data (if it exists) + self.buffer = null; + + var message = this.getMessage(ber); + if (!message) + return end(); + + try { + message.parse(ber); + } catch (e) { + this.emit('error', e, message); + return false; + } + this.emit('message', message); + + return end(); +}; + + +Parser.prototype.getMessage = function (ber) { + assert.ok(ber); + + var self = this; + + var messageID = ber.readInt(); + var type = ber.readSequence(); + + var Message; + switch (type) { + + case Protocol.LDAP_REQ_ABANDON: + Message = AbandonRequest; + break; + + case Protocol.LDAP_REQ_ADD: + Message = AddRequest; + break; + + case Protocol.LDAP_REP_ADD: + Message = AddResponse; + break; + + case Protocol.LDAP_REQ_BIND: + Message = BindRequest; + break; + + case Protocol.LDAP_REP_BIND: + Message = BindResponse; + break; + + case Protocol.LDAP_REQ_COMPARE: + Message = CompareRequest; + break; + + case Protocol.LDAP_REP_COMPARE: + Message = CompareResponse; + break; + + case Protocol.LDAP_REQ_DELETE: + Message = DeleteRequest; + break; + + case Protocol.LDAP_REP_DELETE: + Message = DeleteResponse; + break; + + case Protocol.LDAP_REQ_EXTENSION: + Message = ExtendedRequest; + break; + + case Protocol.LDAP_REP_EXTENSION: + Message = ExtendedResponse; + break; + + case Protocol.LDAP_REQ_MODIFY: + Message = ModifyRequest; + break; + + case Protocol.LDAP_REP_MODIFY: + Message = ModifyResponse; + break; + + case Protocol.LDAP_REQ_MODRDN: + Message = ModifyDNRequest; + break; + + case Protocol.LDAP_REP_MODRDN: + Message = ModifyDNResponse; + break; + + case Protocol.LDAP_REQ_SEARCH: + Message = SearchRequest; + break; + + case Protocol.LDAP_REP_SEARCH_ENTRY: + Message = SearchEntry; + break; + + case Protocol.LDAP_REP_SEARCH_REF: + Message = SearchReference; + break; + + case Protocol.LDAP_REP_SEARCH: + Message = SearchResponse; + break; + + case Protocol.LDAP_REQ_UNBIND: + Message = UnbindRequest; + break; + + default: + this.emit('error', + new Error('protocolOp 0x' + + (type ? type.toString(16) : '??') + + ' not supported'), + new LDAPResult({ + messageID: messageID, + protocolOp: type || Protocol.LDAP_REP_EXTENSION + })); + + return false; + } + + + return new Message({ + messageID: messageID, + log: self.log + }); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/result.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/result.js new file mode 100644 index 0000000..54f8d5c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/result.js @@ -0,0 +1,142 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var dtrace = require('../dtrace'); +var LDAPMessage = require('./message'); +var Protocol = require('../protocol'); + + + +///--- Globals + +var Ber = asn1.Ber; +var BerWriter = asn1.BerWriter; + + + +///--- API + +function LDAPResult(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options (object) required'); + if (options.status && typeof (options.status) !== 'number') + throw new TypeError('options.status must be a number'); + if (options.matchedDN && typeof (options.matchedDN) !== 'string') + throw new TypeError('options.matchedDN must be a string'); + if (options.errorMessage && typeof (options.errorMessage) !== 'string') + throw new TypeError('options.errorMessage must be a string'); + + if (options.referrals) { + if (!(options.referrals instanceof Array)) + throw new TypeError('options.referrrals must be an array[string]'); + options.referrals.forEach(function (r) { + if (typeof (r) !== 'string') + throw new TypeError('options.referrals must be an array[string]'); + }); + } + } else { + options = {}; + } + + LDAPMessage.call(this, options); + + this.status = options.status || 0; // LDAP SUCCESS + this.matchedDN = options.matchedDN || ''; + this.errorMessage = options.errorMessage || ''; + this.referrals = options.referrals || []; + + this.connection = options.connection || null; + + this.__defineGetter__('type', function () { return 'LDAPResult'; }); +} +util.inherits(LDAPResult, LDAPMessage); +module.exports = LDAPResult; + + +LDAPResult.prototype.end = function (status) { + assert.ok(this.connection); + + if (typeof (status) === 'number') + this.status = status; + + var ber = this.toBer(); + if (this.log.debug()) + this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json); + + try { + var self = this; + this.connection.write(ber); + + if (self._dtraceOp && self._dtraceId) { + dtrace.fire('server-' + self._dtraceOp + '-done', function () { + var c = self.connection || {ldap: {}}; + return [ + self._dtraceId || 0, + (c.remoteAddress || ''), + c.ldap.bindDN ? c.ldap.bindDN.toString() : '', + (self.requestDN ? self.requestDN.toString() : ''), + status || self.status, + self.errorMessage + ]; + }); + } + + } catch (e) { + this.log.warn(e, '%s failure to write message %j', + this.connection.ldap.id, this.json); + } + +}; + + +LDAPResult.prototype._parse = function (ber) { + assert.ok(ber); + + this.status = ber.readEnumeration(); + this.matchedDN = ber.readString(); + this.errorMessage = ber.readString(); + + var t = ber.peek(); + + if (t === Protocol.LDAP_REP_REFERRAL) { + var end = ber.offset + ber.length; + while (ber.offset < end) + this.referrals.push(ber.readString()); + } + + return true; +}; + + +LDAPResult.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeEnumeration(this.status); + ber.writeString(this.matchedDN || ''); + ber.writeString(this.errorMessage || ''); + + if (this.referrals.length) { + ber.startSequence(Protocol.LDAP_REP_REFERRAL); + ber.writeStringArray(this.referrals); + ber.endSequence(); + } + + return ber; +}; + + +LDAPResult.prototype._json = function (j) { + assert.ok(j); + + j.status = this.status; + j.matchedDN = this.matchedDN; + j.errorMessage = this.errorMessage; + j.referrals = this.referrals; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_entry.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_entry.js new file mode 100644 index 0000000..3daa52c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_entry.js @@ -0,0 +1,173 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var Attribute = require('../attribute'); +var dn = require('../dn'); +var Protocol = require('../protocol'); + + + +///--- Globals + +var BerWriter = asn1.BerWriter; + + + +///--- API + +function SearchEntry(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.objectName && !(options.objectName instanceof dn.DN)) + throw new TypeError('options.objectName must be a DN'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_SEARCH_ENTRY; + LDAPMessage.call(this, options); + + this.objectName = options.objectName || null; + this.setAttributes(options.attributes || []); + + var self = this; + this.__defineGetter__('type', function () { return 'SearchEntry'; }); + this.__defineGetter__('object', function () { + var obj = { + dn: self.dn.toString(), + controls: [] + }; + self.attributes.forEach(function (a) { + if (a.vals && a.vals.length) { + if (a.vals.length > 1) { + obj[a.type] = a.vals.slice(); + } else { + obj[a.type] = a.vals[0]; + } + } else { + obj[a.type] = []; + } + }); + self.controls.forEach(function (element, index, array) { + obj.controls.push(element.json); + }); + return obj; + }); + this.__defineGetter__('_dn', function () { + return self.objectName; + }); +} +util.inherits(SearchEntry, LDAPMessage); +module.exports = SearchEntry; + + +SearchEntry.prototype.addAttribute = function (attr) { + if (!attr || typeof (attr) !== 'object') + throw new TypeError('attr (attribute) required'); + + this.attributes.push(attr); +}; + + +SearchEntry.prototype.toObject = function () { + return this.object; +}; + + +SearchEntry.prototype.fromObject = function (obj) { + if (typeof (obj) !== 'object') + throw new TypeError('object required'); + + var self = this; + if (obj.controls) + this.controls = obj.controls; + + if (obj.attributes) + obj = obj.attributes; + this.attributes = []; + + Object.keys(obj).forEach(function (k) { + self.attributes.push(new Attribute({type: k, vals: obj[k]})); + }); + + return true; +}; + +SearchEntry.prototype.setAttributes = function (obj) { + if (typeof (obj) !== 'object') + throw new TypeError('object required'); + + if (Array.isArray(obj)) { + obj.forEach(function (a) { + if (!Attribute.isAttribute(a)) + throw new TypeError('entry must be an Array of Attributes'); + }); + this.attributes = obj; + } else { + var self = this; + + self.attributes = []; + Object.keys(obj).forEach(function (k) { + var attr = new Attribute({type: k}); + if (Array.isArray(obj[k])) { + obj[k].forEach(function (v) { + attr.addValue(v.toString()); + }); + } else { + attr.addValue(obj[k].toString()); + } + self.attributes.push(attr); + }); + } +}; + + +SearchEntry.prototype._json = function (j) { + assert.ok(j); + + j.objectName = this.objectName.toString(); + j.attributes = []; + this.attributes.forEach(function (a) { + j.attributes.push(a.json || a); + }); + + return j; +}; + + +SearchEntry.prototype._parse = function (ber) { + assert.ok(ber); + + this.objectName = ber.readString(); + assert.ok(ber.readSequence()); + + var end = ber.offset + ber.length; + while (ber.offset < end) { + var a = new Attribute(); + a.parse(ber); + this.attributes.push(a); + } + + return true; +}; + + +SearchEntry.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.objectName.toString()); + ber.startSequence(); + this.attributes.forEach(function (a) { + // This may or may not be an attribute + ber = Attribute.toBer(a, ber); + }); + ber.endSequence(); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_reference.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_reference.js new file mode 100644 index 0000000..2b1ee9e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_reference.js @@ -0,0 +1,103 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var Protocol = require('../protocol'); +var dn = require('../dn'); +var url = require('../url'); + + + +///--- Globals + +var BerWriter = asn1.BerWriter; +var parseURL = url.parse; + + + +///--- API + +function SearchReference(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + if (options.objectName && !(options.objectName instanceof dn.DN)) + throw new TypeError('options.objectName must be a DN'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REP_SEARCH_REF; + LDAPMessage.call(this, options); + + this.uris = options.uris || []; + + var self = this; + this.__defineGetter__('type', function () { return 'SearchReference'; }); + this.__defineGetter__('object', function () { + return { + dn: self.dn.toString(), + uris: self.uris.slice() + }; + }); + this.__defineGetter__('_dn', function () { + return new dn.DN(''); + }); + this.__defineGetter__('urls', function () { + return self.uris; + }); + this.__defineSetter__('urls', function (u) { + self.uris = u.slice(); + }); +} +util.inherits(SearchReference, LDAPMessage); +module.exports = SearchReference; + + +SearchReference.prototype.toObject = function () { + return this.object; +}; + + +SearchReference.prototype.fromObject = function (obj) { + if (typeof (obj) !== 'object') + throw new TypeError('object required'); + + this.uris = obj.uris ? obj.uris.slice() : []; + + return true; +}; + +SearchReference.prototype._json = function (j) { + assert.ok(j); + j.uris = this.uris.slice(); + return j; +}; + + +SearchReference.prototype._parse = function (ber, length) { + assert.ok(ber); + + while (ber.offset < length) { + var _url = ber.readString(); + parseURL(_url); + this.uris.push(_url); + } + + return true; +}; + + +SearchReference.prototype._toBer = function (ber) { + assert.ok(ber); + + this.uris.forEach(function (u) { + ber.writeString(u.href || u); + }); + + return ber; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_request.js new file mode 100644 index 0000000..a31e49a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_request.js @@ -0,0 +1,155 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var filters = require('../filters'); +var Protocol = require('../protocol'); + + + +///--- Globals + +var Ber = asn1.Ber; + + + +///--- API + +function SearchRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_SEARCH; + LDAPMessage.call(this, options); + + var self = this; + this.__defineGetter__('type', function () { return 'SearchRequest'; }); + this.__defineGetter__('_dn', function () { + return self.baseObject; + }); + this.__defineGetter__('scope', function () { + switch (self._scope) { + case Protocol.SCOPE_BASE_OBJECT: return 'base'; + case Protocol.SCOPE_ONE_LEVEL: return 'one'; + case Protocol.SCOPE_SUBTREE: return 'sub'; + default: + throw new Error(self._scope + ' is an invalid search scope'); + } + }); + this.__defineSetter__('scope', function (s) { + if (typeof (s) === 'string') { + switch (s) { + case 'base': + self._scope = Protocol.SCOPE_BASE_OBJECT; + break; + case 'one': + self._scope = Protocol.SCOPE_ONE_LEVEL; + break; + case 'sub': + self._scope = Protocol.SCOPE_SUBTREE; + break; + default: + throw new Error(s + ' is an invalid search scope'); + } + } else { + self._scope = s; + } + }); + + this.baseObject = options.baseObject || new dn.DN([ {} ]); + this.scope = options.scope || 'base'; + this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES; + this.sizeLimit = options.sizeLimit || 0; + this.timeLimit = options.timeLimit || 0; + this.typesOnly = options.typesOnly || false; + this.filter = options.filter || null; + this.attributes = options.attributes ? options.attributes.slice(0) : []; +} +util.inherits(SearchRequest, LDAPMessage); +module.exports = SearchRequest; + + +SearchRequest.prototype.newResult = function () { + var self = this; + + return new LDAPResult({ + messageID: self.messageID, + protocolOp: Protocol.LDAP_REP_SEARCH + }); +}; + + +SearchRequest.prototype._parse = function (ber) { + assert.ok(ber); + + this.baseObject = dn.parse(ber.readString()); + this.scope = ber.readEnumeration(); + this.derefAliases = ber.readEnumeration(); + this.sizeLimit = ber.readInt(); + this.timeLimit = ber.readInt(); + this.typesOnly = ber.readBoolean(); + + this.filter = filters.parse(ber); + + // look for attributes + if (ber.peek() === 0x30) { + ber.readSequence(); + var end = ber.offset + ber.length; + while (ber.offset < end) + this.attributes.push(ber.readString().toLowerCase()); + } + + return true; +}; + + +SearchRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + ber.writeString(this.baseObject.toString()); + ber.writeEnumeration(this._scope); + ber.writeEnumeration(this.derefAliases); + ber.writeInt(this.sizeLimit); + ber.writeInt(this.timeLimit); + ber.writeBoolean(this.typesOnly); + + var f = this.filter || new filters.PresenceFilter({attribute: 'objectclass'}); + ber = f.toBer(ber); + + ber.startSequence(Ber.Sequence | Ber.Constructor); + if (this.attributes && this.attributes.length) { + this.attributes.forEach(function (a) { + ber.writeString(a); + }); + } + ber.endSequence(); + + return ber; +}; + + +SearchRequest.prototype._json = function (j) { + assert.ok(j); + + j.baseObject = this.baseObject; + j.scope = this.scope; + j.derefAliases = this.derefAliases; + j.sizeLimit = this.sizeLimit; + j.timeLimit = this.timeLimit; + j.typesOnly = this.typesOnly; + j.filter = this.filter.toString(); + j.attributes = this.attributes; + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_response.js new file mode 100644 index 0000000..33d8523 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/search_response.js @@ -0,0 +1,158 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var LDAPResult = require('./result'); +var SearchEntry = require('./search_entry'); +var SearchReference = require('./search_reference'); + +var dtrace = require('../dtrace'); +var parseDN = require('../dn').parse; +var parseURL = require('../url').parse; +var Protocol = require('../protocol'); + + + +///--- API + +function SearchResponse(options) { + if (!options) + options = {}; + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + + options.protocolOp = Protocol.LDAP_REP_SEARCH; + LDAPResult.call(this, options); + + this.attributes = options.attributes ? options.attributes.slice() : []; + this.notAttributes = []; + this.sentEntries = 0; +} +util.inherits(SearchResponse, LDAPResult); +module.exports = SearchResponse; + + +/** + * Allows you to send a SearchEntry back to the client. + * + * @param {Object} entry an instance of SearchEntry. + * @param {Boolean} nofiltering skip filtering notAttributes and '_' attributes. + * Defaults to 'false'. + */ +SearchResponse.prototype.send = function (entry, nofiltering) { + if (!entry || typeof (entry) !== 'object') + throw new TypeError('entry (SearchEntry) required'); + if (nofiltering === undefined) + nofiltering = false; + if (typeof (nofiltering) !== 'boolean') + throw new TypeError('noFiltering must be a boolean'); + + var self = this; + + if (entry instanceof SearchEntry || entry instanceof SearchReference) { + if (!entry.messageID) + entry.messageID = this.messageID; + if (entry.messageID !== this.messageID) + throw new Error('SearchEntry messageID mismatch'); + } else { + if (!entry.attributes) + throw new Error('entry.attributes required'); + + var savedAttrs = {}; + var all = (self.attributes.indexOf('*') !== -1); + Object.keys(entry.attributes).forEach(function (a) { + var _a = a.toLowerCase(); + if (!nofiltering && _a.length && _a[0] === '_') { + savedAttrs[a] = entry.attributes[a]; + delete entry.attributes[a]; + } else if (!nofiltering && self.notAttributes.indexOf(_a) !== -1) { + savedAttrs[a] = entry.attributes[a]; + delete entry.attributes[a]; + } else if (all) { + return; + } else if (self.attributes.length && self.attributes.indexOf(_a) === -1) { + savedAttrs[a] = entry.attributes[a]; + delete entry.attributes[a]; + } + }); + + var save = entry; + entry = new SearchEntry({ + objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn, + messageID: self.messageID, + log: self.log + }); + entry.fromObject(save); + } + + try { + if (this.log.debug()) + this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json); + + this.connection.write(entry.toBer()); + this.sentEntries++; + + if (self._dtraceOp && self._dtraceId) { + dtrace.fire('server-search-entry', function () { + var c = self.connection || {ldap: {}}; + return [ + self._dtraceId || 0, + (c.remoteAddress || ''), + c.ldap.bindDN ? c.ldap.bindDN.toString() : '', + (self.requestDN ? self.requestDN.toString() : ''), + entry.objectName.toString(), + entry.attributes.length + ]; + }); + } + + // Restore attributes + Object.keys(savedAttrs || {}).forEach(function (k) { + save.attributes[k] = savedAttrs[k]; + }); + + } catch (e) { + this.log.warn(e, '%s failure to write message %j', + this.connection.ldap.id, this.json); + } +}; + + + +SearchResponse.prototype.createSearchEntry = function (object) { + if (!object || typeof (object) !== 'object') + throw new TypeError('object required'); + + var self = this; + + var entry = new SearchEntry({ + messageID: self.messageID, + log: self.log, + objectName: object.objectName || object.dn + }); + entry.fromObject((object.attributes || object)); + + return entry; +}; + + +SearchResponse.prototype.createSearchReference = function (uris) { + if (!uris) + throw new TypeError('uris ([string]) required'); + + if (!Array.isArray(uris)) + uris = [uris]; + + for (var i = 0; i < uris.length; i++) { + if (typeof (uris[i]) == 'string') + uris[i] = parseURL(uris[i]); + } + + var self = this; + return new SearchReference({ + messageID: self.messageID, + log: self.log, + uris: uris + }); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/unbind_request.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/unbind_request.js new file mode 100644 index 0000000..bafa060 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/unbind_request.js @@ -0,0 +1,89 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var asn1 = require('asn1'); + +var LDAPMessage = require('./message'); +var LDAPResult = require('./result'); + +var dn = require('../dn'); +var Protocol = require('../protocol'); + + + +///--- Globals + +var Ber = asn1.Ber; + +var DN = dn.DN; +var RDN = dn.RDN; + + +///--- API + +function UnbindRequest(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + } else { + options = {}; + } + + options.protocolOp = Protocol.LDAP_REQ_UNBIND; + LDAPMessage.call(this, options); + + var self = this; + this.__defineGetter__('type', function () { return 'UnbindRequest'; }); + this.__defineGetter__('_dn', function () { + if (self.connection) + return self.connection.ldap.bindDN; + + return new DN([new RDN({cn: 'anonymous'})]); + }); +} +util.inherits(UnbindRequest, LDAPMessage); +module.exports = UnbindRequest; + + +UnbindRequest.prototype.newResult = function () { + // This one is special, so just hack up the result object + function UnbindResponse(options) { + LDAPMessage.call(this, options); + this.__defineGetter__('type', function () { return 'UnbindResponse'; }); + } + util.inherits(UnbindResponse, LDAPMessage); + UnbindResponse.prototype.end = function (status) { + this.log.trace('%s: unbinding!', this.connection.ldap.id); + this.connection.end(); + }; + UnbindResponse.prototype._json = function (j) { return j; }; + + return new UnbindResponse({ + messageID: 0, + protocolOp: 0, + status: 0 // Success + }); +}; + + +UnbindRequest.prototype._parse = function (ber) { + assert.ok(ber); + + return true; +}; + + +UnbindRequest.prototype._toBer = function (ber) { + assert.ok(ber); + + return ber; +}; + + +UnbindRequest.prototype._json = function (j) { + assert.ok(j); + + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/unbind_response.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/unbind_response.js new file mode 100644 index 0000000..c1d8d87 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/messages/unbind_response.js @@ -0,0 +1,62 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var util = require('util'); + +var dtrace = require('../dtrace'); + +var LDAPMessage = require('./result'); +var Protocol = require('../protocol'); + + +///--- API +// Ok, so there's really no such thing as an unbind 'response', but to make +// the framework not suck, I just made this up, and have it stubbed so it's +// not such a one-off. + +function UnbindResponse(options) { + if (!options) + options = {}; + if (typeof (options) !== 'object') + throw new TypeError('options must be an object'); + + options.protocolOp = 0; + LDAPMessage.call(this, options); + this.__defineGetter__('type', function () { return 'UnbindResponse'; }); +} +util.inherits(UnbindResponse, LDAPMessage); +module.exports = UnbindResponse; + + +/** + * Special override that just ends the connection, if present. + * + * @param {Number} status completely ignored. + */ +UnbindResponse.prototype.end = function (status) { + assert.ok(this.connection); + + this.log.trace('%s: unbinding!', this.connection.ldap.id); + + this.connection.end(); + + var self = this; + if (self._dtraceOp && self._dtraceId) { + dtrace.fire('server-' + self._dtraceOp + '-done', function () { + var c = self.connection || {ldap: {}}; + return [ + self._dtraceId || 0, + (c.remoteAddress || ''), + c.ldap.bindDN ? c.ldap.bindDN.toString() : '', + (self.requestDN ? self.requestDN.toString() : ''), + 0, + '' + ]; + }); + } +}; + + +UnbindResponse.prototype._json = function (j) { + return j; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/persistent_search.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/persistent_search.js new file mode 100644 index 0000000..79087aa --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/persistent_search.js @@ -0,0 +1,123 @@ +///--- Globals + +var parseDN = require('./dn').parse; + +var EntryChangeNotificationControl = + require('./controls').EntryChangeNotificationControl; + +///--- API + +// Cache used to store connected persistent search clients +function PersistentSearch() { + this.clientList = []; +} + + +PersistentSearch.prototype.addClient = function (req, res, callback) { + if (typeof (req) !== 'object') + throw new TypeError('req must be an object'); + if (typeof (res) !== 'object') + throw new TypeError('res must be an object'); + if (callback && typeof (callback) !== 'function') + throw new TypeError('callback must be a function'); + + var log = req.log; + + var client = {}; + client.req = req; + client.res = res; + + log.debug('%s storing client', req.logId); + + this.clientList.push(client); + + log.debug('%s stored client', req.logId); + log.debug('%s total number of clients %s', + req.logId, this.clientList.length); + if (callback) + callback(client); +}; + + +PersistentSearch.prototype.removeClient = function (req, res, callback) { + if (typeof (req) !== 'object') + throw new TypeError('req must be an object'); + if (typeof (res) !== 'object') + throw new TypeError('res must be an object'); + if (callback && typeof (callback) !== 'function') + throw new TypeError('callback must be a function'); + + var log = req.log; + log.debug('%s removing client', req.logId); + var client = {}; + client.req = req; + client.res = res; + + // remove the client if it exists + this.clientList.forEach(function (element, index, array) { + if (element.req === client.req) { + log.debug('%s removing client from list', req.logId); + array.splice(index, 1); + } + }); + + log.debug('%s number of persistent search clients %s', + req.logId, this.clientList.length); + if (callback) + callback(client); +}; + + +function getOperationType(requestType) { + switch (requestType) { + case 'AddRequest': + case 'add': + return 1; + case 'DeleteRequest': + case 'delete': + return 2; + case 'ModifyRequest': + case 'modify': + return 4; + case 'ModifyDNRequest': + case 'modrdn': + return 8; + default: + throw new TypeError('requestType %s, is an invalid request type', + requestType); + } +} + + +function getEntryChangeNotificationControl(req, obj, callback) { + // if we want to return a ECNC + if (req.persistentSearch.value.returnECs) { + var attrs = obj.attributes; + var value = {}; + value.changeType = getOperationType(attrs.changetype); + // if it's a modDN request, fill in the previous DN + if (value.changeType === 8 && attrs.previousDN) { + value.previousDN = attrs.previousDN; + } + + value.changeNumber = attrs.changenumber; + return new EntryChangeNotificationControl({ value: value }); + } else { + return false; + } +} + + +function checkChangeType(req, requestType) { + return (req.persistentSearch.value.changeTypes & + getOperationType(requestType)); +} + + +///--- Exports + +module.exports = { + PersistentSearchCache: PersistentSearch, + checkChangeType: checkChangeType, + getEntryChangeNotificationControl: getEntryChangeNotificationControl +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/protocol.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/protocol.js new file mode 100644 index 0000000..398f7b3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/protocol.js @@ -0,0 +1,54 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + + +module.exports = { + + // Misc + LDAP_VERSION_3: 0x03, + LBER_SET: 0x31, + LDAP_CONTROLS: 0xa0, + + // Search + SCOPE_BASE_OBJECT: 0, + SCOPE_ONE_LEVEL: 1, + SCOPE_SUBTREE: 2, + + NEVER_DEREF_ALIASES: 0, + DEREF_IN_SEARCHING: 1, + DEREF_BASE_OBJECT: 2, + DEREF_ALWAYS: 3, + + FILTER_AND: 0xa0, + FILTER_OR: 0xa1, + FILTER_NOT: 0xa2, + FILTER_EQUALITY: 0xa3, + FILTER_SUBSTRINGS: 0xa4, + FILTER_GE: 0xa5, + FILTER_LE: 0xa6, + FILTER_PRESENT: 0x87, + FILTER_APPROX: 0xa8, + FILTER_EXT: 0xa9, + + // Protocol Operations + LDAP_REQ_BIND: 0x60, + LDAP_REQ_UNBIND: 0x42, + LDAP_REQ_SEARCH: 0x63, + LDAP_REQ_MODIFY: 0x66, + LDAP_REQ_ADD: 0x68, + LDAP_REQ_DELETE: 0x4a, + LDAP_REQ_MODRDN: 0x6c, + LDAP_REQ_COMPARE: 0x6e, + LDAP_REQ_ABANDON: 0x50, + LDAP_REQ_EXTENSION: 0x77, + + LDAP_REP_BIND: 0x61, + LDAP_REP_SEARCH_ENTRY: 0x64, + LDAP_REP_SEARCH_REF: 0x73, + LDAP_REP_SEARCH: 0x65, + LDAP_REP_MODIFY: 0x67, + LDAP_REP_ADD: 0x69, + LDAP_REP_DELETE: 0x6b, + LDAP_REP_MODRDN: 0x6d, + LDAP_REP_COMPARE: 0x6f, + LDAP_REP_EXTENSION: 0x78 +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/server.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/server.js new file mode 100644 index 0000000..da64af4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/server.js @@ -0,0 +1,817 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var assert = require('assert'); +var EventEmitter = require('events').EventEmitter; +var net = require('net'); +var tls = require('tls'); +var util = require('util'); + +var asn1 = require('asn1'); + +var dn = require('./dn'); +var dtrace = require('./dtrace'); +var errors = require('./errors'); +var Protocol = require('./protocol'); + +var Parser = require('./messages').Parser; +var AbandonResponse = require('./messages/abandon_response'); +var AddResponse = require('./messages/add_response'); +var BindResponse = require('./messages/bind_response'); +var CompareResponse = require('./messages/compare_response'); +var DeleteResponse = require('./messages/del_response'); +var ExtendedResponse = require('./messages/ext_response'); +var LDAPResult = require('./messages/result'); +var ModifyResponse = require('./messages/modify_response'); +var ModifyDNResponse = require('./messages/moddn_response'); +var SearchRequest = require('./messages/search_request'); +var SearchResponse = require('./messages/search_response'); +var UnbindResponse = require('./messages/unbind_response'); + + + +///--- Globals + +var Ber = asn1.Ber; +var BerReader = asn1.BerReader; +var DN = dn.DN; + +var sprintf = util.format; + + +///--- Helpers + +function mergeFunctionArgs(argv, start, end) { + assert.ok(argv); + + if (!start) + start = 0; + if (!end) + end = argv.length; + + var handlers = []; + + for (var i = start; i < end; i++) { + if (argv[i] instanceof Array) { + var arr = argv[i]; + for (var j = 0; j < arr.length; j++) { + if (!(arr[j] instanceof Function)) { + throw new TypeError('Invalid argument type: ' + typeof (arr[j])); + } + handlers.push(arr[j]); + } + } else if (argv[i] instanceof Function) { + handlers.push(argv[i]); + } else { + throw new TypeError('Invalid argument type: ' + typeof (argv[i])); + } + } + + return handlers; +} + + +function getResponse(req) { + assert.ok(req); + + var Response; + + switch (req.protocolOp) { + case Protocol.LDAP_REQ_BIND: + Response = BindResponse; + break; + case Protocol.LDAP_REQ_ABANDON: + Response = AbandonResponse; + break; + case Protocol.LDAP_REQ_ADD: + Response = AddResponse; + break; + case Protocol.LDAP_REQ_COMPARE: + Response = CompareResponse; + break; + case Protocol.LDAP_REQ_DELETE: + Response = DeleteResponse; + break; + case Protocol.LDAP_REQ_EXTENSION: + Response = ExtendedResponse; + break; + case Protocol.LDAP_REQ_MODIFY: + Response = ModifyResponse; + break; + case Protocol.LDAP_REQ_MODRDN: + Response = ModifyDNResponse; + break; + case Protocol.LDAP_REQ_SEARCH: + Response = SearchResponse; + break; + case Protocol.LDAP_REQ_UNBIND: + Response = UnbindResponse; + break; + default: + return null; + } + assert.ok(Response); + + var res = new Response({ + messageID: req.messageID, + log: req.log, + attributes: ((req instanceof SearchRequest) ? req.attributes : undefined) + }); + res.connection = req.connection; + res.logId = req.logId; + + return res; +} + + +function defaultHandler(req, res, next) { + assert.ok(req); + assert.ok(res); + assert.ok(next); + + res.matchedDN = req.dn.toString(); + res.errorMessage = 'Server method not implemented'; + res.end(errors.LDAP_OTHER); + return next(); +} + + +function defaultNoOpHandler(req, res, next) { + assert.ok(req); + assert.ok(res); + assert.ok(next); + + res.end(); + return next(); +} + + +function noSuffixHandler(req, res, next) { + assert.ok(req); + assert.ok(res); + assert.ok(next); + + res.errorMessage = 'No tree found for: ' + req.dn.toString(); + res.end(errors.LDAP_NO_SUCH_OBJECT); + return next(); +} + + +function noExOpHandler(req, res, next) { + assert.ok(req); + assert.ok(res); + assert.ok(next); + + res.errorMessage = req.requestName + ' not supported'; + res.end(errors.LDAP_PROTOCOL_ERROR); + return next(); +} + + +function fireDTraceProbe(req, res) { + assert.ok(req); + + req._dtraceId = res._dtraceId = dtrace._nextId(); + var probeArgs = [ + req._dtraceId, + req.connection.remoteAddress || 'localhost', + req.connection.ldap.bindDN.toString(), + req.dn.toString() + ]; + + var op; + switch (req.protocolOp) { + case Protocol.LDAP_REQ_ABANDON: + op = 'abandon'; + break; + case Protocol.LDAP_REQ_ADD: + op = 'add'; + probeArgs.push(req.attributes.length); + break; + case Protocol.LDAP_REQ_BIND: + op = 'bind'; + break; + case Protocol.LDAP_REQ_COMPARE: + op = 'compare'; + probeArgs.push(req.attribute); + probeArgs.push(req.value); + break; + case Protocol.LDAP_REQ_DELETE: + op = 'delete'; + break; + case Protocol.LDAP_REQ_EXTENSION: + op = 'exop'; + probeArgs.push(req.name); + probeArgs.push(req.value); + break; + case Protocol.LDAP_REQ_MODIFY: + op = 'modify'; + probeArgs.push(req.changes.length); + break; + case Protocol.LDAP_REQ_MODRDN: + op = 'modifydn'; + probeArgs.push(req.newRdn.toString()); + probeArgs.push((req.newSuperior ? req.newSuperior.toString() : '')); + break; + case Protocol.LDAP_REQ_SEARCH: + op = 'search'; + probeArgs.push(req.scope); + probeArgs.push(req.filter.toString()); + break; + case Protocol.LDAP_REQ_UNBIND: + op = 'unbind'; + break; + default: + break; + } + + res._dtraceOp = op; + dtrace.fire('server-' + op + '-start', function () { + return probeArgs; + }); +} + + + +///--- API + +/** + * Constructs a new server that you can call .listen() on, in the various + * forms node supports. You need to first assign some handlers to the various + * LDAP operations however. + * + * The options object currently only takes a certificate/private key, and a + * bunyan logger handle. + * + * This object exposes the following events: + * - 'error' + * - 'close' + * + * @param {Object} options (optional) parameterization object. + * @throws {TypeError} on bad input. + */ +function Server(options) { + if (options) { + if (typeof (options) !== 'object') + throw new TypeError('options (object) required'); + if (typeof (options.log) !== 'object') + throw new TypeError('options.log must be an object'); + + if (options.certificate || options.key) { + if (!(options.certificate && options.key) || + typeof (options.certificate) !== 'string' || + typeof (options.key) !== 'string') { + throw new TypeError('options.certificate and options.key (string) ' + + 'are both required for TLS'); + } + } + } else { + options = {}; + } + var self = this; + + EventEmitter.call(this, options); + + this._chain = []; + this.log = options.log; + + var log = this.log; + + function setupConnection(c) { + assert.ok(c); + + if (c.type === 'unix') { + c.remoteAddress = self.server.path; + c.remotePort = c.fd; + } else if (c.socket) { // TLS + c.remoteAddress = c.socket.remoteAddress; + c.remotePort = c.socket.remotePort; + } + + + var rdn = new dn.RDN({cn: 'anonymous'}); + + c.ldap = { + id: c.remoteAddress + ':' + c.remotePort, + config: options, + _bindDN: new DN([rdn]) + }; + c.addListener('timeout', function () { + log.trace('%s timed out', c.ldap.id); + c.destroy(); + }); + c.addListener('end', function () { + log.trace('%s shutdown', c.ldap.id); + }); + c.addListener('error', function (err) { + log.warn('%s unexpected connection error', c.ldap.id, err); + self.emit('clientError', err); + c.destroy(); + }); + c.addListener('close', function (had_err) { + log.trace('%s close; had_err=%j', c.ldap.id, had_err); + c.end(); + }); + + c.ldap.__defineGetter__('bindDN', function () { + return c.ldap._bindDN; + }); + c.ldap.__defineSetter__('bindDN', function (val) { + if (!(val instanceof DN)) + throw new TypeError('DN required'); + + c.ldap._bindDN = val; + return val; + }); + return c; + } + + function newConnection(c) { + setupConnection(c); + log.trace('new connection from %s', c.ldap.id); + + dtrace.fire('server-connection', function () { + return [c.remoteAddress]; + }); + + c.parser = new Parser({ + log: options.log + }); + c.parser.on('message', function (req) { + req.connection = c; + req.logId = c.ldap.id + '::' + req.messageID; + req.startTime = new Date().getTime(); + + if (log.debug()) + log.debug('%s: message received: req=%j', c.ldap.id, req.json); + + var res = getResponse(req); + if (!res) { + log.warn('Unimplemented server method: %s', req.type); + c.destroy(); + return false; + } + + res.connection = c; + res.logId = req.logId; + res.requestDN = req.dn; + + var chain = self._getHandlerChain(req, res); + + var i = 0; + return function (err) { + function sendError(err) { + res.status = err.code || errors.LDAP_OPERATIONS_ERROR; + res.matchedDN = req.suffix ? req.suffix.toString() : ''; + res.errorMessage = err.message || ''; + return res.end(); + } + + function after() { + if (!self._postChain || !self._postChain.length) + return; + + function next() {} // stub out next for the post chain + + self._postChain.forEach(function (c) { + c.call(self, req, res, next); + }); + } + + if (err) { + log.trace('%s sending error: %s', req.logId, err.stack || err); + self.emit('clientError', err); + sendError(err); + return after(); + } + + try { + var next = arguments.callee; + if (chain.handlers[i]) + return chain.handlers[i++].call(chain.backend, req, res, next); + + if (req.protocolOp === Protocol.LDAP_REQ_BIND && res.status === 0) + c.ldap.bindDN = req.dn; + + return after(); + } catch (e) { + if (!e.stack) + e.stack = e.toString(); + log.error('%s uncaught exception: %s', req.logId, e.stack); + return sendError(new errors.OperationsError(e.message)); + } + + }(); + }); + + c.parser.on('error', function (err, message) { + log.error('Exception happened parsing for %s: %s', + c.ldap.id, err.stack); + + + if (!message) + return c.destroy(); + + var res = getResponse(message); + if (!res) + return c.destroy(); + + res.status = 0x02; // protocol error + res.errorMessage = err.toString(); + return c.end(res.toBer()); + }); + + c.on('data', function (data) { + if (log.trace()) + log.trace('data on %s: %s', c.ldap.id, util.inspect(data)); + + try { + c.parser.write(data); + } catch (e) { + log.warn('Unable to parse message [c.on(\'data\')]: %s', e.stack); + c.end(new LDAPResult({ + status: 0x02, + errorMessage: e.toString(), + connection: c + }).toBer()); + } + }); + + } // end newConnection + + this.routes = {}; + if ((options.cert || options.certificate) && options.key) { + options.cert = options.cert || options.certificate; + this.server = tls.createServer(options, newConnection); + } else { + this.server = net.createServer(newConnection); + } + this.server.log = options.log; + this.server.ldap = { + config: options + }; + this.server.on('close', function () { + self.emit('close'); + }); + this.server.on('error', function (err) { + self.emit('error', err); + }); + + this.__defineGetter__('maxConnections', function () { + return self.server.maxConnections; + }); + this.__defineSetter__('maxConnections', function (val) { + self.server.maxConnections = val; + }); + this.__defineGetter__('connections', function () { + return self.server.connections; + }); + this.__defineGetter__('name', function () { + return 'LDAPServer'; + }); + this.__defineGetter__('url', function () { + var str; + if (this.server instanceof tls.Server) { + str = 'ldaps://'; + } else { + str = 'ldap://'; + } + str += self.host || 'localhost'; + str += ':'; + str += self.port || 389; + return str; + }); +} +util.inherits(Server, EventEmitter); +module.exports = Server; + + +/** + * Adds a handler (chain) for the LDAP add method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name the DN to mount this handler chain at. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.add = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_ADD, name, args); +}; + + +/** + * Adds a handler (chain) for the LDAP bind method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name the DN to mount this handler chain at. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.bind = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_BIND, name, args); +}; + + +/** + * Adds a handler (chain) for the LDAP compare method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name the DN to mount this handler chain at. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.compare = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_COMPARE, name, args); +}; + + +/** + * Adds a handler (chain) for the LDAP delete method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name the DN to mount this handler chain at. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.del = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_DELETE, name, args); +}; + + +/** + * Adds a handler (chain) for the LDAP exop method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name OID to assign this handler chain to. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input. + */ +Server.prototype.exop = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true); +}; + + +/** + * Adds a handler (chain) for the LDAP modify method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name the DN to mount this handler chain at. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.modify = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_MODIFY, name, args); +}; + + +/** + * Adds a handler (chain) for the LDAP modifyDN method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name the DN to mount this handler chain at. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.modifyDN = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_MODRDN, name, args); +}; + + +/** + * Adds a handler (chain) for the LDAP search method. + * + * Note that this is of the form f(name, [function]) where the second...N + * arguments can all either be functions or arrays of functions. + * + * @param {String} name the DN to mount this handler chain at. + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.search = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + return this._mount(Protocol.LDAP_REQ_SEARCH, name, args); +}; + + +/** + * Adds a handler (chain) for the LDAP unbind method. + * + * This method is different than the others and takes no mount point, as unbind + * is a connection-wide operation, not constrianed to part of the DIT. + * + * @return {Server} this so you can chain calls. + * @throws {TypeError} on bad input + */ +Server.prototype.unbind = function () { + var args = Array.prototype.slice.call(arguments, 0); + return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true); +}; + + +Server.prototype.use = function use() { + var args = Array.prototype.slice.call(arguments); + var chain = mergeFunctionArgs(args, 0, args.length); + var self = this; + chain.forEach(function (c) { + self._chain.push(c); + }); +}; + + +Server.prototype.after = function () { + if (!this._postChain) + this._postChain = []; + + var self = this; + mergeFunctionArgs(arguments).forEach(function (h) { + self._postChain.push(h); + }); +}; + + +// All these just reexpose the requisite net.Server APIs +Server.prototype.listen = function (port, host, callback) { + if (!port) + throw new TypeError('port (number) required'); + + if (typeof (host) === 'function') { + callback = host; + host = '0.0.0.0'; + } + var self = this; + + function _callback() { + if (typeof (port) === 'number') { + self.host = host; + self.port = port; + } else { + self.host = port; + self.port = self.server.fd; + } + + if (typeof (callback) === 'function') + callback(); + } + + if (typeof (port) === 'number') { + return this.server.listen(port, host, _callback); + } else { + return this.server.listen(port, _callback); + } +}; +Server.prototype.listenFD = function (fd) { + this.host = 'unix-domain-socket'; + this.port = fd; + return this.server.listenFD(fd); +}; +Server.prototype.close = function () { + return this.server.close(); +}; +Server.prototype.address = function () { + return this.server.address(); +}; + + +Server.prototype._getRoute = function (_dn, backend) { + assert.ok(dn); + + if (!backend) + backend = this; + + var name; + if (_dn instanceof dn.DN) { + name = _dn.toString(); + } else { + name = _dn; + } + + if (!this.routes[name]) { + this.routes[name] = {}; + this.routes[name].backend = backend; + this.routes[name].dn = _dn; + } + + return this.routes[name]; +}; + + +Server.prototype._getHandlerChain = function _getHandlerChain(req, res) { + assert.ok(req); + + fireDTraceProbe(req, res); + + // check anonymous bind + if (req.protocolOp === Protocol.LDAP_REQ_BIND && + req.dn.toString() === '' && + req.credentials === '') { + return { + backend: self, + handlers: [defaultNoOpHandler] + }; + } + + var op = '0x' + req.protocolOp.toString(16); + + var self = this; + var routes = this.routes; + var keys = Object.keys(routes); + for (var i = 0; i < keys.length; i++) { + var r = keys[i]; + var route = routes[r]; + + // Special cases are abandons, exops and unbinds, handle those first. + if (req.protocolOp === Protocol.LDAP_REQ_EXTENSION) { + if (r === req.requestName) { + return { + backend: routes.backend, + handlers: route[op] || [noExOpHandler] + }; + } + } else if (req.protocolOp === Protocol.LDAP_REQ_UNBIND) { + return { + backend: routes['unbind'] ? routes['unbind'].backend : self, + handlers: function getUnbindChain() { + if (routes['unbind'] && routes['unbind'][op]) + return routes['unbind'][op]; + + self.log.debug('%s unbind request %j', req.logId, req.json); + return [defaultNoOpHandler]; + } + }; + } else if (req.protocolOp === Protocol.LDAP_REQ_ABANDON) { + return { + backend: self, + handlers: [defaultNoOpHandler] + }; + } else if (route[op]) { + // Otherwise, match via DN rules + assert.ok(req.dn); + assert.ok(route.dn); + + if (route.dn.equals(req.dn) || route.dn.parentOf(req.dn)) { + // We should be good to go. + req.suffix = route.dn; + return { + backend: route.backend, + handlers: route[op] || [defaultHandler] + }; + } + } + } + + // We're here, so nothing matched. + return { + backend: self, + handlers: [(req.protocolOp !== Protocol.LDAP_REQ_EXTENSION ? + noSuffixHandler : noExOpHandler)] + }; +}; + + +Server.prototype._mount = function (op, name, argv, notDN) { + assert.ok(op); + assert.ok(name !== undefined); + assert.ok(argv); + + if (typeof (name) !== 'string') + throw new TypeError('name (string) required'); + if (!argv.length) + throw new Error('at least one handler required'); + + var backend = this; + var index = 0; + + if (typeof (argv[0]) === 'object' && !Array.isArray(argv[0])) { + backend = argv[0]; + index = 1; + } + var route = this._getRoute(notDN ? name : dn.parse(name), backend); + + var chain = this._chain.slice(); + argv.slice(index).forEach(function (a) { + chain.push(a); + }); + route['0x' + op.toString(16)] = mergeFunctionArgs(chain); + + return this; +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/lib/url.js b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/url.js new file mode 100644 index 0000000..d9d69a7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/lib/url.js @@ -0,0 +1,66 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var querystring = require('querystring'); +var url = require('url'); +var util = require('util'); + +var dn = require('./dn'); +var filter = require('./filters/index'); + + +module.exports = { + + parse: function (urlStr, parseDN) { + var u = url.parse(urlStr); + if (!u.protocol || !(u.protocol === 'ldap:' || u.protocol === 'ldaps:')) + throw new TypeError(urlStr + ' is an invalid LDAP url (protocol)'); + + u.secure = (u.protocol === 'ldaps:'); + + if (!u.hostname) + u.hostname = 'localhost'; + + if (!u.port) { + u.port = (u.secure ? 636 : 389); + } else { + u.port = parseInt(u.port, 10); + } + + if (u.pathname) { + u.pathname = querystring.unescape(u.pathname.substr(1)); + u.DN = parseDN ? dn.parse(u.pathname) : u.pathname; + } + + if (u.search) { + u.attributes = []; + var tmp = u.search.substr(1).split('?'); + if (tmp && tmp.length) { + if (tmp[0]) { + tmp[0].split(',').forEach(function (a) { + u.attributes.push(querystring.unescape(a.trim())); + }); + } + } + if (tmp[1]) { + if (tmp[1] !== 'base' && tmp[1] !== 'one' && tmp[1] !== 'sub') + throw new TypeError(urlStr + ' is an invalid LDAP url (scope)'); + u.scope = tmp[1]; + } + if (tmp[2]) { + u.filter = querystring.unescape(tmp[2]); + } + if (tmp[3]) { + u.extensions = querystring.unescape(tmp[3]); + } + if (!u.scope) + u.scope = 'base'; + if (!u.filter) + u.filter = filter.parseString('(objectclass=*)'); + else + u.filter = filter.parseString(u.filter); + } + + return u; + } + +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/.npmignore new file mode 100644 index 0000000..eb03e3e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/.npmignore @@ -0,0 +1,2 @@ +node_modules +*.log diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/LICENSE b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/LICENSE new file mode 100644 index 0000000..9b5dcdb --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Mark Cavage, All rights reserved. + +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/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/README.md new file mode 100644 index 0000000..7cebf7a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/README.md @@ -0,0 +1,50 @@ +node-asn1 is a library for encoding and decoding ASN.1 datatypes in pure JS. +Currently BER encoding is supported; at some point I'll likely have to do DER. + +## Usage + +Mostly, if you're *actually* needing to read and write ASN.1, you probably don't +need this readme to explain what and why. If you have no idea what ASN.1 is, +see this: ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc + +The source is pretty much self-explanatory, and has read/write methods for the +common types out there. + +### Decoding + +The following reads an ASN.1 sequence with a boolean. + + var Ber = require('asn1').Ber; + + var reader = new Ber.Reader(new Buffer([0x30, 0x03, 0x01, 0x01, 0xff])); + + reader.readSequence(); + console.log('Sequence len: ' + reader.length); + if (reader.peek() === Ber.Boolean) + console.log(reader.readBoolean()); + +### Encoding + +The following generates the same payload as above. + + var Ber = require('asn1').Ber; + + var writer = new Ber.Writer(); + + writer.startSequence(); + writer.writeBoolean(true); + writer.endSequence(); + + console.log(writer.buffer); + +## Installation + + npm install asn1 + +## License + +MIT. + +## Bugs + +See . diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/errors.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/errors.js new file mode 100644 index 0000000..ff21d4f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/errors.js @@ -0,0 +1,13 @@ +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + + newInvalidAsn1Error: function(msg) { + var e = new Error(); + e.name = 'InvalidAsn1Error'; + e.message = msg || ''; + return e; + } + +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/index.js new file mode 100644 index 0000000..4fb90ae --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/index.js @@ -0,0 +1,27 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +var errors = require('./errors'); +var types = require('./types'); + +var Reader = require('./reader'); +var Writer = require('./writer'); + + +///--- Exports + +module.exports = { + + Reader: Reader, + + Writer: Writer + +}; + +for (var t in types) { + if (types.hasOwnProperty(t)) + module.exports[t] = types[t]; +} +for (var e in errors) { + if (errors.hasOwnProperty(e)) + module.exports[e] = errors[e]; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/reader.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/reader.js new file mode 100644 index 0000000..bd3357a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/reader.js @@ -0,0 +1,267 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = require('assert'); + +var ASN1 = require('./types'); +var errors = require('./errors'); + + +///--- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + + + +///--- API + +function Reader(data) { + if (!data || !Buffer.isBuffer(data)) + throw new TypeError('data must be a node Buffer'); + + this._buf = data; + this._size = data.length; + + // These hold the "current" state + this._len = 0; + this._offset = 0; + + var self = this; + this.__defineGetter__('length', function() { return self._len; }); + this.__defineGetter__('offset', function() { return self._offset; }); + this.__defineGetter__('remain', function() { + return self._size - self._offset; + }); + this.__defineGetter__('buffer', function() { + return self._buf.slice(self._offset); + }); +} + + +/** + * Reads a single byte and advances offset; you can pass in `true` to make this + * a "peek" operation (i.e., get the byte, but don't advance the offset). + * + * @param {Boolean} peek true means don't move offset. + * @return {Number} the next byte, null if not enough data. + */ +Reader.prototype.readByte = function(peek) { + if (this._size - this._offset < 1) + return null; + + var b = this._buf[this._offset] & 0xff; + + if (!peek) + this._offset += 1; + + return b; +}; + + +Reader.prototype.peek = function() { + return this.readByte(true); +}; + + +/** + * Reads a (potentially) variable length off the BER buffer. This call is + * not really meant to be called directly, as callers have to manipulate + * the internal buffer afterwards. + * + * As a result of this call, you can call `Reader.length`, until the + * next thing called that does a readLength. + * + * @return {Number} the amount of offset to advance the buffer. + * @throws {InvalidAsn1Error} on bad ASN.1 + */ +Reader.prototype.readLength = function(offset) { + if (offset === undefined) + offset = this._offset; + + if (offset >= this._size) + return null; + + var lenB = this._buf[offset++] & 0xff; + if (lenB === null) + return null; + + if ((lenB & 0x80) == 0x80) { + lenB &= 0x7f; + + if (lenB == 0) + throw newInvalidAsn1Error('Indefinite length not supported'); + + if (lenB > 4) + throw newInvalidAsn1Error('encoding too long'); + + if (this._size - offset < lenB) + return null; + + this._len = 0; + for (var i = 0; i < lenB; i++) + this._len = (this._len << 8) + (this._buf[offset++] & 0xff); + + } else { + // Wasn't a variable length + this._len = lenB; + } + + return offset; +}; + + +/** + * Parses the next sequence in this BER buffer. + * + * To get the length of the sequence, call `Reader.length`. + * + * @return {Number} the sequence's tag. + */ +Reader.prototype.readSequence = function(tag) { + var seq = this.peek(); + if (seq === null) + return null; + if (tag !== undefined && tag !== seq) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + seq.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + this._offset = o; + return seq; +}; + + +Reader.prototype.readInt = function() { + return this._readTag(ASN1.Integer); +}; + + +Reader.prototype.readBoolean = function() { + return (this._readTag(ASN1.Boolean) === 0 ? false : true); +}; + + +Reader.prototype.readEnumeration = function() { + return this._readTag(ASN1.Enumeration); +}; + + +Reader.prototype.readString = function(tag, retbuf) { + if (!tag) + tag = ASN1.OctetString; + + var b = this.peek(); + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + + if (o === null) + return null; + + if (this.length > this._size - o) + return null; + + this._offset = o; + + if (this.length === 0) + return ''; + + var str = this._buf.slice(this._offset, this._offset + this.length); + this._offset += this.length; + + return retbuf ? str : str.toString('utf8'); +}; + +Reader.prototype.readOID = function(tag) { + if (!tag) + tag = ASN1.OID; + + var b = this.peek(); + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + if (this.length > this._size - o) + return null; + + this._offset = o; + + var values = []; + var value = 0; + + for (var i = 0; i < this.length; i++) { + var byte = this._buf[this._offset++] & 0xff; + + value <<= 7; + value += byte & 0x7f; + if ((byte & 0x80) == 0) { + values.push(value); + value = 0; + } + } + + value = values.shift(); + values.unshift(value % 40); + values.unshift((value / 40) >> 0); + + return values.join('.'); +}; + + +Reader.prototype._readTag = function(tag) { + assert.ok(tag !== undefined); + + var b = this.peek(); + + if (b === null) + return null; + + if (b !== tag) + throw newInvalidAsn1Error('Expected 0x' + tag.toString(16) + + ': got 0x' + b.toString(16)); + + var o = this.readLength(this._offset + 1); // stored in `length` + if (o === null) + return null; + + if (this.length > 4) + throw newInvalidAsn1Error('Integer too long: ' + this.length); + + if (this.length > this._size - o) + return null; + this._offset = o; + + var fb = this._buf[this._offset++]; + var value = 0; + + value = fb & 0x7F; + for (var i = 1; i < this.length; i++) { + value <<= 8; + value |= (this._buf[this._offset++] & 0xff); + } + + if ((fb & 0x80) == 0x80) + value = -value; + + return value; +}; + + + +///--- Exported API + +module.exports = Reader; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/types.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/types.js new file mode 100644 index 0000000..8aea000 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/types.js @@ -0,0 +1,36 @@ +// Copyright 2011 Mark Cavage All rights reserved. + + +module.exports = { + EOC: 0, + Boolean: 1, + Integer: 2, + BitString: 3, + OctetString: 4, + Null: 5, + OID: 6, + ObjectDescriptor: 7, + External: 8, + Real: 9, // float + Enumeration: 10, + PDV: 11, + Utf8String: 12, + RelativeOID: 13, + Sequence: 16, + Set: 17, + NumericString: 18, + PrintableString: 19, + T61String: 20, + VideotexString: 21, + IA5String: 22, + UTCTime: 23, + GeneralizedTime: 24, + GraphicString: 25, + VisibleString: 26, + GeneralString: 28, + UniversalString: 29, + CharacterString: 30, + BMPString: 31, + Constructor: 32, + Context: 128 +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/writer.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/writer.js new file mode 100644 index 0000000..7b445cc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/ber/writer.js @@ -0,0 +1,317 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +var assert = require('assert'); +var ASN1 = require('./types'); +var errors = require('./errors'); + + +///--- Globals + +var newInvalidAsn1Error = errors.newInvalidAsn1Error; + +var DEFAULT_OPTS = { + size: 1024, + growthFactor: 8 +}; + + +///--- Helpers + +function merge(from, to) { + assert.ok(from); + assert.equal(typeof(from), 'object'); + assert.ok(to); + assert.equal(typeof(to), 'object'); + + var keys = Object.getOwnPropertyNames(from); + keys.forEach(function(key) { + if (to[key]) + return; + + var value = Object.getOwnPropertyDescriptor(from, key); + Object.defineProperty(to, key, value); + }); + + return to; +} + + + +///--- API + +function Writer(options) { + options = merge(DEFAULT_OPTS, options || {}); + + this._buf = new Buffer(options.size || 1024); + this._size = this._buf.length; + this._offset = 0; + this._options = options; + + // A list of offsets in the buffer where we need to insert + // sequence tag/len pairs. + this._seq = []; + + var self = this; + this.__defineGetter__('buffer', function() { + if (self._seq.length) + throw new InvalidAsn1Error(self._seq.length + ' unended sequence(s)'); + + return self._buf.slice(0, self._offset); + }); +} + + +Writer.prototype.writeByte = function(b) { + if (typeof(b) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(1); + this._buf[this._offset++] = b; +}; + + +Writer.prototype.writeInt = function(i, tag) { + if (typeof(i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof(tag) !== 'number') + tag = ASN1.Integer; + + var sz = 4; + + while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) && + (sz > 1)) { + sz--; + i <<= 8; + } + + if (sz > 4) + throw new InvalidAsn1Error('BER ints cannot be > 0xffffffff'); + + this._ensure(2 + sz); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = sz; + + while (sz-- > 0) { + this._buf[this._offset++] = ((i & 0xff000000) >> 24); + i <<= 8; + } + +}; + + +Writer.prototype.writeNull = function() { + this.writeByte(ASN1.Null); + this.writeByte(0x00); +}; + + +Writer.prototype.writeEnumeration = function(i, tag) { + if (typeof(i) !== 'number') + throw new TypeError('argument must be a Number'); + if (typeof(tag) !== 'number') + tag = ASN1.Enumeration; + + return this.writeInt(i, tag); +}; + + +Writer.prototype.writeBoolean = function(b, tag) { + if (typeof(b) !== 'boolean') + throw new TypeError('argument must be a Boolean'); + if (typeof(tag) !== 'number') + tag = ASN1.Boolean; + + this._ensure(3); + this._buf[this._offset++] = tag; + this._buf[this._offset++] = 0x01; + this._buf[this._offset++] = b ? 0xff : 0x00; +}; + + +Writer.prototype.writeString = function(s, tag) { + if (typeof(s) !== 'string') + throw new TypeError('argument must be a string (was: ' + typeof(s) + ')'); + if (typeof(tag) !== 'number') + tag = ASN1.OctetString; + + var len = Buffer.byteLength(s); + this.writeByte(tag); + this.writeLength(len); + if (len) { + this._ensure(len); + this._buf.write(s, this._offset); + this._offset += len; + } +}; + + +Writer.prototype.writeBuffer = function(buf, tag) { + if (typeof(tag) !== 'number') + throw new TypeError('tag must be a number'); + if (!Buffer.isBuffer(buf)) + throw new TypeError('argument must be a buffer'); + + this.writeByte(tag); + this.writeLength(buf.length); + this._ensure(buf.length); + buf.copy(this._buf, this._offset, 0, buf.length); + this._offset += buf.length; +}; + + +Writer.prototype.writeStringArray = function(strings) { + if ((!strings instanceof Array)) + throw new TypeError('argument must be an Array[String]'); + + var self = this; + strings.forEach(function(s) { + self.writeString(s); + }); +}; + +// This is really to solve DER cases, but whatever for now +Writer.prototype.writeOID = function(s, tag) { + if (typeof(s) !== 'string') + throw new TypeError('argument must be a string'); + if (typeof(tag) !== 'number') + tag = ASN1.OID; + + if (!/^([0-9]+\.){3,}[0-9]+$/.test(s)) + throw new Error('argument is not a valid OID string'); + + function encodeOctet(bytes, octet) { + if (octet < 128) { + bytes.push(octet); + } else if (octet < 16384) { + bytes.push((octet >>> 7) | 0x80); + bytes.push(octet & 0x7F); + } else if (octet < 2097152) { + bytes.push((octet >>> 14) | 0x80); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else if (octet < 268435456) { + bytes.push((octet >>> 21) | 0x80); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } else { + bytes.push(((octet >>> 28) | 0x80) & 0xFF); + bytes.push(((octet >>> 21) | 0x80) & 0xFF); + bytes.push(((octet >>> 14) | 0x80) & 0xFF); + bytes.push(((octet >>> 7) | 0x80) & 0xFF); + bytes.push(octet & 0x7F); + } + } + + var tmp = s.split('.'); + var bytes = []; + bytes.push(parseInt(tmp[0], 10) * 40 + parseInt(tmp[1], 10)); + tmp.slice(2).forEach(function(b) { + encodeOctet(bytes, parseInt(b, 10)); + }); + + var self = this; + this._ensure(2 + bytes.length); + this.writeByte(tag); + this.writeLength(bytes.length); + bytes.forEach(function(b) { + self.writeByte(b); + }); +}; + + +Writer.prototype.writeLength = function(len) { + if (typeof(len) !== 'number') + throw new TypeError('argument must be a Number'); + + this._ensure(4); + + if (len <= 0x7f) { + this._buf[this._offset++] = len; + } else if (len <= 0xff) { + this._buf[this._offset++] = 0x81; + this._buf[this._offset++] = len; + } else if (len <= 0xffff) { + this._buf[this._offset++] = 0x82; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else if (len <= 0xffffff) { + this._shift(start, len, 1); + this._buf[this._offset++] = 0x83; + this._buf[this._offset++] = len >> 16; + this._buf[this._offset++] = len >> 8; + this._buf[this._offset++] = len; + } else { + throw new InvalidAsn1ERror('Length too long (> 4 bytes)'); + } +}; + +Writer.prototype.startSequence = function(tag) { + if (typeof(tag) !== 'number') + tag = ASN1.Sequence | ASN1.Constructor; + + this.writeByte(tag); + this._seq.push(this._offset); + this._ensure(3); + this._offset += 3; +}; + + +Writer.prototype.endSequence = function() { + var seq = this._seq.pop(); + var start = seq + 3; + var len = this._offset - start; + + if (len <= 0x7f) { + this._shift(start, len, -2); + this._buf[seq] = len; + } else if (len <= 0xff) { + this._shift(start, len, -1); + this._buf[seq] = 0x81; + this._buf[seq + 1] = len; + } else if (len <= 0xffff) { + this._buf[seq] = 0x82; + this._buf[seq + 1] = len >> 8; + this._buf[seq + 2] = len; + } else if (len <= 0xffffff) { + this._shift(start, len, 1); + this._buf[seq] = 0x83; + this._buf[seq + 1] = len >> 16; + this._buf[seq + 2] = len >> 8; + this._buf[seq + 3] = len; + } else { + throw new InvalidAsn1Error('Sequence too long'); + } +}; + + +Writer.prototype._shift = function(start, len, shift) { + assert.ok(start !== undefined); + assert.ok(len !== undefined); + assert.ok(shift); + + this._buf.copy(this._buf, start + shift, start, start + len); + this._offset += shift; +}; + +Writer.prototype._ensure = function(len) { + assert.ok(len); + + if (this._size - this._offset < len) { + var sz = this._size * this._options.growthFactor; + if (sz - this._offset < len) + sz += len; + + var buf = new Buffer(sz); + + this._buf.copy(buf, 0, 0, this._offset); + this._buf = buf; + this._size = sz; + } +}; + + + +///--- Exported API + +module.exports = Writer; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/index.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/index.js new file mode 100644 index 0000000..d1766e7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/lib/index.js @@ -0,0 +1,20 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +// If you have no idea what ASN.1 or BER is, see this: +// ftp://ftp.rsa.com/pub/pkcs/ascii/layman.asc + +var Ber = require('./ber/index'); + + + +///--- Exported API + +module.exports = { + + Ber: Ber, + + BerReader: Ber.Reader, + + BerWriter: Ber.Writer + +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/package.json new file mode 100644 index 0000000..2735bda --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/package.json @@ -0,0 +1,26 @@ +{ + "author": "Mark Cavage ", + "contributors": [ + "David Gwynne ", + "Yunong Xiao " + ], + "name": "asn1", + "description": "Contains parsers and serializers for ASN.1 (currently BER only)", + "version": "0.1.11", + "repository": { + "type": "git", + "url": "git://github.com/mcavage/node-asn1.git" + }, + "main": "lib/index.js", + "engines": { + "node": ">=0.4.9" + }, + "dependencies": {}, + "devDependencies": { + "tap": "0.1.4" + }, + "scripts": { + "pretest": "which gjslint; if [[ \"$?\" = 0 ]] ; then gjslint --nojsdoc -r lib -r tst; else echo \"Missing gjslint. Skipping lint\"; fi", + "test": "./node_modules/.bin/tap ./tst" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/tst/ber/reader.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/tst/ber/reader.test.js new file mode 100644 index 0000000..0b78b47 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/tst/ber/reader.test.js @@ -0,0 +1,172 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +var test = require('tap').test; + + + +///--- Globals + +var BerReader; + + + +///--- Tests + +test('load library', function(t) { + BerReader = require('../../lib/index').BerReader; + t.ok(BerReader); + try { + new BerReader(); + t.fail('Should have thrown'); + } catch (e) { + t.ok(e instanceof TypeError, 'Should have been a type error'); + } + t.end(); +}); + + +test('read byte', function(t) { + var reader = new BerReader(new Buffer([0xde])); + t.ok(reader); + t.equal(reader.readByte(), 0xde, 'wrong value'); + t.end(); +}); + + +test('read 1 byte int', function(t) { + var reader = new BerReader(new Buffer([0x02, 0x01, 0x03])); + t.ok(reader); + t.equal(reader.readInt(), 0x03, 'wrong value'); + t.equal(reader.length, 0x01, 'wrong length'); + t.end(); +}); + + +test('read 2 byte int', function(t) { + var reader = new BerReader(new Buffer([0x02, 0x02, 0x7e, 0xde])); + t.ok(reader); + t.equal(reader.readInt(), 0x7ede, 'wrong value'); + t.equal(reader.length, 0x02, 'wrong length'); + t.end(); +}); + + +test('read 3 byte int', function(t) { + var reader = new BerReader(new Buffer([0x02, 0x03, 0x7e, 0xde, 0x03])); + t.ok(reader); + t.equal(reader.readInt(), 0x7ede03, 'wrong value'); + t.equal(reader.length, 0x03, 'wrong length'); + t.end(); +}); + + +test('read 4 byte int', function(t) { + var reader = new BerReader(new Buffer([0x02, 0x04, 0x7e, 0xde, 0x03, 0x01])); + t.ok(reader); + t.equal(reader.readInt(), 0x7ede0301, 'wrong value'); + t.equal(reader.length, 0x04, 'wrong length'); + t.end(); +}); + + +test('read boolean true', function(t) { + var reader = new BerReader(new Buffer([0x01, 0x01, 0xff])); + t.ok(reader); + t.equal(reader.readBoolean(), true, 'wrong value'); + t.equal(reader.length, 0x01, 'wrong length'); + t.end(); +}); + + +test('read boolean false', function(t) { + var reader = new BerReader(new Buffer([0x01, 0x01, 0x00])); + t.ok(reader); + t.equal(reader.readBoolean(), false, 'wrong value'); + t.equal(reader.length, 0x01, 'wrong length'); + t.end(); +}); + + +test('read enumeration', function(t) { + var reader = new BerReader(new Buffer([0x0a, 0x01, 0x20])); + t.ok(reader); + t.equal(reader.readEnumeration(), 0x20, 'wrong value'); + t.equal(reader.length, 0x01, 'wrong length'); + t.end(); +}); + + +test('read string', function(t) { + var dn = 'cn=foo,ou=unit,o=test'; + var buf = new Buffer(dn.length + 2); + buf[0] = 0x04; + buf[1] = Buffer.byteLength(dn); + buf.write(dn, 2); + var reader = new BerReader(buf); + t.ok(reader); + t.equal(reader.readString(), dn, 'wrong value'); + t.equal(reader.length, dn.length, 'wrong length'); + t.end(); +}); + + +test('read sequence', function(t) { + var reader = new BerReader(new Buffer([0x30, 0x03, 0x01, 0x01, 0xff])); + t.ok(reader); + t.equal(reader.readSequence(), 0x30, 'wrong value'); + t.equal(reader.length, 0x03, 'wrong length'); + t.equal(reader.readBoolean(), true, 'wrong value'); + t.equal(reader.length, 0x01, 'wrong length'); + t.end(); +}); + + +test('anonymous LDAPv3 bind', function(t) { + var BIND = new Buffer(14); + BIND[0] = 0x30; // Sequence + BIND[1] = 12; // len + BIND[2] = 0x02; // ASN.1 Integer + BIND[3] = 1; // len + BIND[4] = 0x04; // msgid (make up 4) + BIND[5] = 0x60; // Bind Request + BIND[6] = 7; // len + BIND[7] = 0x02; // ASN.1 Integer + BIND[8] = 1; // len + BIND[9] = 0x03; // v3 + BIND[10] = 0x04; // String (bind dn) + BIND[11] = 0; // len + BIND[12] = 0x80; // ContextSpecific (choice) + BIND[13] = 0; // simple bind + + // Start testing ^^ + var ber = new BerReader(BIND); + t.equal(ber.readSequence(), 48, 'Not an ASN.1 Sequence'); + t.equal(ber.length, 12, 'Message length should be 12'); + t.equal(ber.readInt(), 4, 'Message id should have been 4'); + t.equal(ber.readSequence(), 96, 'Bind Request should have been 96'); + t.equal(ber.length, 7, 'Bind length should have been 7'); + t.equal(ber.readInt(), 3, 'LDAP version should have been 3'); + t.equal(ber.readString(), '', 'Bind DN should have been empty'); + t.equal(ber.length, 0, 'string length should have been 0'); + t.equal(ber.readByte(), 0x80, 'Should have been ContextSpecific (choice)'); + t.equal(ber.readByte(), 0, 'Should have been simple bind'); + t.equal(null, ber.readByte(), 'Should be out of data'); + t.end(); +}); + + +test('long string', function(t) { + var buf = new Buffer(256); + var o; + var s = + '2;649;CN=Red Hat CS 71GA Demo,O=Red Hat CS 71GA Demo,C=US;' + + 'CN=RHCS Agent - admin01,UID=admin01,O=redhat,C=US [1] This is ' + + 'Teena Vradmin\'s description.'; + buf[0] = 0x04; + buf[1] = 0x81; + buf[2] = 0x94; + buf.write(s, 3); + var ber = new BerReader(buf.slice(0, 3 + s.length)); + t.equal(ber.readString(), s); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/tst/ber/writer.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/tst/ber/writer.test.js new file mode 100644 index 0000000..add0b9f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/asn1/tst/ber/writer.test.js @@ -0,0 +1,296 @@ +// Copyright 2011 Mark Cavage All rights reserved. + +var test = require('tap').test; +var sys = require('sys'); + +///--- Globals + +var BerWriter; + +var BerReader; + + +///--- Tests + +test('load library', function(t) { + BerWriter = require('../../lib/index').BerWriter; + t.ok(BerWriter); + t.ok(new BerWriter()); + t.end(); +}); + + +test('write byte', function(t) { + var writer = new BerWriter(); + + writer.writeByte(0xC2); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 1, 'Wrong length'); + t.equal(ber[0], 0xC2, 'value wrong'); + + t.end(); +}); + + +test('write 1 byte int', function(t) { + var writer = new BerWriter(); + + writer.writeInt(0x7f); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 3, 'Wrong length for an int: ' + ber.length); + t.equal(ber[0], 0x02, 'ASN.1 tag wrong (2) -> ' + ber[0]); + t.equal(ber[1], 0x01, 'length wrong(1) -> ' + ber[1]); + t.equal(ber[2], 0x7f, 'value wrong(3) -> ' + ber[2]); + + t.end(); +}); + + +test('write 2 byte int', function(t) { + var writer = new BerWriter(); + + writer.writeInt(0x7ffe); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 4, 'Wrong length for an int'); + t.equal(ber[0], 0x02, 'ASN.1 tag wrong'); + t.equal(ber[1], 0x02, 'length wrong'); + t.equal(ber[2], 0x7f, 'value wrong (byte 1)'); + t.equal(ber[3], 0xfe, 'value wrong (byte 2)'); + + t.end(); +}); + + +test('write 3 byte int', function(t) { + var writer = new BerWriter(); + + writer.writeInt(0x7ffffe); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 5, 'Wrong length for an int'); + t.equal(ber[0], 0x02, 'ASN.1 tag wrong'); + t.equal(ber[1], 0x03, 'length wrong'); + t.equal(ber[2], 0x7f, 'value wrong (byte 1)'); + t.equal(ber[3], 0xff, 'value wrong (byte 2)'); + t.equal(ber[4], 0xfe, 'value wrong (byte 3)'); + + t.end(); +}); + + +test('write 4 byte int', function(t) { + var writer = new BerWriter(); + + writer.writeInt(0x7ffffffe); + var ber = writer.buffer; + + t.ok(ber); + + t.equal(ber.length, 6, 'Wrong length for an int'); + t.equal(ber[0], 0x02, 'ASN.1 tag wrong'); + t.equal(ber[1], 0x04, 'length wrong'); + t.equal(ber[2], 0x7f, 'value wrong (byte 1)'); + t.equal(ber[3], 0xff, 'value wrong (byte 2)'); + t.equal(ber[4], 0xff, 'value wrong (byte 3)'); + t.equal(ber[5], 0xfe, 'value wrong (byte 4)'); + + t.end(); +}); + + +test('write boolean', function(t) { + var writer = new BerWriter(); + + writer.writeBoolean(true); + writer.writeBoolean(false); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 6, 'Wrong length'); + t.equal(ber[0], 0x01, 'tag wrong'); + t.equal(ber[1], 0x01, 'length wrong'); + t.equal(ber[2], 0xff, 'value wrong'); + t.equal(ber[3], 0x01, 'tag wrong'); + t.equal(ber[4], 0x01, 'length wrong'); + t.equal(ber[5], 0x00, 'value wrong'); + + t.end(); +}); + + +test('write string', function(t) { + var writer = new BerWriter(); + writer.writeString('hello world'); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 13, 'wrong length'); + t.equal(ber[0], 0x04, 'wrong tag'); + t.equal(ber[1], 11, 'wrong length'); + t.equal(ber.slice(2).toString('utf8'), 'hello world', 'wrong value'); + + t.end(); +}); + +test('write buffer', function(t) { + var writer = new BerWriter(); + // write some stuff to start with + writer.writeString('hello world'); + var ber = writer.buffer; + var buf = new Buffer([0x04, 0x0b, 0x30, 0x09, 0x02, 0x01, 0x0f, 0x01, 0x01, + 0xff, 0x01, 0x01, 0xff]); + writer.writeBuffer(buf.slice(2, buf.length), 0x04); + ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 26, 'wrong length'); + t.equal(ber[0], 0x04, 'wrong tag'); + t.equal(ber[1], 11, 'wrong length'); + t.equal(ber.slice(2, 13).toString('utf8'), 'hello world', 'wrong value'); + t.equal(ber[13], buf[0], 'wrong tag'); + t.equal(ber[14], buf[1], 'wrong length'); + for (var i = 13, j = 0; i < ber.length && j < buf.length; i++, j++) { + t.equal(ber[i], buf[j], 'buffer contents not identical'); + } + t.end(); +}); + +test('write string array', function(t) { + var writer = new BerWriter(); + writer.writeStringArray(['hello world', 'fubar!']); + var ber = writer.buffer; + + t.ok(ber); + + t.equal(ber.length, 21, 'wrong length'); + t.equal(ber[0], 0x04, 'wrong tag'); + t.equal(ber[1], 11, 'wrong length'); + t.equal(ber.slice(2, 13).toString('utf8'), 'hello world', 'wrong value'); + + t.equal(ber[13], 0x04, 'wrong tag'); + t.equal(ber[14], 6, 'wrong length'); + t.equal(ber.slice(15).toString('utf8'), 'fubar!', 'wrong value'); + + t.end(); +}); + + +test('resize internal buffer', function(t) { + var writer = new BerWriter({size: 2}); + writer.writeString('hello world'); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 13, 'wrong length'); + t.equal(ber[0], 0x04, 'wrong tag'); + t.equal(ber[1], 11, 'wrong length'); + t.equal(ber.slice(2).toString('utf8'), 'hello world', 'wrong value'); + + t.end(); +}); + + +test('sequence', function(t) { + var writer = new BerWriter({size: 25}); + writer.startSequence(); + writer.writeString('hello world'); + writer.endSequence(); + var ber = writer.buffer; + + t.ok(ber); + console.log(ber); + t.equal(ber.length, 15, 'wrong length'); + t.equal(ber[0], 0x30, 'wrong tag'); + t.equal(ber[1], 13, 'wrong length'); + t.equal(ber[2], 0x04, 'wrong tag'); + t.equal(ber[3], 11, 'wrong length'); + t.equal(ber.slice(4).toString('utf8'), 'hello world', 'wrong value'); + + t.end(); +}); + + +test('nested sequence', function(t) { + var writer = new BerWriter({size: 25}); + writer.startSequence(); + writer.writeString('hello world'); + writer.startSequence(); + writer.writeString('hello world'); + writer.endSequence(); + writer.endSequence(); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 30, 'wrong length'); + t.equal(ber[0], 0x30, 'wrong tag'); + t.equal(ber[1], 28, 'wrong length'); + t.equal(ber[2], 0x04, 'wrong tag'); + t.equal(ber[3], 11, 'wrong length'); + t.equal(ber.slice(4, 15).toString('utf8'), 'hello world', 'wrong value'); + t.equal(ber[15], 0x30, 'wrong tag'); + t.equal(ber[16], 13, 'wrong length'); + t.equal(ber[17], 0x04, 'wrong tag'); + t.equal(ber[18], 11, 'wrong length'); + t.equal(ber.slice(19, 30).toString('utf8'), 'hello world', 'wrong value'); + + t.end(); +}); + + +test('LDAP bind message', function(t) { + var dn = 'cn=foo,ou=unit,o=test'; + var writer = new BerWriter(); + writer.startSequence(); + writer.writeInt(3); // msgid = 3 + writer.startSequence(0x60); // ldap bind + writer.writeInt(3); // ldap v3 + writer.writeString(dn); + writer.writeByte(0x80); + writer.writeByte(0x00); + writer.endSequence(); + writer.endSequence(); + var ber = writer.buffer; + + t.ok(ber); + t.equal(ber.length, 35, 'wrong length (buffer)'); + t.equal(ber[0], 0x30, 'wrong tag'); + t.equal(ber[1], 33, 'wrong length'); + t.equal(ber[2], 0x02, 'wrong tag'); + t.equal(ber[3], 1, 'wrong length'); + t.equal(ber[4], 0x03, 'wrong value'); + t.equal(ber[5], 0x60, 'wrong tag'); + t.equal(ber[6], 28, 'wrong length'); + t.equal(ber[7], 0x02, 'wrong tag'); + t.equal(ber[8], 1, 'wrong length'); + t.equal(ber[9], 0x03, 'wrong value'); + t.equal(ber[10], 0x04, 'wrong tag'); + t.equal(ber[11], dn.length, 'wrong length'); + t.equal(ber.slice(12, 33).toString('utf8'), dn, 'wrong value'); + t.equal(ber[33], 0x80, 'wrong tag'); + t.equal(ber[34], 0x00, 'wrong len'); + + t.end(); +}); + + +test('Write OID', function(t) { + var oid = '1.2.840.113549.1.1.1'; + var writer = new BerWriter(); + writer.writeOID(oid); + + var ber = writer.buffer; + t.ok(ber); + console.log(require('util').inspect(ber)); + console.log(require('util').inspect(new Buffer([0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01]))); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/README.md new file mode 100644 index 0000000..c0c3a53 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/README.md @@ -0,0 +1,126 @@ +# node-assert-plus + +This library is a super small wrapper over node's assert module that has two +things: (1) the ability to disable assertions with the environment variable +NODE_NDEBUG, and (2) some API wrappers for argument testing. Like +`assert.string(myArg, 'myArg')`. As a simple example, most of my code looks +like this: + + var assert = require('assert-plus'); + + function fooAccount(options, callback) { + assert.object(options, 'options'); + assert.number(options.id, 'options.id); + assert.bool(options.isManager, 'options.isManager'); + assert.string(options.name, 'options.name'); + assert.arrayOfString(options.email, 'options.email'); + assert.func(callback, 'callback'); + + // Do stuff + callback(null, {}); + } + +# API + +All methods that *aren't* part of node's core assert API are simply assumed to +take an argument, and then a string 'name' that's not a message; `AssertionError` +will be thrown if the assertion fails with a message like: + + AssertionError: foo (string) is required + at test (/home/mark/work/foo/foo.js:3:9) + at Object. (/home/mark/work/foo/foo.js:15:1) + at Module._compile (module.js:446:26) + at Object..js (module.js:464:10) + at Module.load (module.js:353:31) + at Function._load (module.js:311:12) + at Array.0 (module.js:484:10) + at EventEmitter._tickCallback (node.js:190:38) + +from: + + function test(foo) { + assert.string(foo, 'foo'); + } + +There you go. You can check that arrays are of a homogenous type with `Arrayof$Type`: + + function test(foo) { + assert.arrayOfString(foo, 'foo'); + } + +You can assert IFF an argument is not `undefined` (i.e., an optional arg): + + assert.optionalString(foo, 'foo'); + +Lastly, you can opt-out of assertion checking altogether by setting the +environment variable `NODE_NDEBUG=1`. This is pseudo-useful if you have +lots of assertions, and don't want to pay `typeof ()` taxes to v8 in +production. + +The complete list of APIs is: + +* assert.bool +* assert.buffer +* assert.func +* assert.number +* assert.object +* assert.string +* assert.arrayOfBool +* assert.arrayOfFunc +* assert.arrayOfNumber +* assert.arrayOfObject +* assert.arrayOfString +* assert.optionalBool +* assert.optionalBuffer +* assert.optionalFunc +* assert.optionalNumber +* assert.optionalObject +* assert.optionalString +* assert.optionalArrayOfBool +* assert.optionalArrayOfFunc +* assert.optionalArrayOfNumber +* assert.optionalArrayOfObject +* assert.optionalArrayOfString +* assert.AssertionError +* assert.fail +* assert.ok +* assert.equal +* assert.notEqual +* assert.deepEqual +* assert.notDeepEqual +* assert.strictEqual +* assert.notStrictEqual +* assert.throws +* assert.doesNotThrow +* assert.ifError + +# Installation + + npm install assert-plus + +## License + +The MIT License (MIT) +Copyright (c) 2012 Mark Cavage + +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. + +## Bugs + +See . diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/assert.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/assert.js new file mode 100644 index 0000000..70583f1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/assert.js @@ -0,0 +1,196 @@ +// Copyright (c) 2012, Mark Cavage. All rights reserved. + +var assert = require('assert'); +var Stream = require('stream').Stream; +var util = require('util'); + + + +///--- Globals + +var NDEBUG = process.env.NODE_NDEBUG || false; + + + +///--- Messages + +var ARRAY_TYPE_REQUIRED = '%s ([%s]) required'; +var TYPE_REQUIRED = '%s (%s) is required'; + + + +///--- Internal + +function capitalize(str) { + return (str.charAt(0).toUpperCase() + str.slice(1)); +} + +function uncapitalize(str) { + return (str.charAt(0).toLowerCase() + str.slice(1)); +} + +function _() { + return (util.format.apply(util, arguments)); +} + + +function _assert(arg, type, name, stackFunc) { + if (!NDEBUG) { + name = name || type; + stackFunc = stackFunc || _assert.caller; + var t = typeof (arg); + + if (t !== type) { + throw new assert.AssertionError({ + message: _(TYPE_REQUIRED, name, type), + actual: t, + expected: type, + operator: '===', + stackStartFunction: stackFunc + }); + } + } +} + + + +///--- API + +function array(arr, type, name) { + if (!NDEBUG) { + name = name || type; + + if (!Array.isArray(arr)) { + throw new assert.AssertionError({ + message: _(ARRAY_TYPE_REQUIRED, name, type), + actual: typeof (arr), + expected: 'array', + operator: 'Array.isArray', + stackStartFunction: array.caller + }); + } + + for (var i = 0; i < arr.length; i++) { + _assert(arr[i], type, name, array); + } + } +} + + +function bool(arg, name) { + _assert(arg, 'boolean', name, bool); +} + + +function buffer(arg, name) { + if (!Buffer.isBuffer(arg)) { + throw new assert.AssertionError({ + message: _(TYPE_REQUIRED, name, type), + actual: typeof (arg), + expected: 'buffer', + operator: 'Buffer.isBuffer', + stackStartFunction: buffer + }); + } +} + + +function func(arg, name) { + _assert(arg, 'function', name); +} + + +function number(arg, name) { + _assert(arg, 'number', name); +} + + +function object(arg, name) { + _assert(arg, 'object', name); +} + + +function stream(arg, name) { + if (!(arg instanceof Stream)) { + throw new assert.AssertionError({ + message: _(TYPE_REQUIRED, name, type), + actual: typeof (arg), + expected: 'Stream', + operator: 'instanceof', + stackStartFunction: buffer + }); + } +} + + +function string(arg, name) { + _assert(arg, 'string', name); +} + + + +///--- Exports + +module.exports = { + bool: bool, + buffer: buffer, + func: func, + number: number, + object: object, + stream: stream, + string: string +}; + + +Object.keys(module.exports).forEach(function (k) { + if (k === 'buffer') + return; + + var name = 'arrayOf' + capitalize(k); + + if (k === 'bool') + k = 'boolean'; + if (k === 'func') + k = 'function'; + module.exports[name] = function (arg, name) { + array(arg, k, name); + }; +}); + +Object.keys(module.exports).forEach(function (k) { + var _name = 'optional' + capitalize(k); + var s = uncapitalize(k.replace('arrayOf', '')); + if (s === 'bool') + s = 'boolean'; + if (s === 'func') + s = 'function'; + + if (k.indexOf('arrayOf') !== -1) { + module.exports[_name] = function (arg, name) { + if (!NDEBUG && arg !== undefined) { + array(arg, s, name); + } + }; + } else { + module.exports[_name] = function (arg, name) { + if (!NDEBUG && arg !== undefined) { + _assert(arg, s, name); + } + }; + } +}); + + +// Reexport built-in assertions +Object.keys(assert).forEach(function (k) { + if (k === 'AssertionError') { + module.exports[k] = assert[k]; + return; + } + + module.exports[k] = function () { + if (!NDEBUG) { + assert[k].apply(assert[k], arguments); + } + }; +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/package.json new file mode 100644 index 0000000..0e3e480 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/assert-plus/package.json @@ -0,0 +1,13 @@ +{ + "author": "Mark Cavage ", + "name": "assert-plus", + "description": "Extra assertions on top of node's assert module", + "version": "0.1.2", + "main": "./assert.js", + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "engines": { + "node": ">=0.6" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.lock-wscript b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.lock-wscript new file mode 100644 index 0000000..5e8ff69 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.lock-wscript @@ -0,0 +1,9 @@ +argv = ['/usr/bin/node-waf', 'configure', 'build'] +blddir = '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/build' +commands = {'dist': 0, 'configure': True, 'distcheck': 0, 'install': 0, 'build': True, 'clean': 0, 'distclean': 0, 'check': 0, 'uninstall': 0} +cwd = '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools' +environ = {'npm_config_color': 'true', 'npm_config_searchopts': '', 'npm_config_group': '10020', 'npm_package_homepage': 'https://github.com/bnoordhuis/node-buffertools', 'npm_config_browser': 'google-chrome', 'npm_config_global': '', 'npm_package_author_url': 'http://bnoordhuis.nl/', 'SHELL': '/bin/bash', 'XDG_DATA_DIRS': '/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/', 'MANDATORY_PATH': '/usr/share/gconf/ubuntu.mandatory.path', 'QT_ACCESSIBILITY': '1', 'npm_config_pre': '', 'npm_config_cache': '/home/zoec/.npm', 'npm_config_logfd': '2', 'npm_config_tmp': '/tmp', 'npm_package_engines_node': '>=0.3.0', 'npm_config_argv': '{"remain":[],"cooked":["rebuild"],"original":["rebuild"]}', 'npm_package_scripts_preinstall': 'node-waf clean || (exit 0); node-waf configure build', 'npm_config_init_version': '0.0.0', 'DBUS_SESSION_BUS_ADDRESS': 'unix:abstract=/tmp/dbus-j5SQDeRAVn,guid=9c4d498d1ab4055e379af66900000051', 'GNOME_KEYRING_PID': '1931', 'npm_lifecycle_event': 'preinstall', 'DESKTOP_SESSION': 'ubuntu', 'npm_config_init_author_name': '', 'npm_config_yes': '', 'npm_config_usage': '', 'npm_package_description': 'Working with node.js buffers made easy.', 'npm_config_logprefix': 'true', 'npm_config_ignore': '', 'npm_config_ca': '"-----BEGIN CERTIFICATE-----\\nMIIChzCCAfACCQDauvz/KHp8ejANBgkqhkiG9w0BAQUFADCBhzELMAkGA1UEBhMC\\nVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMQwwCgYDVQQKEwNucG0x\\nIjAgBgNVBAsTGW5wbSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxDjAMBgNVBAMTBW5w\\nbUNBMRcwFQYJKoZIhvcNAQkBFghpQGl6cy5tZTAeFw0xMTA5MDUwMTQ3MTdaFw0y\\nMTA5MDIwMTQ3MTdaMIGHMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNV\\nBAcTB09ha2xhbmQxDDAKBgNVBAoTA25wbTEiMCAGA1UECxMZbnBtIENlcnRpZmlj\\nYXRlIEF1dGhvcml0eTEOMAwGA1UEAxMFbnBtQ0ExFzAVBgkqhkiG9w0BCQEWCGlA\\naXpzLm1lMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLI4tIqPpRW+ACw9GE\\nOgBlJZwK5f8nnKCLK629Pv5yJpQKs3DENExAyOgDcyaF0HD0zk8zTp+ZsLaNdKOz\\nGn2U181KGprGKAXP6DU6ByOJDWmTlY6+Ad1laYT0m64fERSpHw/hjD3D+iX4aMOl\\ny0HdbT5m1ZGh6SJz3ZqxavhHLQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAC4ySDbC\\nl7W1WpLmtLGEQ/yuMLUf6Jy/vr+CRp4h+UzL+IQpCv8FfxsYE7dhf/bmWTEupBkv\\nyNL18lipt2jSvR3v6oAHAReotvdjqhxddpe5Holns6EQd1/xEZ7sB1YhQKJtvUrl\\nZNufy1Jf1r0ldEGeA+0ISck7s+xSh9rQD2Op\\n-----END CERTIFICATE-----"', 'npm_config_globalconfig': '/etc/npmrc', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'npm_package_contributors_2_email': 'justmoon@members.fsf.org', 'npm_config_parseable': '', 'npm_package_contributors_1_email': 'nathan@tootallnate.net', 'XDG_CURRENT_DESKTOP': 'Unity', 'npm_config_userignorefile': '/home/zoec/.npmignore', 'USER': 'zoec', 'npm_package_author_name': 'Ben Noordhuis', 'npm_config_versions': '', 'XAUTHORITY': '/home/zoec/.Xauthority', 'LANGUAGE': 'en_GB:en', 'SESSION_MANAGER': 'local/axolotl:@/tmp/.ICE-unix/1942,unix/axolotl:/tmp/.ICE-unix/1942', 'SHLVL': '1', 'npm_config_init_author_url': '', 'LD_LIBRARY_PATH': '/opt/instantclient', 'COMPIZ_CONFIG_PROFILE': 'ubuntu', 'WINDOWID': '77616815', 'GPG_AGENT_INFO': '/tmp/keyring-NPVAVZ/gpg:0:1', 'npm_config_proxy': '', 'npm_config_depth': 'null', 'npm_config_shell': '/bin/bash', 'npm_config_umask': '022', 'GDMSESSION': 'ubuntu', 'npm_config_long': '', 'npm_package_contributors_0_name': 'Ben Noordhuis', 'npm_config_editor': 'vi', 'XDG_SEAT_PATH': '/org/freedesktop/DisplayManager/Seat0', '_': '/usr/bin/npm', 'npm_package_repository_type': 'git', 'npm_package_contributors_1_name': 'Nathan Rajlich', 'npm_config_npat': '', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/etc/xdg', 'UBUNTU_MENUPROXY': 'libappmenu.so', 'npm_config_json': '', 'npm_config_searchsort': 'name', 'npm_config_user': '', 'npm_lifecycle_script': 'node-waf clean || (exit 0); node-waf configure build', 'COLORTERM': 'gnome-terminal', 'npm_config_git': 'git', 'npm_config_rollback': 'true', 'npm_package_repository_url': 'git://github.com/bnoordhuis/node-buffertools.git', 'HOME': '/home/zoec', 'DISPLAY': ':0.0', 'LANG': 'en_GB.UTF-8', 'npm_config_save': '', 'npm_config_registry': 'https://registry.npmjs.org/', 'npm_config_unicode': 'true', 'npm_config_production': '', 'npm_config_message': '%s', 'npm_config_always_auth': '', 'npm_config_prefix': '/usr/local', 'npm_config_searchexclude': '', 'npm_config_loglevel': 'http', 'npm_config_strict_ssl': 'true', 'npm_package_keywords_0': 'buffer', 'npm_package_keywords_1': 'buffers', 'npm_package_contributors_2_name': 'Stefan Thomas', 'npm_package_main': 'buffertools', 'npm_config_tag': 'latest', 'DEFAULTS_PATH': '/usr/share/gconf/ubuntu.default.path', 'npm_config_globalignorefile': '/etc/npmignore', 'npm_config_version': '', 'npm_package_contributors_0_email': 'info@bnoordhuis.nl', 'npm_config_force': '', 'npm_config_username': '', 'npm_package_author_email': 'info@bnoordhuis.nl', 'npm_config_link': '', 'npm_package_name': 'buffertools', 'npm_config_userconfig': '/home/zoec/.npmrc', 'npm_config_dev': '', 'npm_config_rebuild_bundle': 'true', 'npm_config_npaturl': 'http://npat.npmjs.org/', 'LOGNAME': 'zoec', 'PATH': '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/node_modules/.bin:/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/.bin:/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/.bin:/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/.bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games', 'GNOME_KEYRING_CONTROL': '/tmp/keyring-NPVAVZ', 'npm_config_coverage': '', 'SSH_AGENT_PID': '1977', 'TERM': 'xterm', 'XDG_SESSION_PATH': '/org/freedesktop/DisplayManager/Session0', 'XDG_SESSION_COOKIE': '3e087ba740f35de0990666e900000009-1361534488.747126-1765586974', 'npm_config_onload_script': '', 'npm_config_description': 'true', 'npm_config_bindist': '0.6-ares1.7.5-evundefined-openssl1.0.1-v83.7.12.22-linux-ia32-3.2.0-38-generic-pae', 'npm_config___DO_NOT_MODIFY_THIS_FILE___use__etc_npmrc_instead_': 'true', 'npm_config_viewer': 'man', 'npm_config_showlevel': '2', 'npm_config_unsafe_perm': 'true', 'SSH_AUTH_SOCK': '/tmp/keyring-NPVAVZ/ssh', 'npm_config_proprietary_attribs': 'true', 'npm_package_version': '1.1.0', 'npm_config_https_proxy': '', 'npm_config_node_version': '0.6.12', 'npm_config_init_author_email': '', 'OLDPWD': '/home/zoec/Projects/XenClient/sync-wui/build', 'npm_config_outfd': '1', 'npm_config_bin_publish': '', 'NODE_PATH': '/usr/lib/nodejs:/usr/share/javascript', 'PWD': '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools'} +files = [] +hash = 0 +options = {'compile_targets': None, 'force': False, 'verbose': 0, 'nocache': False, 'progress_bar': 0, 'destdir': '', 'keep': False, 'zones': '', 'blddir': '', 'prefix': '/usr/local/', 'jobs': 4, 'srcdir': '', 'check_cxx_compiler': 'g++ icpc sunc++'} +srcdir = '/home/zoec/Projects/XenClient/sync-wui/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools' diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.mailmap b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.mailmap new file mode 100644 index 0000000..95c708b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.mailmap @@ -0,0 +1,2 @@ +# update AUTHORS with: +# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.npmignore new file mode 100644 index 0000000..057457f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/.npmignore @@ -0,0 +1 @@ +buffertools.node diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/AUTHORS b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/AUTHORS new file mode 100644 index 0000000..c9d45f8 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/AUTHORS @@ -0,0 +1,4 @@ +# Authors ordered by first contribution. +Ben Noordhuis +Nathan Rajlich +Stefan Thomas diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/BoyerMoore.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/BoyerMoore.h new file mode 100644 index 0000000..830f4eb --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/BoyerMoore.h @@ -0,0 +1,127 @@ +/* adapted from http://en.wikipedia.org/wiki/Boyer–Moore_string_search_algorithm */ +#ifndef BOYER_MOORE_H +#define BOYER_MOORE_H + +#include +#include +#include + +#define ALPHABET_SIZE (1 << CHAR_BIT) + +static void compute_prefix(const uint8_t* str, size_t size, int result[]) { + size_t q; + int k; + result[0] = 0; + + k = 0; + for (q = 1; q < size; q++) { + while (k > 0 && str[k] != str[q]) + k = result[k-1]; + + if (str[k] == str[q]) + k++; + + result[q] = k; + } +} + +static void prepare_badcharacter_heuristic(const uint8_t *str, size_t size, int result[ALPHABET_SIZE]) { + size_t i; + + for (i = 0; i < ALPHABET_SIZE; i++) + result[i] = -1; + + for (i = 0; i < size; i++) + result[str[i]] = i; +} + +void prepare_goodsuffix_heuristic(const uint8_t *normal, const size_t size, int result[]) { + const uint8_t *left = normal; + const uint8_t *right = left + size; + uint8_t * reversed = new uint8_t[size+1]; + uint8_t *tmp = reversed + size; + size_t i; + + /* reverse string */ + *tmp = 0; + while (left < right) + *(--tmp) = *(left++); + + int * prefix_normal = new int[size]; + int * prefix_reversed = new int[size]; + + compute_prefix(normal, size, prefix_normal); + compute_prefix(reversed, size, prefix_reversed); + + for (i = 0; i <= size; i++) { + result[i] = size - prefix_normal[size-1]; + } + + for (i = 0; i < size; i++) { + const int j = size - prefix_reversed[i]; + const int k = i - prefix_reversed[i]+1; + + if (result[j] > k) + result[j] = k; + } + + delete[] reversed; + delete[] prefix_normal; + delete[] prefix_reversed; +} + +/* +* Boyer-Moore search algorithm +*/ +const uint8_t *boyermoore_search(const uint8_t *haystack, size_t haystack_len, const uint8_t *needle, size_t needle_len) { + /* + * Simple checks + */ + if(haystack_len == 0) + return NULL; + if(needle_len == 0) + return NULL; + if(needle_len > haystack_len) + return NULL; + + /* + * Initialize heuristics + */ + int badcharacter[ALPHABET_SIZE]; + int * goodsuffix = new int[needle_len+1]; + + prepare_badcharacter_heuristic(needle, needle_len, badcharacter); + prepare_goodsuffix_heuristic(needle, needle_len, goodsuffix); + + /* + * Boyer-Moore search + */ + size_t s = 0; + while(s <= (haystack_len - needle_len)) + { + size_t j = needle_len; + while(j > 0 && needle[j-1] == haystack[s+j-1]) + j--; + + if(j > 0) + { + int k = badcharacter[haystack[s+j-1]]; + int m; + if(k < (int)j && (m = j-k-1) > goodsuffix[j]) + s+= m; + else + s+= goodsuffix[j]; + } + else + { + delete[] goodsuffix; + return haystack + s; + } + } + + delete[] goodsuffix; + /* not found */ + return NULL; +} + +#endif /* BoyerMoore.h */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/README.md new file mode 100644 index 0000000..929d0be --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/README.md @@ -0,0 +1,119 @@ +# node-buffertools + +Utilities for manipulating buffers. + +## Installing the module + +Easy! With [npm](http://npmjs.org/): + + npm install buffertools + +From source: + + node-gyp configure + node-gyp build + +Now you can include the module in your project. + + require('buffertools'); + new Buffer(42).clear(); + +## Methods + +Note that most methods that take a buffer as an argument, will also accept a string. + +### Buffer.clear() + +Clear the buffer. This is equivalent to `Buffer.fill(0)`. +Returns the buffer object so you can chain method calls. + +### Buffer.compare(buffer|string) + +Lexicographically compare two buffers. Returns a number less than zero +if a < b, zero if a == b or greater than zero if a > b. + +Buffers are considered equal when they are of the same length and contain +the same binary data. + +Smaller buffers are considered to be less than larger ones. Some buffers +find this hurtful. + +### Buffer.concat(a, b, c, ...) +### buffertools.concat(a, b, c, ...) + +Concatenate two or more buffers/strings and return the result. Example: + + // identical to new Buffer('foobarbaz') + a = new Buffer('foo'); + b = new Buffer('bar'); + c = a.concat(b, 'baz'); + console.log(a, b, c); // "foo bar foobarbaz" + + // static variant + buffertools.concat('foo', new Buffer('bar'), 'baz'); + +### Buffer.equals(buffer|string) + +Returns true if this buffer equals the argument, false otherwise. + +Buffers are considered equal when they are of the same length and contain +the same binary data. + +Caveat emptor: If your buffers contain strings with different character encodings, +they will most likely *not* be equal. + +### Buffer.fill(integer|string|buffer) + +Fill the buffer (repeatedly if necessary) with the argument. +Returns the buffer object so you can chain method calls. + +### Buffer.fromHex() + +Assumes this buffer contains hexadecimal data (packed, no whitespace) +and decodes it into binary data. Returns a new buffer with the decoded +content. Throws an exception if non-hexadecimal data is encountered. + +### Buffer.indexOf(buffer|string, [start=0]) + +Search this buffer for the first occurrence of the argument, starting at +offset `start`. Returns the zero-based index or -1 if there is no match. + +### Buffer.reverse() + +Reverse the content of the buffer in place. Example: + + b = new Buffer('live'); + b.reverse(); + console.log(b); // "evil" + +### Buffer.toHex() + +Returns the contents of this buffer encoded as a hexadecimal string. + +## Classes + +Singular, actually. To wit: + +## WritableBufferStream + +This is a regular node.js [writable stream](http://nodejs.org/docs/v0.3.4/api/streams.html#writable_Stream) +that accumulates the data it receives into a buffer. + +Example usage: + + // slurp stdin into a buffer + process.stdin.resume(); + ostream = new WritableBufferStream(); + util.pump(process.stdin, ostream); + console.log(ostream.getBuffer()); + +The stream never emits 'error' or 'drain' events. + +### WritableBufferStream.getBuffer() + +Return the data accumulated so far as a buffer. + +## TODO + +* Logical operations on buffers (AND, OR, XOR). +* Add lastIndexOf() functions. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/binding.gyp b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/binding.gyp new file mode 100644 index 0000000..17e337b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'buffertools', + 'sources': [ 'buffertools.cc' ] + } + ] +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/buffertools.cc b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/buffertools.cc new file mode 100644 index 0000000..ddfbf88 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/buffertools.cc @@ -0,0 +1,350 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "BoyerMoore.h" + +using namespace v8; +using namespace node; + +namespace { + +// this is an application of the Curiously Recurring Template Pattern +template struct UnaryAction { + Handle apply(Handle& buffer, const Arguments& args, HandleScope& scope); + + Handle operator()(const Arguments& args) { + HandleScope scope; + + Local self = args.This(); + if (!Buffer::HasInstance(self)) { + return ThrowException(Exception::TypeError(String::New( + "Argument should be a buffer object."))); + } + + return static_cast(this)->apply(self, args, scope); + } +}; + +template struct BinaryAction { + Handle apply(Handle& buffer, const uint8_t* data, size_t size, const Arguments& args, HandleScope& scope); + + Handle operator()(const Arguments& args) { + HandleScope scope; + + Local self = args.This(); + if (!Buffer::HasInstance(self)) { + return ThrowException(Exception::TypeError(String::New( + "Argument should be a buffer object."))); + } + + if (args[0]->IsString()) { + String::Utf8Value s(args[0]->ToString()); + return static_cast(this)->apply(self, (uint8_t*) *s, s.length(), args, scope); + } + if (Buffer::HasInstance(args[0])) { + Local other = args[0]->ToObject(); + return static_cast(this)->apply( + self, (const uint8_t*) Buffer::Data(other), Buffer::Length(other), args, scope); + } + + static Persistent illegalArgumentException = Persistent::New(String::New( + "Second argument must be a string or a buffer.")); + return ThrowException(Exception::TypeError(illegalArgumentException)); + } +}; + +// +// helper functions +// +Handle clear(Handle& buffer, int c) { + size_t length = Buffer::Length(buffer); + uint8_t* data = (uint8_t*) Buffer::Data(buffer); + memset(data, c, length); + return buffer; +} + +Handle fill(Handle& buffer, void* pattern, size_t size) { + size_t length = Buffer::Length(buffer); + uint8_t* data = (uint8_t*) Buffer::Data(buffer); + + if (size >= length) { + memcpy(data, pattern, length); + } else { + const int n_copies = length / size; + const int remainder = length % size; + for (int i = 0; i < n_copies; i++) { + memcpy(data + size * i, pattern, size); + } + memcpy(data + size * n_copies, pattern, remainder); + } + + return buffer; +} + +int compare(Handle& buffer, const uint8_t* data2, size_t length2) { + size_t length = Buffer::Length(buffer); + if (length != length2) { + return length > length2 ? 1 : -1; + } + + const uint8_t* data = (const uint8_t*) Buffer::Data(buffer); + return memcmp(data, data2, length); +} + +// +// actions +// +struct ClearAction: UnaryAction { + Handle apply(Handle& buffer, const Arguments& args, HandleScope& scope) { + return clear(buffer, 0); + } +}; + +struct FillAction: UnaryAction { + Handle apply(Handle& buffer, const Arguments& args, HandleScope& scope) { + if (args[0]->IsInt32()) { + int c = args[0]->ToInt32()->Int32Value(); + return clear(buffer, c); + } + + if (args[0]->IsString()) { + String::Utf8Value s(args[0]->ToString()); + return fill(buffer, *s, s.length()); + } + + if (Buffer::HasInstance(args[0])) { + Handle other = args[0]->ToObject(); + size_t length = Buffer::Length(other); + uint8_t* data = (uint8_t*) Buffer::Data(other); + return fill(buffer, data, length); + } + + static Persistent illegalArgumentException = Persistent::New(String::New( + "Second argument should be either a string, a buffer or an integer.")); + return ThrowException(Exception::TypeError(illegalArgumentException)); + } +}; + +struct ReverseAction: UnaryAction { + // O(n/2) for all cases which is okay, might be optimized some more with whole-word swaps + // XXX won't this trash the L1 cache something awful? + Handle apply(Handle& buffer, const Arguments& args, HandleScope& scope) { + uint8_t* head = (uint8_t*) Buffer::Data(buffer); + uint8_t* tail = head + Buffer::Length(buffer) - 1; + + // xor swap, just because I can + while (head < tail) *head ^= *tail, *tail ^= *head, *head ^= *tail, ++head, --tail; + + return buffer; + } +}; + +struct EqualsAction: BinaryAction { + Handle apply(Handle& buffer, const uint8_t* data, size_t size, const Arguments& args, HandleScope& scope) { + return compare(buffer, data, size) == 0 ? True() : False(); + } +}; + +struct CompareAction: BinaryAction { + Handle apply(Handle& buffer, const uint8_t* data, size_t size, const Arguments& args, HandleScope& scope) { + return scope.Close(Integer::New(compare(buffer, data, size))); + } +}; + +struct IndexOfAction: BinaryAction { + Handle apply(Handle& buffer, const uint8_t* data2, size_t size2, const Arguments& args, HandleScope& scope) { + const uint8_t* data = (const uint8_t*) Buffer::Data(buffer); + const size_t size = Buffer::Length(buffer); + + int32_t start = args[1]->Int32Value(); + + if (start < 0) + start = size - std::min(size, -start); + else if (static_cast(start) > size) + start = size; + + const uint8_t* p = boyermoore_search( + data + start, size - start, data2, size2); + + const ptrdiff_t offset = p ? (p - data) : -1; + return scope.Close(Integer::New(offset)); + } +}; + +static char toHexTable[] = "0123456789abcdef"; + +// CHECKME is this cache efficient? +static char fromHexTable[] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1, + 10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 +}; + +inline Handle decodeHex(const uint8_t* const data, const size_t size, const Arguments& args, HandleScope& scope) { + if (size & 1) { + return ThrowException(Exception::Error(String::New( + "Odd string length, this is not hexadecimal data."))); + } + + if (size == 0) { + return String::Empty(); + } + + Handle& buffer = Buffer::New(size / 2)->handle_; + + uint8_t *src = (uint8_t *) data; + uint8_t *dst = (uint8_t *) (const uint8_t*) Buffer::Data(buffer); + + for (size_t i = 0; i < size; i += 2) { + int a = fromHexTable[*src++]; + int b = fromHexTable[*src++]; + + if (a == -1 || b == -1) { + return ThrowException(Exception::Error(String::New( + "This is not hexadecimal data."))); + } + + *dst++ = b | (a << 4); + } + + return buffer; +} + +struct FromHexAction: UnaryAction { + Handle apply(Handle& buffer, const Arguments& args, HandleScope& scope) { + const uint8_t* data = (const uint8_t*) Buffer::Data(buffer); + size_t length = Buffer::Length(buffer); + return decodeHex(data, length, args, scope); + } +}; + +struct ToHexAction: UnaryAction { + Handle apply(Handle& buffer, const Arguments& args, HandleScope& scope) { + const size_t size = Buffer::Length(buffer); + const uint8_t* data = (const uint8_t*) Buffer::Data(buffer); + + if (size == 0) { + return String::Empty(); + } + + std::string s(size * 2, 0); + for (size_t i = 0; i < size; ++i) { + const uint8_t c = (uint8_t) data[i]; + s[i * 2] = toHexTable[c >> 4]; + s[i * 2 + 1] = toHexTable[c & 15]; + } + + return scope.Close(String::New(s.c_str(), s.size())); + } +}; + +// +// V8 function callbacks +// +Handle Clear(const Arguments& args) { + return ClearAction()(args); +} + +Handle Fill(const Arguments& args) { + return FillAction()(args); +} + +Handle Reverse(const Arguments& args) { + return ReverseAction()(args); +} + +Handle Equals(const Arguments& args) { + return EqualsAction()(args); +} + +Handle Compare(const Arguments& args) { + return CompareAction()(args); +} + +Handle IndexOf(const Arguments& args) { + return IndexOfAction()(args); +} + +Handle FromHex(const Arguments& args) { + return FromHexAction()(args); +} + +Handle ToHex(const Arguments& args) { + return ToHexAction()(args); +} + +Handle Concat(const Arguments& args) { + HandleScope scope; + + size_t size = 0; + for (int index = 0, length = args.Length(); index < length; ++index) { + Local arg = args[index]; + if (arg->IsString()) { + // Utf8Length() because we need the length in bytes, not characters + size += arg->ToString()->Utf8Length(); + } + else if (Buffer::HasInstance(arg)) { + size += Buffer::Length(arg->ToObject()); + } + else { + std::stringstream s; + s << "Argument #" << index << " is neither a string nor a buffer object."; + return ThrowException( + Exception::TypeError( + String::New(s.str().c_str()))); + } + } + + Buffer& dst = *Buffer::New(size); + uint8_t* s = (uint8_t*) Buffer::Data(dst.handle_); + + for (int index = 0, length = args.Length(); index < length; ++index) { + Local arg = args[index]; + if (arg->IsString()) { + String::Utf8Value v(arg->ToString()); + memcpy(s, *v, v.length()); + s += v.length(); + } + else if (Buffer::HasInstance(arg)) { + Local b = arg->ToObject(); + const uint8_t* data = (const uint8_t*) Buffer::Data(b); + size_t length = Buffer::Length(b); + memcpy(s, data, length); + s += length; + } + else { + return ThrowException(Exception::Error(String::New( + "Congratulations! You have run into a bug: argument is neither a string nor a buffer object. " + "Please make the world a better place and report it."))); + } + } + + return scope.Close(dst.handle_); +} + +void RegisterModule(Handle target) { + target->Set(String::NewSymbol("concat"), FunctionTemplate::New(Concat)->GetFunction()); + target->Set(String::NewSymbol("fill"), FunctionTemplate::New(Fill)->GetFunction()); + target->Set(String::NewSymbol("clear"), FunctionTemplate::New(Clear)->GetFunction()); + target->Set(String::NewSymbol("reverse"), FunctionTemplate::New(Reverse)->GetFunction()); + target->Set(String::NewSymbol("equals"), FunctionTemplate::New(Equals)->GetFunction()); + target->Set(String::NewSymbol("compare"), FunctionTemplate::New(Compare)->GetFunction()); + target->Set(String::NewSymbol("indexOf"), FunctionTemplate::New(IndexOf)->GetFunction()); + target->Set(String::NewSymbol("fromHex"), FunctionTemplate::New(FromHex)->GetFunction()); + target->Set(String::NewSymbol("toHex"), FunctionTemplate::New(ToHex)->GetFunction()); +} + +} // anonymous namespace + +NODE_MODULE(buffertools, RegisterModule) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/buffertools.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/buffertools.js new file mode 100644 index 0000000..e1736df --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/buffertools.js @@ -0,0 +1,90 @@ +buffertools = require('./build/Release/buffertools.node'); +SlowBuffer = require('buffer').SlowBuffer; +Buffer = require('buffer').Buffer; + +// requires node 3.1 +events = require('events'); +util = require('util'); + +// extend object prototypes +for (var key in buffertools) { + var val = buffertools[key]; + SlowBuffer.prototype[key] = val; + Buffer.prototype[key] = val; + exports[key] = val; +} + +// bug fix, see https://github.com/bnoordhuis/node-buffertools/issues/#issue/6 +Buffer.prototype.concat = SlowBuffer.prototype.concat = function() { + var args = [this].concat(Array.prototype.slice.call(arguments)); + return buffertools.concat.apply(buffertools, args); +}; + +// +// WritableBufferStream +// +// - never emits 'error' +// - never emits 'drain' +// +function WritableBufferStream() { + this.writable = true; + this.buffer = null; +} + +util.inherits(WritableBufferStream, events.EventEmitter); + +WritableBufferStream.prototype._append = function(buffer, encoding) { + if (!this.writable) { + throw new Error('Stream is not writable.'); + } + + if (Buffer.isBuffer(buffer)) { + // no action required + } + else if (typeof buffer == 'string') { + // TODO optimize + buffer = new Buffer(buffer, encoding || 'utf8'); + } + else { + throw new Error('Argument should be either a buffer or a string.'); + } + + // FIXME optimize! + if (this.buffer) { + this.buffer = buffertools.concat(this.buffer, buffer); + } + else { + this.buffer = new Buffer(buffer.length); + buffer.copy(this.buffer); + } +}; + +WritableBufferStream.prototype.write = function(buffer, encoding) { + this._append(buffer, encoding); + + // signal that it's safe to immediately write again + return true; +}; + +WritableBufferStream.prototype.end = function(buffer, encoding) { + if (buffer) { + this._append(buffer, encoding); + } + + this.emit('close'); + + this.writable = false; +}; + +WritableBufferStream.prototype.getBuffer = function() { + if (this.buffer) { + return this.buffer; + } + return new Buffer(0); +}; + +WritableBufferStream.prototype.toString = function() { + return this.getBuffer().toString(); +}; + +exports.WritableBufferStream = WritableBufferStream; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/package.json new file mode 100644 index 0000000..0c31824 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/package.json @@ -0,0 +1,20 @@ +{ + "name": "buffertools", + "main": "buffertools", + "version": "1.1.0", + "keywords": ["buffer", "buffers"], + "description": "Working with node.js buffers made easy.", + "homepage": "https://github.com/bnoordhuis/node-buffertools", + "author": { + "name": "Ben Noordhuis", + "email": "info@bnoordhuis.nl", + "url": "http://bnoordhuis.nl/" + }, + "repository": { + "type": "git", + "url": "https://github.com/bnoordhuis/node-buffertools.git" + }, + "engines": { + "node": ">=0.3.0" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/test.js new file mode 100644 index 0000000..444504b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/test.js @@ -0,0 +1,118 @@ +buffertools = require('./buffertools'); +Buffer = require('buffer').Buffer; +assert = require('assert'); + +WritableBufferStream = buffertools.WritableBufferStream; + +// these trigger the code paths for UnaryAction and BinaryAction +assert.throws(function() { buffertools.clear({}); }); +assert.throws(function() { buffertools.equals({}, {}); }); + +a = new Buffer('abcd'), b = new Buffer('abcd'), c = new Buffer('efgh'); +assert.ok(a.equals(b)); +assert.ok(!a.equals(c)); +assert.ok(a.equals('abcd')); +assert.ok(!a.equals('efgh')); + +assert.ok(a.compare(a) == 0); +assert.ok(a.compare(c) < 0); +assert.ok(c.compare(a) > 0); + +assert.ok(a.compare('abcd') == 0); +assert.ok(a.compare('efgh') < 0); +assert.ok(c.compare('abcd') > 0); + +b = new Buffer('****'); +assert.equal(b, b.clear()); +assert.equal(b.inspect(), ''); // FIXME brittle test + +b = new Buffer(4); +assert.equal(b, b.fill(42)); +assert.equal(b.inspect(), ''); + +b = new Buffer(4); +assert.equal(b, b.fill('*')); +assert.equal(b.inspect(), ''); + +b = new Buffer(4); +assert.equal(b, b.fill('ab')); +assert.equal(b.inspect(), ''); + +b = new Buffer(4); +assert.equal(b, b.fill('abcd1234')); +assert.equal(b.inspect(), ''); + +b = new Buffer('Hello, world!'); +assert.equal(-1, b.indexOf(new Buffer('foo'))); +assert.equal(0, b.indexOf(new Buffer('Hell'))); +assert.equal(7, b.indexOf(new Buffer('world'))); +assert.equal(7, b.indexOf(new Buffer('world!'))); +assert.equal(-1, b.indexOf('foo')); +assert.equal(0, b.indexOf('Hell')); +assert.equal(7, b.indexOf('world')); +assert.equal(-1, b.indexOf('')); +assert.equal(-1, b.indexOf('x')); +assert.equal(7, b.indexOf('w')); +assert.equal(0, b.indexOf('Hello, world!')); +assert.equal(-1, b.indexOf('Hello, world!1')); +assert.equal(7, b.indexOf('world', 7)); +assert.equal(-1, b.indexOf('world', 8)); +assert.equal(7, b.indexOf('world', -256)); +assert.equal(7, b.indexOf('world', -6)); +assert.equal(-1, b.indexOf('world', -5)); +assert.equal(-1, b.indexOf('world', 256)); +assert.equal(-1, b.indexOf('', 256)); + +b = new Buffer("\t \r\n"); +assert.equal('09200d0a', b.toHex()); +assert.equal(b.toString(), new Buffer('09200d0a').fromHex().toString()); + +// https://github.com/bnoordhuis/node-buffertools/pull/9 +b = new Buffer(4); +b[0] = 0x98; +b[1] = 0x95; +b[2] = 0x60; +b[3] = 0x2f; +assert.equal('9895602f', b.toHex()); + +assert.equal('', buffertools.concat()); +assert.equal('', buffertools.concat('')); +assert.equal('foobar', new Buffer('foo').concat('bar')); +assert.equal('foobarbaz', buffertools.concat(new Buffer('foo'), 'bar', new Buffer('baz'))); +assert.throws(function() { buffertools.concat('foo', 123, 'baz'); }); +// assert that the buffer is copied, not returned as-is +a = new Buffer('For great justice.'), b = buffertools.concat(a); +assert.equal(a.toString(), b.toString()); +assert.notEqual(a, b); + +assert.equal('', new Buffer('').reverse()); +assert.equal('For great justice.', new Buffer('.ecitsuj taerg roF').reverse()); + +// bug fix, see http://github.com/bnoordhuis/node-buffertools/issues#issue/5 +endOfHeader = new Buffer('\r\n\r\n'); +assert.equal(0, endOfHeader.indexOf(endOfHeader)); +assert.equal(0, endOfHeader.indexOf('\r\n\r\n')); + +// feature request, see https://github.com/bnoordhuis/node-buffertools/issues#issue/8 +closed = false; +stream = new WritableBufferStream(); + +stream.on('close', function() { closed = true; }); +stream.write('Hello,'); +stream.write(' '); +stream.write('world!'); +stream.end(); + +assert.equal(true, closed); +assert.equal(false, stream.writable); +assert.equal('Hello, world!', stream.toString()); +assert.equal('Hello, world!', stream.getBuffer().toString()); + +// closed stream should throw +assert.throws(function() { stream.write('ZIG!'); }); + +// GH-10 indexOf sometimes incorrectly returns -1 +for (i = 0; i < 100; i++) { + buffer = new Buffer('9A8B3F4491734D18DEFC6D2FA96A2D3BC1020EECB811F037F977D039B4713B1984FBAB40FCB4D4833D4A31C538B76EB50F40FA672866D8F50D0A1063666721B8D8322EDEEC74B62E5F5B959393CD3FCE831CC3D1FA69D79C758853AFA3DC54D411043263596BAD1C9652970B80869DD411E82301DF93D47DCD32421A950EF3E555152E051C6943CC3CA71ED0461B37EC97C5A00EBACADAA55B9A7835F148DEF8906914617C6BD3A38E08C14735FC2EFE075CC61DFE5F2F9686AB0D0A3926604E320160FDC1A4488A323CB4308CDCA4FD9701D87CE689AF999C5C409854B268D00B063A89C2EEF6673C80A4F4D8D0A00163082EDD20A2F1861512F6FE9BB479A22A3D4ACDD2AA848254BA74613190957C7FCD106BF7441946D0E1A562DA68BC37752B1551B8855C8DA08DFE588902D44B2CAB163F3D7D7706B9CC78900D0AFD5DAE5492535A17DB17E24389F3BAA6F5A95B9F6FE955193D40932B5988BC53E49CAC81955A28B81F7B36A1EDA3B4063CBC187B0488FCD51FAE71E4FBAEE56059D847591B960921247A6B7C5C2A7A757EC62A2A2A2A2A2A2A25552591C03EF48994BD9F594A5E14672F55359EF1B38BF2976D1216C86A59847A6B7C4A5C585A0D0A2A6D9C8F8B9E999C2A836F786D577A79816F7C577A797D7E576B506B57A05B5B8C4A8D99989E8B8D9E644A6B9D9D8F9C9E4A504A6B968B93984A93984A988FA19D919C999F9A4A8B969E588C93988B9C938F9D588D8B9C9E9999989D58909C8F988D92588E0D0A3D79656E642073697A653D373035393620706172743D31207063726333323D33616230646235300D0A2E0D0A').fromHex(); + assert.equal(551, buffer.indexOf('=yend')); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/wscript b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/wscript new file mode 100644 index 0000000..fba0661 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/buffertools/wscript @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +def set_options(ctx): + ctx.tool_options('compiler_cxx') + +def configure(ctx): + ctx.check_tool('compiler_cxx') + ctx.check_tool('node_addon') + ctx.env.set_variant('Release') + +def build(ctx): + t = ctx.new_task_gen('cxx', 'shlib', 'node_addon') + t.target = 'buffertools' + t.source = 'buffertools.cc' diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/.npmignore new file mode 100644 index 0000000..3de30f0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/.npmignore @@ -0,0 +1,4 @@ +/tmp +/node_modules +*.log +!/test/corpus/*.log diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/34.patch b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/34.patch new file mode 100644 index 0000000..56cf025 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/34.patch @@ -0,0 +1,71 @@ +From cdab7fc428cac630f65d1eae5b0dbccd4ea68298 Mon Sep 17 00:00:00 2001 +From: Rob Gulewich +Date: Mon, 20 Aug 2012 15:43:14 -0700 +Subject: [PATCH] Log req/res body if present (as per the restify audit + logger) + +--- + bin/bunyan | 20 ++++++++++++++++---- + 1 file changed, 16 insertions(+), 4 deletions(-) + +diff --git a/bin/bunyan b/bin/bunyan +index db15e06..61ff203 100755 +--- a/bin/bunyan ++++ b/bin/bunyan +@@ -614,30 +614,39 @@ function emitRecord(rec, line, opts, stylize) { + + if (rec.req) { + var headers = rec.req.headers; +- details.push(indent(format("%s %s HTTP/1.1%s", rec.req.method, ++ var s = format("%s %s HTTP/1.1%s", rec.req.method, + rec.req.url, + (headers + ? '\n' + Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n') + : '') +- ))); ++ ); ++ if (rec.req.body) { ++ s += '\n\n' + rec.req.body ++ } ++ details.push(indent(s)); + } + delete rec.req; + + if (rec.client_req) { + var headers = rec.client_req.headers; + var hostHeaderLine = ''; ++ var s = ''; + if (rec.client_req.address) { + hostHeaderLine = 'Host: ' + rec.client_req.address; + if (rec.client_req.port) + hostHeaderLine += ':' + rec.client_req.port; + hostHeaderLine += '\n'; + } +- details.push(indent(format("%s %s HTTP/1.1\n%s%s", rec.client_req.method, ++ s += format("%s %s HTTP/1.1\n%s%s", rec.client_req.method, + rec.client_req.url, + hostHeaderLine, + Object.keys(headers).map( +- function (h) { return h + ': ' + headers[h]; }).join('\n')))); ++ function (h) { return h + ': ' + headers[h]; }).join('\n')); ++ if (rec.client_req.body) { ++ s += '\n\n' + rec.client_req.body ++ } ++ details.push(indent(s)); + } + delete rec.client_req; + +@@ -654,6 +663,9 @@ function emitRecord(rec, line, opts, stylize) { + s += Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n'); + } ++ if (rec.res.body) { ++ s += '\n\n' + rec.res.body ++ } + if (s) { + details.push(indent(s)); + } +-- +1.7.10 + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/AUTHORS b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/AUTHORS new file mode 100644 index 0000000..ba2edfe --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/AUTHORS @@ -0,0 +1,6 @@ +Trent Mick (http://trentm.com) +Mark Cavage (https://github.com/mcavage) +Dave Pacheco (https://github.com/davepacheco) +Michael Hart (https://github.com/mhart) +Isaac Schlueter (https://github.com/isaacs) +Rob Gulewich (https://github.com/rgulewich) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/CHANGES.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/CHANGES.md new file mode 100644 index 0000000..a51e635 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/CHANGES.md @@ -0,0 +1,382 @@ +# bunyan Changelog + +## bunyan 0.14.0 + +- [pull #41] Safe `JSON.stringify`ing of emitted log records to avoid blowing + up on circular objects (by Isaac Schlueter). + + +## bunyan 0.13.5 + +- [issue #39] Fix a bug with `client_req` handling in the default output + of the `bunyan` CLI. + + +## bunyan 0.13.4 + +- [issue #38] Fix the default `bunyan` CLI output of a `req.body` that is an + object instead of a string. + + +## bunyan 0.13.3 + +- Export `bunyan.resolveLevel(NAME-OR-NUM)` to resolve a level name or number + to its log level number value: + + > bunyan.resolveLevel('INFO') + 30 + > bunyan.resolveLevel('debug') + 20 + + A side-effect of this change is that the uppercase level name is now allowed + in the logger constructor. + + +## bunyan 0.13.2 + +- [issue #35] Ensure that an accidental `log.info(BUFFER)`, where BUFFER is + a node.js Buffer object, doesn't blow up. + + +## bunyan 0.13.1 + +- [issue #34] Ensure `req.body`, `res.body` and other request/response fields + are emitted by the `bunyan` CLI (mostly by Rob Gulewich). + + + +## bunyan 0.13.0 + +- [issue #31] Re-instate defines for the (uppercase) log level names (TRACE, + DEBUG, etc.) in `bunyan -c "..."` filtering condition code. E.g.: + + $ ... | bunyan -c 'level >= ERROR' + + +## bunyan 0.12.0 + +- [pull #32] `bunyan -o short` for more concise output (by Dave Pacheco). E.g.: + + 22:56:52.856Z INFO myservice: My message + + instead of: + + [2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: My message + + +## bunyan 0.11.3 + +- Add '--strict' option to `bunyan` CLI to suppress all but legal Bunyan JSON + log lines. By default non-JSON, and non-Bunyan lines are passed through. + + +## bunyan 0.11.2 + +- [issue #30] Robust handling of 'req' field without a 'headers' subfield + in `bunyan` CLI. +- [issue #31] Pull the TRACE, DEBUG, et al defines from `bunyan -c "..."` + filtering code. This was added in v0.11.1, but has a significant adverse + affect. + + +## bunyan 0.11.1 + +- **Bad release. The TRACE et al names are bleeding into the log records + when using '-c'.** +- Add defines for the (uppercase) log level names (TRACE, DEBUG, etc.) in + `bunyan -c "..."` filtering condition code. E.g.: + + $ ... | bunyan -c 'level >= ERROR' + + +## bunyan 0.11.0 + +- [pull #29] Add -l/--level for level filtering, and -c/--condition for + arbitrary conditional filtering (by github.com/isaacs): + + $ ... | bunyan -l error # filter out log records below error + $ ... | bunyan -l 50 # numeric value works too + $ ... | bunyan -c 'level===50' # equiv with -c filtering + $ ... | bunyan -c 'pid===123' # filter on any field + $ ... | bunyan -c 'pid===123' -c '_audit' # multiple filters + + +## bunyan 0.10.0 + +- [pull #24] Support for gzip'ed log files in the bunyan CLI (by + github.com/mhart): + + $ bunyan foo.log.gz + ... + + +## bunyan 0.9.0 + +- [pull #16] Bullet proof the `bunyan.stdSerializers` (by github.com/rlidwka). + +- [pull #15] The `bunyan` CLI will now chronologically merge multiple log + streams when it is given multiple file arguments. (by github.com/davepacheco) + + $ bunyan foo.log bar.log + ... merged log records ... + +- [pull #15] A new `bunyan.RingBuffer` stream class that is useful for + keeping the last N log messages in memory. This can be a fast way to keep + recent, and thus hopefully relevant, log messages. (by @dapsays, + github.com/davepacheco) + + Potential uses: Live debugging if a running process could inspect those + messages. One could dump recent log messages at a finer log level than is + typically logged on + [`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception). + + var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); + var log = new bunyan({ + name: 'foo', + streams: [{ + type: 'raw', + stream: ringbuffer, + level: 'debug' + }] + }); + + log.info('hello world'); + console.log(ringbuffer.records); + +- Add support for "raw" streams. This is a logging stream that is given + raw log record objects instead of a JSON-stringified string. + + function Collector() { + this.records = []; + } + Collector.prototype.write = function (rec) { + this.records.push(rec); + } + var log = new Logger({ + name: 'mylog', + streams: [{ + type: 'raw', + stream: new Collector() + }] + }); + + See "examples/raw-stream.js". I expect raw streams to be useful for + piping Bunyan logging to separate services (e.g. , + ) or to separate in-process handling. + +- Add test/corpus/*.log files (accidentally excluded) so the test suite + actually works(!). + + +## bunyan 0.8.0 + +- [pull #21] Bunyan loggers now re-emit `fs.createWriteStream` error events. + By github.com/EvanOxfeld. See "examples/handle-fs-error.js" and + "test/error-event.js" for details. + + var log = new Logger({name: 'mylog', streams: [{path: FILENAME}]}); + log.on('error', function (err, stream) { + // Handle error writing to or creating FILENAME. + }); + +- jsstyle'ing (via `make check`) + + +## bunyan 0.7.0 + +- [issue #12] Add `bunyan.createLogger(OPTIONS)` form, as is more typical in + node.js APIs. This'll eventually become the preferred form. + + +## bunyan 0.6.9 + +- Change `bunyan` CLI default output to color "src" info red. Before the "src" + information was uncolored. The "src" info is the filename, line number and + function name resulting from using `src: true` in `Logger` creation. I.e., + the `(/Users/trentm/tm/node-bunyan/examples/hi.js:10)` in: + + [2012-04-10T22:28:58.237Z] INFO: myapp/39339 on banana.local (/Users/trentm/tm/node-bunyan/examples/hi.js:10): hi + +- Tweak `bunyan` CLI default output to still show an "err" field if it doesn't + have a "stack" attribute. + + +## bunyan 0.6.8 + +- Fix bad bug in `log.child({...}, true);` where the added child fields **would + be added to the parent's fields**. This bug only existed for the "fast child" + path (that second `true` argument). A side-effect of fixing this is that + the "fast child" path is only 5 times as fast as the regular `log.child`, + instead of 10 times faster. + + +## bunyan 0.6.7 + +- [issue #6] Fix bleeding 'type' var to global namespace. (Thanks Mike!) + + +## bunyan 0.6.6 + +- Add support to the `bunyan` CLI taking log file path args, `bunyan foo.log`, + in addition to the usual `cat foo.log | bunyan`. +- Improve reliability of the default output formatting of the `bunyan` CLI. + Before it could blow up processing log records missing some expected + fields. + + +## bunyan 0.6.5 + +- ANSI coloring output from `bunyan` CLI tool (for the default output mode/style). + Also add the '--color' option to force coloring if the output stream is not + a TTY, e.g. `cat my.log | bunyan --color | less -R`. Use `--no-color` to + disable coloring, e.g. if your terminal doesn't support ANSI codes. +- Add 'level' field to log record before custom fields for that record. This + just means that the raw record JSON will show the 'level' field earlier, + which is a bit nicer for raw reading. + + +## bunyan 0.6.4 + +- [issue #5] Fix `log.info() -> boolean` to work properly. Previous all were + returning false. Ditto all trace/debug/.../fatal methods. + + +## bunyan 0.6.3 + +- Allow an optional `msg` and arguments to the `log.info( err)` logging + form. For example, before: + + log.debug(my_error_instance) // good + log.debug(my_error_instance, "boom!") // wasn't allowed + + Now the latter is allowed if you want to expliciting set the log msg. Of course + this applies to all the `log.{trace|debug|info...}()` methods. + +- `bunyan` cli output: clarify extra fields with quoting if empty or have + spaces. E.g. 'cmd' and 'stderr' in the following: + + [2012-02-12T00:30:43.736Z] INFO: mo-docs/43194 on banana.local: buildDocs results (req_id=185edca2-2886-43dc-911c-fe41c09ec0f5, route=PutDocset, error=null, stderr="", cmd="make docs") + + +## bunyan 0.6.2 + +- Fix/guard against unintended inclusion of some files in npm published package + due to + + +## bunyan 0.6.1 + +- Internal: starting jsstyle usage. +- Internal: add .npmignore. Previous packages had reams of bunyan crud in them. + + +## bunyan 0.6.0 + +- Add 'pid' automatic log record field. + + +## bunyan 0.5.3 + +- Add 'client_req' (HTTP client request) standard formatting in `bunyan` CLI + default output. +- Improve `bunyan` CLI default output to include *all* log record keys. Unknown keys + are either included in the first line parenthetical (if short) or in the indented + subsequent block (if long or multiline). + + +## bunyan 0.5.2 + +- [issue #3] More type checking of `new Logger(...)` and `log.child(...)` + options. +- Start a test suite. + + +## bunyan 0.5.1 + +- [issue #2] Add guard on `JSON.stringify`ing of log records before emission. + This will prevent `log.info` et al throwing on record fields that cannot be + represented as JSON. An error will be printed on stderr and a clipped log + record emitted with a 'bunyanMsg' key including error details. E.g.: + + bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON + { + "name": "foo", + "hostname": "banana.local", + "bunyanMsg": "bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON", + ... + + Some timing shows this does effect log speed: + + $ node tools/timeguard.js # before + Time try/catch-guard on JSON.stringify: + - log.info: 0.07365ms per iteration + $ node tools/timeguard.js # after + Time try/catch-guard on JSON.stringify: + - log.info: 0.07368ms per iteration + + +## bunyan 0.5.0 + +- Use 10/20/... instead of 1/2/... for level constant values. Ostensibly this + allows for intermediary levels from the defined "trace/debug/..." set. + However, that is discouraged. I'd need a strong user argument to add + support for easily using alternative levels. Consider using a separate + JSON field instead. +- s/service/name/ for Logger name field. "service" is unnecessarily tied + to usage for a service. No need to differ from log4j Logger "name". +- Add `log.level(...)` and `log.levels(...)` API for changing logger stream + levels. +- Add `TRACE|DEBUG|INFO|WARN|ERROR|FATAL` level constants to exports. +- Add `log.info(err)` special case for logging an `Error` instance. For + example `log.info(new TypeError("boom")` will produce: + + ... + "err": { + "message": "boom", + "name": "TypeError", + "stack": "TypeError: boom\n at Object. ..." + }, + "msg": "boom", + ... + + +## bunyan 0.4.0 + +- Add `new Logger({src: true})` config option to have a 'src' attribute be + automatically added to log records with the log call source info. Example: + + "src": { + "file": "/Users/trentm/tm/node-bunyan/examples/src.js", + "line": 20, + "func": "Wuzzle.woos" + }, + + +## bunyan 0.3.0 + +- `log.child(options[, simple])` Added `simple` boolean arg. Set `true` to + assert that options only add fields (no config changes). Results in a 10x + speed increase in child creation. See "tools/timechild.js". On my Mac, + "fast child" creation takes about 0.001ms. IOW, if your app is dishing + 10,000 req/s, then creating a log child for each request will take + about 1% of the request time. +- `log.clone` -> `log.child` to better reflect the relationship: streams and + serializers are inherited. Streams can't be removed as part of the child + creation. The child doesn't own the parent's streams (so can't close them). +- Clean up Logger creation. The goal here was to ensure `log.child` usage + is fast. TODO: measure that. +- Add `Logger.stdSerializers.err` serializer which is necessary to get good + Error object logging with node 0.6 (where core Error object properties + are non-enumerable). + + +## bunyan 0.2.0 + +- Spec'ing core/recommended log record fields. +- Add `LOG_VERSION` to exports. +- Improvements to request/response serializations. + + +## bunyan 0.1.0 + +First release. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/Makefile new file mode 100644 index 0000000..b74f643 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/Makefile @@ -0,0 +1,67 @@ + +#---- Tools + +TAP := ./node_modules/.bin/tap + + +#---- Files + +JSSTYLE_FILES := $(shell find lib test tools examples -name "*.js") bin/bunyan + + + +#---- Targets + +all: + +# Ensure all version-carrying files have the same version. +.PHONY: versioncheck +versioncheck: + [[ `cat package.json | json version` == `grep '^## ' CHANGES.md | head -1 | awk '{print $$3}'` ]] + [[ `cat package.json | json version` == `grep '^var VERSION' bin/bunyan | awk -F'"' '{print $$2}'` ]] + [[ `cat package.json | json version` == `grep '^var VERSION' lib/bunyan.js | awk -F"'" '{print $$2}'` ]] + @echo Version check ok. + +.PHONY: cutarelease +cutarelease: versioncheck + ./tools/cutarelease.py -p bunyan -f package.json -f lib/bunyan.js -f bin/bunyan + + +#---- test + +.PHONY: test +test: $(TAP) + TAP=1 $(TAP) test/*.test.js + +# Test will all node supported versions (presumes install locations I use on my machine). +.PHONY: testall +testall: test08 test06 test09 + +.PHONY: test09 +test09: + @echo "# Test node 0.9.x (with node `$(HOME)/opt/node-0.9/bin/node --version`)" + PATH="$(HOME)/opt/node-0.9/bin:$(PATH)" TAP=1 $(TAP) test/*.test.js +.PHONY: test08 +test08: + @echo "# Test node 0.8.x (with node `$(HOME)/opt/node-0.8/bin/node --version`)" + PATH="$(HOME)/opt/node-0.8/bin:$(PATH)" TAP=1 $(TAP) test/*.test.js +.PHONY: test06 +test06: + @echo "# Test node 0.6.x (with node `$(HOME)/opt/node-0.6/bin/node --version`)" + PATH="$(HOME)/opt/node-0.6/bin:$(PATH)" TAP=1 $(TAP) test/*.test.js + + + +#---- check + +.PHONY: check-jsstyle +check-jsstyle: $(JSSTYLE_FILES) + ./tools/jsstyle -o indent=2,doxygen,unparenthesized-return=0,blank-after-start-comment=0,leading-right-paren-ok $(JSSTYLE_FILES) + +.PHONY: check +check: check-jsstyle + @echo "Check ok." + +.PHONY: prepush +prepush: check testall + @echo "Okay to push." diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/README.md new file mode 100644 index 0000000..7c8c3d7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/README.md @@ -0,0 +1,534 @@ +Bunyan is **a simple and fast a JSON logging library** for node.js services and +**a `bunyan` CLI tool** for nicely viewing those logs). + +![bunyan CLI screenshot](https://raw.github.com/trentm/node-bunyan/master/tools/screenshot1.png) + +Server logs should be structured. JSON's a good format. Let's do that: a log +record is one line of `JSON.stringify`'d output. Let's also specify some common +names for the requisite and common fields for a log record (see below). + +Also: log4j is way more than you need. + + +# Current Status + +Solid core functionality is there. Joyent is using this for a number of +production services. Bunyan supports node 0.6 and greater. + +Follow @trentmick +for updates to Bunyan. + +See also: [Bunyan for Bash](https://github.com/trevoro/bash-bunyan). + + +# Installation + + npm install bunyan + + +# Usage + +**The usual.** All loggers must provide a "name". This is somewhat akin +to log4j logger "name", but Bunyan doesn't do hierarchical logger names. + + $ cat hi.js + var Logger = require('bunyan'); + var log = new Logger({name: "myapp"}); + log.info("hi"); + +Alternatively, bunyan 0.7.0 and up supports a more node.js-land typical +style (which might become the preferred form over time): + + var bunyan = require('bunyan'); + var log = bunyan.createLogger({name: "myapp"}); + +**Log records are JSON.** "hostname", "time" and "v" (the Bunyan log +format version) are added for you. + + $ node hi.js + {"name":"myapp","hostname":"banana.local","pid":123,"level":2,"msg":"hi","time":"2012-01-31T00:07:44.216Z","v":0} + +The full `log.{trace|debug|...|fatal}(...)` API is: + + log.info(); // Returns a boolean: is the "info" level enabled? + + log.info('hi'); // Log a simple string message. + log.info('hi %s', bob, anotherVar); // Uses `util.format` for msg formatting. + + log.info({foo: 'bar'}, 'hi'); // Adds "foo" field to log record. + + log.info(err); // Special case to log an `Error` instance, adds "err" + // key with exception details (including the stack) and + // sets "msg" to the exception message. + log.info(err, 'more on this: %s', more); + // ... or you can specify the "msg". + +Note that this implies **you cannot pass any object as the first argument +to log it**. IOW, `log.info(myobject)` isn't going to work the way you +expect. Adding support for this API would necessitate (a) JSON-ifying safely +that given object (sometimes hard, and probably slow) and (b) putting all its +attribs under a top-level 'x' field name or something to avoid field name +collisions. + + +## bunyan tool + +A `bunyan` tool is provided **for pretty-printing bunyan logs** and, eventually, +for filtering (e.g. `| bunyan -c 'level>3'`). This shows the default output +(which is fluid right now) and indented-JSON output. More output formats will +be added, including support for custom formats. + + $ node hi.js | ./bin/bunyan # CLI tool to filter/pretty-print JSON logs. + [2012-01-31T00:08:11.387Z] INFO: myapp on banana.local/123: hi + + $ node hi.js | ./bin/bunyan -o json + { + "name": "myapp", + "hostname": "banana.local", + "pid": 123, + "level": 2, + "msg": "hi", + "time": "2012-01-31T00:10:00.676Z", + "v": 0 + } + + +## streams + +By default, log output is to stdout (**stream**) and at the "info" level. +Explicitly that looks like: + + var log = new Logger({name: "myapp", stream: process.stdout, + level: "info"}); + +That is an abbreviated form for a single stream. **You can define multiple +streams at different levels**. + + var log = new Logger({ + name: "amon", + streams: [ + { + level: "info", + stream: process.stdout, // log INFO and above to stdout + }, + { + level: "error", + path: "tmp/error.log" // log ERROR and above to a file + } + ] + }); + +More on streams in the "Streams" section below. + + +## log.child + +A `log.child(...)` is provided to **specialize a logger for a sub-component**. +The following will have log records from "Wuzzle" instances use exactly the +same config as its parent, plus include the "component" field. + + var log = new Logger(...); + + ... + + function Wuzzle(options) { + this.log = options.log; + this.log.info("creating a wuzzle") + } + Wuzzle.prototype.woos = function () { + this.log.warn("This wuzzle is woosey.") + } + + var wuzzle = new Wuzzle({log: log.child({component: "wuzzle"})}); + wuzzle.woos(); + log.info("done with the wuzzle") + +The [node-restify](https://github.com/mcavage/node-restify) +framework integrates bunyan. One feature of its integration, is that each +restify request handler includes a `req.log` logger that is: + + log.child({req_id: }, true) + +Apps using restify can then use `req.log` and have all such log records +include the unique request id (as "req_id"). Handy. + + +## serializers + +Bunyan has a concept of **"serializers" to produce a JSON-able object from a +JavaScript object**, so you can easily do the following: + + log.info({req: }, "something about handling this request"); + +Association is by log record field name, "req" in this example, so this +requires a registered serializer something like this: + + function reqSerializer(req) { + return { + method: req.method, + url: req.url, + headers: req.headers + } + } + var log = new Logger({ + ... + serializers: { + req: reqSerializer + } + }); + +Or this: + + var log = new Logger({ + ... + serializers: {req: Logger.stdSerializers.req} + }); + +because Buyan includes a small set of standard serializers. To use all the +standard serializers you can use: + + var log = new Logger({ + ... + serializers: Logger.stdSerializers + }); + +*Note: Your own serializers should never throw, otherwise you'll get an +ugly message on stderr from Bunyan (along with the traceback) and the field +in your log record will be replaced with a short error message.* + +## src + +The **source file, line and function of the log call site** can be added to +log records by using the `src: true` config option: + + var log = new Logger({src: true, ...}); + +This adds the call source info with the 'src' field, like this: + + { + "name": "src-example", + "hostname": "banana.local", + "pid": 123, + "component": "wuzzle", + "level": 4, + "msg": "This wuzzle is woosey.", + "time": "2012-02-06T04:19:35.605Z", + "src": { + "file": "/Users/trentm/tm/node-bunyan/examples/src.js", + "line": 20, + "func": "Wuzzle.woos" + }, + "v": 0 + } + +**WARNING: Determining the call source info is slow. Never use this option +in production.** + + +# Levels + +The log levels in bunyan are: + +- "fatal" (60): the service/app is going to stop or become unusable now +- "error" (50): fatal for a particular request, but the service/app continues servicing other requests +- "warn" (40): a note on something that should probably be looked at by an operator +- "info" (30): detail on regular operation +- "debug" (20): anything else, i.e. too verbose to be included in "info" level. +- "trace" (10): logging from external libraries used by your app + +General level usage suggestions: "debug" should be used sparingly. +Information that will be useful to debug errors *post mortem* should usually +be included in "info" messages if it's generally relevant or else with the +corresponding "error" event. Don't rely on spewing mostly irrelevant debug +messages all the time and sifting through them when an error occurs. + +Integers are used for the actual level values (10 for "trace", ..., 60 for +"fatal") and constants are defined for the (Logger.TRACE ... Logger.DEBUG). +The lowercase level names are aliases supported in the API. + +Here is the API for changing levels in an existing logger: + + log.level() -> INFO // gets current level (lowest level of all streams) + + log.level(INFO) // set all streams to level INFO + log.level("info") // set all streams to level INFO + + log.levels() -> [DEBUG, INFO] // get array of levels of all streams + log.levels(0) -> DEBUG // get level of stream at index 0 + log.levels("foo") // get level of stream with name "foo" + + log.levels(0, INFO) // set level of stream 0 to INFO + log.levels(0, "info") // can use "info" et al aliases + log.levels("foo", WARN) // set stream named "foo" to WARN + + + +# Log Record Fields + +This section will describe *rules* for the Bunyan log format: field names, +field meanings, required fields, etc. However, a Bunyan library doesn't +strictly enforce all these rules while records are being emitted. For example, +Bunyan will add a `time` field with the correct format to your log records, +but you can specify your own. It is the caller's responsibility to specify +the appropriate format. + +The reason for the above leniency is because IMO logging a message should +never break your app. This leads to this rule of logging: **a thrown +exception from `log.info(...)` or equivalent (other than for calling with the +incorrect signature) is always a bug in Bunyan.** + + +A typical Bunyan log record looks like this: + + {"name":"myserver","hostname":"banana.local","pid":123,"req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","connection":"close"}},"level":3,"msg":"start request","time":"2012-02-03T19:02:46.178Z","v":0} + +Pretty-printed: + + { + "name": "myserver", + "hostname": "banana.local", + "pid": 123, + "req": { + "method": "GET", + "url": "/path?q=1#anchor", + "headers": { + "x-hi": "Mom", + "connection": "close" + }, + "remoteAddress": "120.0.0.1", + "remotePort": 51244 + }, + "level": 3, + "msg": "start request", + "time": "2012-02-03T19:02:57.534Z", + "v": 0 + } + + +Core fields: + +- `v`: Required. Integer. Added by Bunion. Cannot be overriden. + This is the Bunyan log format version (`require('bunyan').LOG_VERSION`). + The log version is a single integer. `0` is until I release a version + "1.0.0" of node-bunyan. Thereafter, starting with `1`, this will be + incremented if there is any backward incompatible change to the log record + format. Details will be in "CHANGES.md" (the change log). +- `level`: Required. Integer. Added by Bunion. Cannot be overriden. + See the "Levels" section. +- `name`: Required. String. Provided at Logger creation. + You must specify a name for your logger when creating it. Typically this + is the name of the service/app using Bunyan for logging. +- `hostname`: Required. String. Provided or determined at Logger creation. + You can specify your hostname at Logger creation or it will be retrieved + vi `os.hostname()`. +- `pid`: Required. Integer. Filled in automatically at Logger creation. +- `time`: Required. String. Added by Bunion. Can be overriden. + The date and time of the event in [ISO 8601 + Extended Format](http://en.wikipedia.org/wiki/ISO_8601) format and in UTC, + as from + [`Date.toISOString()`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString). +- `msg`: Required. String. + Every `log.debug(...)` et al call must provide a log message. +- `src`: Optional. Object giving log call source info. This is added + automatically by Bunyan if the "src: true" config option is given to the + Logger. Never use in production as this is really slow. + + +Go ahead and add more fields, and nest ones are fine (and recommended) as +well. This is why we're using JSON. Some suggestions and best practices +follow (feedback from actual users welcome). + + +Recommended/Best Practice Fields: + +- `err`: Object. A caught JS exception. Log that thing with `log.info(err)` + to get: + + ... + "err": { + "message": "boom", + "name": "TypeError", + "stack": "TypeError: boom\n at Object. ..." + }, + "msg": "boom", + ... + + Or use the `Logger.stdSerializers.err` serializer in your Logger and + do this `log.error({err: err}, "oops")`. See "examples/err.js". + +- `req_id`: String. A request identifier. Including this field in all logging + tied to handling a particular request to your server is strongly suggested. + This allows post analysis of logs to easily collate all related logging + for a request. This really shines when you have a SOA with multiple services + and you carry a single request ID from the top API down through all APIs + (as [node-restify](https://github.com/mcavage/node-restify) facilitates + with its 'X-Request-Id' header). + +- `req`: An HTTP server request. Bunyan provides `Logger.stdSerializers.req` + to serialize a request with a suggested set of keys. Example: + + { + "method": "GET", + "url": "/path?q=1#anchor", + "headers": { + "x-hi": "Mom", + "connection": "close" + }, + "remoteAddress": "120.0.0.1", + "remotePort": 51244 + } + +- `res`: An HTTP server response. Bunyan provides `Logger.stdSerializers.res` + to serialize a response with a suggested set of keys. Example: + + { + "statusCode": 200, + "header": "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n" + } + + +Other fields to consider: + +- `req.username`: Authenticated user (or for a 401, the user attempting to + auth). +- Some mechanism to calculate response latency. "restify" users will have + a "X-Response-Time" header. A `latency` custom field would be fine. +- `req.body`: If you know that request bodies are small (common in APIs, + for example), then logging the request body is good. + + +# Streams + +A "stream" is Bunyan's name for an output for log messages (the equivalent +to a log4j Appender). Ultimately Bunyan uses a +[Writable Stream](http://nodejs.org/docs/latest/api/all.html#writable_Stream) +interface, but there are some additional attributes used to create and +manage the stream. A Bunyan Logger instance has one or more streams. +In general streams are specified with the "streams" option: + + var Logger = require('bunyan'); + var log = new Logger({ + name: "foo", + streams: [ + { + stream: process.stderr, + level: "debug" + }, + ... + ] + }) + +For convenience, if there is only one stream, it can specified with the +"stream" and "level" options (internal converted to a `Logger.streams`): + + var log = new Logger({ + name: "foo", + stream: process.stderr, + level: "debug" + }) + +If none are specified, the default is a stream on `process.stdout` at the +"info" level. + +`Logger.streams` is an array of stream objects with the following attributes: + +- `type`: One of "stream", "file" or "raw". See below. Often this is + implied from the other arguments. +- `path`: A file path for a file stream. If `path` is given and `type` is + not specified, then `type` will be set to "file". +- `stream`: This is the "Writable Stream", e.g. a std handle or an open + file write stream. If `stream` is given and `type` is not specified, then + `type` will be set to "stream". +- `level`: The level at which logging to this stream is enabled. If not + specified it defaults to INFO. + +Supported stream types are: + +- `stream`: A plain ol' node.js [Writable + Stream](http://nodejs.org/docs/latest/api/all.html#writable_Stream). + A "stream" (the writeable stream) value is required. + +- `file`: A "path" argument is given. Bunyan will open this file for + appending. E.g.: + + { + "path": "/var/log/foo.log", + "level": "warn" + } + + Bunyan re-emits error events from the created `WriteStream`. So you can + do this: + + var log = new Logger({name: 'mylog', streams: [{path: LOG_PATH}]}); + log.on('error', function (err, stream) { + // Handle stream write or create error here. + }); + +- `raw`: Similar to a "stream" writeable stream, except that the write method + is given raw log record *Object*s instead of a JSON-stringified string. + This can be useful for hooking on further processing to all Bunyan logging: + pushing to an external service, a RingBuffer (see below), etc. + + +## RingBuffer Stream + +Bunyan comes with a special stream called a RingBuffer which keeps the last N +records in memory and does *not* write the data anywhere else. One common +strategy is to log 'info' and higher to a normal log file but log all records +(including 'trace') to a ringbuffer that you can access via a debugger, or your +own HTTP interface, or a post-mortem facility like MDB or node-panic. + +To use a RingBuffer: + + /* Create a ring buffer that stores the last 100 records. */ + var bunyan = require('bunyan'); + var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); + var log = new bunyan({ + name: 'foo', + streams: [ + { + level: 'info', + stream: process.stdout + }, + { + level: 'trace', + type: 'raw', // use 'raw' to get raw log record objects + stream: ringbuffer + } + ] + }); + + log.info('hello world'); + console.log(ringbuffer.records); + +This example emits: + + [ { name: 'foo', + hostname: '912d2b29', + pid: 50346, + level: 30, + msg: 'hello world', + time: '2012-06-19T21:34:19.906Z', + v: 0 } ] + + +# Versioning + +The scheme I follow is most succintly described by the bootstrap guys +[here](https://github.com/twitter/bootstrap#versioning). + +tl;dr: All versions are `..` which will be incremented for +breaking backward compat and major reworks, new features without breaking +change, and bug fixes, respectively. + + + +# License + +MIT. + + + +# Future + +See "TODO.md". diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/TODO.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/TODO.md new file mode 100644 index 0000000..33c7c73 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/TODO.md @@ -0,0 +1,81 @@ +- "all" or "off" levels? log4j? logging.py? + logging.py has NOTSET === 0. I think that is only needed/used for + multi-level hierarchical effective level. +- move custom keys out to 'x' ? What about req, res? Compat issues there? + Bunyan CLI would have to deal with both for a while. Just a change in + record.v from 0 to 1. +- buffered writes to increase speed: + - I'd start with a tools/timeoutput.js for some numbers to compare + before/after. Sustained high output to a file. + - perhaps this would be a "buffered: true" option on the stream object + - then wrap the "stream" with a local class that handles the buffering + - to finish this, need the 'log.close' and `process.on('exit', ...)` + work that Trent has started. +- "canWrite" handling for full streams. Need to buffer a la log4js +- test file log with logadm rotation: does it handle that? +- test suite: + - test for a cloned logger double-`stream.end()` causing problems. + Perhaps the "closeOnExit" for existing streams should be false for + clones. + - test that a `log.clone(...)` adding a new field matching a serializer + works *and* that an existing field in the parent is not *re-serialized*. +- a "rolling-file" stream: but specifically by time, e.g. hourly. (MarkC + requested) +- split out `bunyan` cli to a "bunyan" or "bunyan-reader" or "node-bunyan-reader" + as the basis for tools to consume bunyan logs. It can grow indep of node-bunyan + for generating the logs. + It would take a Bunyan log record object and be expected to emit it. + + node-bunyan-reader + .createReadStream(path, [options]) ? + +- document "well-known" keys from bunyan CLI p.o.v.. Add "client_req". +- bunyan tool: built in less usage (a la git?) so that I don't have to + go through this: `bunyan --color master.log | less -R` +- want `bunyan -f foo.log` a la `tail -f` + + +# someday/maybe + +- More `bunyan` output formats and filtering features. +- Think about a bunyan dashboard that supports organizing and viewing logs + from multiple hosts and services. +- Syslog support. +- A vim plugin (a la http://vim.cybermirror.org/runtime/autoload/zip.vim ?) to + allow browsing (read-only) a bunyan log in rendered form. +- Some speed comparisons with others to get a feel for Bunyan's speed. +- remove "rm -rf tmp" when this fixed: +- what about promoting 'latency' field and making that easier? +- `log.close` to close streams and shutdown and `this.closed` + process.on('exit', log.close) +- bunyan cli: -c COND args a la json +- bunyan cli: more layouts (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/EnhancedPatternLayout.html) + Custom log formats (in config file? in '-f' arg) using printf or hogan.js + or whatever. Dap wants field width control for lining up. Hogan.js is + probably overkill for this. +- syslog: Josh uses https://github.com/chrisdew/node-syslog + streams: [ + ... + { + level: "warn", + type: "syslog", + syslog_facility: "LOG_LOCAL1", // one of the syslog facility defines + syslog_pid: true, // syslog logopt "LOG_PID" + syslog_cons: false // syslog logopt "LOG_CONS" + } +- bunyan "compact" or "light", '-l'? Something like. Or pehaps this (with + color) could be the default, with '-l' for long output. + 13:51.340 [src.js:20#Wuzzle.woos] WARN: This wuzzle is woosey. +- get Mark to show me dtrace provider stuff and consider adding for + logging, if helpful. +- add option to "streams" to take the raw object, not serialized. + It would be a good hook for people with custom needs that Bunyan doesn't + care about (e.g. http://loggly.com/ or hook.io or whatever). +- serializer `req_id` that pulls it from req? `log.info({req_id: req}, "hi")` +- serializer support: + - restify-server.js example -> restifyReq ? or have `req` detect that. + That is nicer for the "use all standard ones". *Does* restify req + have anything special? + - differential HTTP *client* req/res with *server* req/res. +- statsd stream? http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/ + Think about it. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/bar.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/bar.js new file mode 100644 index 0000000..d09b054 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/bar.js @@ -0,0 +1,2 @@ +s = '[2012-02-23T03:12:15.498Z] \u001b[36m INFO\u001b[39m: amon-relay/2909 on headnode: \u001b[36mGetting master URL from MAPI.\u001b[39m\u001b[90m\u001b[39m\n\u001b[90m\u001b[39m' +console.log(s) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/bin/bunyan b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/bin/bunyan new file mode 100755 index 0000000..f20a6f8 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/bin/bunyan @@ -0,0 +1,1024 @@ +#!/usr/bin/env node +// +// bunyan -- filter and pretty-print JSON logs, like Bunyan logs. +// +// See . +// + +var VERSION = "0.14.0"; + +var util = require('util'); +var pathlib = require('path'); +var vm = require('vm'); +var http = require('http'); +var fs = require('fs'); +var warn = console.warn; + + + +//---- globals and constants + +// Output modes. +var OM_PAUL = 1; +var OM_JSON = 2; +var OM_INSPECT = 3; +var OM_SIMPLE = 4; +var OM_SHORT = 5; +var OM_FROM_NAME = { + "paul": OM_PAUL, + "json": OM_JSON, + "inspect": OM_INSPECT, + "simple": OM_SIMPLE, + "short": OM_SHORT +}; + + +// Levels +var TRACE = 10; +var DEBUG = 20; +var INFO = 30; +var WARN = 40; +var ERROR = 50; +var FATAL = 60; + +var levelFromName = { + 'trace': TRACE, + 'debug': DEBUG, + 'info': INFO, + 'warn': WARN, + 'error': ERROR, + 'fatal': FATAL +}; +var nameFromLevel = {}; +var upperNameFromLevel = {}; +var upperPaddedNameFromLevel = {}; +Object.keys(levelFromName).forEach(function (name) { + var lvl = levelFromName[name]; + nameFromLevel[lvl] = name; + upperNameFromLevel[lvl] = name.toUpperCase(); + upperPaddedNameFromLevel[lvl] = ( + name.length === 4 ? ' ' : '') + name.toUpperCase(); +}); + + + +//---- support functions + +function getVersion() { + return VERSION; +} + + +var format = util.format; +if (!format) { + // If not node 0.6, then use its `util.format`: + // : + var inspect = util.inspect; + var formatRegExp = /%[sdj%]/g; + format = function format(f) { + if (typeof f !== 'string') { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': return JSON.stringify(args[i++]); + case '%%': return '%'; + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (x === null || typeof x !== 'object') { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; + }; +} + +function indent(s) { + return ' ' + s.split(/\r?\n/).join('\n '); +} + +function objCopy(obj) { + if (obj === null) { + return null; + } else if (Array.isArray(obj)) { + return obj.slice(); + } else { + var copy = {}; + Object.keys(obj).forEach(function (k) { + copy[k] = obj[k]; + }); + return copy; + } +} + +function printHelp() { + console.log("Usage:"); + console.log(" bunyan [OPTIONS] [FILE ...]"); + console.log(""); + console.log("Filter and pretty-print Bunyan log file content."); + console.log(""); + console.log("General options:"); + console.log(" -h, --help print this help info and exit"); + console.log(" --version print version of this command and exit"); + console.log(""); + console.log("Filtering options:"); + console.log(" -l, --level LEVEL"); + console.log(" Only show messages at or above the specified level."); + console.log(" You can specify level *names* or numeric values."); + console.log(" (See 'Log Levels' below.)"); + console.log(" -c, --condition CONDITION"); + console.log(" Run each log message through the condition"); + console.log(" and only show those that resolve to a truish value."); + console.log(" E.g. `-c 'pid == 123'`, `-c 'level == 50'`. You must"); + console.log(" use the numeric values for filtering by level."); + console.log(" --strict Suppress all but legal Bunyan JSON log lines. By default"); + console.log(" non-JSON, and non-Bunyan lines are passed through."); + console.log(""); + console.log("Output options:"); + console.log(" --color Colorize output. Defaults to try if output"); + console.log(" stream is a TTY."); + console.log(" --no-color Force no coloring (e.g. terminal doesn't support it)"); + console.log(" -o, --output MODE"); + console.log(" Specify an output mode/format. One of"); + console.log(" paul: (the default) pretty"); + console.log(" json: JSON output, 2-space indent"); + console.log(" json-N: JSON output, N-space indent, e.g. 'json-4'"); + console.log(" inspect: node.js `util.inspect` output"); + console.log(" short: like paul, but more concise"); + console.log(" -j shortcut for `-o json`"); + console.log(""); + console.log("Log Levels:"); + console.log(" Either numeric values or their associated strings are valid for the"); + console.log(" -l|--level argument. However, -c|--condition scripts will see a numeric"); + console.log(" 'level' value, not a string."); + console.log(""); + Object.keys(levelFromName).forEach(function(name) { + var n = name; + while (n.length < 6) + n += " "; + console.log(" %s %d", n, levelFromName[name]); + }); + console.log(""); + console.log("See for more complete docs."); + console.log("Please report bugs to ."); +} + +/* + * If the user specifies multiple input sources, we want to print out records + * from all sources in a single, chronologically ordered stream. To do this + * efficiently, we first assume that all records within each source are ordered + * already, so we need only keep track of the next record in each source and + * the time of the last record emitted. To avoid excess memory usage, we + * pause() streams that are ahead of others. + * + * "streams" is an object indexed by source name (file name) which specifies: + * + * stream Actual stream object, so that we can pause and resume it. + * + * records Array of log records we've read, but not yet emitted. Each + * record includes "line" (the raw line), "rec" (the JSON + * record), and "time" (the parsed time value). + * + * done Whether the stream has any more records to emit. + */ +var streams = {}; + +function gotRecord(file, line, rec, opts, stylize) +{ + var time = new Date(rec.time); + + streams[file]['records'].push({ line: line, rec: rec, time: time }); + emitNextRecord(opts, stylize); +} + +function filterRecord(rec, opts) +{ + if (opts.level && rec.level < opts.level) { + return false; + } + + if (opts.conditions) { + for (var i = 0; i < opts.conditions.length; i++) { + var pass = opts.conditions[i].runInNewContext(rec); + if (!pass) + return false; + } + } + + return true; +} + +function emitNextRecord(opts, stylize) +{ + var ofile, ready, minfile, rec; + + for (;;) { + /* + * Take a first pass through the input streams to see if we have a record + * from all of them. If not, we'll pause any streams for which we do + * already have a record (to avoid consuming excess memory) and then wait + * until we have records from the others before emitting the next record. + * + * As part of the same pass, we look for the earliest record we have not yet + * emitted. + */ + minfile = undefined; + ready = true; + for (ofile in streams) { + + if (streams[ofile].stream === null || + (!streams[ofile].done && streams[ofile].records.length === 0)) { + ready = false; + break; + } + + if (streams[ofile].records.length > 0 && (minfile === undefined || + streams[minfile].records[0].time > streams[ofile].records[0].time)) { + minfile = ofile; + } + } + + if (!ready || minfile === undefined) { + for (ofile in streams) { + if (!streams[ofile].stream || streams[ofile].done) + continue; + + if (streams[ofile].records.length > 0) { + if (!streams[ofile].paused) { + streams[ofile].paused = true; + streams[ofile].stream.pause(); + } + } else if (streams[ofile].paused) { + streams[ofile].paused = false; + streams[ofile].stream.resume(); + } + } + + return; + } + + /* + * Emit the next record for "minfile", and invoke ourselves again to make + * sure we emit as many records as we can right now. + */ + rec = streams[minfile].records.shift(); + emitRecord(rec.rec, rec.line, opts, stylize); + } +} + +/** + * Parse the command-line options and arguments into an object. + * + * { + * 'args': [...] // arguments + * 'help': true, // true if '-h' option given + * // etc. + * } + * + * @return {Object} The parsed options. `.args` is the argument list. + * @throws {Error} If there is an error parsing argv. + */ +function parseArgv(argv) { + var parsed = { + args: [], + help: false, + color: process.stdout.isTTY, + outputMode: OM_PAUL, + jsonIndent: 2, + level: null, + conditions: null, + strict: false + }; + + // Turn '-iH' into '-i -H', except for argument-accepting options. + var args = argv.slice(2); // drop ['node', 'scriptname'] + var newArgs = []; + var optTakesArg = {'d': true, 'o': true, 'c': true, 'l': true}; + for (var i = 0; i < args.length; i++) { + if (args[i].charAt(0) === "-" && args[i].charAt(1) !== '-' && args[i].length > 2) { + var splitOpts = args[i].slice(1).split(""); + for (var j = 0; j < splitOpts.length; j++) { + newArgs.push('-' + splitOpts[j]); + if (optTakesArg[splitOpts[j]]) { + var optArg = splitOpts.slice(j+1).join(""); + if (optArg.length) { + newArgs.push(optArg); + } + break; + } + } + } else { + newArgs.push(args[i]); + } + } + args = newArgs; + + var condDefines = []; + Object.keys(upperNameFromLevel).forEach(function (lvl) { + condDefines.push( + format("Object.prototype.%s = %s;", upperNameFromLevel[lvl], lvl)); + }); + condDefines = condDefines.join('\n') + '\n'; + + var endOfOptions = false; + while (args.length > 0) { + var arg = args.shift(); + switch(arg) { + case "--": + endOfOptions = true; + break; + case "-h": // display help and exit + case "--help": + parsed.help = true; + break; + case "--version": + parsed.version = true; + break; + case "--strict": + parsed.strict = true; + break; + case "--color": + parsed.color = true; + break; + case "--no-color": + parsed.color = false; + break; + case "-o": + case "--output": + var name = args.shift(); + var idx = name.lastIndexOf('-'); + if (idx !== -1) { + var indent = Number(name.slice(idx+1)); + if (! isNaN(indent)) { + parsed.jsonIndent = indent; + name = name.slice(0, idx); + } + } + parsed.outputMode = OM_FROM_NAME[name]; + if (parsed.outputMode === undefined) { + throw new Error("unknown output mode: '"+name+"'"); + } + break; + case "-j": // output with JSON.stringify + parsed.outputMode = OM_JSON; + break; + case "-l": + case "--level": + var levelArg = args.shift(); + var level = +(levelArg); + if (isNaN(level)) { + level = +levelFromName[levelArg.toLowerCase()]; + } + if (isNaN(level)) { + throw new Error("unknown level value: '"+levelArg+"'"); + } + parsed.level = level; + break; + case "-c": + case "--condition": + var condition = args.shift(); + parsed.conditions = parsed.conditions || []; + var scriptName = 'bunyan-condition-'+parsed.conditions.length; + var script = vm.createScript(condDefines + condition, scriptName); + parsed.conditions.push(script); + break; + default: // arguments + if (!endOfOptions && arg.length > 0 && arg[0] === '-') { + throw new Error("unknown option '"+arg+"'"); + } + parsed.args.push(arg); + break; + } + } + //TODO: '--' handling and error on a first arg that looks like an option. + + return parsed; +} + + +function isInteger(s) { + return (s.search(/^-?[0-9]+$/) == 0); +} + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +// Suggested colors (some are unreadable in common cases): +// - Good: cyan, yellow (limited use), grey, bold, green, magenta, red +// - Bad: blue (not visible on cmd.exe) +var colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +function stylizeWithColor(str, color) { + if (!str) + return ''; + var codes = colors[color]; + if (codes) { + return '\033[' + codes[0] + 'm' + str + + '\033[' + codes[1] + 'm'; + } else { + return str; + } +} + +function stylizeWithoutColor(str, color) { + return str; +} + + +/** + * Is this a valid Bunyan log record. + */ +function isValidRecord(rec) { + if (rec.v == null || + rec.level == null || + rec.name == null || + rec.hostname == null || + rec.pid == null || + rec.time == null || + rec.msg == null) { + // Not valid Bunyan log. + return false; + } else { + return true; + } +} + + +/** + * Parses the given log line and either emits it right away (for invalid + * records) or enqueues it for emitting later when it's the next line to show. + */ +function handleLogLine(file, line, opts, stylize) { + // Emit non-JSON lines immediately. + var rec; + if (!line) { + if (!opts.strict) emit(line + '\n'); + return; + } else if (line[0] !== '{') { + if (!opts.strict) emit(line + '\n'); // not JSON + return; + } else { + try { + rec = JSON.parse(line); + } catch(e) { + if (!opts.strict) emit(line + '\n'); + return; + } + } + + if (!isValidRecord(rec)) { + if (!opts.strict) emitRecord(rec, line, opts, stylize); + return; + } + + if (!filterRecord(rec, opts)) + return; + + if (file === null) + return emitRecord(rec, line, opts, stylize); + + return gotRecord(file, line, rec, opts, stylize); +} + +/** + * Print out a single result, considering input options. + */ +function emitRecord(rec, line, opts, stylize) { + var short = false; + + switch (opts.outputMode) { + case OM_SHORT: + short = true; + /* jsl:fall-thru */ + + case OM_PAUL: + // [time] LEVEL: name[/component]/pid on hostname (src): msg* (extras...) + // msg* + // -- + // long and multi-line extras + // ... + // If 'msg' is single-line, then it goes in the top line. + // If 'req', show the request. + // If 'res', show the response. + // If 'err' and 'err.stack' then show that. + if (!isValidRecord(rec)) { + return emit(line + '\n'); + } + + delete rec.v; + + /* + * We assume the Date is formatted according to ISO8601, in which case we + * can safely chop off the date information. + */ + if (short && rec.time[10] == 'T') { + var time = rec.time.substr(11); + time = stylize(time, 'XXX'); + } else { + var time = stylize('[' + rec.time + ']', 'XXX'); + } + + delete rec.time; + + var nameStr = rec.name; + delete rec.name; + + if (rec.component) { + nameStr += '/' + rec.component; + } + delete rec.component; + + if (!short) + nameStr += '/' + rec.pid; + delete rec.pid; + + var level = (upperPaddedNameFromLevel[rec.level] || "LVL" + rec.level); + if (opts.color) { + var colorFromLevel = { + 10: 'grey', // TRACE + 20: 'grey', // DEBUG + 30: 'cyan', // INFO + 40: 'magenta', // WARN + 50: 'red', // ERROR + 60: 'inverse', // FATAL + }; + level = stylize(level, colorFromLevel[rec.level]); + } + delete rec.level; + + var src = ""; + if (rec.src && rec.src.file) { + var s = rec.src; + if (s.func) { + src = format(" (%s:%d in %s)", s.file, s.line, s.func); + } else { + src = format(" (%s:%d)", s.file, s.line); + } + src = stylize(src, 'green'); + } + delete rec.src; + + var hostname = rec.hostname; + delete rec.hostname; + + var extras = []; + var details = []; + + if (rec.req_id) { + extras.push("req_id=" + rec.req_id); + } + delete rec.req_id; + + if (rec.latency) { + extras.push(rec.latency + "ms"); + } + delete rec.latency; + + var onelineMsg; + if (rec.msg.indexOf('\n') !== -1) { + onelineMsg = ''; + details.push(indent(stylize(rec.msg, 'cyan'))); + } else { + onelineMsg = ' ' + stylize(rec.msg, 'cyan'); + } + delete rec.msg; + + if (rec.req) { + var req = rec.req; + var headers = req.headers; + var s = format("%s %s HTTP/%s%s", req.method, + req.url, + req.httpVersion || "1.1", + (headers + ? '\n' + Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n') + : '') + ); + delete req.url; + delete req.method; + delete req.httpVersion; + delete req.headers; + if (req.body) { + s += '\n\n' + (typeof(req.body) === 'object' + ? JSON.stringify(req.body, null, 2) : req.body); + delete req.body; + } + if (req.trailers && Object.keys(req.trailers) > 0) { + s += '\n' + Object.keys(req.trailers).map( + function (t) { return t + ': ' + req.trailers[t]; }).join('\n'); + } + delete req.trailers; + details.push(indent(s)); + // E.g. for extra 'foo' field on 'req', add 'req.foo' at top-level. + // This *does* have the potential to stomp on a literal 'req.foo' key. + Object.keys(req).forEach(function (k) { + rec["req." + k] = req[k]; + }) + } + delete rec.req; + + if (rec.client_req) { + var client_req = rec.client_req; + var headers = client_req.headers; + var hostHeaderLine = ''; + var s = ''; + if (client_req.address) { + hostHeaderLine = 'Host: ' + client_req.address; + if (client_req.port) + hostHeaderLine += ':' + client_req.port; + hostHeaderLine += '\n'; + } + delete client_req.headers; + delete client_req.address; + delete client_req.port; + s += format("%s %s HTTP/%s\n%s%s", client_req.method, + client_req.url, + client_req.httpVersion || "1.1", + hostHeaderLine, + Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n')); + delete client_req.method; + delete client_req.url; + delete client_req.httpVersion; + if (client_req.body) { + s += '\n\n' + (typeof(client_req.body) === 'object' + ? JSON.stringify(client_req.body, null, 2) : client_req.body); + delete client_req.body; + } + // E.g. for extra 'foo' field on 'client_req', add 'client_req.foo' at + // top-level. This *does* have the potential to stomp on a literal + // 'client_req.foo' key. + Object.keys(client_req).forEach(function (k) { + rec["client_req." + k] = client_req[k]; + }) + details.push(indent(s)); + } + delete rec.client_req; + + if (rec.res) { + var res = rec.res; + var s = ''; + if (res.header) { + s += res.header.trimRight(); + } else if (res.headers) { + if (res.statusCode) { + s += format("HTTP/1.1 %s %s\n", res.statusCode, + http.STATUS_CODES[res.statusCode]); + } + var headers = res.headers; + s += Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n'); + } + delete res.header; + delete res.headers; + delete res.statusCode; + if (res.body) { + s += '\n\n' + res.body + delete res.body; + } + if (res.trailer) { + s += '\n' + res.trailer; + } + delete res.trailer; + if (s) { + details.push(indent(s)); + } + // E.g. for extra 'foo' field on 'res', add 'res.foo' at top-level. + // This *does* have the potential to stomp on a literal 'res.foo' key. + Object.keys(res).forEach(function (k) { + rec["res." + k] = res[k]; + }) + } + delete rec.res; + + if (rec.err && rec.err.stack) { + details.push(indent(rec.err.stack)); + delete rec.err; + } + + var leftover = Object.keys(rec); + for (var i = 0; i < leftover.length; i++) { + var key = leftover[i]; + var value = rec[key]; + var stringified = false; + if (typeof(value) !== 'string') { + value = JSON.stringify(value, null, 2); + stringified = true; + } + if (value.indexOf('\n') !== -1 || value.length > 50) { + details.push(indent(key + ': ' + value)); + } else if (!stringified && (value.indexOf(' ') != -1 || + value.length === 0)) { + extras.push(key + '=' + JSON.stringify(value)); + } else { + extras.push(key + '=' + value); + } + } + + extras = stylize( + (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'grey'); + details = stylize( + (details.length ? details.join('\n --\n') + '\n' : ''), 'grey'); + if (!short) + emit(format("%s %s: %s on %s%s:%s%s\n%s", + time, + level, + nameStr, + hostname || "", + src, + onelineMsg, + extras, + details)); + else + emit(format("%s %s %s:%s%s\n%s", + time, + level, + nameStr, + onelineMsg, + extras, + details)); + break; + + case OM_INSPECT: + emit(util.inspect(rec, false, Infinity, true) + '\n'); + break; + + case OM_JSON: + emit(JSON.stringify(rec, null, opts.jsonIndent) + '\n'); + break; + + case OM_SIMPLE: + // + if (!isValidRecord(rec)) { + return emit(line + '\n'); + } + emit(format("%s - %s\n", upperNameFromLevel[rec.level] || "LVL" + rec.level, + rec.msg)); + break; + default: + throw new Error("unknown output mode: "+opts.outputMode); + } +} + + +var stdoutFlushed = true; +function emit(s) { + // TODO:PERF If this is try/catch is too slow (too granular): move up to + // mainline and be sure to only catch this particular error. + try { + stdoutFlushed = process.stdout.write(s); + } catch (e) { + // Handle any exceptions in stdout writing in the "error" event above. + } +} + +process.stdout.on("error", function (err) { + if (err.code === "EPIPE") { + // Pass. See . + } else { + warn(err); + drainStdoutAndExit(1); + } +}); + + +/** + * A hacked up version of "process.exit" that will first drain stdout + * before exiting. *WARNING: This doesn't stop event processing.* IOW, + * callers have to be careful that code following this call isn't + * accidentally executed. + * + * In node v0.6 "process.stdout and process.stderr are blocking when they + * refer to regular files or TTY file descriptors." However, this hack might + * still be necessary in a shell pipeline. + */ +function drainStdoutAndExit(code) { + process.stdout.on('drain', function () { + process.exit(code); + }); + if (stdoutFlushed) { + process.exit(code); + } +} + + +/** + * Process all input from stdin. + * + * @params opts {Object} Bunyan options object. + * @param stylize {Function} Output stylize function to use. + * @param callback {Function} `function ()` + */ +function processStdin(opts, stylize, callback) { + var leftover = ""; // Left-over partial line from last chunk. + var stdin = process.stdin; + stdin.resume(); + stdin.setEncoding('utf8'); + stdin.on('data', function (chunk) { + var lines = chunk.split(/\r\n|\n/); + var length = lines.length; + if (length === 1) { + leftover += lines[0]; + return; + } + + if (length > 1) { + handleLogLine(null, leftover + lines[0], opts, stylize); + } + leftover = lines.pop(); + length -= 1; + for (var i=1; i < length; i++) { + handleLogLine(null, lines[i], opts, stylize); + } + }); + stdin.on('end', function () { + if (leftover) { + handleLogLine(null, leftover, opts, stylize); + leftover = ''; + } + callback(); + }); +} + + +/** + * Process all input from the given log file. + * + * @param file {String} Log file path to process. + * @params opts {Object} Bunyan options object. + * @param stylize {Function} Output stylize function to use. + * @param callback {Function} `function ()` + */ +function processFile(file, opts, stylize, callback) { + var stream = fs.createReadStream(file); + if (/\.gz$/.test(file)) { + stream = stream.pipe(require('zlib').createGunzip()); + } + // Manually decode streams - lazy load here as per node/lib/fs.js + var decoder = new (require('string_decoder').StringDecoder)('utf8'); + + streams[file].stream = stream; + + stream.on('error', function (err) { + streams[file].done = true; + callback(err); + }); + + var leftover = ''; // Left-over partial line from last chunk. + stream.on('data', function (data) { + var chunk = decoder.write(data); + if (!chunk.length) { + return; + } + var lines = chunk.split(/\r\n|\n/); + var length = lines.length; + if (length === 1) { + leftover += lines[0]; + return; + } + + if (length > 1) { + handleLogLine(file, leftover + lines[0], opts, stylize); + } + leftover = lines.pop(); + length -= 1; + for (var i=1; i < length; i++) { + handleLogLine(file, lines[i], opts, stylize); + } + }); + + stream.on('end', function () { + streams[file].done = true; + if (leftover) { + handleLogLine(file, leftover, opts, stylize); + leftover = ''; + } else { + emitNextRecord(opts, stylize); + } + callback(); + }); +} + + +/** + * From node async module. + */ +function asyncForEach(arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + arr.forEach(function (x) { + iterator(x, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(); + } + } + }); + }); +}; + + + +//---- mainline + +function main(argv) { + var opts; + try { + opts = parseArgv(argv); + } catch (e) { + warn("bunyan: error: %s", e.message); + return drainStdoutAndExit(1); + } + if (opts.help) { + printHelp(); + return; + } + if (opts.version) { + console.log("bunyan " + getVersion()); + return; + } + var stylize = (opts.color ? stylizeWithColor : stylizeWithoutColor); + + var retval = 0; + if (opts.args.length > 0) { + var files = opts.args; + files.forEach(function (file) { + streams[file] = { stream: null, records: [], done: false } + }); + asyncForEach(files, + function (file, next) { + processFile(file, opts, stylize, function (err) { + if (err) { + warn('bunyan: %s', err.message); + retval += 1; + } + next(); + }); + }, + function (err) { + if (err) { + warn("bunyan: unexpected error: %s", err.stack || err); + return drainStdoutAndExit(1); + } + process.exit(retval); + } + ); + } else { + processStdin(opts, stylize, function () { + process.exit(retval); + }); + } +} + +if (require.main === module) { + // HACK guard for . + // We override the `process.stdout.end` guard that core node.js puts in + // place. The real fix is that `.end()` shouldn't be called on stdout + // in node core. Node v0.6.9 fixes that. Only guard for v0.6.0..v0.6.8. + var nodeVer = process.versions.node.split('.').map(Number); + if ([0,6,0] <= nodeVer && nodeVer <= [0,6,8]) { + var stdout = process.stdout; + stdout.end = stdout.destroy = stdout.destroySoon = function() { + /* pass */ + }; + } + + main(process.argv); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/err.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/err.js new file mode 100644 index 0000000..2eacc7f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/err.js @@ -0,0 +1,58 @@ +// Example logging an error: + +var http = require('http'); +var Logger = require('../lib/bunyan'); +var util = require('util'); + +var log = new Logger({ + name: 'myserver', + serializers: { + err: Logger.stdSerializers.err, // <--- use this + } +}); + +try { + throw new TypeError('boom'); +} catch (err) { + log.warn({err: err}, 'operation went boom: %s', err) // <--- here +} + +log.info(new TypeError('how about this?')) // <--- alternatively this + + +try { + throw 'boom string'; +} catch (err) { + log.error(err) +} + +/* BEGIN JSSTYLED */ +/** + * + * $ node err.js | ../bin/bunyan -j + * { + * "name": "myserver", + * "hostname": "banana.local", + * "err": { + * "stack": "TypeError: boom\n at Object. (/Users/trentm/tm/node-bunyan/examples/err.js:15:9)\n at Module._compile (module.js:411:26)\n at Object..js (module.js:417:10)\n at Module.load (module.js:343:31)\n at Function._load (module.js:302:12)\n at Array.0 (module.js:430:10)\n at EventEmitter._tickCallback (node.js:126:26)", + * "name": "TypeError", + * "message": "boom" + * }, + * "level": 4, + * "msg": "operation went boom: TypeError: boom", + * "time": "2012-02-02T04:42:53.206Z", + * "v": 0 + * } + * $ node err.js | ../bin/bunyan + * [2012-02-02T05:02:39.412Z] WARN: myserver on banana.local: operation went boom: TypeError: boom + * TypeError: boom + * at Object. (/Users/trentm/tm/node-bunyan/examples/err.js:15:9) + * at Module._compile (module.js:411:26) + * at Object..js (module.js:417:10) + * at Module.load (module.js:343:31) + * at Function._load (module.js:302:12) + * at Array.0 (module.js:430:10) + * at EventEmitter._tickCallback (node.js:126:26) + * + */ +/* END JSSTYLED */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/handle-fs-error.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/handle-fs-error.js new file mode 100644 index 0000000..af08056 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/handle-fs-error.js @@ -0,0 +1,36 @@ +// Example handling an fs error for a Bunyan-created +// stream: we create a logger to a file that is read-only. + +var fs = require('fs'); +var path = require('path'); +var Logger = require('../lib/bunyan'); + +var FILENAME = 'handle-fs-error.log'; +var S_IWUSR = 00200; // mask for owner write permission in stat mode + +console.warn('- Log file is "%s".', FILENAME); +if (!path.existsSync(FILENAME)) { + console.warn('- Touch log file.'); + fs.writeFileSync(FILENAME, 'touch\n'); +} +if (fs.statSync(FILENAME).mode & S_IWUSR) { + console.warn('- Make log file read-only.'); + fs.chmodSync(FILENAME, 0444); +} + +console.warn('- Create logger.') +var log = new Logger({name: 'handle-fs-error', streams: [{path: FILENAME}]}); + +log.on('error', function (err) { + console.warn('- The logger emitted an error:', err); +}); + +console.warn('- Call log.info(...).') +log.info('info log message'); +console.warn('- Called log.info(...).') + +setTimeout(function () { + console.warn('- Call log.warn(...).') + log.warn('warn log message'); + console.warn('- Called log.warn(...).') +}, 1000); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/hi.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/hi.js new file mode 100644 index 0000000..0f5f21b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/hi.js @@ -0,0 +1,32 @@ +var Logger = require('../lib/bunyan'); + +// Basic usage. +var log = new Logger({name: 'myapp', level: 'info', src: true}); + +// isInfoEnabled replacement +console.log('log.info() is:', log.info()) + +// `util.format`-based printf handling +log.info('hi'); +log.info('hi', 'trent'); +log.info('hi %s there', true); + +// First arg as an object adds fields to the log record. +log.info({foo:'bar', multiline:'one\ntwo\nthree'}, 'hi %d', 1, 'two', 3); + + +// Shows `log.child(...)` to specialize a logger for a sub-component. +console.log('\n') + +function Wuzzle(options) { + this.log = options.log; + this.log.info('creating a wuzzle') +} + +Wuzzle.prototype.woos = function () { + this.log.warn('This wuzzle is woosey.') +} + +var wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})}); +wuzzle.woos(); +log.info('done with the wuzzle') diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/level.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/level.js new file mode 100644 index 0000000..80bb563 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/level.js @@ -0,0 +1,46 @@ +// Play with setting levels. +// +// TODO: put this in a damn test suite + +var Logger = require('../lib/bunyan'), + DEBUG = Logger.DEBUG, + INFO = Logger.INFO, + WARN = Logger.WARN; +var assert = require('assert'); + +// Basic usage. +var log = new Logger({ + name: 'example-level', + streams: [ + { + name: 'stdout', + stream: process.stdout, + level: 'debug' + }, + { + name: 'stderr', + stream: process.stderr + } + ] +}); + +assert.equal(log.level(), DEBUG); +assert.equal(log.levels()[0], DEBUG); +assert.equal(log.levels()[1], INFO); +assert.equal(log.levels(0), DEBUG); +assert.equal(log.levels(1), INFO); + +assert.equal(log.levels('stdout'), DEBUG) +try { + log.levels('foo') +} catch (e) { + assert.ok(e.message.indexOf('name') !== -1) +} + +log.trace('no one should see this') +log.debug('should see this once (on stdout)') +log.info('should see this twice') +log.levels('stdout', INFO) +log.debug('no one should see this either') +log.level('trace') +log.trace('should see this twice as 4th and 5th emitted log messages') diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/multi.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/multi.js new file mode 100644 index 0000000..81400ee --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/multi.js @@ -0,0 +1,20 @@ +var Logger = require('../lib/bunyan'); +log = new Logger({ + name: 'amon', + streams: [ + { + level: 'info', + stream: process.stdout, + }, + { + level: 'error', + path: 'multi.log' + } + ] +}); + + +log.debug('hi nobody on debug'); +log.info('hi stdout on info'); +log.error('hi both on error'); +log.fatal('hi both on fatal'); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/raw-stream.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/raw-stream.js new file mode 100644 index 0000000..32b905b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/raw-stream.js @@ -0,0 +1,37 @@ +// Example of a "raw" stream in a Bunyan Logger. A raw stream is one to +// which log record *objects* are written instead of the JSON-serialized +// string. + +var Logger = require('../lib/bunyan'); + + +/** + * A raw Bunyan Logger stream object. It takes raw log records and writes + * them to stdout with an added "yo": "yo" field. + */ +function MyRawStream() {} +MyRawStream.prototype.write = function (rec) { + if (typeof (rec) !== 'object') { + console.error('error: raw stream got a non-object record: %j', rec) + } else { + rec.yo = 'yo'; + process.stdout.write(JSON.stringify(rec) + '\n'); + } +} + + +// A Logger using the raw stream. +var log = new Logger({ + name: 'raw-example', + streams: [ + { + level: 'info', + stream: new MyRawStream(), + type: 'raw' + }, + ] +}); + + +log.info('hi raw stream'); +log.info({foo: 'bar'}, 'added "foo" key'); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/ringbuffer.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/ringbuffer.js new file mode 100644 index 0000000..74ffb31 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/ringbuffer.js @@ -0,0 +1,14 @@ +/* Create a ring buffer that stores the last 100 records. */ +var bunyan = require('..'); +var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); +var log = new bunyan({ + name: 'foo', + streams: [{ + type: 'raw', + stream: ringbuffer, + level: 'debug' + }] +}); + +log.info('hello world'); +console.log(ringbuffer.records); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/server.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/server.js new file mode 100644 index 0000000..c8bdac1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/server.js @@ -0,0 +1,63 @@ +// Example logging HTTP server request and response objects. + +var http = require('http'); +var Logger = require('../lib/bunyan'); + +var log = new Logger({ + name: 'myserver', + serializers: { + req: Logger.stdSerializers.req, + res: Logger.stdSerializers.res + } +}); + +var server = http.createServer(function (req, res) { + log.info({req: req}, 'start request'); // <-- this is the guy we're testing + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Hello World\n'); + log.info({res: res}, 'done response'); // <-- this is the guy we're testing +}); +server.listen(1337, '127.0.0.1', function () { + log.info('server listening'); + var options = { + port: 1337, + hostname: '127.0.0.1', + path: '/path?q=1#anchor', + headers: { + 'X-Hi': 'Mom' + } + }; + var req = http.request(options); + req.on('response', function (res) { + res.on('end', function () { + process.exit(); + }) + }); + req.write('hi from the client'); + req.end(); +}); + + +/* BEGIN JSSTYLED */ +/** + * + * $ node server.js + * {"service":"myserver","hostname":"banana.local","level":3,"msg":"server listening","time":"2012-02-02T05:32:13.257Z","v":0} + * {"service":"myserver","hostname":"banana.local","req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","connection":"close"}},"level":3,"msg":"start request","time":"2012-02-02T05:32:13.260Z","v":0} + * {"service":"myserver","hostname":"banana.local","res":{"statusCode":200,"_hasBody":true,"_header":"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n","_trailer":""},"level":3,"msg":"done response","time":"2012-02-02T05:32:13.261Z","v":0} + * + * $ node server.js | ../bin/bunyan + * [2012-02-02T05:32:16.006Z] INFO: myserver on banana.local: server listening + * [2012-02-02T05:32:16.010Z] INFO: myserver on banana.local: start request + * GET /path?q=1#anchor + * x-hi: Mom + * connection: close + * [2012-02-02T05:32:16.011Z] INFO: myserver on banana.local: done response + * HTTP/1.1 200 OK + * Content-Type: text/plain + * Connection: close + * Transfer-Encoding: chunked + * (body) + * + */ +/* END JSSTYLED */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/src.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/src.js new file mode 100644 index 0000000..fe4ab50 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/src.js @@ -0,0 +1,25 @@ +// Show the usage of `src: true` config option to get log call source info in +// log records (the `src` field). + +var Logger = require('../lib/bunyan'); + +var log = new Logger({name: 'src-example', src: true}); + +log.info('one'); +log.info('two'); +function doSomeFoo() { + log.info({foo:'bar'}, 'three'); +} +doSomeFoo(); + +function Wuzzle(options) { + this.log = options.log; + this.log.info('creating a wuzzle') +} +Wuzzle.prototype.woos = function () { + this.log.warn('This wuzzle is woosey.') +} + +var wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})}); +wuzzle.woos(); +log.info('done with the wuzzle') diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/unstringifyable.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/unstringifyable.js new file mode 100644 index 0000000..ddad545 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/examples/unstringifyable.js @@ -0,0 +1,12 @@ +// See how bunyan behaves with an un-stringify-able object. +var Logger = require('../lib/bunyan'); + +var log = new Logger({src: true, name: 'foo'}); + +// Make a circular object (cannot be JSON-ified). +var myobj = { + foo: 'bar' +}; +myobj.myobj = myobj; + +log.info({obj: myobj}, 'hi there'); // <--- here diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/follow.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/follow.js new file mode 100644 index 0000000..113dcaa --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/follow.js @@ -0,0 +1,25 @@ +//var fs = require('fs'); + +//fs.watch('a.log', {}, function (ev, fn) { +// console.log('event', ev, fn); +//}); + +// fs.open(path, flags, [mode], [callback]) +// fs.openSync(path, flags, [mode]) + +// fs.read(fd, buffer, offset, length, position, [callback]) + + +//var tailf = require('tailf'); +// +//var watchinglog = new tailf.simpleTailf('a.log'); +// +//watchinglog.on('data', function (data) { +// console.log('Data arrived: ' , data.toString()); +//}); + +var tail = require('tailfd').tail, +watcher = tail('a.log',function(line,tailInfo){ + //default line listener. optional. + console.log('line of data> ',line); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/foo.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/foo.js new file mode 100644 index 0000000..8f769a2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/foo.js @@ -0,0 +1,6 @@ +var b = require('./lib/bunyan'); +var log = b.createLogger({name: 'foo'}) +var data = new Buffer([72,84,84,80,47,49,46,49]); +log.info({buf: data}, 'my message'); + +//console.log(data.length, data.toString()) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/lib/bunyan.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/lib/bunyan.js new file mode 100644 index 0000000..76efcf3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/lib/bunyan.js @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * The bunyan logging library for node.js. + */ + +var VERSION = '0.14.0'; + +// Bunyan log format version. This becomes the 'v' field on all log records. +// `0` is until I release a version '1.0.0' of node-bunyan. Thereafter, +// starting with `1`, this will be incremented if there is any backward +// incompatible change to the log record format. Details will be in +// 'CHANGES.md' (the change log). +var LOG_VERSION = 0; + + +var xxx = function xxx(s) { // internal dev/debug logging + var args = ['XX' + 'X: '+s].concat(Array.prototype.slice.call(arguments, 1)); + console.error.apply(this, args); +}; +var xxx = function xxx() {}; // uncomment to turn of debug logging + + +var os = require('os'); +var fs = require('fs'); +var util = require('util'); +var assert = require('assert'); +var EventEmitter = require('events').EventEmitter; + + + +//---- Internal support stuff + +function objCopy(obj) { + if (obj === null) { + return null; + } else if (Array.isArray(obj)) { + return obj.slice(); + } else { + var copy = {}; + Object.keys(obj).forEach(function (k) { + copy[k] = obj[k]; + }); + return copy; + } +} + +var format = util.format; +if (!format) { + // If not node 0.6, then use its `util.format`: + // : + var inspect = util.inspect; + var formatRegExp = /%[sdj%]/g; + format = function format(f) { + if (typeof (f) !== 'string') { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function (x) { + if (i >= len) + return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': return JSON.stringify(args[i++], safeCycles()); + case '%%': return '%'; + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (x === null || typeof (x) !== 'object') { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; + }; +} + + +/** + * Gather some caller info 3 stack levels up. + * See . + */ +function getCaller3Info() { + var obj = {}; + var saveLimit = Error.stackTraceLimit; + var savePrepare = Error.prepareStackTrace; + Error.stackTraceLimit = 3; + Error.captureStackTrace(this, getCaller3Info); + Error.prepareStackTrace = function (_, stack) { + var caller = stack[2]; + obj.file = caller.getFileName(); + obj.line = caller.getLineNumber(); + var func = caller.getFunctionName(); + if (func) + obj.func = func; + }; + this.stack; + Error.stackTraceLimit = saveLimit; + Error.prepareStackTrace = savePrepare; + return obj; +} + + +/** + * Warn about an bunyan processing error. + * + * If file/line are given, this makes an attempt to warn on stderr only once. + * + * @param msg {String} Message with which to warn. + * @param file {String} Optional. File path relevant for the warning. + * @param line {String} Optional. Line number in `file` path relevant for + * the warning. + */ +function _warn(msg, file, line) { + assert.ok(msg); + var key; + if (file && line) { + key = file + ':' + line; + if (_warned[key]) { + return; + } + _warned[key] = true; + } + process.stderr.write(msg + '\n'); +} +var _warned = {}; + + + +//---- Levels + +var TRACE = 10; +var DEBUG = 20; +var INFO = 30; +var WARN = 40; +var ERROR = 50; +var FATAL = 60; + +var levelFromName = { + 'trace': TRACE, + 'debug': DEBUG, + 'info': INFO, + 'warn': WARN, + 'error': ERROR, + 'fatal': FATAL +}; + + +/** + * Resolve a level number, name (upper or lowercase) to a level number value. + * + * @api public + */ +function resolveLevel(nameOrNum) { + var level = (typeof (nameOrNum) === 'string' + ? levelFromName[nameOrNum.toLowerCase()] + : nameOrNum); + if (! (TRACE <= level && level <= FATAL)) { + throw new Error('invalid level: ' + nameOrNum); + } + return level; +} + + + +//---- Logger class + +/** + * Create a Logger instance. + * + * @param options {Object} See documentation for full details. At minimum + * this must include a 'name' string key. Configuration keys: + * - `streams`: specify the logger output streams. This is an array of + * objects with these fields: + * - `type`: The stream type. See README.md for full details. + * Often this is implied by the other fields. Examples are + * "file", "stream" and "raw". + * - `level`: Defaults to "info". + * - `path` or `stream`: The specify the file path or writeable + * stream to which log records are written. E.g. + * `stream: process.stdout`. + * - `closeOnExit` (boolean): Optional. Default is true for a + * "file" stream when `path` is given, false otherwise. + * See README.md for full details. + * - `level`: set the level for a single output stream (cannot be used + * with `streams`) + * - `stream`: the output stream for a logger with just one, e.g. + * `process.stdout` (cannot be used with `streams`) + * - `serializers`: object mapping log record field names to + * serializing functions. See README.md for details. + * - `src`: Boolean (default false). Set true to enable 'src' automatic + * field with log call source info. + * All other keys are log record fields. + * + * An alternative *internal* call signature is used for creating a child: + * new Logger(, [, ]); + * + * @param _childSimple (Boolean) An assertion that the given `_childOptions` + * (a) only add fields (no config) and (b) no serialization handling is + * required for them. IOW, this is a fast path for frequent child + * creation. + */ +function Logger(options, _childOptions, _childSimple) { + xxx('Logger start:', options) + if (! this instanceof Logger) { + return new Logger(options, _childOptions); + } + + // Input arg validation. + var parent; + if (_childOptions !== undefined) { + parent = options; + options = _childOptions; + if (! parent instanceof Logger) { + throw new TypeError('invalid Logger creation: do not pass a second arg'); + } + } + if (!options) { + throw new TypeError('options (object) is required'); + } + if (!parent) { + if (!options.name) { + throw new TypeError('options.name (string) is required'); + } + } else { + if (options.name) { + throw new TypeError('invalid options.name: child cannot set logger name'); + } + } + if ((options.stream || options.level) && options.streams) { + throw new TypeError( + 'cannot mix "streams" with "stream" or "level" options'); + } + if (options.streams && !Array.isArray(options.streams)) { + throw new TypeError('invalid options.streams: must be an array') + } + if (options.serializers && (typeof (options.serializers) !== 'object' || + Array.isArray(options.serializers))) { + throw new TypeError('invalid options.serializers: must be an object') + } + + EventEmitter.call(this); + + // Fast path for simple child creation. + if (parent && _childSimple) { + // `_isSimpleChild` is a signal to stream close handling that this child + // owns none of its streams. + this._isSimpleChild = true; + + this._level = parent._level; + this.streams = parent.streams; + this.serializers = parent.serializers; + this.src = parent.src; + var fields = this.fields = {}; + var parentFieldNames = Object.keys(parent.fields); + for (var i = 0; i < parentFieldNames.length; i++) { + var name = parentFieldNames[i]; + fields[name] = parent.fields[name]; + } + var names = Object.keys(options); + for (var i = 0; i < names.length; i++) { + var name = names[i]; + fields[name] = options[name]; + } + return; + } + + // Null values. + var self = this; + if (parent) { + this._level = parent._level; + this.streams = []; + for (var i = 0; i < parent.streams.length; i++) { + var s = objCopy(parent.streams[i]); + s.closeOnExit = false; // Don't own parent stream. + this.streams.push(s); + } + this.serializers = objCopy(parent.serializers); + this.src = parent.src; + this.fields = objCopy(parent.fields); + } else { + this._level = Number.POSITIVE_INFINITY; + this.streams = []; + this.serializers = null; + this.src = false; + this.fields = {}; + } + + // Helpers + function addStream(s) { + s = objCopy(s); + + // Implicit 'type' from other args. + var type = s.type; + if (!s.type) { + if (s.stream) { + s.type = 'stream'; + } else if (s.path) { + s.type = 'file' + } + } + s.raw = (s.type === 'raw'); // PERF: Allow for faster check in `_emit`. + + if (s.level) { + s.level = resolveLevel(s.level); + } else { + s.level = INFO; + } + if (s.level < self._level) { + self._level = s.level; + } + + switch (s.type) { + case 'stream': + if (!s.closeOnExit) { + s.closeOnExit = false; + } + break; + case 'file': + if (!s.stream) { + s.stream = fs.createWriteStream(s.path, + {flags: 'a', encoding: 'utf8'}); + s.stream.on('error', function (err) { + self.emit('error', err, s); + }); + if (!s.closeOnExit) { + s.closeOnExit = true; + } + } else { + if (!s.closeOnExit) { + s.closeOnExit = false; + } + } + break; + case 'raw': + if (!s.closeOnExit) { + s.closeOnExit = false; + } + break; + default: + throw new TypeError('unknown stream type "' + s.type + '"'); + } + + self.streams.push(s); + } + + function addSerializers(serializers) { + if (!self.serializers) { + self.serializers = {}; + } + Object.keys(serializers).forEach(function (field) { + var serializer = serializers[field]; + if (typeof (serializer) !== 'function') { + throw new TypeError(format( + 'invalid serializer for "%s" field: must be a function', field)); + } else { + self.serializers[field] = serializer; + } + }); + } + + // Handle *config* options. + if (options.stream) { + addStream({ + type: 'stream', + stream: options.stream, + closeOnExit: false, + level: (options.level ? resolveLevel(options.level) : INFO) + }); + } else if (options.streams) { + options.streams.forEach(addStream); + } else if (!parent) { + addStream({ + type: 'stream', + stream: process.stdout, + closeOnExit: false, + level: (options.level ? resolveLevel(options.level) : INFO) + }); + } + if (options.serializers) { + addSerializers(options.serializers); + } + if (options.src) { + this.src = true; + } + xxx('Logger: ', self) + + // Fields. + // These are the default fields for log records (minus the attributes + // removed in this constructor). To allow storing raw log records + // (unrendered), `this.fields` must never be mutated. Create a copy for + // any changes. + var fields = objCopy(options); + delete fields.stream; + delete fields.level; + delete fields.streams; + delete fields.serializers; + delete fields.src; + if (this.serializers) { + this._applySerializers(fields); + } + if (!fields.hostname) { + fields.hostname = os.hostname(); + } + if (!fields.pid) { + fields.pid = process.pid; + } + Object.keys(fields).forEach(function (k) { + self.fields[k] = fields[k]; + }); +} + +util.inherits(Logger, EventEmitter); + + +/** + * Create a child logger, typically to add a few log record fields. + * + * This can be useful when passing a logger to a sub-component, e.g. a + * 'wuzzle' component of your service: + * + * var wuzzleLog = log.child({component: 'wuzzle'}) + * var wuzzle = new Wuzzle({..., log: wuzzleLog}) + * + * Then log records from the wuzzle code will have the same structure as + * the app log, *plus the component='wuzzle' field*. + * + * @param options {Object} Optional. Set of options to apply to the child. + * All of the same options for a new Logger apply here. Notes: + * - The parent's streams are inherited and cannot be removed in this + * call. + * - The parent's serializers are inherited, though can effectively be + * overwritten by using duplicate keys. + * @param simple {Boolean} Optional. Set to true to assert that `options` + * (a) only add fields (no config) and (b) no serialization handling is + * required for them. IOW, this is a fast path for frequent child + * creation. See 'tools/timechild.js' for numbers. + */ +Logger.prototype.child = function (options, simple) { + return new Logger(this, options || {}, simple); +} + + +/* BEGIN JSSTYLED */ +/** + * Close this logger. + * + * This closes streams (that it owns, as per 'endOnClose' attributes on + * streams), etc. Typically you **don't** need to bother calling this. +Logger.prototype.close = function () { + if (this._closed) { + return; + } + if (!this._isSimpleChild) { + self.streams.forEach(function (s) { + if (s.endOnClose) { + xxx('closing stream s:', s); + s.stream.end(); + s.endOnClose = false; + } + }); + } + this._closed = true; +} + */ +/* END JSSTYLED */ + + +/** + * Get/set the level of all streams on this logger. + * + * Get Usage: + * // Returns the current log level (lowest level of all its streams). + * log.level() -> INFO + * + * Set Usage: + * log.level(INFO) // set all streams to level INFO + * log.level('info') // can use 'info' et al aliases + */ +Logger.prototype.level = function level(value) { + if (value === undefined) { + return this._level; + } + var newLevel = resolveLevel(value); + var len = this.streams.length; + for (var i = 0; i < len; i++) { + this.streams[i].level = newLevel; + } + this._level = newLevel; +} + + +/** + * Get/set the level of a particular stream on this logger. + * + * Get Usage: + * // Returns an array of the levels of each stream. + * log.levels() -> [TRACE, INFO] + * + * // Returns a level of the identified stream. + * log.levels(0) -> TRACE // level of stream at index 0 + * log.levels('foo') // level of stream with name 'foo' + * + * Set Usage: + * log.levels(0, INFO) // set level of stream 0 to INFO + * log.levels(0, 'info') // can use 'info' et al aliases + * log.levels('foo', WARN) // set stream named 'foo' to WARN + * + * Stream names: When streams are defined, they can optionally be given + * a name. For example, + * log = new Logger({ + * streams: [ + * { + * name: 'foo', + * path: '/var/log/my-service/foo.log' + * level: 'trace' + * }, + * ... + * + * @param name {String|Number} The stream index or name. + * @param value {Number|String} The level value (INFO) or alias ('info'). + * If not given, this is a 'get' operation. + * @throws {Error} If there is no stream with the given name. + */ +Logger.prototype.levels = function levels(name, value) { + if (name === undefined) { + assert.equal(value, undefined); + return this.streams.map( + function (s) { return s.level }); + } + var stream; + if (typeof (name) === 'number') { + stream = this.streams[name]; + if (stream === undefined) { + throw new Error('invalid stream index: ' + name); + } + } else { + var len = this.streams.length; + for (var i = 0; i < len; i++) { + var s = this.streams[i]; + if (s.name === name) { + stream = s; + break; + } + } + if (!stream) { + throw new Error(format('no stream with name "%s"', name)); + } + } + if (value === undefined) { + return stream.level; + } else { + var newLevel = resolveLevel(value); + stream.level = newLevel; + if (newLevel < this._level) { + this._level = newLevel; + } + } +} + + +/** + * Apply registered serializers to the appropriate keys in the given fields. + * + * Pre-condition: This is only called if there is at least one serializer. + * + * @param fields (Object) The log record fields. + * @param keys (Array) Optional array of keys to which to limit processing. + */ +Logger.prototype._applySerializers = function (fields, keys) { + var self = this; + + // Mapping of keys to potentially serialize. + var applyKeys = fields; + if (keys) { + applyKeys = {}; + for (var i = 0; i < keys.length; i++) { + applyKeys[keys[i]] = true; + } + } + + xxx('_applySerializers: applyKeys', applyKeys); + + // Check each serializer against these (presuming number of serializers + // is typically less than number of fields). + Object.keys(this.serializers).forEach(function (name) { + if (applyKeys[name]) { + xxx('_applySerializers; apply to "%s" key', name) + try { + fields[name] = self.serializers[name](fields[name]); + } catch (err) { + _warn(format('bunyan: ERROR: This should never happen. ' + + 'This is a bug in or ' + + 'in this application. Exception from "%s" Logger serializer: %s', + name, err.stack || err)); + fields[name] = format('(Error in Bunyan log "%s" serializer ' + + 'broke field. See stderr for details.)', name); + } + } + }); +} + + +/** + * A log record is a 4-tuple: + * [, + * , + * , + * ] + * For Perf reasons, we only render this down to a single object when + * it is emitted. + */ +Logger.prototype._mkRecord = function (fields, level, msgArgs) { + var recFields = (fields ? objCopy(fields) : null); + return [this.fields, recFields, level, msgArgs]; +} + + +/** + * Emit a log record. + * + * @param rec {log record} + */ +Logger.prototype._emit = function (rec) { + var i; + + var obj = objCopy(rec[0]); + var level = obj.level = rec[2]; + var recFields = rec[1]; + if (recFields) { + if (this.serializers) { + this._applySerializers(recFields); + } + Object.keys(recFields).forEach(function (k) { + obj[k] = recFields[k]; + }); + } + xxx('Record:', rec) + obj.msg = format.apply(this, rec[3]); + if (!obj.time) { + obj.time = (new Date()); + } + // Get call source info + if (this.src && !obj.src) { + obj.src = getCaller3Info() + } + obj.v = LOG_VERSION; + + // Lazily determine if this Logger has non-"raw" streams. If there are + // any, then we need to stringify the log record. + if (this.haveNonRawStreams === undefined) { + this.haveNonRawStreams = false; + for (i = 0; i < this.streams.length; i++) { + if (!this.streams[i].raw) { + this.haveNonRawStreams = true; + break; + } + } + } + + // Stringify the object. Attempt to warn/recover on error. + var str; + if (this.haveNonRawStreams) { + str = JSON.stringify(obj, safeCycles()) + '\n'; + } + + for (i = 0; i < this.streams.length; i++) { + var s = this.streams[i]; + if (s.level <= level) { + xxx('writing log rec "%s" to "%s" stream (%d <= %d): %j', + obj.msg, s.type, s.level, level, obj); + s.stream.write(s.raw ? obj : str); + } + }; +} + + +/** + * Log a record at TRACE level. + * + * Usages: + * log.trace() -> boolean is-trace-enabled + * log.trace( err, [ msg, ...]) + * log.trace( msg, ...) + * log.trace( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.trace = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.trace()` + return (this._level <= TRACE); + } else if (this._level > TRACE) { + return; + } else if (arguments[0] instanceof Error) { + // `log.trace(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.trace(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.trace(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.trace(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, TRACE, msgArgs); + this._emit(rec); +} + +/** + * Log a record at DEBUG level. + * + * Usages: + * log.debug() -> boolean is-debug-enabled + * log.debug( err, [ msg, ...]) + * log.debug( msg, ...) + * log.debug( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.debug = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.debug()` + return (this._level <= DEBUG); + } else if (this._level > DEBUG) { + return; + } else if (arguments[0] instanceof Error) { + // `log.debug(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.debug(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.debug(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.debug(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, DEBUG, msgArgs); + this._emit(rec); +} + +/** + * Log a record at INFO level. + * + * Usages: + * log.info() -> boolean is-info-enabled + * log.info( err, [ msg, ...]) + * log.info( msg, ...) + * log.info( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.info = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.info()` + return (this._level <= INFO); + } else if (this._level > INFO) { + return; + } else if (arguments[0] instanceof Error) { + // `log.info(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.info(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.info(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.info(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, INFO, msgArgs); + this._emit(rec); +} + +/** + * Log a record at WARN level. + * + * Usages: + * log.warn() -> boolean is-warn-enabled + * log.warn( err, [ msg, ...]) + * log.warn( msg, ...) + * log.warn( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.warn = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.warn()` + return (this._level <= WARN); + } else if (this._level > WARN) { + return; + } else if (arguments[0] instanceof Error) { + // `log.warn(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.warn(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.warn(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.warn(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, WARN, msgArgs); + this._emit(rec); +} + +/** + * Log a record at ERROR level. + * + * Usages: + * log.error() -> boolean is-error-enabled + * log.error( err, [ msg, ...]) + * log.error( msg, ...) + * log.error( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.error = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.error()` + return (this._level <= ERROR); + } else if (this._level > ERROR) { + return; + } else if (arguments[0] instanceof Error) { + // `log.error(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.error(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.error(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.error(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, ERROR, msgArgs); + this._emit(rec); +} + +/** + * Log a record at FATAL level. + * + * Usages: + * log.fatal() -> boolean is-fatal-enabled + * log.fatal( err, [ msg, ...]) + * log.fatal( msg, ...) + * log.fatal( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.fatal = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.fatal()` + return (this._level <= FATAL); + } else if (this._level > FATAL) { + return; + } else if (arguments[0] instanceof Error) { + // `log.fatal(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.fatal(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.fatal(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.fatal(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, FATAL, msgArgs); + this._emit(rec); +} + + + +//---- Standard serializers +// A serializer is a function that serializes a JavaScript object to a +// JSON representation for logging. There is a standard set of presumed +// interesting objects in node.js-land. + +Logger.stdSerializers = {}; + +// Serialize an HTTP request. +Logger.stdSerializers.req = function req(req) { + if (!req || !req.connection) + return req; + return { + method: req.method, + url: req.url, + headers: req.headers, + remoteAddress: req.connection.remoteAddress, + remotePort: req.connection.remotePort + }; + // Trailers: Skipping for speed. If you need trailers in your app, then + // make a custom serializer. + //if (Object.keys(trailers).length > 0) { + // obj.trailers = req.trailers; + //} +}; + +// Serialize an HTTP response. +Logger.stdSerializers.res = function res(res) { + if (!res || !res.statusCode) + return res; + return { + statusCode: res.statusCode, + header: res._header + } +}; + +// Serialize an Error object +// (Core error properties are enumerable in node 0.4, not in 0.6). +var errSerializer = Logger.stdSerializers.err = function err(err) { + if (!err || !err.stack) + return err; + var obj = { + message: err.message, + name: err.name, + stack: err.stack + } + Object.keys(err).forEach(function (k) { + if (err[k] !== undefined) { + obj[k] = err[k]; + } + }); + return obj; +}; + +// A JSON stringifier that handles cycles safely. +// Usage: JSON.stringify(obj, safeCycles()) +function safeCycles() { + var seen = []; + return function(key, val) { + if (!val || typeof val !== 'object') { + return val; + } + if (seen.indexOf(val) !== -1) { + return '[Circular]'; + } + seen.push(val); + return val; + }; +} + +/** + * RingBuffer is a Writable Stream that just stores the last N records in + * memory. + * + * @param options {Object}, with the following fields: + * + * - limit: number of records to keep in memory + */ +function RingBuffer(options) { + this.limit = options && options.limit ? options.limit : 100; + this.writable = true; + this.records = []; + EventEmitter.call(this); +} + +util.inherits(RingBuffer, EventEmitter); + +RingBuffer.prototype.write = function (record) { + if (!this.writable) + throw (new Error('RingBuffer has been ended already')); + + this.records.push(record); + + if (this.records.length > this.limit) + this.records.shift(); + + return (true); +}; + +RingBuffer.prototype.end = function () { + if (arguments.length > 0) + this.write.apply(this, Array.prototype.slice.call(arguments)); + this.writable = false; +}; + +RingBuffer.prototype.destroy = function () { + this.writable = false; + this.emit('close'); +}; + +RingBuffer.prototype.destroySoon = function () { + this.destroy(); +}; + + +//---- Exports + +module.exports = Logger; + +module.exports.TRACE = TRACE; +module.exports.DEBUG = DEBUG; +module.exports.INFO = INFO; +module.exports.WARN = WARN; +module.exports.ERROR = ERROR; +module.exports.FATAL = FATAL; +module.exports.resolveLevel = resolveLevel; + +module.exports.VERSION = VERSION; +module.exports.LOG_VERSION = LOG_VERSION; + +module.exports.createLogger = function createLogger(options) { + return new Logger(options); +}; + +module.exports.RingBuffer = RingBuffer; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/package.json new file mode 100644 index 0000000..1e42eac --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/package.json @@ -0,0 +1,26 @@ +{ + "name": "bunyan", + "version": "0.14.0", + "description": "a JSON Logger library for node.js services", + "author": "Trent Mick (http://trentm.com)", + "main": "./lib/bunyan.js", + "bin": { + "bunyan": "./bin/bunyan" + }, + + "repository": { + "type": "git", + "url": "git://github.com/trentm/node-bunyan.git" + }, + "engines": ["node >=0.6.0"], + "keywords": ["log", "logging", "log4j", "json"], + + "devDependencies": { + "tap": "0.2.0", + "ben": "0.0.0" + }, + + "scripts": { + "test": "tap test/*.js" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/buffer.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/buffer.test.js new file mode 100644 index 0000000..13c374a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/buffer.test.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * Copyright (c) 2012 Joyent Inc. All rights reserved. + * + * Test logging with (accidental) usage of buffers. + */ + +var util = require('util'), + inspect = util.inspect, + format = util.format; +var test = require('tap').test; +var bunyan = require('../lib/bunyan'); + + +function Catcher() { + this.records = []; +} +Catcher.prototype.write = function (record) { + this.records.push(record); +} + +var catcher = new Catcher(); +var log = new bunyan.createLogger({ + name: 'buffer.test', + streams: [ + { + type: 'raw', + stream: catcher, + level: 'trace' + } + ] +}); + + +test('log.info(BUFFER)', function (t) { + var b = new Buffer('foo'); + + ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(function (lvl) { + log[lvl].call(log, b); + var rec = catcher.records[catcher.records.length - 1]; + t.equal(rec.msg, inspect(b), + format('log.%s msg is inspect(BUFFER)', lvl)); + t.ok(rec["0"] === undefined, + 'no "0" array index key in record: ' + inspect(rec["0"])); + t.ok(rec["parent"] === undefined, + 'no "parent" array index key in record: ' + inspect(rec["parent"])); + + log[lvl].call(log, b, 'bar'); + var rec = catcher.records[catcher.records.length - 1]; + t.equal(rec.msg, inspect(b) + ' bar', + format('log.%s(BUFFER, "bar") msg is inspect(BUFFER) + " bar"', lvl)); + }); + + t.end(); +}); + + +//test('log.info({buf: BUFFER})', function (t) { +// var b = new Buffer('foo'); +// +// // Really there isn't much Bunyan can do here. See +// // . An unwelcome hack would +// // be to monkey-patch in Buffer.toJSON. Bletch. +// log.info({buf: b}, 'my message'); +// var rec = catcher.records[catcher.records.length - 1]; +// +// t.end(); +//}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/cli.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/cli.test.js new file mode 100644 index 0000000..99bdfb5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/cli.test.js @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test the `bunyan` CLI. + */ + +var path = require('path'); +var exec = require('child_process').exec; +var format = require('util').format; +var test = require('tap').test; +var debug = console.warn; + +var BUNYAN = path.resolve(__dirname, '../bin/bunyan'); + +//child = exec('cat *.js bad_file | wc -l', +// function (error, stdout, stderr) { +// console.log('stdout: ' + stdout); +// console.log('stderr: ' + stderr); +// if (error !== null) { +// console.log('exec error: ' + error); +// } +//}); + +test('--version', function (t) { + var version = require('../package.json').version; + exec(BUNYAN + ' --version', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, 'bunyan ' + version + '\n'); + t.end(); + }); +}); + +test('--help', function (t) { + exec(BUNYAN + ' --help', function (err, stdout, stderr) { + t.error(err) + t.ok(stdout.indexOf('General options:') !== -1); + t.end(); + }); +}); + +test('-h', function (t) { + exec(BUNYAN + ' -h', function (err, stdout, stderr) { + t.error(err) + t.ok(stdout.indexOf('General options:') !== -1); + t.end(); + }); +}); + +test('--bogus', function (t) { + exec(BUNYAN + ' --bogus', function (err, stdout, stderr) { + t.ok(err, 'should error out') + t.equal(err.code, 1, '... with exit code 1') + t.end(); + }); +}); + +test('simple.log', function (t) { + exec(BUNYAN + ' corpus/simple.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message\n'); + t.end(); + }); +}); +test('cat simple.log', function (t) { + exec(format('cat corpus/simple.log | %s', BUNYAN), + function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message\n'); + t.end(); + } + ); +}); +test('simple.log with color', function (t) { + exec(BUNYAN + ' --color corpus/simple.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] \u001b[36m INFO\u001b[39m: myservice/123 ' + + 'on example.com: \u001b[36mMy message\u001b[39m\n'); + t.end(); + }); +}); + +test('extrafield.log', function (t) { + exec(BUNYAN + ' corpus/extrafield.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message (extra=field)\n'); + t.end(); + }); +}); +test('extrafield.log with color', function (t) { + exec(BUNYAN + ' --color corpus/extrafield.log', + function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] \u001b[36m INFO\u001b[39m: myservice/123 ' + + 'on example.com: \u001b[36mMy message\u001b[39m' + + '\u001b[90m (extra=field)\u001b[39m\n'); + t.end(); + }); +}); + +test('bogus.log', function (t) { + exec(BUNYAN + ' corpus/bogus.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, 'not a JSON line\n{"hi": "there"}\n'); + t.end(); + }); +}); + +test('bogus.log -j', function (t) { + exec(BUNYAN + ' -j corpus/bogus.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, 'not a JSON line\n{\n "hi": "there"\n}\n'); + t.end(); + }); +}); + +test('all.log', function (t) { + exec(BUNYAN + ' corpus/all.log', function (err, stdout, stderr) { + // Just make sure don't blow up on this. + t.error(err) + t.end(); + }); +}); + +test('simple.log doesnotexist1.log doesnotexist2.log', function (t) { + exec(BUNYAN + ' corpus/simple.log doesnotexist1.log doesnotexist2.log', + function (err, stdout, stderr) { + t.ok(err) + t.equal(err.code, 2) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message\n'); + // Note: node v0.6.10: + // ENOENT, no such file or directory 'asdf.log' + // but node v0.6.14: + // ENOENT, open 'asdf.log' + // Somewhat annoying change. + t.equal(stderr, + 'bunyan: ENOENT, open \'doesnotexist1.log\'\nbunyan: ENOENT, ' + + 'open \'doesnotexist2.log\'\n'); + t.end(); + } + ); +}); + +test('multiple logs', function (t) { + exec(BUNYAN + ' corpus/log1.log corpus/log2.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, [ + '[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n', + ].join('')); + t.end(); + }); +}); + +test('log1.log.gz', function (t) { + exec(BUNYAN + ' corpus/log1.log.gz', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, [ + '[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + ].join('')); + t.end(); + }); +}); + +test('mixed text and gzip logs', function (t) { + exec(BUNYAN + ' corpus/log1.log.gz corpus/log2.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, [ + '[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n', + ].join('')); + t.end(); + }); +}); + +test('--level 40', function (t) { + expect = [ + '# levels\n', + '[2012-02-08T22:56:53.856Z] WARN: myservice/123 on example.com: My message\n', + '[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n', + '[2012-02-08T22:56:55.856Z] LVL55: myservice/123 on example.com: My message\n', + '[2012-02-08T22:56:56.856Z] FATAL: myservice/123 on example.com: My message\n', + '\n', + '# extra fields\n', + '\n', + '# bogus\n', + 'not a JSON line\n', + '{"hi": "there"}\n' + ].join(''); + exec(BUNYAN + ' -l 40 corpus/all.log', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + exec(BUNYAN + ' --level 40 corpus/all.log', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); + }); +}); + +test('--condition "level === 10 && pid === 123"', function (t) { + var expect = [ + '# levels\n', + '[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\n', + '\n', + '# extra fields\n', + '\n', + '# bogus\n', + 'not a JSON line\n', + '{"hi": "there"}\n' + ].join(''); + exec(BUNYAN + ' -c "level === 10 && pid === 123" corpus/all.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + exec(BUNYAN + ' --condition "level === 10 && pid === 123" corpus/all.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); + }); +}); + +// multiple +// not sure if this is a bug or a feature. let's call it a feature! +test('multiple --conditions', function (t) { + var expect = [ + '# levels\n', + '[2012-02-08T22:56:53.856Z] WARN: myservice/1 on example.com: My message\n', + '\n', + '# extra fields\n', + '\n', + '# bogus\n', + 'not a JSON line\n', + '{"hi": "there"}\n' + ].join(''); + exec(BUNYAN + ' corpus/all.log ' + + '-c "if (level === 40) pid = 1; true" ' + + '-c "pid === 1"', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); +}); + +// https://github.com/trentm/node-bunyan/issues/30 +// +// One of the records in corpus/withreq.log has a 'req' +// field with no 'headers'. Ditto for the 'res' field. +test('robust req handling', function (t) { + var expect = [ + '[2012-08-08T10:25:47.636Z] DEBUG: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: headAgentProbes respond (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, route=HeadAgentProbes, contentMD5=11FxOYiYfpMxmANj4kGJzg==)', + '[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, 3ms, audit=true, remoteAddress=10.2.207.2, remotePort=50394, secure=false, _audit=true, req.version=*)', + ' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1', + ' accept: application/json', + ' content-type: application/json', + ' host: 10.2.207.16', + ' connection: keep-alive', + ' --', + ' HTTP/1.1 200 OK', + ' content-md5: 11FxOYiYfpMxmANj4kGJzg==', + ' access-control-allow-origin: *', + ' access-control-allow-headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version', + ' access-control-allow-methods: HEAD', + ' access-control-expose-headers: X-Api-Version, X-Request-Id, X-Response-Time', + ' connection: Keep-Alive', + ' date: Wed, 08 Aug 2012 10:25:47 GMT', + ' server: Amon Master/1.0.0', + ' x-request-id: cce79d15-ffc2-487c-a4e4-e940bdaac31e', + ' x-response-time: 3', + ' --', + ' route: {', + ' "name": "HeadAgentProbes",', + ' "version": false', + ' }', + '[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, 3ms, audit=true, remoteAddress=10.2.207.2, remotePort=50394, secure=false, _audit=true, req.version=*)', + ' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1', + ' --', + ' route: {', + ' "name": "HeadAgentProbes",', + ' "version": false', + ' }' + ].join('\n') + '\n'; + exec(BUNYAN + ' corpus/withreq.log', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/all.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/all.log new file mode 100644 index 0000000..5d59151 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/all.log @@ -0,0 +1,20 @@ +# levels +{"name":"myservice","pid":123,"hostname":"example.com","level":10,"msg":"My message","time":"2012-02-08T22:56:50.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":20,"msg":"My message","time":"2012-02-08T22:56:51.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":40,"msg":"My message","time":"2012-02-08T22:56:53.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":50,"msg":"My message","time":"2012-02-08T22:56:54.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":55,"msg":"My message","time":"2012-02-08T22:56:55.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":60,"msg":"My message","time":"2012-02-08T22:56:56.856Z","v":0} + +# extra fields +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"one":"short","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"two":"short with space","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"three":"multi\nline","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"four":"over 50 chars long long long long long long long long long","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"five":{"a": "json object"},"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"six":["a", "json", "array"],"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} + +# bogus +not a JSON line +{"hi": "there"} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/bogus.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/bogus.log new file mode 100644 index 0000000..1c9e5fd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/bogus.log @@ -0,0 +1,2 @@ +not a JSON line +{"hi": "there"} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/extrafield.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/extrafield.log new file mode 100644 index 0000000..aac64b9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/extrafield.log @@ -0,0 +1 @@ +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"extra":"field","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log1.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log1.log new file mode 100644 index 0000000..1482e98 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log1.log @@ -0,0 +1,4 @@ +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:57:55.586Z","v":0} +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.339Z","v":0} +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0} +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log1.log.gz b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log1.log.gz new file mode 100644 index 0000000..43068dc Binary files /dev/null and b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log1.log.gz differ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log2.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log2.log new file mode 100644 index 0000000..e636081 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/log2.log @@ -0,0 +1,5 @@ +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:58:55.586Z","v":0} +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:01:49.339Z","v":0} +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:47.404Z","v":0} +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:57.404Z","v":0} +{"name":"agent2","pid":76156,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:08:01.105Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/simple.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/simple.log new file mode 100644 index 0000000..ebccc0d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/simple.log @@ -0,0 +1 @@ +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/withreq.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/withreq.log new file mode 100644 index 0000000..e5ece7a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/corpus/withreq.log @@ -0,0 +1,3 @@ +{"name":"amon-master","hostname":"9724a190-27b6-4fd8-830b-a574f839c67d","pid":12859,"route":"HeadAgentProbes","req_id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","level":20,"contentMD5":"11FxOYiYfpMxmANj4kGJzg==","msg":"headAgentProbes respond","time":"2012-08-08T10:25:47.636Z","v":0} +{"name":"amon-master","hostname":"9724a190-27b6-4fd8-830b-a574f839c67d","pid":12859,"audit":true,"level":30,"remoteAddress":"10.2.207.2","remotePort":50394,"req_id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","req":{"method":"HEAD","url":"/agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037","headers":{"accept":"application/json","content-type":"application/json","host":"10.2.207.16","connection":"keep-alive"},"httpVersion":"1.1","trailers":{},"version":"*"},"res":{"statusCode":200,"headers":{"content-md5":"11FxOYiYfpMxmANj4kGJzg==","access-control-allow-origin":"*","access-control-allow-headers":"Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version","access-control-allow-methods":"HEAD","access-control-expose-headers":"X-Api-Version, X-Request-Id, X-Response-Time","connection":"Keep-Alive","date":"Wed, 08 Aug 2012 10:25:47 GMT","server":"Amon Master/1.0.0","x-request-id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","x-response-time":3},"trailer":false},"route":{"name":"HeadAgentProbes","version":false},"latency":3,"secure":false,"_audit":true,"msg":"HeadAgentProbes handled: 200","time":"2012-08-08T10:25:47.637Z","v":0} +{"name":"amon-master","hostname":"9724a190-27b6-4fd8-830b-a574f839c67d","pid":12859,"audit":true,"level":30,"remoteAddress":"10.2.207.2","remotePort":50394,"req_id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","req":{"method":"HEAD","url":"/agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037","httpVersion":"1.1","trailers":{},"version":"*"},"res":{"statusCode":200,"trailer":false},"route":{"name":"HeadAgentProbes","version":false},"latency":3,"secure":false,"_audit":true,"msg":"HeadAgentProbes handled: 200","time":"2012-08-08T10:25:47.637Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/ctor.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/ctor.test.js new file mode 100644 index 0000000..9db59be --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/ctor.test.js @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test type checking on creation of the Logger. + */ + +var test = require('tap').test; +var bunyan = require('../lib/bunyan'), + Logger = bunyan; + + + +test('ensure Logger creation options', function (t) { + t.throws(function () { new Logger(); }, + {name: 'TypeError', message: 'options (object) is required'}, + 'no options should throw'); + + t.throws(function () { new Logger({}); }, + {name: 'TypeError', message: 'options.name (string) is required'}, + 'no options.name should throw'); + + t.doesNotThrow(function () { new Logger({name: 'foo'}); }, + 'just options.name should be sufficient'); + + var options = {name: 'foo', stream: process.stdout, streams: []}; + t.throws(function () { new Logger(options); }, + 'cannot use "stream" and "streams"'); + + options = {name: 'foo', level: 'info', streams: []}; + t.throws(function () { new Logger(options); }, + 'cannot use "level" and "streams"'); + + // https://github.com/trentm/node-bunyan/issues/3 + options = {name: 'foo', streams: {}}; + t.throws(function () { new Logger(options); }, + {name: 'TypeError', message: 'invalid options.streams: must be an array'}, + '"streams" must be an array'); + + options = {name: 'foo', serializers: 'a string'}; + t.throws(function () { new Logger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be a string'); + + options = {name: 'foo', serializers: [1, 2, 3]}; + t.throws(function () { new Logger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be an array'); + + t.end(); +}); + + +test('ensure Logger creation options (createLogger)', function (t) { + t.throws(function () { bunyan.createLogger(); }, + {name: 'TypeError', message: 'options (object) is required'}, + 'no options should throw'); + + t.throws(function () { bunyan.createLogger({}); }, + {name: 'TypeError', message: 'options.name (string) is required'}, + 'no options.name should throw'); + + t.doesNotThrow(function () { bunyan.createLogger({name: 'foo'}); }, + 'just options.name should be sufficient'); + + var options = {name: 'foo', stream: process.stdout, streams: []}; + t.throws(function () { bunyan.createLogger(options); }, + 'cannot use "stream" and "streams"'); + + options = {name: 'foo', level: 'info', streams: []}; + t.throws(function () { bunyan.createLogger(options); }, + 'cannot use "level" and "streams"'); + + // https://github.com/trentm/node-bunyan/issues/3 + options = {name: 'foo', streams: {}}; + t.throws(function () { bunyan.createLogger(options); }, + {name: 'TypeError', message: 'invalid options.streams: must be an array'}, + '"streams" must be an array'); + + options = {name: 'foo', serializers: 'a string'}; + t.throws(function () { bunyan.createLogger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be a string'); + + options = {name: 'foo', serializers: [1, 2, 3]}; + t.throws(function () { bunyan.createLogger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be an array'); + + t.end(); +}); + + +test('ensure Logger child() options', function (t) { + var log = new Logger({name: 'foo'}); + + t.doesNotThrow(function () { log.child(); }, + 'no options should be fine'); + + t.doesNotThrow(function () { log.child({}); }, + 'empty options should be fine too'); + + t.throws(function () { log.child({name: 'foo'}); }, + { + name: 'TypeError', + message: 'invalid options.name: child cannot set logger name' + }, + 'child cannot change name'); + + var options = {stream: process.stdout, streams: []}; + t.throws(function () { log.child(options); }, + 'cannot use "stream" and "streams"'); + + options = {level: 'info', streams: []}; + t.throws(function () { log.child(options); }, + 'cannot use "level" and "streams"'); + + // https://github.com/trentm/node-bunyan/issues/3 + options = {streams: {}}; + t.throws(function () { log.child(options); }, + {name: 'TypeError', message: 'invalid options.streams: must be an array'}, + '"streams" must be an array'); + + options = {serializers: 'a string'}; + t.throws(function () { log.child(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be a string'); + + options = {serializers: [1, 2, 3]}; + t.throws(function () { log.child(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be an array'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/cycles.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/cycles.test.js new file mode 100644 index 0000000..c845aae --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/cycles.test.js @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Make sure cycles are safe. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan.js'); + +var Stream = require('stream').Stream; +var outstr = new Stream; +outstr.writable = true; +var output = []; +outstr.write = function (c) { + output.push(JSON.parse(c + '')); +}; +outstr.end = function (c) { + if (c) this.write(c); + this.emit('end'); +}; + +// these are lacking a few fields that will probably never match +var expect = + [ + { + "name": "blammo", + "level": 30, + "msg": "bango { bang: 'boom', KABOOM: [Circular] }", + "v": 0 + }, + { + "name": "blammo", + "level": 30, + "msg": "kaboom { bang: 'boom', KABOOM: [Circular] }", + "v": 0 + }, + { + "name": "blammo", + "level": 30, + "bang": "boom", + "KABOOM": { + "bang": "boom", + "KABOOM": "[Circular]" + }, + "msg": "", + "v": 0 + } + ]; + +var log = new Logger({ + name: 'blammo', + streams: [ + { + type: 'stream', + level: 'info', + stream: outstr + } + ] +}); + +test('cycles', function (t) { + outstr.on('end', function () { + output.forEach(function (o, i) { + t.has(o, expect[i], 'log item ' + i + ' matches'); + }); + t.end(); + }); + + var obj = { bang: 'boom' }; + obj.KABOOM = obj; + log.info('bango', obj); + log.info('kaboom', obj.KABOOM); + log.info(obj); + outstr.end(); + t.pass('did not throw'); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/error-event.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/error-event.test.js new file mode 100644 index 0000000..6c8f0cf --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/error-event.test.js @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test emission and handling of 'error' event in a logger with a 'path' + * stream. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan'); + +test('error event on log write', function (t) { + LOG_PATH = '/this/path/is/bogus.log' + var log = new Logger({name: 'error-event', streams: [{path: LOG_PATH}]}); + t.plan(5); + log.on('error', function (err, stream) { + t.ok(err, 'got err in error event: ' + err); + t.equal(err.code, 'ENOENT', 'error code is ENOENT'); + t.ok(stream, 'got a stream argument'); + t.equal(stream.path, LOG_PATH); + t.equal(stream.type, 'file'); + t.end(); + }); + log.info('info log message'); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/log.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/log.test.js new file mode 100644 index 0000000..264d946 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/log.test.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test the `log.trace(...)`, `log.debug(...)`, ..., `log.fatal(...)` API. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan'); + +var log1 = new Logger({ + name: 'log1', + streams: [ + { + path: __dirname + '/log.test.log1.log', + level: 'info' + } + ] +}); + +var log2 = new Logger({ + name: 'log2', + streams: [ + { + path: __dirname + '/log.test.log2a.log', + level: 'error' + }, + { + path: __dirname + '/log.test.log2b.log', + level: 'debug' + } + ] +}) + +test('log.LEVEL() -> boolean', function (t) { + t.equal(log1.trace(), false) + t.equal(log1.debug(), false) + t.equal(log1.info(), true) + t.equal(log1.warn(), true) + t.equal(log1.error(), true) + t.equal(log1.fatal(), true) + + // Level is the *lowest* level of all streams. + t.equal(log2.trace(), false) + t.equal(log2.debug(), true) + t.equal(log2.info(), true) + t.equal(log2.warn(), true) + t.equal(log2.error(), true) + t.equal(log2.fatal(), true) + t.end(); +}); + +//TODO: +// - rest of api diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/other-api.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/other-api.test.js new file mode 100644 index 0000000..25bee68 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/other-api.test.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test other parts of the exported API. + */ + +var test = require('tap').test; +var bunyan = require('../lib/bunyan'); + +test('bunyan.s', function (t) { + t.ok(bunyan.TRACE, 'TRACE'); + t.ok(bunyan.DEBUG, 'DEBUG'); + t.ok(bunyan.INFO, 'INFO'); + t.ok(bunyan.WARN, 'WARN'); + t.ok(bunyan.ERROR, 'ERROR'); + t.ok(bunyan.FATAL, 'FATAL'); + t.end(); +}); + +test('bunyan.resolveLevel()', function (t) { + t.equal(bunyan.resolveLevel('trace'), bunyan.TRACE, 'TRACE'); + t.equal(bunyan.resolveLevel('TRACE'), bunyan.TRACE, 'TRACE'); + t.equal(bunyan.resolveLevel('debug'), bunyan.DEBUG, 'DEBUG'); + t.equal(bunyan.resolveLevel('DEBUG'), bunyan.DEBUG, 'DEBUG'); + t.equal(bunyan.resolveLevel('info'), bunyan.INFO, 'INFO'); + t.equal(bunyan.resolveLevel('INFO'), bunyan.INFO, 'INFO'); + t.equal(bunyan.resolveLevel('warn'), bunyan.WARN, 'WARN'); + t.equal(bunyan.resolveLevel('WARN'), bunyan.WARN, 'WARN'); + t.equal(bunyan.resolveLevel('error'), bunyan.ERROR, 'ERROR'); + t.equal(bunyan.resolveLevel('ERROR'), bunyan.ERROR, 'ERROR'); + t.equal(bunyan.resolveLevel('fatal'), bunyan.FATAL, 'FATAL'); + t.equal(bunyan.resolveLevel('FATAL'), bunyan.FATAL, 'FATAL'); + t.end(); +}); + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/raw-stream.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/raw-stream.test.js new file mode 100644 index 0000000..8eb1336 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/raw-stream.test.js @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test `type: 'raw'` Logger streams. + */ + +var format = require('util').format; +var test = require('tap').test; +var Logger = require('../lib/bunyan'); + + + +function CapturingStream(recs) { + this.recs = recs; +} +CapturingStream.prototype.write = function (rec) { + this.recs.push(rec); +} + + +test('raw stream', function (t) { + var recs = []; + + var log = new Logger({ + name: 'raw-stream-test', + streams: [ + { + stream: new CapturingStream(recs), + type: 'raw' + } + ] + }); + log.info('first'); + log.info({two: 'deux'}, 'second'); + + t.equal(recs.length, 2); + t.equal(typeof (recs[0]), 'object', 'first rec is an object'); + t.equal(recs[1].two, 'deux', '"two" field made it through'); + t.end(); +}); + + +test('raw streams and regular streams can mix', function (t) { + var rawRecs = []; + var nonRawRecs = []; + + var log = new Logger({ + name: 'raw-stream-test', + streams: [ + { + stream: new CapturingStream(rawRecs), + type: 'raw' + }, + { + stream: new CapturingStream(nonRawRecs) + } + ] + }); + log.info('first'); + log.info({two: 'deux'}, 'second'); + + t.equal(rawRecs.length, 2); + t.equal(typeof (rawRecs[0]), 'object', 'first rawRec is an object'); + t.equal(rawRecs[1].two, 'deux', '"two" field made it through'); + + t.equal(nonRawRecs.length, 2); + t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string'); + + t.end(); +}); + + +test('child adding a non-raw stream works', function (t) { + var parentRawRecs = []; + var rawRecs = []; + var nonRawRecs = []; + + var logParent = new Logger({ + name: 'raw-stream-test', + streams: [ + { + stream: new CapturingStream(parentRawRecs), + type: 'raw' + } + ] + }); + var logChild = logParent.child({ + child: true, + streams: [ + { + stream: new CapturingStream(rawRecs), + type: 'raw' + }, + { + stream: new CapturingStream(nonRawRecs) + } + ] + }); + logParent.info('first'); + logChild.info({two: 'deux'}, 'second'); + + t.equal(rawRecs.length, 1, + format('rawRecs length should be 1 (is %d)', rawRecs.length)); + t.equal(typeof (rawRecs[0]), 'object', 'rawRec entry is an object'); + t.equal(rawRecs[0].two, 'deux', '"two" field made it through'); + + t.equal(nonRawRecs.length, 1); + t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/ringbuffer.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/ringbuffer.test.js new file mode 100644 index 0000000..75a6a12 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/ringbuffer.test.js @@ -0,0 +1,39 @@ +/* + * Test the RingBuffer output stream. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan'); +var ringbuffer = new Logger.RingBuffer({ 'limit': 5 }); + +var log1 = new Logger({ + name: 'log1', + streams: [ + { + stream: ringbuffer, + type: 'raw', + level: 'info' + } + ] +}); + +test('ringbuffer', function (t) { + log1.info('hello'); + log1.trace('there'); + log1.error('android'); + t.equal(ringbuffer.records.length, 2); + t.equal(ringbuffer.records[0]['msg'], 'hello'); + t.equal(ringbuffer.records[1]['msg'], 'android'); + log1.error('one'); + log1.error('two'); + log1.error('three'); + t.equal(ringbuffer.records.length, 5); + log1.error('four'); + t.equal(ringbuffer.records.length, 5); + t.equal(ringbuffer.records[0]['msg'], 'android'); + t.equal(ringbuffer.records[1]['msg'], 'one'); + t.equal(ringbuffer.records[2]['msg'], 'two'); + t.equal(ringbuffer.records[3]['msg'], 'three'); + t.equal(ringbuffer.records[4]['msg'], 'four'); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/std-serializers.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/std-serializers.test.js new file mode 100644 index 0000000..132bb4f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/test/std-serializers.test.js @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test the standard serializers in Bunyan. + */ + +var test = require('tap').test; +var bunyan = require('../lib/bunyan'); +var http = require('http'); + + +function CapturingStream(recs) { + this.recs = recs; +} +CapturingStream.prototype.write = function (rec) { + this.recs.push(rec); +} + + +test('req serializer', function (t) { + var records = []; + var log = bunyan.createLogger({ + name: 'serializer-test', + streams: [ + { + stream: new CapturingStream(records), + type: 'raw' + } + ], + serializers: { + req: bunyan.stdSerializers.req + } + }); + + // None of these should blow up. + var bogusReqs = [ + undefined, + null, + {}, + 1, + 'string', + [1,2,3], + {'foo':'bar'} + ]; + for (var i = 0; i < bogusReqs.length; i++) { + log.info({req: bogusReqs[i]}, "hi"); + t.equal(records[i].req, bogusReqs[i]); + } + + // Get http request and response objects to play with and test. + var theReq, theRes; + var server = http.createServer(function (req, res) { + theReq = req; + theRes = res; + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Hello World\n'); + }) + server.listen(8765, function () { + http.get({host: '127.0.0.1', port: 8765, path: '/'}, function(res) { + log.info({req: theReq}, 'the request'); + var lastRecord = records[records.length-1]; + t.equal(lastRecord.req.method, 'GET'); + t.equal(lastRecord.req.url, theReq.url); + t.equal(lastRecord.req.remoteAddress, theReq.connection.remoteAddress); + t.equal(lastRecord.req.remotePort, theReq.connection.remotePort); + server.close(); + t.end(); + }).on('error', function (err) { + t.ok(false, 'error requesting to our test server: ' + err); + server.close(); + t.end(); + }); + }); +}); + + +test('res serializer', function (t) { + var records = []; + var log = bunyan.createLogger({ + name: 'serializer-test', + streams: [ + { + stream: new CapturingStream(records), + type: 'raw' + } + ], + serializers: { + res: bunyan.stdSerializers.res + } + }); + + // None of these should blow up. + var bogusRess = [ + undefined, + null, + {}, + 1, + 'string', + [1,2,3], + {'foo':'bar'} + ]; + for (var i = 0; i < bogusRess.length; i++) { + log.info({res: bogusRess[i]}, "hi"); + t.equal(records[i].res, bogusRess[i]); + } + + // Get http request and response objects to play with and test. + var theReq, theRes; + var server = http.createServer(function (req, res) { + theReq = req; + theRes = res; + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Hello World\n'); + }) + server.listen(8765, function () { + http.get({host: '127.0.0.1', port: 8765, path: '/'}, function(res) { + log.info({res: theRes}, 'the response'); + var lastRecord = records[records.length-1]; + t.equal(lastRecord.res.statusCode, theRes.statusCode); + t.equal(lastRecord.res.header, theRes._header); + server.close(); + t.end(); + }).on('error', function (err) { + t.ok(false, 'error requesting to our test server: ' + err); + server.close(); + t.end(); + }); + }); +}); + + +test('err serializer', function (t) { + var records = []; + var log = bunyan.createLogger({ + name: 'serializer-test', + streams: [ + { + stream: new CapturingStream(records), + type: 'raw' + } + ], + serializers: { + err: bunyan.stdSerializers.err + } + }); + + // None of these should blow up. + var bogusErrs = [ + undefined, + null, + {}, + 1, + 'string', + [1,2,3], + {'foo':'bar'} + ]; + for (var i = 0; i < bogusErrs.length; i++) { + log.info({err: bogusErrs[i]}, "hi"); + t.equal(records[i].err, bogusErrs[i]); + } + + var theErr = new TypeError('blah'); + + log.info(theErr, 'the error'); + var lastRecord = records[records.length-1]; + t.equal(lastRecord.err.message, theErr.message); + t.equal(lastRecord.err.name, theErr.name); + t.equal(lastRecord.err.stack, theErr.stack); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/tools/cutarelease.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/tools/cutarelease.py new file mode 100755 index 0000000..2cfeb98 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/tools/cutarelease.py @@ -0,0 +1,609 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2012 Trent Mick + +"""cutarelease -- Cut a release of your project. + +A script that will help cut a release for a git-based project that follows +a few conventions. It'll update your changelog (CHANGES.md), add a git +tag, push those changes, update your version to the next patch level release +and create a new changelog section for that new version. + +Conventions: +- XXX +""" + +__version_info__ = (1, 0, 7) +__version__ = '.'.join(map(str, __version_info__)) + +import sys +import os +from os.path import join, dirname, normpath, abspath, exists, basename, splitext +from glob import glob +from pprint import pprint +import re +import codecs +import logging +import optparse +import json + + + +#---- globals and config + +log = logging.getLogger("cutarelease") + +class Error(Exception): + pass + + + +#---- main functionality + +def cutarelease(project_name, version_files, dry_run=False): + """Cut a release. + + @param project_name {str} + @param version_files {list} List of paths to files holding the version + info for this project. + + If none are given it attempts to guess the version file: + package.json or VERSION.txt or VERSION or $project_name.py + or lib/$project_name.py or $project_name.js or lib/$project_name.js. + + The version file can be in one of the following forms: + + - A .py file, in which case the file is expect to have a top-level + global called "__version_info__" as follows. [1] + + __version_info__ = (0, 7, 6) + + Note that I typically follow that with the following to get a + string version attribute on my modules: + + __version__ = '.'.join(map(str, __version_info__)) + + - A .js file, in which case the file is expected to have a top-level + global called "VERSION" as follows: + + ver VERSION = "1.2.3"; + + - A "package.json" file, typical of a node.js npm-using project. + The package.json file must have a "version" field. + + - TODO: A simple version file whose only content is a "1.2.3"-style version + string. + + [1]: This is a convention I tend to follow in my projects. + Granted it might not be your cup of tea. I should add support for + just `__version__ = "1.2.3"`. I'm open to other suggestions too. + """ + dry_run_str = dry_run and " (dry-run)" or "" + + if not version_files: + log.info("guessing version file") + candidates = [ + "package.json", + "VERSION.txt", + "VERSION", + "%s.py" % project_name, + "lib/%s.py" % project_name, + "%s.js" % project_name, + "lib/%s.js" % project_name, + ] + for candidate in candidates: + if exists(candidate): + version_files = [candidate] + break + else: + raise Error("could not find a version file: specify its path or " + "add one of the following to your project: '%s'" + % "', '".join(candidates)) + log.info("using '%s' as version file", version_files[0]) + + parsed_version_files = [_parse_version_file(f) for f in version_files] + version_file_type, version_info = parsed_version_files[0] + version = _version_from_version_info(version_info) + + # Confirm + if not dry_run: + answer = query_yes_no("* * *\n" + "Are you sure you want cut a %s release?\n" + "This will involved commits and a push." % version, + default="no") + print "* * *" + if answer != "yes": + log.info("user abort") + return + log.info("cutting a %s release%s", version, dry_run_str) + + # Checks: Ensure there is a section in changes for this version. + + + + changes_path = "CHANGES.md" + changes_txt, changes, nyr = parse_changelog(changes_path) + #pprint(changes) + top_ver = changes[0]["version"] + if top_ver != version: + raise Error("changelog '%s' top section says " + "version %r, expected version %r: aborting" + % (changes_path, top_ver, version)) + top_verline = changes[0]["verline"] + if not top_verline.endswith(nyr): + answer = query_yes_no("\n* * *\n" + "The changelog '%s' top section doesn't have the expected\n" + "'%s' marker. Has this been released already?" + % (changes_path, nyr), default="yes") + print "* * *" + if answer != "no": + log.info("abort") + return + top_body = changes[0]["body"] + if top_body.strip() == "(nothing yet)": + raise Error("top section body is `(nothing yet)': it looks like " + "nothing has been added to this release") + + # Commits to prepare release. + changes_txt_before = changes_txt + changes_txt = changes_txt.replace(" (not yet released)", "", 1) + if not dry_run and changes_txt != changes_txt_before: + log.info("prepare `%s' for release", changes_path) + f = codecs.open(changes_path, 'w', 'utf-8') + f.write(changes_txt) + f.close() + run('git commit %s -m "prepare for %s release"' + % (changes_path, version)) + + # Tag version and push. + curr_tags = set(t for t in _capture_stdout(["git", "tag", "-l"]).split('\n') if t) + if not dry_run and version not in curr_tags: + log.info("tag the release") + run('git tag -a "%s" -m "version %s"' % (version, version)) + run('git push --tags') + + # Optionally release. + if exists("package.json"): + answer = query_yes_no("\n* * *\nPublish to npm?", default="yes") + print "* * *" + if answer == "yes": + if dry_run: + log.info("skipping npm publish (dry-run)") + else: + run('npm publish') + elif exists("setup.py"): + answer = query_yes_no("\n* * *\nPublish to pypi?", default="yes") + print "* * *" + if answer == "yes": + if dry_run: + log.info("skipping pypi publish (dry-run)") + else: + run("%spython setup.py sdist --formats zip upload" + % _setup_command_prefix()) + + # Commits to prepare for future dev and push. + # - update changelog file + next_version_info = _get_next_version_info(version_info) + next_version = _version_from_version_info(next_version_info) + log.info("prepare for future dev (version %s)", next_version) + marker = "## " + changes[0]["verline"] + if marker.endswith(nyr): + marker = marker[0:-len(nyr)] + if marker not in changes_txt: + raise Error("couldn't find `%s' marker in `%s' " + "content: can't prep for subsequent dev" % (marker, changes_path)) + next_verline = "%s %s%s" % (marker.rsplit(None, 1)[0], next_version, nyr) + changes_txt = changes_txt.replace(marker + '\n', + "%s\n\n(nothing yet)\n\n\n%s\n" % (next_verline, marker)) + if not dry_run: + f = codecs.open(changes_path, 'w', 'utf-8') + f.write(changes_txt) + f.close() + + # - update version file + next_version_tuple = _tuple_from_version(next_version) + for i, ver_file in enumerate(version_files): + ver_content = codecs.open(ver_file, 'r', 'utf-8').read() + ver_file_type, ver_info = parsed_version_files[i] + if ver_file_type == "json": + marker = '"version": "%s"' % version + if marker not in ver_content: + raise Error("couldn't find `%s' version marker in `%s' " + "content: can't prep for subsequent dev" % (marker, ver_file)) + ver_content = ver_content.replace(marker, + '"version": "%s"' % next_version) + elif ver_file_type == "javascript": + candidates = [ + ("single", "var VERSION = '%s';" % version), + ("double", 'var VERSION = "%s";' % version), + ] + for quote_type, marker in candidates: + if marker in ver_content: + break + else: + raise Error("couldn't find any candidate version marker in " + "`%s' content: can't prep for subsequent dev: %r" + % (ver_file, candidates)) + if quote_type == "single": + ver_content = ver_content.replace(marker, + "var VERSION = '%s';" % next_version) + else: + ver_content = ver_content.replace(marker, + 'var VERSION = "%s";' % next_version) + elif ver_file_type == "python": + marker = "__version_info__ = %r" % (version_info,) + if marker not in ver_content: + raise Error("couldn't find `%s' version marker in `%s' " + "content: can't prep for subsequent dev" % (marker, ver_file)) + ver_content = ver_content.replace(marker, + "__version_info__ = %r" % (next_version_tuple,)) + elif ver_file_type == "version": + ver_content = next_version + else: + raise Error("unknown ver_file_type: %r" % ver_file_type) + if not dry_run: + log.info("update version to '%s' in '%s'", next_version, ver_file) + f = codecs.open(ver_file, 'w', 'utf-8') + f.write(ver_content) + f.close() + + if not dry_run: + run('git commit %s %s -m "prep for future dev"' % ( + changes_path, ' '.join(version_files))) + run('git push') + + + +#---- internal support routines + +def _indent(s, indent=' '): + return indent + indent.join(s.splitlines(True)) + +def _tuple_from_version(version): + def _intify(s): + try: + return int(s) + except ValueError: + return s + return tuple(_intify(b) for b in version.split('.')) + +def _get_next_version_info(version_info): + next = list(version_info[:]) + next[-1] += 1 + return tuple(next) + +def _version_from_version_info(version_info): + v = str(version_info[0]) + state_dot_join = True + for i in version_info[1:]: + if state_dot_join: + try: + int(i) + except ValueError: + state_dot_join = False + else: + pass + if state_dot_join: + v += "." + str(i) + else: + v += str(i) + return v + +_version_re = re.compile(r"^(\d+)\.(\d+)(?:\.(\d+)([abc](\d+)?)?)?$") +def _version_info_from_version(version): + m = _version_re.match(version) + if not m: + raise Error("could not convert '%s' version to version info" % version) + version_info = [] + for g in m.groups(): + if g is None: + break + try: + version_info.append(int(g)) + except ValueError: + version_info.append(g) + return tuple(version_info) + +def _parse_version_file(version_file): + """Get version info from the given file. It can be any of: + + Supported version file types (i.e. types of files from which we know + how to parse the version string/number -- often by some convention): + - json: use the "version" key + - javascript: look for a `var VERSION = "1.2.3";` or + `var VERSION = '1.2.3';` + - python: Python script/module with `__version_info__ = (1, 2, 3)` + - version: a VERSION.txt or VERSION file where the whole contents are + the version string + + @param version_file {str} Can be a path or "type:path", where "type" + is one of the supported types. + """ + # Get version file *type*. + version_file_type = None + match = re.compile("^([a-z]+):(.*)$").search(version_file) + if match: + version_file = match.group(2) + version_file_type = match.group(1) + aliases = { + "js": "javascript" + } + if version_file_type in aliases: + version_file_type = aliases[version_file_type] + + f = codecs.open(version_file, 'r', 'utf-8') + content = f.read() + f.close() + + if not version_file_type: + # Guess the type. + base = basename(version_file) + ext = splitext(base)[1] + if ext == ".json": + version_file_type = "json" + elif ext == ".py": + version_file_type = "python" + elif ext == ".js": + version_file_type = "javascript" + elif content.startswith("#!"): + shebang = content.splitlines(False)[0] + shebang_bits = re.split(r'[/ \t]', shebang) + for name, typ in {"python": "python", "node": "javascript"}.items(): + if name in shebang_bits: + version_file_type = typ + break + elif base in ("VERSION", "VERSION.txt"): + version_file_type = "version" + if not version_file_type: + raise RuntimeError("can't extract version from '%s': no idea " + "what type of file it it" % version_file) + + if version_file_type == "json": + obj = json.loads(content) + version_info = _version_info_from_version(obj["version"]) + elif version_file_type == "python": + m = re.search(r'^__version_info__ = (.*?)$', content, re.M) + version_info = eval(m.group(1)) + elif version_file_type == "javascript": + m = re.search(r'^var VERSION = (\'|")(.*?)\1;$', content, re.M) + version_info = _version_info_from_version(m.group(2)) + elif version_file_type == "version": + version_info = _version_info_from_version(content.strip()) + else: + raise RuntimeError("unexpected version_file_type: %r" + % version_file_type) + return version_file_type, version_info + + +def parse_changelog(changes_path): + """Parse the given changelog path and return `(content, parsed, nyr)` + where `nyr` is the ' (not yet released)' marker and `parsed` looks like: + + [{'body': u'\n(nothing yet)\n\n', + 'verline': u'restify 1.0.1 (not yet released)', + 'version': u'1.0.1'}, # version is parsed out for top section only + {'body': u'...', + 'verline': u'1.0.0'}, + {'body': u'...', + 'verline': u'1.0.0-rc2'}, + {'body': u'...', + 'verline': u'1.0.0-rc1'}] + + A changelog (CHANGES.md) is expected to look like this: + + # $project Changelog + + ## $next_version (not yet released) + + ... + + ## $version1 + + ... + + ## $version2 + + ... and so on + + The version lines are enforced as follows: + + - The top entry should have a " (not yet released)" suffix. "Should" + because recovery from half-cutarelease failures is supported. + - A version string must be extractable from there, but it tries to + be loose (though strict "X.Y.Z" versioning is preferred). Allowed + + ## 1.0.0 + ## my project 1.0.1 + ## foo 1.2.3-rc2 + + Basically, (a) the " (not yet released)" is stripped, (b) the + last token is the version, and (c) that version must start with + a digit (sanity check). + """ + if not exists(changes_path): + raise Error("changelog file '%s' not found" % changes_path) + content = codecs.open(changes_path, 'r', 'utf-8').read() + + parser = re.compile( + r'^##\s*(?P[^\n]*?)\s*$(?P.*?)(?=^##|\Z)', + re.M | re.S) + sections = parser.findall(content) + + # Sanity checks on changelog format. + if not sections: + template = "## 1.0.0 (not yet released)\n\n(nothing yet)\n" + raise Error("changelog '%s' must have at least one section, " + "suggestion:\n\n%s" % (changes_path, _indent(template))) + first_section_verline = sections[0][0] + nyr = ' (not yet released)' + #if not first_section_verline.endswith(nyr): + # eg = "## %s%s" % (first_section_verline, nyr) + # raise Error("changelog '%s' top section must end with %r, " + # "naive e.g.: '%s'" % (changes_path, nyr, eg)) + + items = [] + for i, section in enumerate(sections): + item = { + "verline": section[0], + "body": section[1] + } + if i == 0: + # We only bother to pull out 'version' for the top section. + verline = section[0] + if verline.endswith(nyr): + verline = verline[0:-len(nyr)] + version = verline.split()[-1] + try: + int(version[0]) + except ValueError: + msg = '' + if version.endswith(')'): + msg = " (cutarelease is picky about the trailing %r " \ + "on the top version line. Perhaps you misspelled " \ + "that?)" % nyr + raise Error("changelog '%s' top section version '%s' is " + "invalid: first char isn't a number%s" + % (changes_path, version, msg)) + item["version"] = version + items.append(item) + + return content, items, nyr + +## {{{ http://code.activestate.com/recipes/577058/ (r2) +def query_yes_no(question, default="yes"): + """Ask a yes/no question via raw_input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is one of "yes" or "no". + """ + valid = {"yes":"yes", "y":"yes", "ye":"yes", + "no":"no", "n":"no"} + if default == None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while 1: + sys.stdout.write(question + prompt) + choice = raw_input().lower() + if default is not None and choice == '': + return default + elif choice in valid.keys(): + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' "\ + "(or 'y' or 'n').\n") +## end of http://code.activestate.com/recipes/577058/ }}} + +def _capture_stdout(argv): + import subprocess + p = subprocess.Popen(argv, stdout=subprocess.PIPE) + return p.communicate()[0] + +class _NoReflowFormatter(optparse.IndentedHelpFormatter): + """An optparse formatter that does NOT reflow the description.""" + def format_description(self, description): + return description or "" + +def run(cmd): + """Run the given command. + + Raises OSError is the command returns a non-zero exit status. + """ + log.debug("running '%s'", cmd) + fixed_cmd = cmd + if sys.platform == "win32" and cmd.count('"') > 2: + fixed_cmd = '"' + cmd + '"' + retval = os.system(fixed_cmd) + if hasattr(os, "WEXITSTATUS"): + status = os.WEXITSTATUS(retval) + else: + status = retval + if status: + raise OSError(status, "error running '%s'" % cmd) + +def _setup_command_prefix(): + prefix = "" + if sys.platform == "darwin": + # http://forums.macosxhints.com/archive/index.php/t-43243.html + # This is an Apple customization to `tar` to avoid creating + # '._foo' files for extended-attributes for archived files. + prefix = "COPY_EXTENDED_ATTRIBUTES_DISABLE=1 " + return prefix + + +#---- mainline + +def main(argv): + logging.basicConfig(format="%(name)s: %(levelname)s: %(message)s") + log.setLevel(logging.INFO) + + # Parse options. + parser = optparse.OptionParser(prog="cutarelease", usage='', + version="%prog " + __version__, description=__doc__, + formatter=_NoReflowFormatter()) + parser.add_option("-v", "--verbose", dest="log_level", + action="store_const", const=logging.DEBUG, + help="more verbose output") + parser.add_option("-q", "--quiet", dest="log_level", + action="store_const", const=logging.WARNING, + help="quieter output (just warnings and errors)") + parser.set_default("log_level", logging.INFO) + parser.add_option("--test", action="store_true", + help="run self-test and exit (use 'eol.py -v --test' for verbose test output)") + parser.add_option("-p", "--project-name", metavar="NAME", + help='the name of this project (default is the base dir name)', + default=basename(os.getcwd())) + parser.add_option("-f", "--version-file", metavar="[TYPE:]PATH", + action='append', dest="version_files", + help='The path to the project file holding the version info. Can be ' + 'specified multiple times if more than one file should be updated ' + 'with new version info. If excluded, it will be guessed.') + parser.add_option("-n", "--dry-run", action="store_true", + help='Do a dry-run', default=False) + opts, args = parser.parse_args() + log.setLevel(opts.log_level) + + cutarelease(opts.project_name, opts.version_files, dry_run=opts.dry_run) + + +## {{{ http://code.activestate.com/recipes/577258/ (r5+) +if __name__ == "__main__": + try: + retval = main(sys.argv) + except KeyboardInterrupt: + sys.exit(1) + except SystemExit: + raise + except: + import traceback, logging + if not log.handlers and not logging.root.handlers: + logging.basicConfig() + skip_it = False + exc_info = sys.exc_info() + if hasattr(exc_info[0], "__name__"): + exc_class, exc, tb = exc_info + if isinstance(exc, IOError) and exc.args[0] == 32: + # Skip 'IOError: [Errno 32] Broken pipe': often a cancelling of `less`. + skip_it = True + if not skip_it: + tb_path, tb_lineno, tb_func = traceback.extract_tb(tb)[-1][:3] + log.error("%s (%s:%s in %s)", exc_info[1], tb_path, + tb_lineno, tb_func) + else: # string exception + log.error(exc_info[0]) + if not skip_it: + if log.isEnabledFor(logging.DEBUG): + traceback.print_exception(*exc_info) + sys.exit(1) + else: + sys.exit(retval) +## end of http://code.activestate.com/recipes/577258/ }}} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/tools/jsstyle b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/tools/jsstyle new file mode 100755 index 0000000..9df33c9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/bunyan/tools/jsstyle @@ -0,0 +1,953 @@ +#!/usr/bin/env perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# +# jsstyle - check for some common stylistic errors. +# +# jsstyle is a sort of "lint" for Javascript coding style. This tool is +# derived from the cstyle tool, used to check for the style used in the +# Solaris kernel, sometimes known as "Bill Joy Normal Form". +# +# There's a lot this can't check for, like proper indentation of code +# blocks. There's also a lot more this could check for. +# +# A note to the non perl literate: +# +# perl regular expressions are pretty much like egrep +# regular expressions, with the following special symbols +# +# \s any space character +# \S any non-space character +# \w any "word" character [a-zA-Z0-9_] +# \W any non-word character +# \d a digit [0-9] +# \D a non-digit +# \b word boundary (between \w and \W) +# \B non-word boundary +# + +require 5.0; +use IO::File; +use Getopt::Std; +use strict; + +my $usage = +"Usage: jsstyle [-h?vcC] [-t ] [-f ] [-o ] file ... + +Check your JavaScript file for style. +See for details on config options. +Report bugs to . + +Options: + -h print this help and exit + -v verbose + + -c check continuation indentation inside functions + -t specify tab width for line length calculation + -C don't check anything in header block comments + + -f PATH + path to a jsstyle config file + -o OPTION1,OPTION2 + set config options, e.g. '-o doxygen,indent=2' + +"; + +my %opts; + +if (!getopts("ch?o:t:f:vC", \%opts)) { + print $usage; + exit 2; +} + +if (defined($opts{'h'}) || defined($opts{'?'})) { + print $usage; + exit; +} + +my $check_continuation = $opts{'c'}; +my $verbose = $opts{'v'}; +my $ignore_hdr_comment = $opts{'C'}; +my $tab_width = $opts{'t'}; + +# By default, tabs are 8 characters wide +if (! defined($opts{'t'})) { + $tab_width = 8; +} + + +# Load config +my %config = ( + indent => "tab", + doxygen => 0, # doxygen comments: /** ... */ + splint => 0, # splint comments. Needed? + "unparenthesized-return" => 1, + "literal-string-quote" => "single", # 'single' or 'double' + "blank-after-start-comment" => 1, + "continuation-at-front" => 0, + "leading-right-paren-ok" => 0, + "strict-indent" => 0 +); +sub add_config_var ($$) { + my ($scope, $str) = @_; + + if ($str !~ /^([\w-]+)(?:\s*=\s*(.*?))?$/) { + die "$scope: invalid option: '$str'"; + } + my $name = $1; + my $value = ($2 eq '' ? 1 : $2); + #print "scope: '$scope', str: '$str', name: '$name', value: '$value'\n"; + + # Validate config var. + if ($name eq "indent") { + # A number of spaces or "tab". + if ($value !~ /^\d+$/ && $value ne "tab") { + die "$scope: invalid '$name': must be a number (of ". + "spaces) or 'tab'"; + } + } elsif ($name eq "doxygen" || # boolean vars + $name eq "splint" || + $name eq "unparenthesized-return" || + $name eq "continuation-at-front" || + $name eq "leading-right-paren-ok" || + $name eq "leading-comma-ok" || + $name eq "uncuddled-else-ok" || + $name eq "whitespace-after-left-paren-ok" || + $name eq "strict-indent" || + $name eq "blank-after-start-comment") { + + if ($value != 1 && $value != 0) { + die "$scope: invalid '$name': don't give a value"; + } + } elsif ($name eq "literal-string-quote") { + if ($value !~ /single|double/) { + die "$scope: invalid '$name': must be 'single' ". + "or 'double'"; + } + } else { + die "$scope: unknown config var: $name"; + } + $config{$name} = $value; +} + +if (defined($opts{'f'})) { + my $path = $opts{'f'}; + my $fh = new IO::File $path, "r"; + if (!defined($fh)) { + die "cannot open config path '$path'"; + } + my $line = 0; + while (<$fh>) { + $line++; + s/^\s*//; # drop leading space + s/\s*$//; # drop trailing space + next if ! $_; # skip empty line + next if /^#/; # skip comments + add_config_var "$path:$line", $_; + } +} + +if (defined($opts{'o'})) { + for my $x (split /,/, $opts{'o'}) { + add_config_var "'-o' option", $x; + } +} + + +my ($filename, $line, $prev); # shared globals + +my $fmt; +my $hdr_comment_start; + +if ($verbose) { + $fmt = "%s: %d: %s\n%s\n"; +} else { + $fmt = "%s: %d: %s\n"; +} + +if ($config{"doxygen"}) { + # doxygen comments look like "/*!" or "/**"; allow them. + $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/; +} else { + $hdr_comment_start = qr/^\s*\/\*$/; +} + +# Note, following must be in single quotes so that \s and \w work right. +my $lint_re = qr/\/\*(?: + jsl:\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*| + CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY| + FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*| + PROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*? + )\*\//x; + +my $splint_re = qr/\/\*@.*?@\*\//x; + +my $err_stat = 0; # exit status + +if ($#ARGV >= 0) { + foreach my $arg (@ARGV) { + my $fh = new IO::File $arg, "r"; + if (!defined($fh)) { + printf "%s: cannot open\n", $arg; + } else { + &jsstyle($arg, $fh); + close $fh; + } + } +} else { + &jsstyle("", *STDIN); +} +exit $err_stat; + +my $no_errs = 0; # set for JSSTYLED-protected lines + +sub err($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $line; + $err_stat = 1; + } +} + +sub err_prefix($$) { + my ($prevline, $error) = @_; + my $out = $prevline."\n".$line; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $out; + $err_stat = 1; + } +} + +sub err_prev($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $. - 1, $error, $prev; + $err_stat = 1; + } +} + +sub jsstyle($$) { + +my ($fn, $filehandle) = @_; +$filename = $fn; # share it globally + +my $in_cpp = 0; +my $next_in_cpp = 0; + +my $in_comment = 0; +my $in_header_comment = 0; +my $comment_done = 0; +my $in_function = 0; +my $in_function_header = 0; +my $in_declaration = 0; +my $note_level = 0; +my $nextok = 0; +my $nocheck = 0; + +my $in_string = 0; + +my ($okmsg, $comment_prefix); + +$line = ''; +$prev = ''; +reset_indent(); + +line: while (<$filehandle>) { + s/\r?\n$//; # strip return and newline + + # save the original line, then remove all text from within + # double or single quotes, we do not want to check such text. + + $line = $_; + + # + # C allows strings to be continued with a backslash at the end of + # the line. We translate that into a quoted string on the previous + # line followed by an initial quote on the next line. + # + # (we assume that no-one will use backslash-continuation with character + # constants) + # + $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); + + # + # normal strings and characters + # + s/'([^\\']|\\.)*'/\'\'/g; + s/"([^\\"]|\\.)*"/\"\"/g; + + # + # detect string continuation + # + if ($nocheck || $in_comment) { + $in_string = 0; + } else { + # + # Now that all full strings are replaced with "", we check + # for unfinished strings continuing onto the next line. + # + $in_string = + (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || + s/^("")*"([^\\"]|\\.)*\\$/""/); + } + + # + # figure out if we are in a cpp directive + # + $in_cpp = $next_in_cpp || /^\s*#/; # continued or started + $next_in_cpp = $in_cpp && /\\$/; # only if continued + + # strip off trailing backslashes, which appear in long macros + s/\s*\\$//; + + # an /* END JSSTYLED */ comment ends a no-check block. + if ($nocheck) { + if (/\/\* *END *JSSTYLED *\*\//) { + $nocheck = 0; + } else { + reset_indent(); + next line; + } + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if ($nextok) { + if ($okmsg) { + err($okmsg); + } + $nextok = 0; + $okmsg = 0; + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + $no_errs = 1; + } elsif ($no_errs) { + $no_errs = 0; + } + + # check length of line. + # first, a quick check to see if there is any chance of being too long. + if ((($line =~ tr/\t/\t/) * ($tab_width - 1)) + length($line) > 80) { + # yes, there is a chance. + # replace tabs with spaces and check again. + my $eline = $line; + 1 while $eline =~ + s/\t+/' ' x + (length($&) * $tab_width - length($`) % $tab_width)/e; + if (length($eline) > 80) { + err("line > 80 characters"); + } + } + + # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). + if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE + s/[^()]//g; # eliminate all non-parens + $note_level += s/\(//g - length; # update paren nest level + next; + } + + # a /* BEGIN JSSTYLED */ comment starts a no-check block. + if (/\/\* *BEGIN *JSSTYLED *\*\//) { + $nocheck = 1; + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + if (/\/\/ *JSSTYLED/) { + /^.*\/\/ *JSSTYLED *(.*)$/; + $okmsg = $1; + $nextok = 1; + } + + # universal checks; apply to everything + if (/\t +\t/) { + err("spaces between tabs"); + } + if (/ \t+ /) { + err("tabs between spaces"); + } + if (/\s$/) { + err("space or tab at end of line"); + } + if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { + err("comment preceded by non-blank"); + } + + # is this the beginning or ending of a function? + # (not if "struct foo\n{\n") + if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { + $in_function = 1; + $in_declaration = 1; + $in_function_header = 0; + $prev = $line; + next line; + } + if (/^}\s*(\/\*.*\*\/\s*)*$/) { + if ($prev =~ /^\s*return\s*;/) { + err_prev("unneeded return at end of function"); + } + $in_function = 0; + reset_indent(); # we don't check between functions + $prev = $line; + next line; + } + if (/^\w*\($/) { + $in_function_header = 1; + } + + # a blank line terminates the declarations within a function. + # XXX - but still a problem in sub-blocks. + if ($in_declaration && /^$/) { + $in_declaration = 0; + } + + if ($comment_done) { + $in_comment = 0; + $in_header_comment = 0; + $comment_done = 0; + } + # does this looks like the start of a block comment? + if (/$hdr_comment_start/) { + if ($config{"indent"} eq "tab") { + if (!/^\t*\/\*/) { + err("block comment not indented by tabs"); + } + } elsif (!/^ *\/\*/) { + err("block comment not indented by spaces"); + } + $in_comment = 1; + /^(\s*)\//; + $comment_prefix = $1; + if ($comment_prefix eq "") { + $in_header_comment = 1; + } + $prev = $line; + next line; + } + # are we still in the block comment? + if ($in_comment) { + if (/^$comment_prefix \*\/$/) { + $comment_done = 1; + } elsif (/\*\//) { + $comment_done = 1; + err("improper block comment close") + unless ($ignore_hdr_comment && $in_header_comment); + } elsif (!/^$comment_prefix \*[ \t]/ && + !/^$comment_prefix \*$/) { + err("improper block comment") + unless ($ignore_hdr_comment && $in_header_comment); + } + } + + if ($in_header_comment && $ignore_hdr_comment) { + $prev = $line; + next line; + } + + # check for errors that might occur in comments and in code. + + # allow spaces to be used to draw pictures in header comments. + #if (/[^ ] / && !/".* .*"/ && !$in_header_comment) { + # err("spaces instead of tabs"); + #} + #if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && + # (!/^ \w/ || $in_function != 0)) { + # err("indent by spaces instead of tabs"); + #} + if ($config{"indent"} eq "tab") { + if (/^ {2,}/ && !/^ [^ ]/) { + err("indent by spaces instead of tabs"); + } + } elsif (/^\t/) { + err("indent by tabs instead of spaces") + } elsif (/^( +)/ && !$in_comment) { + my $indent = $1; + if (length($indent) < $config{"indent"}) { + err("indent of " . length($indent) . + " space(s) instead of " . $config{"indent"}); + } elsif ($config{"strict-indent"} && + length($indent) % $config{"indent"} != 0) { + err("indent is " . length($indent) . + " not a multiple of " . $config{'indent'} . " spaces"); + } + } + if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { + err("continuation line not indented by 4 spaces"); + } + + # A multi-line block comment must not have content on the first line. + if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) { + err("improper first line of block comment"); + } + + if ($in_comment) { # still in comment, don't do further checks + $prev = $line; + next line; + } + + if ((/[^(]\/\*\S/ || /^\/\*\S/) && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank after open comment"); + } + if (/\S\*\/[^)]|\S\*\/$/ && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank before close comment"); + } + if ($config{"blank-after-start-comment"} && /(?\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\s,=]/ || + (/[^->]>[^,=>\s]/ && !/[^->]>$/) || + (/[^<]<[^,=<\s]/ && !/[^<]<$/) || + /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { + err("missing space around relational operator"); + } + if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || + (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || + (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { + # XXX - should only check this for C++ code + # XXX - there are probably other forms that should be allowed + if (!/\soperator=/) { + err("missing space around assignment operator"); + } + } + if (/[,;]\S/ && !/\bfor \(;;\)/ && + # Allow a comma in a regex quantifier. + !/\/.*?\{\d+,?\d*\}.*?\//) { + err("comma or semicolon followed by non-blank"); + } + # check for commas preceded by blanks + if ((!$config{"leading-comma-ok"} && /^\s*,/) || (!/^\s*,/ && /\s,/)) { + err("comma preceded by blank"); + } + # check for semicolons preceded by blanks + # allow "for" statements to have empty "while" clauses + if (/\s;/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { + err("semicolon preceded by blank"); + } + if (!$config{"continuation-at-front"} && /^\s*(&&|\|\|)/) { + err("improper boolean continuation"); + } elsif ($config{"continuation-at-front"} && /(&&|\|\||\+)$/) { + err("improper continuation"); + } + if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { + err("more than one space around boolean operator"); + } + # We allow methods which look like obj.delete() but not keywords without + # spaces ala: delete(obj) + if (/(?) + + * 0.0.2: + Solaris i386 support. + Fixes memory leaks + Improved performance, enabled- and disabled-probe. + + * 0.0.1: + First working version: OSX x86_64 only. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/LICENCE b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/LICENCE new file mode 100644 index 0000000..558a833 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/LICENCE @@ -0,0 +1,21 @@ +Copyright 2011 Chris Andrews. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY CHRIS ANDREWS ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CHRIS ANDREWS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/README.md new file mode 100644 index 0000000..30b4258 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/README.md @@ -0,0 +1,114 @@ +# dtrace-provider - Native DTrace providers for Node.js apps. + +This extension allows you to create native DTrace providers for your +Node.js applications. That is, to create providers and probes which +expose information specific to your application, rather than +information about the node runtime. + +You could use this to expose high-level information about the inner +workings of your application, or to create a specific context in which +to look at information from other runtime or system-level providers. + +The provider is not created in the usual way, by declaring it and then +changing the build process to include it, but instead dynamically at +runtime. This is done entirely in-process, and there is no background +compiler or dtrace(1) invocation. The process creating the provider +need not run as root. + +## INSTALL + + $ npm install dtrace-provider + +## EXAMPLE + +Here's a simple example of creating a provider: + + var d = require('dtrace-provider'); + + var dtp = d.createDTraceProvider("nodeapp"); + var p1 = dtp.addProbe("probe1", "int", "int"); + var p2 = dtp.addProbe("probe2", "char *"); + dtp.enable(); + +Probes may be fired via the provider object: + + dtp.fire("probe1", function(p) { + return [1, 2]; + }); + dtp.fire("probe2", function(p) { + return ["hello, dtrace via provider", "foo"]; + }); + +or via the probe objects themselves: + + p1.fire(function(p) { + return [1, 2, 3, 4, 5, 6]; + }); + p2.fire(function(p) { + return ["hello, dtrace via probe", "foo"]; + }); + +This example creates a provider called "nodeapp", and adds two +probes. It then enables the provider, at which point the provider +becomes visible to DTrace. + +The probes are then fired, which produces this output: + + $ sudo dtrace -Z -n 'nodeapp*:::probe1{ trace(arg0); trace(arg1) }' \ + -n 'nodeapp*:::probe2{ trace(copyinstr(arg0)); }' + dtrace: description 'nodeapp*:::probe1' matched 0 probes + dtrace: description 'nodeapp*:::probe2' matched 0 probes + CPU ID FUNCTION:NAME + 1 123562 func:probe1 1 2 + 1 123563 func:probe2 hello, dtrace + +Arguments are captured by a callback only executed when the probe is +enabled. This means you can do more expensive work to gather arguments. + +The maximum number of arguments supported is 32. + +## PLATFORM SUPPORT + +This libusdt-based Node.JS module supports 64 and 32 bit processes on +Mac OS X and Solaris-like systems such as Illumos or SmartOS. As more +platform support is added to libusdt, those platforms will be +supported by this module. See libusdt's status at: + + https://github.com/chrisa/libusdt#readme + +FreeBSD is supported in principle but is restricted to only 4 working +arguments per probe. + +Platforms not supporting DTrace (notably, Linux and Windows) may +install this module without building libusdt, with a stub no-op +implementation provided for compatibility. This allows cross-platform +npm modules to embed probes and include a dependency on this module. + +## LIMITATIONS + +The data types supported are "int" and "char *". Support for +structured types is planned, depending on support from the host DTrace +implementation for the necessary translators. + +## CAVEATS + +There is some overhead to probes, even when disabled. Probes are +already using the "is-enabled" feature of DTrace to control execution +of the arguments-gathering callback, but some work still needs to be +done before that's checked. This overhead should not be a problem +unless probes are placed in particularly hot code paths. + +## CONTRIBUTING + +The source is available at: + + https://github.com/chrisa/node-dtrace-provider. + +For issues, please use the Github issue tracker linked to the +repository. Github pull requests are very welcome. + +## OTHER IMPLEMENTATIONS + +This node extension is derived from the ruby-dtrace gem, via the Perl +module Devel::DTrace::Provider, both of which provide the same +functionality to those languages. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/README.md~ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/README.md~ new file mode 100644 index 0000000..d2fd4e0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/README.md~ @@ -0,0 +1,136 @@ +# dtrace-provider - Native DTrace providers for Node.js apps. + +This extension allows you to create native DTrace providers for your +Node.js applications. That is, to create providers and probes which +expose information specific to your application, rather than +information about the node runtime. + +You could use this to expose high-level information about the inner +workings of your application, or to create a specific context in which +to look at information from other runtime or system-level providers. + +The provider is not created in the usual way, by declaring it and then +changing the build process to include it, but instead dynamically at +runtime. This is done entirely in-process, and there is no background +compiler or dtrace(1) invocation. The process creating the provider +need not run as root. + +## INSTALL + + $ npm install dtrace-provider + +## EXAMPLE + +Here's a simple example of creating a provider: + + var d = require('dtrace-provider'); + + var dtp = d.createDTraceProvider("nodeapp"); + var p1 = dtp.addProbe("probe1", "int", "int"); + var p2 = dtp.addProbe("probe2", "char *"); + dtp.enable(); + +Probes may be fired via the provider object: + + dtp.fire("probe1", function(p) { + return [1, 2]; + }); + dtp.fire("probe2", function(p) { + return ["hello, dtrace via provider", "foo"]; + }); + +or via the probe objects themselves: + + p1.fire(function(p) { + return [1, 2, 3, 4, 5, 6]; + }); + p2.fire(function(p) { + return ["hello, dtrace via probe", "foo"]; + }); + +This example creates a provider called "nodeapp", and adds two +probes. It then enables the provider, at which point the provider +becomes visible to DTrace. + +The probes are then fired, which produces this output: + + $ sudo dtrace -Z -n 'nodeapp*:::probe1{ trace(arg0); trace(arg1) }' \ + -n 'nodeapp*:::probe2{ trace(copyinstr(arg0)); }' + dtrace: description 'nodeapp*:::probe1' matched 0 probes + dtrace: description 'nodeapp*:::probe2' matched 0 probes + CPU ID FUNCTION:NAME + 1 123562 func:probe1 1 2 + 1 123563 func:probe2 hello, dtrace + +Arguments are captured by a callback only executed when the probe is +enabled. This means you can do more expensive work to gather arguments. + +The maximum number of arguments supported is 32. + +## PLATFORM SUPPORT + +This libusdt-based Node.JS module supports 64 and 32 bit processes on +Mac OS X and Solaris-like systems such as Illumos or SmartOS. As more +platform support is added to libusdt, those platforms will be +supported by this module. See libusdt's status at: + + https://github.com/chrisa/libusdt#readme + +FreeBSD is supported in principle but is restricted to only 4 working +arguments per probe. + +Platforms not supporting DTrace (notably, Linux and Windows) may +install this module without building libusdt, with a stub no-op +implementation provided for compatibility. This allows cross-platform +npm modules to embed probes and include a dependency on this module. + +## LIMITATIONS + +<<<<<<< HEAD +The data types supported are "int" and "char *". There's definitely +scope to improve this, with more elaborate argument handling - see +TODO.md + +You can only create a provider once - although you don't have to do it +immediately, once you've set up a provider you can't change its +definition. It should be possible to enable updates - again, see +TODO.md. + +## CAVEATS + +Performance is not where it should be, most especially the +disabled-probe effect. Probes are already using the "is-enabled" +feature of DTrace to control execution of the arguments-gathering +callback, but too much work needs to be done before that's +checked. That being said, unless your (disabled) probes are +insanely hot, this shouldn't be a problem. + +Please see TODO.md for the details. +======= +The data types supported are "int" and "char *". Support for +structured types is planned, depending on support from the host DTrace +implementation for the necessary translators. + +## CAVEATS + +There is some overhead to probes, even when disabled. Probes are +already using the "is-enabled" feature of DTrace to control execution +of the arguments-gathering callback, but some work still needs to be +done before that's checked. This overhead should not be a problem +unless probes are placed in particularly hot code paths. +>>>>>>> libusdt + +## CONTRIBUTING + +The source is available at: + + https://github.com/chrisa/node-dtrace-provider. + +For issues, please use the Github issue tracker linked to the +repository. Github pull requests are very welcome. + +## OTHER IMPLEMENTATIONS + +This node extension is derived from the ruby-dtrace gem, via the Perl +module Devel::DTrace::Provider, both of which provide the same +functionality to those languages. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/TODO.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/TODO.md new file mode 100644 index 0000000..255a92f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/TODO.md @@ -0,0 +1,13 @@ +# dtrace-provider - TODO + +## FEATURES + +### Structured Arguments + +The current support for argument types is limited to "char *" and +"int", i.e. strings and integer types. Native string types in +Javascript are converted to raw C strings for use with DTrace. + +With support for dynamic types and translators from the host DTrace +implementation, it'd be possible to provide a mapping from Javascript +objects to C structs, which could then be inspected in D scripts. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/binding.gyp b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/binding.gyp new file mode 100644 index 0000000..c4997f7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/binding.gyp @@ -0,0 +1,40 @@ +{ + 'targets': [ + { + 'target_name': 'DTraceProviderBindings', + 'conditions': [ + ['OS=="mac" or OS=="solaris" or OS=="freebsd"', { + 'sources': [ + 'dtrace_provider.cc', + 'dtrace_probe.cc', + ], + 'include_dirs': [ + 'libusdt' + ], + 'dependencies': [ + 'libusdt' + ], + 'libraries': [ + '-L<(module_root_dir)/libusdt -l usdt' + ] + }] + ] + }, + { + 'target_name': 'libusdt', + 'type': 'none', + 'conditions': [ + ['OS=="mac" or OS=="solaris" or OS=="freebsd"', { + 'actions': [{ + 'inputs': [''], + 'outputs': [''], + 'action_name': 'build_libusdt', + 'action': [ + 'sh', 'libusdt-build.sh' + ] + }] + }] + ] + } + ] +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace-provider.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace-provider.js new file mode 100644 index 0000000..7bf69bd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace-provider.js @@ -0,0 +1,37 @@ +var DTraceProvider; + +function DTraceProviderStub() {} +DTraceProviderStub.prototype.addProbe = function() { + return { + 'fire': function() { } + }; +}; +DTraceProviderStub.prototype.enable = function() {}; +DTraceProviderStub.prototype.fire = function() {}; + +var builds = ['Release', 'default', 'Debug']; + +for (var i in builds) { + try { + var binding = require('./build/' + builds[i] + '/DTraceProviderBindings'); + DTraceProvider = binding.DTraceProvider; + break; + } catch (e) { + // if the platform looks like it _should_ have DTrace + // available, log a failure to load the bindings. + if (process.platform == 'darwin' || + process.platform == 'solaris' || + process.platform == 'freebsd') { + console.log(e); + } + } +} + +if (!DTraceProvider) { + DTraceProvider = DTraceProviderStub; +} + +exports.DTraceProvider = DTraceProvider; +exports.createDTraceProvider = function(name) { + return new DTraceProvider(name); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_probe.cc b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_probe.cc new file mode 100644 index 0000000..d33ae45 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_probe.cc @@ -0,0 +1,94 @@ +#include "dtrace_provider.h" +#include + +#include + +namespace node { + + using namespace v8; + + Persistent DTraceProbe::constructor_template; + + void DTraceProbe::Initialize(Handle target) { + HandleScope scope; + + Local t = FunctionTemplate::New(DTraceProbe::New); + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("DTraceProbe")); + + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fire", DTraceProbe::Fire); + + target->Set(String::NewSymbol("DTraceProbe"), constructor_template->GetFunction()); + } + + Handle DTraceProbe::New(const Arguments& args) { + DTraceProbe *probe = new DTraceProbe(); + probe->Wrap(args.This()); + return args.This(); + } + + Handle DTraceProbe::Fire(const Arguments& args) { + HandleScope scope; + DTraceProbe *pd = ObjectWrap::Unwrap(args.Holder()); + return pd->_fire(args[0]); + } + + Handle DTraceProbe::_fire(v8::Local argsfn) { + + if (usdt_is_enabled(this->probedef->probe) == 0) { + return Undefined(); + } + + // invoke fire callback + TryCatch try_catch; + + if (!argsfn->IsFunction()) { + return ThrowException(Exception::Error(String::New( + "Must give probe value callback as argument"))); + } + + Local cb = Local::Cast(argsfn); + Local probe_args = cb->Call(this->handle_, 0, NULL); + + // exception in args callback? + if (try_catch.HasCaught()) { + FatalException(try_catch); + return Undefined(); + } + + // check return + if (!probe_args->IsArray()) { + return Undefined(); + } + + Local a = Local::Cast(probe_args); + void *argv[USDT_ARG_MAX]; + + // limit argc to the defined number of probe args + size_t argc = a->Length(); + if (argc > this->probedef->argc) + argc = this->probedef->argc; + + for (size_t i = 0; i < argc; i++) { + if (this->probedef->types[i] == USDT_ARGTYPE_STRING) { + // char * + String::AsciiValue str(a->Get(i)->ToString()); + argv[i] = (void *) strdup(*str); + } + else { + // int +#ifdef __x86_64__ + argv[i] = (void *)(long) a->Get(i)->ToInteger()->Value(); +#else + argv[i] = (void *)(int) a->Get(i)->ToInt32()->Value(); +#endif + } + } + + usdt_fire_probe(this->probedef->probe, argc, argv); + + return True(); + } + +} // namespace node diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_provider.cc b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_provider.cc new file mode 100644 index 0000000..992517b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_provider.cc @@ -0,0 +1,152 @@ +#include "dtrace_provider.h" +#include + +#include +#include + +namespace node { + + using namespace v8; + + Persistent DTraceProvider::constructor_template; + + void DTraceProvider::Initialize(Handle target) { + HandleScope scope; + + Local t = FunctionTemplate::New(DTraceProvider::New); + constructor_template = Persistent::New(t); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("DTraceProvider")); + + NODE_SET_PROTOTYPE_METHOD(constructor_template, "addProbe", DTraceProvider::AddProbe); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "removeProbe", DTraceProvider::RemoveProbe); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "enable", DTraceProvider::Enable); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "disable", DTraceProvider::Disable); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "fire", DTraceProvider::Fire); + + target->Set(String::NewSymbol("DTraceProvider"), constructor_template->GetFunction()); + + DTraceProbe::Initialize(target); + } + + Handle DTraceProvider::New(const Arguments& args) { + HandleScope scope; + DTraceProvider *p = new DTraceProvider(); + + p->Wrap(args.This()); + + if (args.Length() != 1 || !args[0]->IsString()) { + return ThrowException(Exception::Error(String::New( + "Must give provider name as argument"))); + } + + String::AsciiValue name(args[0]->ToString()); + + if ((p->provider = usdt_create_provider(*name, "modname")) == NULL) { + return ThrowException(Exception::Error(String::New( + "usdt_create_provider failed"))); + } + + return args.This(); + } + + Handle DTraceProvider::AddProbe(const Arguments& args) { + HandleScope scope; + const char *types[USDT_ARG_MAX]; + int argc = 0; + + Handle obj = args.Holder(); + DTraceProvider *provider = ObjectWrap::Unwrap(obj); + + // create a DTraceProbe object + Handle klass = DTraceProbe::constructor_template->GetFunction(); + Handle pd = Persistent::New(klass->NewInstance()); + + // store in provider object + DTraceProbe *probe = ObjectWrap::Unwrap(pd->ToObject()); + obj->Set(args[0]->ToString(), pd); + + // add probe to provider + for (int i = 0; i < USDT_ARG_MAX; i++) { + if (i < args.Length() - 1) { + String::AsciiValue type(args[i + 1]->ToString()); + types[i] = strdup(*type); + argc++; + } + } + String::AsciiValue name(args[0]->ToString()); + probe->probedef = usdt_create_probe(*name, *name, argc, types); + usdt_provider_add_probe(provider->provider, probe->probedef); + + return pd; + } + + Handle DTraceProvider::RemoveProbe(const Arguments& args) { + HandleScope scope; + + Handle provider_obj = args.Holder(); + DTraceProvider *provider = ObjectWrap::Unwrap(provider_obj); + + Handle probe_obj = Local::Cast(args[0]); + DTraceProbe *probe = ObjectWrap::Unwrap(probe_obj); + + Handle name = String::New(probe->probedef->name); + provider_obj->Delete(name); + + if (usdt_provider_remove_probe(provider->provider, probe->probedef) != 0) + return ThrowException(Exception::Error(String::New(usdt_errstr(provider->provider)))); + + return True(); + } + + Handle DTraceProvider::Enable(const Arguments& args) { + HandleScope scope; + DTraceProvider *provider = ObjectWrap::Unwrap(args.Holder()); + + if (usdt_provider_enable(provider->provider) != 0) + return ThrowException(Exception::Error(String::New(usdt_errstr(provider->provider)))); + + return Undefined(); + } + + Handle DTraceProvider::Disable(const Arguments& args) { + HandleScope scope; + DTraceProvider *provider = ObjectWrap::Unwrap(args.Holder()); + + if (usdt_provider_disable(provider->provider) != 0) + return ThrowException(Exception::Error(String::New(usdt_errstr(provider->provider)))); + + return Undefined(); + } + + Handle DTraceProvider::Fire(const Arguments& args) { + HandleScope scope; + + if (!args[0]->IsString()) { + return ThrowException(Exception::Error(String::New( + "Must give probe name as first argument"))); + } + + if (!args[1]->IsFunction()) { + return ThrowException(Exception::Error(String::New( + "Must give probe value callback as second argument"))); + } + + Handle provider = args.Holder(); + Handle probe = Local::Cast(provider->Get(args[0])); + + DTraceProbe *p = ObjectWrap::Unwrap(probe); + if (p == NULL) + return Undefined(); + + p->_fire(args[1]); + + return True(); + } + + extern "C" void + init(Handle target) { + DTraceProvider::Initialize(target); + } + +} // namespace node diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_provider.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_provider.h new file mode 100644 index 0000000..f30c8aa --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/dtrace_provider.h @@ -0,0 +1,76 @@ +#include +#include +#include + +extern "C" { +#include +} + +#include +#include +#include + +#include +#include +#include +#include + +#ifndef __APPLE__ +#include +#include +#endif + +namespace node { + + using namespace v8; + + class DTraceProbe : ObjectWrap { + + public: + static void Initialize(v8::Handle target); + usdt_probedef_t *probedef; + + static v8::Handle New(const v8::Arguments& args); + static v8::Handle Fire(const v8::Arguments& args); + + Handle _fire(v8::Local); + + static Persistent constructor_template; + + DTraceProbe() : ObjectWrap() { + probedef = NULL; + } + + ~DTraceProbe() { + } + + private: + }; + + class DTraceProvider : ObjectWrap { + + public: + static void Initialize(v8::Handle target); + usdt_provider_t *provider; + + static v8::Handle New(const v8::Arguments& args); + static v8::Handle AddProbe(const v8::Arguments& args); + static v8::Handle RemoveProbe(const v8::Arguments& args); + static v8::Handle Enable(const v8::Arguments& args); + static v8::Handle Disable(const v8::Arguments& args); + static v8::Handle Fire(const v8::Arguments& args); + + DTraceProvider() : ObjectWrap() { + provider = NULL; + } + + ~DTraceProvider() { + usdt_provider_disable(provider); + } + + private: + static Persistent constructor_template; + }; + + void InitDTraceProvider(v8::Handle target); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt-build.sh b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt-build.sh new file mode 100755 index 0000000..bd6ee4e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt-build.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# GYP's MAKEFLAGS confuses libusdt's Makefile +# +unset MAKEFLAGS + +# Ask node what arch it's been built for, and build libusdt to match. +# +# (this will need to change at the point that GYP is able to build +# node extensions universal on the Mac - for now we'll go with x86_64 +# on a 64 bit Mac) +# +ARCH=`node -e "console.log(process.config.variables.target_arch == 'x64' ? 'x86_64' : 'i386')"` +echo "Building libusdt for ${ARCH}" +export ARCH + +# Build. +# +make -C libusdt clean all diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/.npmignore new file mode 100644 index 0000000..da2bc84 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/.npmignore @@ -0,0 +1,9 @@ +*.o +*.a +*.gch +*~ +*# +.#* +test_usdt +test_usdt32 +test_usdt64 diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/LICENCE b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/LICENCE new file mode 100644 index 0000000..e74a6f8 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/LICENCE @@ -0,0 +1,21 @@ +Copyright 2012 Chris Andrews. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY CHRIS ANDREWS ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CHRIS ANDREWS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/Makefile new file mode 100644 index 0000000..3ab0c96 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/Makefile @@ -0,0 +1,144 @@ +# MAC_BUILD - set this to "universal" to build a 2-way fat library +# CFLAGS - to taste +# CC - C compiler, cc, gcc or clang +# +MAC_BUILD=universal +CFLAGS= #-Wall -pedantic -O2 +ifndef CC +CC=cc +endif + +# if ARCH set, disable universal build on the mac +ifdef ARCH +MAC_BUILD= +else +ARCH=$(shell uname -m) +endif + +UNAME=$(shell uname) + +ifeq ($(UNAME), SunOS) +PATH +=:/usr/perl5/5.10.0/bin +CFLAGS +=-fPIC +ifeq ($(ARCH), i86pc) +ARCH=$(shell isainfo -k) +ifeq ($(ARCH), amd64) +ARCH=x86_64 +else +ARCH=i386 +endif +endif +ifeq ($(ARCH), x86_64) +CFLAGS += -m64 +endif +endif + +ifeq ($(UNAME), FreeBSD) +CFLAGS += -Wno-error=unknown-pragmas -I/usr/src/sys/cddl/compat/opensolaris -I/usr/src/sys/cddl/contrib/opensolaris/uts/common +ifeq ($(ARCH), i386) +CFLAGS += -m32 +endif +endif + +ifeq ($(UNAME), Darwin) +ifeq ($(MAC_BUILD), universal) +CFLAGS += -arch i386 -arch x86_64 +else +CFLAGS += -arch $(ARCH) +endif +endif + +# main library build +objects = usdt.o usdt_dof_file.o usdt_tracepoints.o usdt_probe.o usdt_dof.o usdt_dof_sections.o +headers = usdt.h usdt_internal.h + +.c.o: $(headers) + +all: libusdt.a + +libusdt.a: $(objects) $(headers) + rm -f libusdt.a + ar cru libusdt.a $(objects) + ranlib libusdt.a + +# Tracepoints build. +# +# If on Darwin and building a universal library, manually assemble a +# two-way fat object file from both the 32 and 64 bit tracepoint asm +# files. +# +# Otherwise, just choose the appropriate asm file based on the build +# architecture. + +ifeq ($(UNAME), Darwin) +ifeq ($(MAC_BUILD), universal) + +usdt_tracepoints_i386.o: usdt_tracepoints_i386.s + as -arch i386 -o usdt_tracepoints_i386.o usdt_tracepoints_i386.s + +usdt_tracepoints_x86_64.o: usdt_tracepoints_x86_64.s + as -arch x86_64 -o usdt_tracepoints_x86_64.o usdt_tracepoints_x86_64.s + +usdt_tracepoints.o: usdt_tracepoints_i386.o usdt_tracepoints_x86_64.o + lipo -create -output usdt_tracepoints.o usdt_tracepoints_i386.o \ + usdt_tracepoints_x86_64.o + +else # Darwin, not universal +usdt_tracepoints.o: usdt_tracepoints_$(ARCH).s + as -arch $(ARCH) -o usdt_tracepoints.o usdt_tracepoints_$(ARCH).s +endif + +else # not Darwin; FreeBSD and Illumos + +ifeq ($(ARCH), x86_64) +usdt_tracepoints.o: usdt_tracepoints_x86_64.s + as --64 -o usdt_tracepoints.o usdt_tracepoints_x86_64.s +endif +ifeq ($(ARCH), i386) +usdt_tracepoints.o: usdt_tracepoints_i386.s + as --32 -o usdt_tracepoints.o usdt_tracepoints_i386.s +endif + +endif + +clean: + rm -f *.gch + rm -f *.o + rm -f libusdt.a + rm -f test_usdt + rm -f test_usdt32 + rm -f test_usdt64 + +.PHONY: clean test + +# testing + +ifeq ($(UNAME), Darwin) +ifeq ($(MAC_BUILD), universal) +test_usdt64: libusdt.a test_usdt.o + $(CC) -arch x86_64 -o test_usdt64 test_usdt.o libusdt.a +test_usdt32: libusdt.a test_usdt.o + $(CC) -arch i386 -o test_usdt32 test_usdt.o libusdt.a +else +test_usdt: libusdt.a test_usdt.o + $(CC) $(CFLAGS) -o test_usdt test_usdt.o libusdt.a +endif +else +test_usdt: libusdt.a test_usdt.o + $(CC) $(CFLAGS) -o test_usdt test_usdt.o libusdt.a +endif + +ifeq ($(UNAME), Darwin) +ifeq ($(MAC_BUILD), universal) +test: test_usdt32 test_usdt64 + sudo prove test.pl :: 64 + sudo prove test.pl :: 32 +else +test: test_usdt + sudo prove test.pl +endif +else +test: test_usdt + sudo prove test.pl +endif + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/README.md new file mode 100644 index 0000000..050f4bc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/README.md @@ -0,0 +1,81 @@ +libusdt +======= + +This is "libusdt", an extraction into a C library of the common parts +of ruby-dtrace[1], perl-dtrace[2] and node-dtrace-provider[3]. + +Those individual language-specific implementations will then become +bindings to this library, rather than containing their own +implementations of DOF generation and probe creation. Other dynamic +language bindings could then easily follow. + +The idea here is to allow the specification of a DTrace provider +dynamically in code and then create the provider at runtime. This +allows providers to be specified in dynamic languages, given suitable +bindings. + +The general approach is to create two stub functions for each probe, +one for the is-enabled check and one for the probe itself. These +contain the appropriate instruction sequences to appear to DTrace as +compiled-in tracepoints. A minimal DOF document is built describing +the provider and indicating these stub functions as the tracepoints, +then submitted to the kernel, creating the provider. The API then +exposes the stubs, through which the probes may be fired. + +Status +------ + +The implementation here works as shown in test_usdt.c on Mac OS X, +i386 and x86_64, on Solaris-like systems, i386 and x86_64 and on +FreeBSD, x86_64 only (so far). + +Is-enabled probes are supported and exposed in the API. + +There is a "test" target which runs a number of tests of the library, +for which perl is required. + +OS X builds are Universal by default, and on Solaris, the ARCH +variable may be set to either i386 or x86_64 to force a particular +build. + +FreeBSD builds suffer from broken argument handling; this is a known +issue with the current state of DTrace generally on FreeBSD: only the +first four arguments work reliably. See: + + http://wiki.freebsd.org/DTraceTODO + +See Also +-------- + +There are experimental Lua bindings available, which are a thin +layer over this library, and should serve as an example of typical use +as a dynamic language extension: + + https://github.com/chrisa/lua-usdt + +There are also Ruby bindings by Kevin Chan, replacing the provider +implementation in ruby-dtrace: + + https://github.com/kevinykchan/ruby-usdt + +To Do +----- + +Platform support: + + * add support for FreeBSD 9.0 i386 + * add support for Mac OS X PowerPC + * add support for Solaris SPARC + +Features: + + * add a "low level" API, allowing alternative provision of + tracepoints for closer integration with language VMs. + + * support structured types, with close integration with the host + DTrace system. + + +[1] https://github.com/chrisa/ruby-dtrace +[2] https://github.com/chrisa/perl-dtrace +[3] https://github.com/chrisa/node-dtrace-provider diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/README.md~ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/README.md~ new file mode 100644 index 0000000..9479bdb --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/README.md~ @@ -0,0 +1,59 @@ +This is "libusdt", an extraction into a C library of the common parts +of ruby-dtrace[1], perl-dtrace[2] and node-dtrace-provider[3]. + +Those individual language-specific implementations will then become +bindings to this library, rather than containing their own +implementations of DOF generation and probe creation. Other dynamic +language bindings could then easily follow. + +The idea here is to allow the specification of a DTrace provider +dynamically in code and then create the provider at runtime. This +allows providers to be specified in dynamic languages, given suitable +bindings. + +The general approach is to create two stub functions for each probe, +one for the is-enabled check and one for the probe itself. These +contain the appropriate instruction sequences to appear to DTrace as +compiled-in tracepoints. A minimal DOF document is built describing +the provider and indicating these stub functions as the tracepoints, +then submitted to the kernel, creating the provider. The API then +exposes the stubs, through which the probes may be fired. + +Status: + +The implementation here works as shown in test_usdt.c on Mac OS X, +i386 and x86_64, on Solaris-like systems, i386 and x86_64 and on +FreeBSD, x86_64 only (so far). + +Is-enabled probes are supported and exposed in the API. + +There is a "test" target which runs a number of tests of the library, +for which perl is required. + +OS X builds are Universal by default, and on Solaris, the ARCH +variable may be set to either i386 or x86_64 to force a particular +build. + +FreeBSD builds suffer from broken argument handling; this is a known +issue with the current state of DTrace generally on FreeBSD: only the +first four arguments work reliably. See: + + http://wiki.freebsd.org/DTraceTODO + +To do: + +Platform support: + + * add support for FreeBSD 9.0 i386 + * add support for Mac OS X PowerPC + * add support for Solaris SPARC + +Features: + + * add a "low level" API, allowing alternative provision of + tracepoints for closer integration with language VMs. + +[1] https://github.com/chrisa/ruby-dtrace +[2] https://github.com/chrisa/perl-dtrace +[3] https://github.com/chrisa/node-dtrace-provider + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/test.pl b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/test.pl new file mode 100755 index 0000000..92729c8 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/test.pl @@ -0,0 +1,112 @@ +#!/usr/bin/perl +use strict; +use warnings; +use IPC::Open3; +use Symbol 'gensym'; +use IO::Handle; +use Test::More qw/ no_plan /; + +my $USDT_ARG_MAX = 32; + +my $arch; +if (scalar @ARGV == 1) { + $arch = $ARGV[0]; +} + +my $user_t = ($^O eq 'solaris') ? 'uintptr_t' : 'user_addr_t'; + +run_tests('c', 'A'); +run_tests('i', 1); + +sub run_tests { + my ($type, $start_arg) = @_; + + for my $i (0..$USDT_ARG_MAX) { + my ($t_status, $d_status, $output) = run_dtrace('func', 'name', split(//, $type x $i)); + is($t_status, 0, 'test exit status is 0'); + is($d_status, 0, 'dtrace exit status is 0'); + like($output, qr/func:name/, 'function and name match'); + + my $arg = $start_arg; + for my $j (0..$i - 1) { + like($output, qr/arg$j:'\Q$arg\E'/, "type '$type' arg $j is $arg"); + if ($type eq 'i') { + $arg++; + } + else { + $arg = chr(ord($arg) + 1); + } + } + } +} + +# -------------------------------------------------------------------------- + +sub gen_d { + my (@types) = @_; + + my $d = 'testlibusdt*:::{ '; + my $i = 0; + for my $type (@types) { + if ($type eq 'i') { + $d .= "printf(\"arg$i:'%i' \", args[$i]); "; + } + if ($type eq 'c') { + $d .= "printf(\"arg$i:'%s' \", copyinstr(($user_t)args[$i])); "; + } + $i++; + } + $d .= '}'; + + return $d; +} + +sub run_dtrace { + my ($func, $name, @types) = @_; + my $d = gen_d(@types); + + my @t_cmd; + if (defined $arch) { + @t_cmd = ("./test_usdt$arch", $func, $name, @types); + } + else { + @t_cmd = ("./test_usdt", $func, $name, @types); + } + + my ($d_wtr, $d_rdr, $d_err); + my ($t_wtr, $t_rdr, $t_err); + + $d_err = gensym; + $t_err = gensym; + + #diag(join(' ', @t_cmd)); + my $t_pid = open3($t_wtr, $t_rdr, $t_err, @t_cmd); + my $enabled = $t_rdr->getline; + + my @d_cmd = ('dtrace', '-p', $t_pid, '-n', $d); + + #diag(join(' ', @d_cmd)); + my $d_pid = open3($d_wtr, $d_rdr, $d_err, @d_cmd); + my $matched = $d_err->getline; # expect "matched 1 probe" + + $t_wtr->print("go\n"); + $t_wtr->flush; + waitpid( $t_pid, 0 ); + my $t_status = $? >> 8; + + my ($header, $output) = ($d_rdr->getline, $d_rdr->getline); + chomp $header; + chomp $output; + #diag("DTrace header: $header\n"); + #diag("DTrace output: $output\n"); + waitpid( $d_pid, 0 ); + + my $d_status = $? >> 8; + while (!$d_err->eof) { + my $error = $d_err->getline; + chomp $error; + #diag "DTrace error: $error"; + } + + return ($t_status, $d_status, $output || ''); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/test_usdt.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/test_usdt.c new file mode 100644 index 0000000..557c667 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/test_usdt.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include "usdt.h" + +#include +#include +#include + +static void +fire_probe(usdt_probedef_t *probedef, int argc, void **argv) +{ + if (usdt_is_enabled(probedef->probe)) + usdt_fire_probe(probedef->probe, argc, argv); +} + +int main(int argc, char **argv) { + usdt_provider_t *provider; + usdt_probedef_t *probedef; + char char_argv[USDT_ARG_MAX]; + int int_argv[USDT_ARG_MAX * 2]; + void **args = NULL; + int i; + char buf[255]; + + for (i = 0; i < USDT_ARG_MAX; i++) + int_argv[i] = i + 1; + for (i = 0; i < USDT_ARG_MAX; i++) + char_argv[i] = (char) i + 65; + + if (argc < 3) { + fprintf(stderr, "usage: %s func name [types ...]\n", argv[0]); + return(1); + } + + if (argc > 3) { + args = malloc((argc-3) * sizeof(void *)); + } + + for (i = 0; i < USDT_ARG_MAX; i++) { + if (argv[i+3] != NULL && i+3 < argc) { + if (strncmp("c", argv[i+3], 1) == 0) { + args[i] = (void *)strndup(&char_argv[i], 1); + argv[i+3] = strdup("char *"); + } + if (strncmp("i", argv[i+3], 1) == 0) { + args[i] = (void *)(long)int_argv[i]; + argv[i+3] = strdup("int"); + } + } + } + + if ((provider = usdt_create_provider("testlibusdt", "modname")) == NULL) { + fprintf(stderr, "unable to create provider\n"); + exit (1); + } + if ((probedef = usdt_create_probe((const char *)argv[1], + (const char *)argv[2], + (argc-3), (const char **)&argv[3])) == NULL) + { + fprintf(stderr, "unable to create probe\n"); + exit (1); + } + usdt_provider_add_probe(provider, probedef); + + if ((usdt_provider_enable(provider)) < 0) { + fprintf(stderr, "unable to enable provider: %s\n", usdt_errstr(provider)); + exit (1); + } + + fprintf(stdout, "enabled\n"); + fflush(stdout); + fgets(buf, 255, stdin); + + fire_probe(probedef, (argc-3), (void **)args); + + if ((usdt_provider_disable(provider)) < 0) { + fprintf(stderr, "unable to disable provider: %s\n", usdt_errstr(provider)); + exit (1); + } + + return 0; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt.c new file mode 100644 index 0000000..68ddb79 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include "usdt_internal.h" + +#include +#include +#include +#include +#include + +char *usdt_errors[] = { + "failed to allocate memory", + "failed to allocate page-aligned memory", + "no probes defined", + "failed to load DOF: %s", + "provider is already enabled", + "failed to unload DOF: %s", + "probe named %s:%s:%s:%s already exists", + "failed to remove probe %s:%s:%s:%s" +}; + +usdt_provider_t * +usdt_create_provider(const char *name, const char *module) +{ + usdt_provider_t *provider; + + if ((provider = malloc(sizeof *provider)) == NULL) + return NULL; + + provider->name = strdup(name); + provider->module = strdup(module); + provider->probedefs = NULL; + provider->enabled = 0; + + return provider; +} + +usdt_probedef_t * +usdt_create_probe(const char *func, const char *name, size_t argc, const char **types) +{ + int i; + usdt_probedef_t *p; + + if (argc > USDT_ARG_MAX) + argc = USDT_ARG_MAX; + + if ((p = malloc(sizeof *p)) == NULL) + return (NULL); + + p->function = strdup(func); + p->name = strdup(name); + p->argc = argc; + p->probe = NULL; + + for (i = 0; i < argc; i++) { + if (strncmp("char *", types[i], 6) == 0) + p->types[i] = USDT_ARGTYPE_STRING; + if (strncmp("int", types[i], 3) == 0) + p->types[i] = USDT_ARGTYPE_INTEGER; + } + + return (p); +} + +int +usdt_provider_add_probe(usdt_provider_t *provider, usdt_probedef_t *probedef) +{ + usdt_probedef_t *pd; + + if (provider->probedefs != NULL) { + for (pd = provider->probedefs; (pd != NULL); pd = pd->next) { + if ((strcmp(pd->name, probedef->name) == 0) && + (strcmp(pd->function, probedef->function) == 0)) { + usdt_error(provider, USDT_ERROR_DUP_PROBE, + provider->name, provider->module, + probedef->function, probedef->name); + return (-1); + } + } + } + + probedef->next = NULL; + if (provider->probedefs == NULL) + provider->probedefs = probedef; + else { + for (pd = provider->probedefs; (pd->next != NULL); pd = pd->next) ; + pd->next = probedef; + } + + return (0); +} + +int +usdt_provider_remove_probe(usdt_provider_t *provider, usdt_probedef_t *probedef) +{ + usdt_probedef_t *pd, *prev_pd = NULL; + + if (provider->probedefs == NULL) { + usdt_error(provider, USDT_ERROR_NOPROBES); + return (-1); + } + + for (pd = provider->probedefs; (pd != NULL); + prev_pd = pd, pd = pd->next) { + + if ((strcmp(pd->name, probedef->name) == 0) && + (strcmp(pd->function, probedef->function) == 0)) { + + if (prev_pd == NULL) + provider->probedefs = pd->next; + else + prev_pd->next = pd->next; + + /* free(probedef); */ + return (0); + } + } + + usdt_error(provider, USDT_ERROR_REMOVE_PROBE, + provider->name, provider->module, + probedef->function, probedef->name); + return (-1); +} + +int +usdt_provider_enable(usdt_provider_t *provider) +{ + usdt_strtab_t strtab; + usdt_dof_file_t *file; + usdt_probedef_t *pd; + int i; + size_t size; + usdt_dof_section_t sects[5]; + + if (provider->enabled == 1) { + usdt_error(provider, USDT_ERROR_ALREADYENABLED); + return (0); /* not fatal */ + } + + if (provider->probedefs == NULL) { + usdt_error(provider, USDT_ERROR_NOPROBES); + return (-1); + } + + for (pd = provider->probedefs; pd != NULL; pd = pd->next) { + if ((pd->probe = malloc(sizeof(*pd->probe))) == NULL) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (-1); + } + } + + if ((usdt_strtab_init(&strtab, 0)) < 0) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (-1); + } + + if ((usdt_strtab_add(&strtab, provider->name)) == 0) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (-1); + } + + if ((usdt_dof_probes_sect(§s[0], provider, &strtab)) < 0) + return (-1); + if ((usdt_dof_prargs_sect(§s[1], provider)) < 0) + return (-1); + + size = usdt_provider_dof_size(provider, &strtab); + if ((file = usdt_dof_file_init(provider, size)) == NULL) + return (-1); + + if ((usdt_dof_proffs_sect(§s[2], provider, file->dof)) < 0) + return (-1); + if ((usdt_dof_prenoffs_sect(§s[3], provider, file->dof)) < 0) + return (-1); + if ((usdt_dof_provider_sect(§s[4], provider)) < 0) + return (-1); + + for (i = 0; i < 5; i++) + usdt_dof_file_append_section(file, §s[i]); + + usdt_dof_file_generate(file, &strtab); + + if ((usdt_dof_file_load(file, provider->module)) < 0) { + usdt_error(provider, USDT_ERROR_LOADDOF, strerror(errno)); + return (-1); + } + + provider->enabled = 1; + provider->file = file; + + return (0); +} + +int +usdt_provider_disable(usdt_provider_t *provider) +{ + if (provider->enabled == 0) + return (0); + + if ((usdt_dof_file_unload((usdt_dof_file_t *)provider->file)) < 0) { + usdt_error(provider, USDT_ERROR_UNLOADDOF, strerror(errno)); + return (-1); + } + + /* free(file) */ + provider->file = NULL; + provider->enabled = 0; + + return (0); +} + +int +usdt_is_enabled(usdt_probe_t *probe) +{ + if (probe != NULL) + return (*probe->isenabled_addr)(); + else + return 0; +} + +void +usdt_fire_probe(usdt_probe_t *probe, size_t argc, void **nargv) +{ + if (probe != NULL) + usdt_probe_args(probe->probe_addr, argc, nargv); +} + +static void +usdt_verror(usdt_provider_t *provider, usdt_error_t error, va_list argp) +{ + vasprintf(&provider->error, usdt_errors[error], argp); +} + +void +usdt_error(usdt_provider_t *provider, usdt_error_t error, ...) +{ + va_list argp; + + va_start(argp, error); + usdt_verror(provider, error, argp); + va_end(argp); +} + +char * +usdt_errstr(usdt_provider_t *provider) +{ + return (provider->error); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt.h new file mode 100644 index 0000000..de1fa47 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include +#include + +typedef uint8_t usdt_argtype_t; +#define USDT_ARGTYPE_NONE 0 +#define USDT_ARGTYPE_STRING 1 +#define USDT_ARGTYPE_INTEGER 2 + +#define USDT_ARG_MAX 32 + +typedef enum usdt_error { + USDT_ERROR_MALLOC = 0, + USDT_ERROR_VALLOC, + USDT_ERROR_NOPROBES, + USDT_ERROR_LOADDOF, + USDT_ERROR_ALREADYENABLED, + USDT_ERROR_UNLOADDOF, + USDT_ERROR_DUP_PROBE, + USDT_ERROR_REMOVE_PROBE +} usdt_error_t; + +typedef struct usdt_probe { + int (*isenabled_addr)(void); + void *probe_addr; +} usdt_probe_t; + +int usdt_is_enabled(usdt_probe_t *probe); +void usdt_fire_probe(usdt_probe_t *probe, size_t argc, void **argv); + +typedef struct usdt_probedef { + const char *name; + const char *function; + size_t argc; + usdt_argtype_t types[USDT_ARG_MAX]; + struct usdt_probe *probe; + struct usdt_probedef *next; +} usdt_probedef_t; + +usdt_probedef_t *usdt_create_probe(const char *func, const char *name, + size_t argc, const char **types); + +typedef struct usdt_provider { + const char *name; + const char *module; + usdt_probedef_t *probedefs; + char *error; + int enabled; + void *file; +} usdt_provider_t; + +usdt_provider_t *usdt_create_provider(const char *name, const char *module); +int usdt_provider_add_probe(usdt_provider_t *provider, usdt_probedef_t *probedef); +int usdt_provider_remove_probe(usdt_provider_t *provider, usdt_probedef_t *probedef); +int usdt_provider_enable(usdt_provider_t *provider); +int usdt_provider_disable(usdt_provider_t *provider); + +void usdt_error(usdt_provider_t *provider, usdt_error_t error, ...); +char *usdt_errstr(usdt_provider_t *provider); + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof.c new file mode 100644 index 0000000..33dc12b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include "usdt_internal.h" + +#include + +int +usdt_dof_section_add_data(usdt_dof_section_t *section, void *data, size_t length) +{ + int newlen = section->size + length; + + if ((section->data = realloc((char *)section->data, newlen)) == NULL) + return (-1); + + memcpy(section->data + section->size, data, length); + section->size = newlen; + return (0); +} + +size_t +usdt_provider_dof_size(usdt_provider_t *provider, usdt_strtab_t *strtab) +{ + uint8_t i, j; + int args = 0; + int probes = 0; + size_t size = 0; + usdt_probedef_t *pd; + size_t sections[8]; + + for (pd = provider->probedefs; pd != NULL; pd = pd->next) { + args += pd->argc; + probes++; + } + + sections[0] = sizeof(dof_hdr_t); + sections[1] = sizeof(dof_sec_t) * 6; + sections[2] = strtab->size; + sections[3] = sizeof(dof_probe_t) * probes; + sections[4] = sizeof(uint8_t) * args; + sections[5] = sizeof(uint32_t) * probes; + sections[6] = sizeof(uint32_t) * probes; + sections[7] = sizeof(dof_provider_t); + + for (i = 0; i < 8; i++) { + size += sections[i]; + j = size % 8; + if (j > 0) + size += (8 - j); + } + + return size; +} + +int +usdt_dof_section_init(usdt_dof_section_t *section, uint32_t type, dof_secidx_t index) +{ + section->type = type; + section->index = index; + section->flags = DOF_SECF_LOAD; + section->offset = 0; + section->size = 0; + section->entsize = 0; + section->pad = 0; + section->next = NULL; + + if ((section->data = malloc(1)) == NULL) + return (-1); + + switch(type) { + case DOF_SECT_PROBES: section->align = 8; break; + case DOF_SECT_PRARGS: section->align = 1; break; + case DOF_SECT_PROFFS: section->align = 4; break; + case DOF_SECT_PRENOFFS: section->align = 4; break; + case DOF_SECT_PROVIDER: section->align = 4; break; + } + + return (0); +} + +int +usdt_strtab_init(usdt_strtab_t *strtab, dof_secidx_t index) +{ + strtab->type = DOF_SECT_STRTAB;; + strtab->index = index; + strtab->flags = DOF_SECF_LOAD; + strtab->offset = 0; + strtab->size = 0; + strtab->entsize = 0; + strtab->pad = 0; + strtab->data = NULL; + strtab->align = 1; + strtab->strindex = 1; + + if ((strtab->data = (char *) malloc(1)) == NULL) + return (-1); + + *strtab->data = '\0'; + + return (0); +} + +dof_stridx_t +usdt_strtab_add(usdt_strtab_t *strtab, const char *string) +{ + size_t length; + int index; + + length = strlen(string); + index = strtab->strindex; + strtab->strindex += (length + 1); + + if ((strtab->data = realloc(strtab->data, strtab->strindex)) == NULL) + return (0); + + memcpy((char *) (strtab->data + index), (char *)string, length + 1); + strtab->size = index + length + 1; + + return (index); +} + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof_file.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof_file.c new file mode 100644 index 0000000..fde7322 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof_file.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include "usdt_internal.h" + +#include +#include + +static uint8_t +dof_version(uint8_t header_version) +{ + uint8_t dof_version; + /* DOF versioning: Apple always needs version 3, but Solaris can use + 1 or 2 depending on whether is-enabled probes are needed. */ +#ifdef __APPLE__ + dof_version = DOF_VERSION_3; +#else + switch(header_version) { + case 1: + dof_version = DOF_VERSION_1; + break; + case 2: + dof_version = DOF_VERSION_2; + break; + default: + dof_version = DOF_VERSION; + } +#endif + return dof_version; +} + +#ifdef __APPLE__ +static const char *helper = "/dev/dtracehelper"; + +static int +load_dof(int fd, dof_helper_t *dh) +{ + int ret; + uint8_t buffer[sizeof(dof_ioctl_data_t) + sizeof(dof_helper_t)]; + dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer; + user_addr_t val; + + ioctlData->dofiod_count = 1; + memcpy(&ioctlData->dofiod_helpers[0], dh, sizeof(dof_helper_t)); + + val = (user_addr_t)(unsigned long)ioctlData; + ret = ioctl(fd, DTRACEHIOC_ADDDOF, &val); + + if (ret < 0) + return ret; + + return (int)(ioctlData->dofiod_helpers[0].dofhp_dof); +} + +#else /* Solaris */ + +/* ignore Sol10 GA ... */ +static const char *helper = "/dev/dtrace/helper"; + +static int +load_dof(int fd, dof_helper_t *dh) +{ + return ioctl(fd, DTRACEHIOC_ADDDOF, dh); +} + +#endif + +static void +pad_section(usdt_dof_section_t *sec) +{ + size_t i, pad; + + if (sec->align > 1) { + i = sec->offset % sec->align; + if (i > 0) { + pad = sec->align - i; + sec->offset = (pad + sec->offset); + sec->pad = pad; + } + } +} + +static void +dof_header(dof_hdr_t *header) +{ + int i; + + header->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0; + header->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1; + header->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2; + header->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3; + + header->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE; + header->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE; + header->dofh_ident[DOF_ID_VERSION] = dof_version(2); + header->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION; + header->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS; + header->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS; + + for (i = DOF_ID_PAD; i < DOF_ID_SIZE; i++) + header->dofh_ident[i] = 0; + + header->dofh_flags = 0; + + header->dofh_hdrsize = sizeof(dof_hdr_t); + header->dofh_secsize = sizeof(dof_sec_t); + header->dofh_secoff = sizeof(dof_hdr_t); + + header->dofh_loadsz = 0; + header->dofh_filesz = 0; + header->dofh_pad = 0; +} + +static size_t +add_header(usdt_dof_file_t *file, size_t offset, usdt_dof_section_t *section) +{ + dof_sec_t header; + + header.dofs_flags = section->flags; + header.dofs_type = section->type; + header.dofs_offset = section->offset; + header.dofs_size = section->size; + header.dofs_entsize = section->entsize; + header.dofs_align = section->align; + + memcpy((file->dof + offset), &header, sizeof(dof_sec_t)); + return (offset + sizeof(dof_sec_t)); +} + +static size_t +add_section(usdt_dof_file_t *file, size_t offset, usdt_dof_section_t *section) +{ + if (section->pad > 0) { + /* maximum padding required is 7 */ + memcpy((file->dof + offset), "\0\0\0\0\0\0\0", section->pad); + offset += section->pad; + } + + memcpy((file->dof + offset), section->data, section->size); + return (offset + section->size); +} + +int +usdt_dof_file_unload(usdt_dof_file_t *file) +{ + int fd, ret; + + if ((fd = open(helper, O_RDWR)) < 0) + return (-1); + + ret = ioctl(fd, DTRACEHIOC_REMOVE, file->gen); + + if (ret < 0) + return (-1); + + if ((close(fd)) < 0) + return (-1); + + return (0); +} + +int +usdt_dof_file_load(usdt_dof_file_t *file, const char *module) +{ + dof_helper_t dh; + dof_hdr_t *dof; + int fd; + + dof = (dof_hdr_t *) file->dof; + + dh.dofhp_dof = (uintptr_t)dof; + dh.dofhp_addr = (uintptr_t)dof; + (void) strncpy(dh.dofhp_mod, module, sizeof (dh.dofhp_mod)); + + if ((fd = open(helper, O_RDWR)) < 0) + return (-1); + + file->gen = load_dof(fd, &dh); + + if ((close(fd)) < 0) + return (-1); + + if (file->gen < 0) + return (-1); + + return (0); +} + +void +usdt_dof_file_append_section(usdt_dof_file_t *file, usdt_dof_section_t *section) +{ + usdt_dof_section_t *s; + + if (file->sections == NULL) { + file->sections = section; + } + else { + for (s = file->sections; (s->next != NULL); s = s->next) ; + s->next = section; + } +} + +void +usdt_dof_file_generate(usdt_dof_file_t *file, usdt_strtab_t *strtab) +{ + dof_hdr_t header; + uint64_t filesz; + uint64_t loadsz; + usdt_dof_section_t *sec; + size_t offset; + + dof_header(&header); + header.dofh_secnum = 6; + + filesz = sizeof(dof_hdr_t) + (sizeof(dof_sec_t) * header.dofh_secnum); + loadsz = filesz; + + strtab->offset = filesz; + pad_section((usdt_dof_section_t *)strtab); + filesz += strtab->size + strtab->pad; + + if (strtab->flags & 1) + loadsz += strtab->size + strtab->pad; + + for (sec = file->sections; sec != NULL; sec = sec->next) { + sec->offset = filesz; + pad_section(sec); + filesz += sec->size + sec->pad; + if (sec->flags & 1) + loadsz += sec->size + sec->pad; + } + + header.dofh_loadsz = loadsz; + header.dofh_filesz = filesz; + memcpy(file->dof, &header, sizeof(dof_hdr_t)); + + offset = sizeof(dof_hdr_t); + + offset = add_header(file, offset, (usdt_dof_section_t *)strtab); + + for (sec = file->sections; sec != NULL; sec = sec->next) + offset = add_header(file, offset, sec); + + offset = add_section(file, offset, (usdt_dof_section_t *)strtab); + + for (sec = file->sections; sec != NULL; sec = sec->next) + offset = add_section(file, offset, sec); +} + +usdt_dof_file_t * +usdt_dof_file_init(usdt_provider_t *provider, size_t size) +{ + usdt_dof_file_t *file; + + if ((file = malloc(sizeof(*file))) == NULL) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (NULL); + } + + if ((file->dof = valloc(size)) == NULL) { + usdt_error(provider, USDT_ERROR_VALLOC); + return (NULL); + } + + file->sections = NULL; + file->size = size; + + return (file); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof_sections.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof_sections.c new file mode 100644 index 0000000..3e04d28 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_dof_sections.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include "usdt_internal.h" + +int +usdt_dof_probes_sect(usdt_dof_section_t *probes, + usdt_provider_t *provider, usdt_strtab_t *strtab) +{ + usdt_probedef_t *pd; + dof_probe_t p; + dof_stridx_t type, argv; + uint8_t argc, i; + uint32_t argidx = 0; + uint32_t offidx = 0; + + usdt_dof_section_init(probes, DOF_SECT_PROBES, 1); + + for (pd = provider->probedefs; pd != NULL; pd = pd->next) { + argc = 0; + argv = 0; + type = 0; + + for (i = 0; i < pd->argc; i++) { + switch(pd->types[i]) { + case USDT_ARGTYPE_INTEGER: + type = usdt_strtab_add(strtab, "int"); + break; + case USDT_ARGTYPE_STRING: + type = usdt_strtab_add(strtab, "char *"); + break; + default: + break; + } + + argc++; + if (argv == 0) + argv = type; + } + + if (usdt_create_tracepoints(pd->probe) < 0) { + usdt_error(provider, USDT_ERROR_VALLOC); + return (-1); + } + +#ifdef __x86_64__ + p.dofpr_addr = (uint64_t) pd->probe->isenabled_addr; +#elif __i386__ || __i386 + p.dofpr_addr = (uint32_t) pd->probe->isenabled_addr; +#else +#error "only x86_64 and i386 supported" +#endif + p.dofpr_func = usdt_strtab_add(strtab, pd->function); + p.dofpr_name = usdt_strtab_add(strtab, pd->name); + p.dofpr_nargv = argv; + p.dofpr_xargv = argv; + p.dofpr_argidx = argidx; + p.dofpr_offidx = offidx; + p.dofpr_nargc = argc; + p.dofpr_xargc = argc; + p.dofpr_noffs = 1; + p.dofpr_enoffidx = offidx; + p.dofpr_nenoffs = 1; + p.dofpr_pad1 = 0; + p.dofpr_pad2 = 0; + + usdt_dof_section_add_data(probes, &p, sizeof(dof_probe_t)); + probes->entsize = sizeof(dof_probe_t); + + argidx += argc; + offidx++; + } + + return (0); +} + +int +usdt_dof_prargs_sect(usdt_dof_section_t *prargs, usdt_provider_t *provider) +{ + usdt_probedef_t *pd; + uint8_t i; + + usdt_dof_section_init(prargs, DOF_SECT_PRARGS, 2); + prargs->entsize = 1; + + for (pd = provider->probedefs; pd != NULL; pd = pd->next) { + for (i = 0; i < pd->argc; i++) + usdt_dof_section_add_data(prargs, &i, 1); + } + if (prargs->size == 0) { + i = 0; + if (usdt_dof_section_add_data(prargs, &i, 1) < 0) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (-1); + } + } + + return (0); +} + +int +usdt_dof_proffs_sect(usdt_dof_section_t *proffs, + usdt_provider_t *provider, char *dof) +{ + usdt_probedef_t *pd; + uint32_t off; + + usdt_dof_section_init(proffs, DOF_SECT_PROFFS, 3); + proffs->entsize = 4; + + for (pd = provider->probedefs; pd != NULL; pd = pd->next) { + off = usdt_probe_offset(pd->probe, dof, pd->argc); + if (usdt_dof_section_add_data(proffs, &off, 4) < 0) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (-1); + } + } + + return (0); +} + +int +usdt_dof_prenoffs_sect(usdt_dof_section_t *prenoffs, + usdt_provider_t *provider, char *dof) +{ + usdt_probedef_t *pd; + uint32_t off; + + usdt_dof_section_init(prenoffs, DOF_SECT_PRENOFFS, 4); + prenoffs->entsize = 4; + + for (pd = provider->probedefs; pd != NULL; pd = pd->next) { + off = usdt_is_enabled_offset(pd->probe, dof); + if (usdt_dof_section_add_data(prenoffs, &off, 4) < 0) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (-1); + } + } + + return (0); +} + +int +usdt_dof_provider_sect(usdt_dof_section_t *provider_s, usdt_provider_t *provider) +{ + dof_provider_t p; + + usdt_dof_section_init(provider_s, DOF_SECT_PROVIDER, 5); + + p.dofpv_strtab = 0; + p.dofpv_probes = 1; + p.dofpv_prargs = 2; + p.dofpv_proffs = 3; + p.dofpv_prenoffs = 4; + p.dofpv_name = 1; /* provider name always first strtab entry. */ + + /* + * Stability is something of a hack. Everything is marked * + * "stable" here to permit use of the "args" array, which is * + * needed to access arguments past "arg9". + * + * It should be up to the creator of the provider to decide + * this, though, and it should be possible to set the + * appropriate stability at creation time. + */ + + p.dofpv_provattr = DOF_ATTR(DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE); + p.dofpv_modattr = DOF_ATTR(DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE); + p.dofpv_funcattr = DOF_ATTR(DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE); + p.dofpv_nameattr = DOF_ATTR(DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE); + p.dofpv_argsattr = DOF_ATTR(DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE, + DTRACE_STABILITY_STABLE); + + if ((usdt_dof_section_add_data(provider_s, &p, sizeof(p))) < 0) { + usdt_error(provider, USDT_ERROR_MALLOC); + return (-1); + } + + return (0); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_internal.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_internal.h new file mode 100644 index 0000000..5b56861 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_internal.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef __APPLE__ +/* solaris and freebsd */ +#include +#endif + +#define FUNC_SIZE 32 + +#include "usdt.h" + +extern void usdt_tracepoint_isenabled(void); +extern void usdt_tracepoint_probe(void); +extern void usdt_tracepoint_end(void); +extern void usdt_probe_args(void *, int, void**); + +uint32_t usdt_probe_offset(usdt_probe_t *probe, char *dof, uint8_t argc); +uint32_t usdt_is_enabled_offset(usdt_probe_t *probe, char *dof); +int usdt_create_tracepoints(usdt_probe_t *probe); + +typedef struct usdt_dof_section { + dof_secidx_t index; + uint32_t type; + uint32_t flags; + uint32_t align; + uint64_t offset; + uint64_t size; + uint32_t entsize; + size_t pad; + struct usdt_dof_section *next; + char *data; +} usdt_dof_section_t; + +int usdt_dof_section_init(usdt_dof_section_t *section, + uint32_t type, dof_secidx_t index); +int usdt_dof_section_add_data(usdt_dof_section_t *section, + void *data, size_t length); + +typedef struct usdt_strtab { + dof_secidx_t index; + uint32_t type; + uint32_t flags; + uint32_t align; + uint64_t offset; + uint64_t size; + uint32_t entsize; + size_t pad; + int strindex; + char *data; +} usdt_strtab_t; + +int usdt_strtab_init(usdt_strtab_t *strtab, dof_secidx_t index); +dof_stridx_t usdt_strtab_add(usdt_strtab_t *strtab, const char *string); +char *usdt_strtab_header(usdt_strtab_t *strtab); +size_t usdt_strtab_size(usdt_strtab_t *strtab); + +size_t usdt_provider_dof_size(usdt_provider_t *provider, usdt_strtab_t *strtab); + +typedef struct usdt_dof_file { + char *dof; + int gen; + size_t size; + usdt_dof_section_t *sections; +} usdt_dof_file_t; + +usdt_dof_file_t *usdt_dof_file_init(usdt_provider_t *provider, size_t size); +void usdt_dof_file_append_section(usdt_dof_file_t *file, usdt_dof_section_t *section); +void usdt_dof_file_generate(usdt_dof_file_t *file, usdt_strtab_t *strtab); +int usdt_dof_file_load(usdt_dof_file_t *file, const char *module); +int usdt_dof_file_unload(usdt_dof_file_t *file); + +int usdt_dof_probes_sect(usdt_dof_section_t *probes, + usdt_provider_t *provider, usdt_strtab_t *strtab); +int usdt_dof_prargs_sect(usdt_dof_section_t *prargs, + usdt_provider_t *provider); +int usdt_dof_proffs_sect(usdt_dof_section_t *proffs, + usdt_provider_t *provider, char *dof); +int usdt_dof_prenoffs_sect(usdt_dof_section_t *prenoffs, + usdt_provider_t *provider, char *dof); +int usdt_dof_provider_sect(usdt_dof_section_t *provider_s, + usdt_provider_t *provider); + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_probe.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_probe.c new file mode 100644 index 0000000..8ac986b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_probe.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +#include "usdt_internal.h" + +#ifdef __APPLE__ + +uint32_t +usdt_probe_offset(usdt_probe_t *probe, char *dof, uint8_t argc) +{ + uint32_t offset; +#ifdef __x86_64__ + offset = ((uint64_t) probe->probe_addr - (uint64_t) dof + 2); +#elif __i386__ + offset = ((uint32_t) probe->probe_addr - (uint32_t) dof + 2); +#else +#error "only x86_64 and i386 supported" +#endif + return (offset); +} + +uint32_t +usdt_is_enabled_offset(usdt_probe_t *probe, char *dof) +{ + uint32_t offset; +#ifdef __x86_64__ + offset = ((uint64_t) probe->isenabled_addr - (uint64_t) dof + 6); +#elif __i386__ + offset = ((uint32_t) probe->isenabled_addr - (uint32_t) dof + 6); +#else +#error "only x86_64 and i386 supported" +#endif + return (offset); +} + +#else /* solaris and freebsd */ + +uint32_t +usdt_probe_offset(usdt_probe_t *probe, char *dof, uint8_t argc) +{ + return (16); +} + +uint32_t +usdt_is_enabled_offset(usdt_probe_t *probe, char *dof) +{ + return (8); +} + +#endif + +int +usdt_create_tracepoints(usdt_probe_t *probe) +{ + size_t size; + + /* ensure that the tracepoints will fit the heap we're allocating */ + size = ((char *)usdt_tracepoint_end - (char *)usdt_tracepoint_isenabled); + assert(size < FUNC_SIZE); + + if ((probe->isenabled_addr = (int (*)())valloc(FUNC_SIZE)) == NULL) + return (-1); + + size = ((char *)usdt_tracepoint_probe - (char *)usdt_tracepoint_isenabled); + probe->probe_addr = (char *)probe->isenabled_addr + size; + + memcpy((void *)probe->isenabled_addr, + (const void *)usdt_tracepoint_isenabled, FUNC_SIZE); + mprotect((void *)probe->isenabled_addr, FUNC_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC); + + return (0); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_tracepoints_i386.s b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_tracepoints_i386.s new file mode 100644 index 0000000..eb19e97 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_tracepoints_i386.s @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +/* + * Stub functions containing DTrace tracepoints for probes and + * is-enabled probes. These functions are copied for each probe + * dynamically created. + * + */ + .text + + .align 4, 0x90 + .globl usdt_tracepoint_isenabled + .globl _usdt_tracepoint_isenabled + .globl usdt_tracepoint_probe + .globl _usdt_tracepoint_probe + .globl usdt_tracepoint_end + .globl _usdt_tracepoint_end + .globl usdt_probe_args + .globl _usdt_probe_args + +usdt_tracepoint_isenabled: +_usdt_tracepoint_isenabled: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + xorl %eax, %eax + nop + nop + leave + ret +usdt_tracepoint_probe: +_usdt_tracepoint_probe: + nop + nop + nop + nop + nop + addl $0x20,%esp + leave +usdt_tracepoint_end: +_usdt_tracepoint_end: + ret + +/* + * Probe argument marshalling, i386 style + * + */ + +usdt_probe_args: +_usdt_probe_args: + pushl %ebp + movl %esp,%ebp + subl $8,%esp + subl $8,%esp + movl 8(%ebp),%ebx + movl 0xc(%ebp),%ecx + test %ecx,%ecx + je fire +args: movl %ecx,%eax + sal $2,%eax + subl $4,%eax + addl 0x10(%ebp),%eax + pushl (%eax) + dec %ecx + jne args +fire: jmp *%ebx + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_tracepoints_x86_64.s b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_tracepoints_x86_64.s new file mode 100644 index 0000000..7db093f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/libusdt/usdt_tracepoints_x86_64.s @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2012, Chris Andrews. All rights reserved. + */ + +/* + * Stub functions containing DTrace tracepoints for probes and + * is-enabled probes. These functions are copied for each probe + * dynamically created. + * + */ + .text + + .align 4, 0x90 + .globl usdt_tracepoint_isenabled + .globl _usdt_tracepoint_isenabled + .globl usdt_tracepoint_probe + .globl _usdt_tracepoint_probe + .globl usdt_tracepoint_end + .globl _usdt_tracepoint_end + .globl usdt_probe_args + .globl _usdt_probe_args + +usdt_tracepoint_isenabled: +_usdt_tracepoint_isenabled: + pushq %rbp + movq %rsp, %rbp + addq $1, %rax + xorq %rax, %rax + nop + nop + leave + ret +usdt_tracepoint_probe: +_usdt_tracepoint_probe: + nop + nop + nop + nop + nop + addq %r14,%rsp + popq %rbx + popq %r14 + popq %r13 + popq %r12 + leave +usdt_tracepoint_end: +_usdt_tracepoint_end: + ret + +/* + * Probe argument marshalling, x86_64 style + * + */ + +usdt_probe_args: +_usdt_probe_args: + pushq %rbp + movq %rsp,%rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %rbx + + movq %rdi,%r12 + movq %rsi,%rbx + movq %rdx,%r11 + movq $0,%r14 + + test %rbx,%rbx + je fire + movq (%r11),%rdi + dec %rbx + test %rbx,%rbx + je fire + addq $8,%r11 + movq (%r11),%rsi + dec %rbx + test %rbx,%rbx + je fire + addq $8,%r11 + movq (%r11),%rdx + dec %rbx + test %rbx,%rbx + je fire + addq $8,%r11 + movq (%r11),%rcx + dec %rbx + test %rbx,%rbx + je fire + addq $8,%r11 + movq (%r11),%r8 + dec %rbx + test %rbx,%rbx + je fire + addq $8,%r11 + movq (%r11),%r9 + + movq %rbx,%r13 +morestack: + dec %rbx + test %rbx,%rbx + je args + subq $16,%rsp + addq $16,%r14 + dec %rbx + test %rbx,%rbx + je args + jmp morestack + +args: + movq %r13,%rbx + movq $0,%r13 +moreargs: + dec %rbx + test %rbx,%rbx + je fire + addq $8,%r11 + movq (%r11),%rax + movq %rax,(%rsp,%r13) + addq $8,%r13 + jmp moreargs + +fire: jmp *%r12 diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/package.json new file mode 100644 index 0000000..80144b2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/package.json @@ -0,0 +1,10 @@ +{ + "name" : "dtrace-provider", + "version" : "0.2.1", + "description" : "Native DTrace providers for node.js applications", + "keywords" : ["dtrace"], + "homepage" : "https://github.com/chrisa/node-dtrace-provider#readme", + "author" : { "name" : "Chris Andrews", "email": "chris@nodnol.org" }, + "main" : "./dtrace-provider.js", + "repository" : { "type" : "git", "url" : "http://github.com/chrisa/node-dtrace-provider.git" } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/dtp.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/dtp.js new file mode 100644 index 0000000..ab10ef2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/dtp.js @@ -0,0 +1,30 @@ +var util = require('util'); +var d = require('../dtrace-provider'); + +var dtp = d.createDTraceProvider("testlibusdt"); +var p = dtp.addProbe("32probe", "int", "int", "int", "int", "int", "int", "int", "int", + "int", "int", "int", "int", "int", "int", "int", "int", + "int", "int", "int", "int", "int", "int", "int", "int", + "int", "int", "int", "int", "int", "int", "int", "int"); + +dtp.enable(); + +util.debug("run: sudo dtrace -n 'testlibusdt*:::32probe{ printf(\"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\", args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19], args[20], args[21], args[22], args[23], args[24], args[25], args[26], args[27], args[28], args[29], args[30], args[31]) }'") + +var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]; + +setTimeout(function() { + + var args = []; + numbers.forEach(function(n) { + args.push(n); + dtp.fire("32probe", function(p) { + return args; + }); + p.fire(function(p) { + return args; + }); + }); + +}, 10000); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/dtpc.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/dtpc.js new file mode 100644 index 0000000..6250924 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/dtpc.js @@ -0,0 +1,31 @@ +var util = require('util'); +var d = require('../dtrace-provider'); + +var dtp = d.createDTraceProvider("testlibusdt"); +var p = dtp.addProbe("32probe", "char *", "char *", "char *", "char *", "char *", "char *", "char *", "char *", + "char *", "char *", "char *", "char *", "char *", "char *", "char *", "char *", + "char *", "char *", "char *", "char *", "char *", "char *", "char *", "char *", + "char *", "char *", "char *", "char *", "char *", "char *", "char *", "char *"); +dtp.enable(); + +util.debug("run: sudo dtrace -n 'testlibusdt*:::32probe{ printf(\"%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\", copyinstr((user_addr_t)args[0]), copyinstr((user_addr_t)args[1]), copyinstr((user_addr_t)args[2]), copyinstr((user_addr_t)args[3]), copyinstr((user_addr_t)args[4]), copyinstr((user_addr_t)args[5]), copyinstr((user_addr_t)args[6]), copyinstr((user_addr_t)args[7]), copyinstr((user_addr_t)args[8]), copyinstr((user_addr_t)args[9]), copyinstr((user_addr_t)args[10]), copyinstr((user_addr_t)args[11]), copyinstr((user_addr_t)args[12]), copyinstr((user_addr_t)args[13]), copyinstr((user_addr_t)args[14]), copyinstr((user_addr_t)args[15]), copyinstr((user_addr_t)args[16]), copyinstr((user_addr_t)args[17]), copyinstr((user_addr_t)args[18]), copyinstr((user_addr_t)args[19]), copyinstr((user_addr_t)args[20]), copyinstr((user_addr_t)args[21]), copyinstr((user_addr_t)args[22]), copyinstr((user_addr_t)args[23]), copyinstr((user_addr_t)args[24]), copyinstr((user_addr_t)args[25]), copyinstr((user_addr_t)args[26]), copyinstr((user_addr_t)args[27]), copyinstr((user_addr_t)args[28]), copyinstr((user_addr_t)args[29]), copyinstr((user_addr_t)args[30]), copyinstr((user_addr_t)args[31])); }'") + + +var letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F']; + +setTimeout(function() { + + var args = []; + letters.forEach(function(l) { + args.push(l); + dtp.fire("32probe", function(p) { + return args; + }); + p.fire(function(p) { + return args; + }); + }); + + +}, 10000); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled-again.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled-again.js new file mode 100644 index 0000000..4bbef11 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled-again.js @@ -0,0 +1,19 @@ +// expected output: +// +// $ sudo dtrace -Zn 'nodeapp*:::{ trace(arg0); }' -c 'node test/enabled-again.js' +// dtrace: description 'nodeapp*:::' matched 0 probes +// CPU ID FUNCTION:NAME +// 1 5456 func:probe1 1 +// 1 5456 func:probe1 2 + +var d = require('../dtrace-provider'); +var dtp = d.createDTraceProvider("nodeapp"); +dtp.addProbe("probe1", "int"); +dtp.enable(); +dtp.fire("probe1", function(p) { return [1]; }); + +for (var i = 0; i<100; i++) { + dtp.enable(); +} +dtp.fire("probe1", function(p) { return [2]; }); +setTimeout(function() { }, 1000); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled-disabled.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled-disabled.js new file mode 100644 index 0000000..0505515 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled-disabled.js @@ -0,0 +1,22 @@ +// expected output: +// +// $ sudo dtrace -Zn 'nodeapp*:::{ trace(arg0); }' -c 'node test/enabled-again.js' +// dtrace: description 'nodeapp*:::' matched 0 probes +// CPU ID FUNCTION:NAME +// 1 5456 func:probe1 1 +// 1 5456 func:probe1 2 + +var d = require('../dtrace-provider'); +var dtp = d.createDTraceProvider("nodeapp"); +dtp.addProbe("probe1", "int"); +dtp.enable(); +dtp.fire("probe1", function(p) { return [1]; }); + +for (var i = 0; i < 2500; i++) { + dtp.enable(); + dtp.fire("probe1", function(p) { return [i]; }); + dtp.disable(); + //gc(); +} +dtp.fire("probe1", function(p) { return [42]; }); + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled_again.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled_again.js new file mode 100644 index 0000000..9dfe74c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/enabled_again.js @@ -0,0 +1,19 @@ +// expected output: +// +// $ sudo dtrace -Zn 'nodeapp*:::{ trace(arg0); }' -c 'node test/enabled_again.js' +// dtrace: description 'nodeapp*:::' matched 0 probes +// CPU ID FUNCTION:NAME +// 1 5456 func:probe1 1 +// 1 5456 func:probe1 2 + +var d = require('../dtrace-provider'); +var dtp = d.createDTraceProvider("nodeapp"); +dtp.addProbe("probe1", "int"); +dtp.enable(); +dtp.fire("probe1", function(p) { return [1]; }); + +for (var i = 0; i<100; i++) { + dtp.enable(); +} +dtp.fire("probe1", function(p) { return [2]; }); +setTimeout(function() { }, 1000); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/gc.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/gc.js new file mode 100644 index 0000000..a1e64a4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/gc.js @@ -0,0 +1,16 @@ +// node --expose_gc ... + +var d = require('../dtrace-provider'); +var dtp = d.createDTraceProvider("testlibusdt"); + +// don't assign the returned probe object anywhere +dtp.addProbe("gcprobe"); +dtp.enable(); + +// run GC +gc(); + +// probe object should still be around +dtp.fire("gcprobe", function() { + return []; +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/not-enabled.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/not-enabled.js new file mode 100644 index 0000000..0216660 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/test/not-enabled.js @@ -0,0 +1,7 @@ +var d = require('../dtrace-provider'); +var dtp = d.createDTraceProvider("nodeapp"); +dtp.addProbe("probe1", "int"); +//dtp.enable(); +dtp.fire("probe1", function(p) { return [1]; }); + + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/wscript b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/wscript new file mode 100644 index 0000000..250c684 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/dtrace-provider/wscript @@ -0,0 +1,30 @@ +import Options, Utils, sys +from os import unlink, symlink, popen +from os.path import exists, islink + +srcdir = '.' +blddir = 'build' +VERSION = '0.2.1' + +libusdtdir = 'libusdt' + +def set_options(ctx): + ctx.tool_options('compiler_cxx') + +def configure(ctx): + ctx.check_tool('compiler_cxx') + ctx.check_tool('node_addon') + +def build(ctx): + if sys.platform.startswith("sunos") or sys.platform.startswith("darwin") or sys.platform.startswith("freebsd"): + ctx.new_task_gen( + rule = "cd ../" + libusdtdir + " && ARCH=i386 make clean all && cd -", + shell = True + ) + + t = ctx.new_task_gen('cxx', 'shlib', 'node_addon') + t.target = 'DTraceProviderBindings' + t.source = ['dtrace_provider.cc', 'dtrace_probe.cc'] + t.includes = [libusdtdir] + t.staticlib = 'usdt' + t.libpath = "../" + libusdtdir diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/.npmignore new file mode 100644 index 0000000..e69de29 diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/LICENSE b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/LICENSE new file mode 100644 index 0000000..05a4010 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/LICENSE @@ -0,0 +1,23 @@ +Copyright 2009, 2010, 2011 Isaac Z. Schlueter. +All rights reserved. + +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/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/README.md new file mode 100644 index 0000000..eeddfd4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/README.md @@ -0,0 +1,208 @@ +If you want to write an option parser, and have it be good, there are +two ways to do it. The Right Way, and the Wrong Way. + +The Wrong Way is to sit down and write an option parser. We've all done +that. + +The Right Way is to write some complex configurable program with so many +options that you go half-insane just trying to manage them all, and put +it off with duct-tape solutions until you see exactly to the core of the +problem, and finally snap and write an awesome option parser. + +If you want to write an option parser, don't write an option parser. +Write a package manager, or a source control system, or a service +restarter, or an operating system. You probably won't end up with a +good one of those, but if you don't give up, and you are relentless and +diligent enough in your procrastination, you may just end up with a very +nice option parser. + +## USAGE + + // my-program.js + var nopt = require("nopt") + , Stream = require("stream").Stream + , path = require("path") + , knownOpts = { "foo" : [String, null] + , "bar" : [Stream, Number] + , "baz" : path + , "bloo" : [ "big", "medium", "small" ] + , "flag" : Boolean + , "pick" : Boolean + , "many" : [String, Array] + } + , shortHands = { "foofoo" : ["--foo", "Mr. Foo"] + , "b7" : ["--bar", "7"] + , "m" : ["--bloo", "medium"] + , "p" : ["--pick"] + , "f" : ["--flag"] + } + // everything is optional. + // knownOpts and shorthands default to {} + // arg list defaults to process.argv + // slice defaults to 2 + , parsed = nopt(knownOpts, shortHands, process.argv, 2) + console.log(parsed) + +This would give you support for any of the following: + +```bash +$ node my-program.js --foo "blerp" --no-flag +{ "foo" : "blerp", "flag" : false } + +$ node my-program.js ---bar 7 --foo "Mr. Hand" --flag +{ bar: 7, foo: "Mr. Hand", flag: true } + +$ node my-program.js --foo "blerp" -f -----p +{ foo: "blerp", flag: true, pick: true } + +$ node my-program.js -fp --foofoo +{ foo: "Mr. Foo", flag: true, pick: true } + +$ node my-program.js --foofoo -- -fp # -- stops the flag parsing. +{ foo: "Mr. Foo", argv: { remain: ["-fp"] } } + +$ node my-program.js --blatzk 1000 -fp # unknown opts are ok. +{ blatzk: 1000, flag: true, pick: true } + +$ node my-program.js --blatzk true -fp # but they need a value +{ blatzk: true, flag: true, pick: true } + +$ node my-program.js --no-blatzk -fp # unless they start with "no-" +{ blatzk: false, flag: true, pick: true } + +$ node my-program.js --baz b/a/z # known paths are resolved. +{ baz: "/Users/isaacs/b/a/z" } + +# if Array is one of the types, then it can take many +# values, and will always be an array. The other types provided +# specify what types are allowed in the list. + +$ node my-program.js --many 1 --many null --many foo +{ many: ["1", "null", "foo"] } + +$ node my-program.js --many foo +{ many: ["foo"] } +``` + +Read the tests at the bottom of `lib/nopt.js` for more examples of +what this puppy can do. + +## Types + +The following types are supported, and defined on `nopt.typeDefs` + +* String: A normal string. No parsing is done. +* path: A file system path. Gets resolved against cwd if not absolute. +* url: A url. If it doesn't parse, it isn't accepted. +* Number: Must be numeric. +* Date: Must parse as a date. If it does, and `Date` is one of the options, + then it will return a Date object, not a string. +* Boolean: Must be either `true` or `false`. If an option is a boolean, + then it does not need a value, and its presence will imply `true` as + the value. To negate boolean flags, do `--no-whatever` or `--whatever + false` +* NaN: Means that the option is strictly not allowed. Any value will + fail. +* Stream: An object matching the "Stream" class in node. Valuable + for use when validating programmatically. (npm uses this to let you + supply any WriteStream on the `outfd` and `logfd` config options.) +* Array: If `Array` is specified as one of the types, then the value + will be parsed as a list of options. This means that multiple values + can be specified, and that the value will always be an array. + +If a type is an array of values not on this list, then those are +considered valid values. For instance, in the example above, the +`--bloo` option can only be one of `"big"`, `"medium"`, or `"small"`, +and any other value will be rejected. + +When parsing unknown fields, `"true"`, `"false"`, and `"null"` will be +interpreted as their JavaScript equivalents, and numeric values will be +interpreted as a number. + +You can also mix types and values, or multiple types, in a list. For +instance `{ blah: [Number, null] }` would allow a value to be set to +either a Number or null. + +To define a new type, add it to `nopt.typeDefs`. Each item in that +hash is an object with a `type` member and a `validate` method. The +`type` member is an object that matches what goes in the type list. The +`validate` method is a function that gets called with `validate(data, +key, val)`. Validate methods should assign `data[key]` to the valid +value of `val` if it can be handled properly, or return boolean +`false` if it cannot. + +You can also call `nopt.clean(data, types, typeDefs)` to clean up a +config object and remove its invalid properties. + +## Error Handling + +By default, nopt outputs a warning to standard error when invalid +options are found. You can change this behavior by assigning a method +to `nopt.invalidHandler`. This method will be called with +the offending `nopt.invalidHandler(key, val, types)`. + +If no `nopt.invalidHandler` is assigned, then it will console.error +its whining. If it is assigned to boolean `false` then the warning is +suppressed. + +## Abbreviations + +Yes, they are supported. If you define options like this: + +```javascript +{ "foolhardyelephants" : Boolean +, "pileofmonkeys" : Boolean } +``` + +Then this will work: + +```bash +node program.js --foolhar --pil +node program.js --no-f --pileofmon +# etc. +``` + +## Shorthands + +Shorthands are a hash of shorter option names to a snippet of args that +they expand to. + +If multiple one-character shorthands are all combined, and the +combination does not unambiguously match any other option or shorthand, +then they will be broken up into their constituent parts. For example: + +```json +{ "s" : ["--loglevel", "silent"] +, "g" : "--global" +, "f" : "--force" +, "p" : "--parseable" +, "l" : "--long" +} +``` + +```bash +npm ls -sgflp +# just like doing this: +npm ls --loglevel silent --global --force --long --parseable +``` + +## The Rest of the args + +The config object returned by nopt is given a special member called +`argv`, which is an object with the following fields: + +* `remain`: The remaining args after all the parsing has occurred. +* `original`: The args as they originally appeared. +* `cooked`: The args after flags and shorthands are expanded. + +## Slicing + +Node programs are called with more or less the exact argv as it appears +in C land, after the v8 and node-specific options have been plucked off. +As such, `argv[0]` is always `node` and `argv[1]` is always the +JavaScript program being run. + +That's usually not very useful to you. So they're sliced off by +default. If you want them, then you can pass in `0` as the last +argument, or any other number that you'd like to slice off the start of +the list. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/bin/nopt.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/bin/nopt.js new file mode 100755 index 0000000..df90c72 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/bin/nopt.js @@ -0,0 +1,44 @@ +#!/usr/bin/env node +var nopt = require("../lib/nopt") + , types = { num: Number + , bool: Boolean + , help: Boolean + , list: Array + , "num-list": [Number, Array] + , "str-list": [String, Array] + , "bool-list": [Boolean, Array] + , str: String } + , shorthands = { s: [ "--str", "astring" ] + , b: [ "--bool" ] + , nb: [ "--no-bool" ] + , tft: [ "--bool-list", "--no-bool-list", "--bool-list", "true" ] + , "?": ["--help"] + , h: ["--help"] + , H: ["--help"] + , n: [ "--num", "125" ] } + , parsed = nopt( types + , shorthands + , process.argv + , 2 ) + +console.log("parsed", parsed) + +if (parsed.help) { + console.log("") + console.log("nopt cli tester") + console.log("") + console.log("types") + console.log(Object.keys(types).map(function M (t) { + var type = types[t] + if (Array.isArray(type)) { + return [t, type.map(function (type) { return type.name })] + } + return [t, type && type.name] + }).reduce(function (s, i) { + s[i[0]] = i[1] + return s + }, {})) + console.log("") + console.log("shorthands") + console.log(shorthands) +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/examples/my-program.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/examples/my-program.js new file mode 100755 index 0000000..142447e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/examples/my-program.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +//process.env.DEBUG_NOPT = 1 + +// my-program.js +var nopt = require("../lib/nopt") + , Stream = require("stream").Stream + , path = require("path") + , knownOpts = { "foo" : [String, null] + , "bar" : [Stream, Number] + , "baz" : path + , "bloo" : [ "big", "medium", "small" ] + , "flag" : Boolean + , "pick" : Boolean + } + , shortHands = { "foofoo" : ["--foo", "Mr. Foo"] + , "b7" : ["--bar", "7"] + , "m" : ["--bloo", "medium"] + , "p" : ["--pick"] + , "f" : ["--flag", "true"] + , "g" : ["--flag"] + , "s" : "--flag" + } + // everything is optional. + // knownOpts and shorthands default to {} + // arg list defaults to process.argv + // slice defaults to 2 + , parsed = nopt(knownOpts, shortHands, process.argv, 2) + +console.log("parsed =\n"+ require("util").inspect(parsed)) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/lib/nopt.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/lib/nopt.js new file mode 100644 index 0000000..ff802da --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/lib/nopt.js @@ -0,0 +1,552 @@ +// info about each config option. + +var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG + ? function () { console.error.apply(console, arguments) } + : function () {} + +var url = require("url") + , path = require("path") + , Stream = require("stream").Stream + , abbrev = require("abbrev") + +module.exports = exports = nopt +exports.clean = clean + +exports.typeDefs = + { String : { type: String, validate: validateString } + , Boolean : { type: Boolean, validate: validateBoolean } + , url : { type: url, validate: validateUrl } + , Number : { type: Number, validate: validateNumber } + , path : { type: path, validate: validatePath } + , Stream : { type: Stream, validate: validateStream } + , Date : { type: Date, validate: validateDate } + } + +function nopt (types, shorthands, args, slice) { + args = args || process.argv + types = types || {} + shorthands = shorthands || {} + if (typeof slice !== "number") slice = 2 + + debug(types, shorthands, args, slice) + + args = args.slice(slice) + var data = {} + , key + , remain = [] + , cooked = args + , original = args.slice(0) + + parse(args, data, remain, types, shorthands) + // now data is full + clean(data, types, exports.typeDefs) + data.argv = {remain:remain,cooked:cooked,original:original} + data.argv.toString = function () { + return this.original.map(JSON.stringify).join(" ") + } + return data +} + +function clean (data, types, typeDefs) { + typeDefs = typeDefs || exports.typeDefs + var remove = {} + , typeDefault = [false, true, null, String, Number] + + Object.keys(data).forEach(function (k) { + if (k === "argv") return + var val = data[k] + , isArray = Array.isArray(val) + , type = types[k] + if (!isArray) val = [val] + if (!type) type = typeDefault + if (type === Array) type = typeDefault.concat(Array) + if (!Array.isArray(type)) type = [type] + + debug("val=%j", val) + debug("types=", type) + val = val.map(function (val) { + // if it's an unknown value, then parse false/true/null/numbers/dates + if (typeof val === "string") { + debug("string %j", val) + val = val.trim() + if ((val === "null" && ~type.indexOf(null)) + || (val === "true" && + (~type.indexOf(true) || ~type.indexOf(Boolean))) + || (val === "false" && + (~type.indexOf(false) || ~type.indexOf(Boolean)))) { + val = JSON.parse(val) + debug("jsonable %j", val) + } else if (~type.indexOf(Number) && !isNaN(val)) { + debug("convert to number", val) + val = +val + } else if (~type.indexOf(Date) && !isNaN(Date.parse(val))) { + debug("convert to date", val) + val = new Date(val) + } + } + + if (!types.hasOwnProperty(k)) { + return val + } + + // allow `--no-blah` to set 'blah' to null if null is allowed + if (val === false && ~type.indexOf(null) && + !(~type.indexOf(false) || ~type.indexOf(Boolean))) { + val = null + } + + var d = {} + d[k] = val + debug("prevalidated val", d, val, types[k]) + if (!validate(d, k, val, types[k], typeDefs)) { + if (exports.invalidHandler) { + exports.invalidHandler(k, val, types[k], data) + } else if (exports.invalidHandler !== false) { + debug("invalid: "+k+"="+val, types[k]) + } + return remove + } + debug("validated val", d, val, types[k]) + return d[k] + }).filter(function (val) { return val !== remove }) + + if (!val.length) delete data[k] + else if (isArray) { + debug(isArray, data[k], val) + data[k] = val + } else data[k] = val[0] + + debug("k=%s val=%j", k, val, data[k]) + }) +} + +function validateString (data, k, val) { + data[k] = String(val) +} + +function validatePath (data, k, val) { + data[k] = path.resolve(String(val)) + return true +} + +function validateNumber (data, k, val) { + debug("validate Number %j %j %j", k, val, isNaN(val)) + if (isNaN(val)) return false + data[k] = +val +} + +function validateDate (data, k, val) { + debug("validate Date %j %j %j", k, val, Date.parse(val)) + var s = Date.parse(val) + if (isNaN(s)) return false + data[k] = new Date(val) +} + +function validateBoolean (data, k, val) { + if (val instanceof Boolean) val = val.valueOf() + else if (typeof val === "string") { + if (!isNaN(val)) val = !!(+val) + else if (val === "null" || val === "false") val = false + else val = true + } else val = !!val + data[k] = val +} + +function validateUrl (data, k, val) { + val = url.parse(String(val)) + if (!val.host) return false + data[k] = val.href +} + +function validateStream (data, k, val) { + if (!(val instanceof Stream)) return false + data[k] = val +} + +function validate (data, k, val, type, typeDefs) { + // arrays are lists of types. + if (Array.isArray(type)) { + for (var i = 0, l = type.length; i < l; i ++) { + if (type[i] === Array) continue + if (validate(data, k, val, type[i], typeDefs)) return true + } + delete data[k] + return false + } + + // an array of anything? + if (type === Array) return true + + // NaN is poisonous. Means that something is not allowed. + if (type !== type) { + debug("Poison NaN", k, val, type) + delete data[k] + return false + } + + // explicit list of values + if (val === type) { + debug("Explicitly allowed %j", val) + // if (isArray) (data[k] = data[k] || []).push(val) + // else data[k] = val + data[k] = val + return true + } + + // now go through the list of typeDefs, validate against each one. + var ok = false + , types = Object.keys(typeDefs) + for (var i = 0, l = types.length; i < l; i ++) { + debug("test type %j %j %j", k, val, types[i]) + var t = typeDefs[types[i]] + if (t && type === t.type) { + var d = {} + ok = false !== t.validate(d, k, val) + val = d[k] + if (ok) { + // if (isArray) (data[k] = data[k] || []).push(val) + // else data[k] = val + data[k] = val + break + } + } + } + debug("OK? %j (%j %j %j)", ok, k, val, types[i]) + + if (!ok) delete data[k] + return ok +} + +function parse (args, data, remain, types, shorthands) { + debug("parse", args, data, remain) + + var key = null + , abbrevs = abbrev(Object.keys(types)) + , shortAbbr = abbrev(Object.keys(shorthands)) + + for (var i = 0; i < args.length; i ++) { + var arg = args[i] + debug("arg", arg) + + if (arg.match(/^-{2,}$/)) { + // done with keys. + // the rest are args. + remain.push.apply(remain, args.slice(i + 1)) + args[i] = "--" + break + } + if (arg.charAt(0) === "-") { + if (arg.indexOf("=") !== -1) { + var v = arg.split("=") + arg = v.shift() + v = v.join("=") + args.splice.apply(args, [i, 1].concat([arg, v])) + } + // see if it's a shorthand + // if so, splice and back up to re-parse it. + var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs) + debug("arg=%j shRes=%j", arg, shRes) + if (shRes) { + debug(arg, shRes) + args.splice.apply(args, [i, 1].concat(shRes)) + if (arg !== shRes[0]) { + i -- + continue + } + } + arg = arg.replace(/^-+/, "") + var no = false + while (arg.toLowerCase().indexOf("no-") === 0) { + no = !no + arg = arg.substr(3) + } + + if (abbrevs[arg]) arg = abbrevs[arg] + + var isArray = types[arg] === Array || + Array.isArray(types[arg]) && types[arg].indexOf(Array) !== -1 + + var val + , la = args[i + 1] + + var isBool = no || + types[arg] === Boolean || + Array.isArray(types[arg]) && types[arg].indexOf(Boolean) !== -1 || + (la === "false" && + (types[arg] === null || + Array.isArray(types[arg]) && ~types[arg].indexOf(null))) + + if (isBool) { + // just set and move along + val = !no + // however, also support --bool true or --bool false + if (la === "true" || la === "false") { + val = JSON.parse(la) + la = null + if (no) val = !val + i ++ + } + + // also support "foo":[Boolean, "bar"] and "--foo bar" + if (Array.isArray(types[arg]) && la) { + if (~types[arg].indexOf(la)) { + // an explicit type + val = la + i ++ + } else if ( la === "null" && ~types[arg].indexOf(null) ) { + // null allowed + val = null + i ++ + } else if ( !la.match(/^-{2,}[^-]/) && + !isNaN(la) && + ~types[arg].indexOf(Number) ) { + // number + val = +la + i ++ + } else if ( !la.match(/^-[^-]/) && ~types[arg].indexOf(String) ) { + // string + val = la + i ++ + } + } + + if (isArray) (data[arg] = data[arg] || []).push(val) + else data[arg] = val + + continue + } + + if (la && la.match(/^-{2,}$/)) { + la = undefined + i -- + } + + val = la === undefined ? true : la + if (isArray) (data[arg] = data[arg] || []).push(val) + else data[arg] = val + + i ++ + continue + } + remain.push(arg) + } +} + +function resolveShort (arg, shorthands, shortAbbr, abbrevs) { + // handle single-char shorthands glommed together, like + // npm ls -glp, but only if there is one dash, and only if + // all of the chars are single-char shorthands, and it's + // not a match to some other abbrev. + arg = arg.replace(/^-+/, '') + if (abbrevs[arg] && !shorthands[arg]) { + return null + } + if (shortAbbr[arg]) { + arg = shortAbbr[arg] + } else { + var singles = shorthands.___singles + if (!singles) { + singles = Object.keys(shorthands).filter(function (s) { + return s.length === 1 + }).reduce(function (l,r) { l[r] = true ; return l }, {}) + shorthands.___singles = singles + } + var chrs = arg.split("").filter(function (c) { + return singles[c] + }) + if (chrs.join("") === arg) return chrs.map(function (c) { + return shorthands[c] + }).reduce(function (l, r) { + return l.concat(r) + }, []) + } + + if (shorthands[arg] && !Array.isArray(shorthands[arg])) { + shorthands[arg] = shorthands[arg].split(/\s+/) + } + return shorthands[arg] +} + +if (module === require.main) { +var assert = require("assert") + , util = require("util") + + , shorthands = + { s : ["--loglevel", "silent"] + , d : ["--loglevel", "info"] + , dd : ["--loglevel", "verbose"] + , ddd : ["--loglevel", "silly"] + , noreg : ["--no-registry"] + , reg : ["--registry"] + , "no-reg" : ["--no-registry"] + , silent : ["--loglevel", "silent"] + , verbose : ["--loglevel", "verbose"] + , h : ["--usage"] + , H : ["--usage"] + , "?" : ["--usage"] + , help : ["--usage"] + , v : ["--version"] + , f : ["--force"] + , desc : ["--description"] + , "no-desc" : ["--no-description"] + , "local" : ["--no-global"] + , l : ["--long"] + , p : ["--parseable"] + , porcelain : ["--parseable"] + , g : ["--global"] + } + + , types = + { aoa: Array + , nullstream: [null, Stream] + , date: Date + , str: String + , browser : String + , cache : path + , color : ["always", Boolean] + , depth : Number + , description : Boolean + , dev : Boolean + , editor : path + , force : Boolean + , global : Boolean + , globalconfig : path + , group : [String, Number] + , gzipbin : String + , logfd : [Number, Stream] + , loglevel : ["silent","win","error","warn","info","verbose","silly"] + , long : Boolean + , "node-version" : [false, String] + , npaturl : url + , npat : Boolean + , "onload-script" : [false, String] + , outfd : [Number, Stream] + , parseable : Boolean + , pre: Boolean + , prefix: path + , proxy : url + , "rebuild-bundle" : Boolean + , registry : url + , searchopts : String + , searchexclude: [null, String] + , shell : path + , t: [Array, String] + , tag : String + , tar : String + , tmp : path + , "unsafe-perm" : Boolean + , usage : Boolean + , user : String + , username : String + , userconfig : path + , version : Boolean + , viewer: path + , _exit : Boolean + } + +; [["-v", {version:true}, []] + ,["---v", {version:true}, []] + ,["ls -s --no-reg connect -d", + {loglevel:"info",registry:null},["ls","connect"]] + ,["ls ---s foo",{loglevel:"silent"},["ls","foo"]] + ,["ls --registry blargle", {}, ["ls"]] + ,["--no-registry", {registry:null}, []] + ,["--no-color true", {color:false}, []] + ,["--no-color false", {color:true}, []] + ,["--no-color", {color:false}, []] + ,["--color false", {color:false}, []] + ,["--color --logfd 7", {logfd:7,color:true}, []] + ,["--color=true", {color:true}, []] + ,["--logfd=10", {logfd:10}, []] + ,["--tmp=/tmp -tar=gtar",{tmp:"/tmp",tar:"gtar"},[]] + ,["--tmp=tmp -tar=gtar", + {tmp:path.resolve(process.cwd(), "tmp"),tar:"gtar"},[]] + ,["--logfd x", {}, []] + ,["a -true -- -no-false", {true:true},["a","-no-false"]] + ,["a -no-false", {false:false},["a"]] + ,["a -no-no-true", {true:true}, ["a"]] + ,["a -no-no-no-false", {false:false}, ["a"]] + ,["---NO-no-No-no-no-no-nO-no-no"+ + "-No-no-no-no-no-no-no-no-no"+ + "-no-no-no-no-NO-NO-no-no-no-no-no-no"+ + "-no-body-can-do-the-boogaloo-like-I-do" + ,{"body-can-do-the-boogaloo-like-I-do":false}, []] + ,["we are -no-strangers-to-love "+ + "--you-know the-rules --and so-do-i "+ + "---im-thinking-of=a-full-commitment "+ + "--no-you-would-get-this-from-any-other-guy "+ + "--no-gonna-give-you-up "+ + "-no-gonna-let-you-down=true "+ + "--no-no-gonna-run-around false "+ + "--desert-you=false "+ + "--make-you-cry false "+ + "--no-tell-a-lie "+ + "--no-no-and-hurt-you false" + ,{"strangers-to-love":false + ,"you-know":"the-rules" + ,"and":"so-do-i" + ,"you-would-get-this-from-any-other-guy":false + ,"gonna-give-you-up":false + ,"gonna-let-you-down":false + ,"gonna-run-around":false + ,"desert-you":false + ,"make-you-cry":false + ,"tell-a-lie":false + ,"and-hurt-you":false + },["we", "are"]] + ,["-t one -t two -t three" + ,{t: ["one", "two", "three"]} + ,[]] + ,["-t one -t null -t three four five null" + ,{t: ["one", "null", "three"]} + ,["four", "five", "null"]] + ,["-t foo" + ,{t:["foo"]} + ,[]] + ,["--no-t" + ,{t:["false"]} + ,[]] + ,["-no-no-t" + ,{t:["true"]} + ,[]] + ,["-aoa one -aoa null -aoa 100" + ,{aoa:["one", null, 100]} + ,[]] + ,["-str 100" + ,{str:"100"} + ,[]] + ,["--color always" + ,{color:"always"} + ,[]] + ,["--no-nullstream" + ,{nullstream:null} + ,[]] + ,["--nullstream false" + ,{nullstream:null} + ,[]] + ,["--notadate 2011-01-25" + ,{notadate: "2011-01-25"} + ,[]] + ,["--date 2011-01-25" + ,{date: new Date("2011-01-25")} + ,[]] + ].forEach(function (test) { + var argv = test[0].split(/\s+/) + , opts = test[1] + , rem = test[2] + , actual = nopt(types, shorthands, argv, 0) + , parsed = actual.argv + delete actual.argv + console.log(util.inspect(actual, false, 2, true), parsed.remain) + for (var i in opts) { + var e = JSON.stringify(opts[i]) + , a = JSON.stringify(actual[i] === undefined ? null : actual[i]) + if (e && typeof e === "object") { + assert.deepEqual(e, a) + } else { + assert.equal(e, a) + } + } + assert.deepEqual(rem, parsed.remain) + }) +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/README.md new file mode 100644 index 0000000..99746fe --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/README.md @@ -0,0 +1,23 @@ +# abbrev-js + +Just like [ruby's Abbrev](http://apidock.com/ruby/Abbrev). + +Usage: + + var abbrev = require("abbrev"); + abbrev("foo", "fool", "folding", "flop"); + + // returns: + { fl: 'flop' + , flo: 'flop' + , flop: 'flop' + , fol: 'folding' + , fold: 'folding' + , foldi: 'folding' + , foldin: 'folding' + , folding: 'folding' + , foo: 'foo' + , fool: 'fool' + } + +This is handy for command-line scripts, or other cases where you want to be able to accept shorthands. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/lib/abbrev.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/lib/abbrev.js new file mode 100644 index 0000000..037de2d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/lib/abbrev.js @@ -0,0 +1,106 @@ + +module.exports = exports = abbrev.abbrev = abbrev + +abbrev.monkeyPatch = monkeyPatch + +function monkeyPatch () { + Array.prototype.abbrev = function () { return abbrev(this) } + Object.prototype.abbrev = function () { return abbrev(Object.keys(this)) } +} + +function abbrev (list) { + if (arguments.length !== 1 || !Array.isArray(list)) { + list = Array.prototype.slice.call(arguments, 0) + } + for (var i = 0, l = list.length, args = [] ; i < l ; i ++) { + args[i] = typeof list[i] === "string" ? list[i] : String(list[i]) + } + + // sort them lexicographically, so that they're next to their nearest kin + args = args.sort(lexSort) + + // walk through each, seeing how much it has in common with the next and previous + var abbrevs = {} + , prev = "" + for (var i = 0, l = args.length ; i < l ; i ++) { + var current = args[i] + , next = args[i + 1] || "" + , nextMatches = true + , prevMatches = true + if (current === next) continue + for (var j = 0, cl = current.length ; j < cl ; j ++) { + var curChar = current.charAt(j) + nextMatches = nextMatches && curChar === next.charAt(j) + prevMatches = prevMatches && curChar === prev.charAt(j) + if (nextMatches || prevMatches) continue + else { + j ++ + break + } + } + prev = current + if (j === cl) { + abbrevs[current] = current + continue + } + for (var a = current.substr(0, j) ; j <= cl ; j ++) { + abbrevs[a] = current + a += current.charAt(j) + } + } + return abbrevs +} + +function lexSort (a, b) { + return a === b ? 0 : a > b ? 1 : -1 +} + + +// tests +if (module === require.main) { + +var assert = require("assert") + , sys +sys = require("util") + +console.log("running tests") +function test (list, expect) { + var actual = abbrev(list) + assert.deepEqual(actual, expect, + "abbrev("+sys.inspect(list)+") === " + sys.inspect(expect) + "\n"+ + "actual: "+sys.inspect(actual)) + actual = abbrev.apply(exports, list) + assert.deepEqual(abbrev.apply(exports, list), expect, + "abbrev("+list.map(JSON.stringify).join(",")+") === " + sys.inspect(expect) + "\n"+ + "actual: "+sys.inspect(actual)) +} + +test([ "ruby", "ruby", "rules", "rules", "rules" ], +{ rub: 'ruby' +, ruby: 'ruby' +, rul: 'rules' +, rule: 'rules' +, rules: 'rules' +}) +test(["fool", "foom", "pool", "pope"], +{ fool: 'fool' +, foom: 'foom' +, poo: 'pool' +, pool: 'pool' +, pop: 'pope' +, pope: 'pope' +}) +test(["a", "ab", "abc", "abcd", "abcde", "acde"], +{ a: 'a' +, ab: 'ab' +, abc: 'abc' +, abcd: 'abcd' +, abcde: 'abcde' +, ac: 'acde' +, acd: 'acde' +, acde: 'acde' +}) + +console.log("pass") + +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/package.json new file mode 100644 index 0000000..ebd082f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/node_modules/abbrev/package.json @@ -0,0 +1,8 @@ +{ "name" : "abbrev" +, "version" : "1.0.3" +, "description" : "Like ruby's abbrev module, but in js" +, "author" : "Isaac Z. Schlueter " +, "main" : "./lib/abbrev.js" +, "scripts" : { "test" : "node lib/abbrev.js" } +, "repository" : "http://github.com/isaacs/abbrev-js" +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/package.json new file mode 100644 index 0000000..d1118e3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/nopt/package.json @@ -0,0 +1,12 @@ +{ "name" : "nopt" +, "version" : "1.0.10" +, "description" : "Option parsing for Node, supporting types, shorthands, etc. Used by npm." +, "author" : "Isaac Z. Schlueter (http://blog.izs.me/)" +, "main" : "lib/nopt.js" +, "scripts" : { "test" : "node lib/nopt.js" } +, "repository" : "http://github.com/isaacs/nopt" +, "bin" : "./bin/nopt.js" +, "license" : + { "type" : "MIT" + , "url" : "https://github.com/isaacs/nopt/raw/master/LICENSE" } +, "dependencies" : { "abbrev" : "1" }} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.dir-locals.el b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.dir-locals.el new file mode 100644 index 0000000..36ed4b8 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.dir-locals.el @@ -0,0 +1,6 @@ +((nil . ((indent-tabs-mode . nil) + (tab-width . 8) + (fill-column . 80))) + (js-mode . ((js-indent-level . 8) + (indent-tabs-mode . nil) + ))) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.gitmodules b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.gitmodules new file mode 100644 index 0000000..d528541 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.gitmodules @@ -0,0 +1,6 @@ +[submodule "deps/jsstyle"] + path = deps/jsstyle + url = git://github.com/davepacheco/jsstyle.git +[submodule "deps/javascriptlint"] + path = deps/javascriptlint + url = git://github.com/davepacheco/javascriptlint.git diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.npmignore new file mode 100644 index 0000000..6b70239 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/.npmignore @@ -0,0 +1,5 @@ +build +cover_html +cscope.* +node_modules +.coverage_data \ No newline at end of file diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/Makefile new file mode 100644 index 0000000..a91970c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/Makefile @@ -0,0 +1,66 @@ +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile: basic Makefile for template API service +# +# This Makefile is a template for new repos. It contains only repo-specific +# logic and uses included makefiles to supply common targets (javascriptlint, +# jsstyle, restdown, etc.), which are used by other repos as well. You may well +# need to rewrite most of this file, but you shouldn't need to touch the +# included makefiles. +# +# If you find yourself adding support for new targets that could be useful for +# other projects too, you should add these to the original versions of the +# included Makefiles (in eng.git) so that other teams can use them too. +# + +# +# Tools +# +NODEUNIT := ./node_modules/.bin/nodeunit +NODECOVER := ./node_modules/.bin/cover +BUNYAN := ./node_modules/.bin/bunyan +NPM := npm + +# +# Files +# +DOC_FILES = index.md +JS_FILES := $(shell find lib test -name '*.js') +JSL_CONF_NODE = tools/jsl.node.conf +JSL_FILES_NODE = $(JS_FILES) +JSSTYLE_FILES = $(JS_FILES) +JSSTYLE_FLAGS = -f tools/jsstyle.conf +SHRINKWRAP = npm-shrinkwrap.json + +CLEAN_FILES += node_modules $(SHRINKWRAP) cscope.files + +include ./tools/mk/Makefile.defs + +# +# Repo-specific targets +# +.PHONY: all +all: $(NODEUNIT) $(REPO_DEPS) + $(NPM) rebuild + +$(NODEUNIT): | $(NPM_EXEC) + $(NPM) install + +$(NODECOVER): | $(NPM_EXEC) + $(NPM) install + +.PHONY: cover +cover: $(NODECOVER) + @rm -fr ./.coverage_data + LOG_LEVEL=trace $(NODECOVER) run $(NODEUNIT) test/*.test.js | $(BUNYAN) + $(NODECOVER) report html + +CLEAN_FILES += $(TAP) ./node_modules/nodeunit + +.PHONY: test +test: $(NODEUNIT) + $(NODEUNIT) test/*.test.js | $(BUNYAN) + +include ./tools/mk/Makefile.deps +include ./tools/mk/Makefile.targ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/README.md new file mode 100644 index 0000000..24ecc60 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/README.md @@ -0,0 +1,122 @@ +# Overview + +[pooling](https://github.com/mcavage/node-pooling) is a small general purpose +resource pooling library. It is heavily inspired from +[James Cooper's](http://bitmechanic.com/) excellent +[generic-pool](https://github.com/coopernurse/node-pool) library. This library +was written after using that library for some time and desiring extra +functionality (namely watching events and health checking). + +# Usage + + var EventEmitter = require('events').EventEmitter; + var pooling = require('pooling'); + + var pool = pooling.createPool({ + checkInterval: 30000, // 30s (default is 30s) + max: 10, // Default is 1 + maxIdleTime: 120000, // 2m (default is 1 hour) + name: 'my pool', + + // Health check. Call the callback with an Error if you want + // to indicate this client should die. destroy will still be + // called (i.e., don't "double free"). + // + // This function is called when an object is unused for + // >= maxIdleTime. If you don't provide a check function, + // the default behavior is to mark the client for death. + check: function check(client, callback) { + if ((client.id % 2) !== 0) + return callback(new Error()); + + return callback(null); + }, + + // callback is of the form function (err, client). + create: function create(callback) { + var client = new EventEmitter(); + client.id = Math.floor(Math.random() * 1000); + return callback(null, client); + }, + + // destroy is for you to do cleanup with; the pool will have already + // discarded the object (hence no callback) + destroy: function destroy(client) { + client.was = client.id; + client.id = -1; + } + }); + + pool.on('create', function (client) { + console.log('client %d created', client.id); + }); + + pool.on('death', function (client) { + console.log('client %d was killed', client.was); + }); + + pool.on('drain', function () { + console.log('pool has no backlog or outstanding clients'); + }); + + pool.acquire(function (err, client) { + if (err) { + console.error('Unable to acquire: %s', err.stack); + process.exit(1); + } + + pool.release(client); + client.emit('error', new Error('die now')); + }); + + // Gracefully block future acquire calls and wait for clients to be + // released + pool.shutdown(function () { + console.log('done'); + process.exit(0); + }); + +By default the pool will remove clients on `close`, `end`, `error` and `timeout` +events. You can override this by passing in an `events` array at pool creation +time. You can also pass in a [Bunyan](https://github.com/trentm/node-bunyan) +`Logger` - `pooling` logs everything at the `trace` level. + +# Install + + npm install pooling + +# Development + +To run unit tests and lint/style checks, just run: + + make prepush + +You can generate coverage data by running: + + make cover + +And opening `./cover_data/index.html`. + + +# License + +The MIT License (MIT) +Copyright (c) 2012 Mark Cavage + +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/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/.npmignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/.npmignore @@ -0,0 +1 @@ +build diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/COPYING b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/Makefile new file mode 100644 index 0000000..398c106 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/Makefile @@ -0,0 +1,68 @@ +BUILDOS=$(shell uname -s) +BUILDDIR = build +INSTALLDIRS = \ + $(BUILDDIR)/install \ + $(BUILDDIR)/install/javascriptlint \ + +CSRCS = \ + nodepos.c \ + pyspidermonkey.c + +OBJECTS = $(CSRCS:%.c=$(BUILDDIR)/%.o) +CFLAGS += -fno-strict-aliasing -O -fPIC + +SOLDFLAGS += -shared +CPPFLAGS += -DNDEBUG -D_REENTRANT \ + -Ispidermonkey/src -Ispidermonkey/src/build \ + -I/usr/include \ + + +PY_PYTHON=$(shell python -c "import sys; print(sys.executable)") +PY_PREFIX=$(shell $(PY_PYTHON) -c "import sys; print(sys.prefix)") +PY_VERSION=$(shell $(PY_PYTHON) -c "import sys; print('.'.join(map(str, sys.version_info[:2])))") +ifeq ($(BUILDOS),Darwin) + PY_ARCH=$(shell $(PY_PYTHON) -c 'import sys; print (sys.maxint > 2**32 and "x86_64" or "i386")') + SOLDFLAGS += $(PY_PREFIX)/Python + CC=gcc -arch $(PY_ARCH) +else + PY_BIT=$(shell $(PY_PYTHON) -c 'import sys; print (sys.maxint > 2**32 and "64" or "32")') + CFLAGS += -m$(PY_BIT) +endif + +CPPFLAGS += -I$(PY_PREFIX)/include/python$(PY_VERSION) +SOFILE = $(BUILDDIR)/pyspidermonkey.so + +all: $(SOFILE) + +$(BUILDDIR) $(INSTALLDIRS): + mkdir -p $@ + +$(OBJECTS): spidermonkey/src/build/libjs.a spidermonkey/src/build/js_operating_system.h + +$(SOFILE): $(OBJECTS) + $(CC) $(CFLAGS) $(SOLDFLAGS) $(LDFLAGS) $(OBJECTS) -Lspidermonkey/src/build -ljs -o $@ + +$(BUILDDIR)/%.o: javascriptlint/pyspidermonkey/%.c | $(BUILDDIR) + $(CC) -o $@ -c $(CFLAGS) $(CPPFLAGS) $< + +spidermonkey/src/build/libjs.a: + (cd spidermonkey/src && CC="$(CC)" CFLAGS="$(CFLAGS)" $(MAKE)) + +spidermonkey/src/build/js_operating_system.h: + echo "#define XP_UNIX" > $@ + +clean: + -rm -rf $(BUILDDIR) $(INSTALLDIRS) + -(cd spidermonkey/src && $(MAKE) clean) + +install: $(SOFILE) javascriptlint/jsl javascriptlint/jsl | $(INSTALLDIRS) + cp $(SOFILE) build/install + cp javascriptlint/*.py build/install/javascriptlint + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/jsl >build/install/jsl + chmod +x build/install/jsl + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/jsl.py >build/install/javascriptlint/jsl.py + chmod +x build/install/javascriptlint/jsl.py + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/jsparse.py >build/install/javascriptlint/jsparse.py + sed -e "1s:#\!/usr/bin/env python:#\!$(PY_PYTHON):" javascriptlint/lint.py >build/install/javascriptlint/lint.py + +.PHONY: install diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/README.md new file mode 100644 index 0000000..1e281bc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/README.md @@ -0,0 +1,74 @@ +Overview +-------- + +This is a fork of Matthias Miller's JavaScript Lint. For the original, see: + + http://javascriptlint.com/ + +This tool has two important features that are uncommon among JavaScript lint +tools: + +- It does not conflate style with lint. Style refers to arbitrary code + formatting rules (like leading whitespace rules). Lint refers to potential + program correctness issues (like missing "break" statements inside a switch). + The line is certainly fuzzy, as in the case of JavaScript semicolon style, + but that's why: + +- It's configurable. Each individual warning can be turned on or off, and + warnings can be overridden for individual lines of code. This is essential + for cases where potentially dangerous behavior is being deliberately used + carefully. + +If you want a style checker, see http://github.com/davepacheco/jsstyle. + + +Synopsis +-------- + + # make install + ... + # build/install/jsl + usage: jsl [options] [files] + + options: + -h, --help show this help message and exit + --conf=CONF set the conf file + --profile turn on hotshot profiling + --recurse recursively search directories on the command line + --enable-wildcards resolve wildcards in the command line + --dump dump this script + --unittest run the python unittests + --quiet minimal output + --verbose verbose output + --nologo suppress version information + --nofilelisting suppress file names + --nosummary suppress lint summary + --help:conf display the default configuration file + +You can define a configuration file for jsl to enable or disable particular +warnings and to define global objects (like "window"). See the --help:conf +option. + + +Supported Platforms +------------------- + +This branch of JSL has been tested on: + +- SmartOS (illumos-based), both 32-bit and 64-bit. +- Mac OS X Snow Leopard, Lion, and Mountain Lion. +- Debian Squeeze (6.0.5). + +All of these use Python 2.6 or later. + +History +------- + +This version forked from the Subversion repo at revision 302 (2011-04-06). +I'll happily look at incorporating new patches from upstream, though the +project has been pretty quiet for the last many months. + +The main purpose of this fork is to fix building on illumos-based systems. +Rather than fix the complex spidermonkey build system to work on illumos, I +stripped out a bunch of unnecessary pieces and Makefiles and wrote a new set of +Makefiles. The result now builds on Mac OS X and Linux as well. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/__init__.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/__init__.py new file mode 100644 index 0000000..2716426 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/__init__.py @@ -0,0 +1,3 @@ +# vim: ts=4 sw=4 expandtab +from jsl import main + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/conf.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/conf.py new file mode 100644 index 0000000..66c31dd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/conf.py @@ -0,0 +1,260 @@ +# vim: ts=4 sw=4 expandtab +import os +import unittest + +import fs +import util +import warnings + +def _getwarningsconf(): + lines = [] + for name in sorted(warnings.warnings.keys()): + message = warnings.warnings[name] + sign = '+' + if name == 'block_without_braces': + sign = '-' + assert len(name) < 29 + lines.append(sign + name.ljust(29) + '# ' + message) + return '\n'.join(lines) + +DEFAULT_CONF = """\ +# +# Configuration File for JavaScript Lint %(version)s +# Developed by Matthias Miller (http://www.JavaScriptLint.com) +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# +%(warnings)s + + +### Output format +# Customize the format of the error message. +# __FILE__ indicates current file path +# __FILENAME__ indicates current file name +# __LINE__ indicates current line +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message +# +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# +-legacy_control_comments + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" ++always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: +#+define window +#+define document + + +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# +""" % { + 'version': '', # TODO + 'warnings': _getwarningsconf(), +} + + +class ConfError(Exception): + def __init__(self, error): + Exception.__init__(self, error) + self.lineno = None + self.path = None + +class Setting: + wants_parm = False + wants_dir = False + +class DeprecatedSetting(Setting): + wants_parm = False + value = None + def load(self, enabled): + raise ConfError, 'This setting is deprecated.' + +class BooleanSetting(Setting): + wants_parm = False + def __init__(self, default): + self.value = default + def load(self, enabled): + self.value = enabled + +class StringSetting(Setting): + wants_parm = True + def __init__(self, default): + self.value = default + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value = parm + +class DeclareSetting(Setting): + wants_parm = True + def __init__(self): + self.value = [] + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value.append(parm) + +class ProcessSetting(Setting): + wants_parm = True + wants_dir = True + def __init__(self, recurse_setting): + self.value = [] + self._recurse = recurse_setting + def load(self, enabled, parm, dir): + if dir: + parm = os.path.join(dir, parm) + self.value.append((self._recurse.value, parm)) + +class JSVersionSetting(Setting): + wants_parm = True + value = util.JSVersion.default() + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + + self.value = util.JSVersion.fromtype(parm) + if not self.value: + raise ConfError, 'Invalid JavaScript version: %s' % parm + +class Conf: + def __init__(self): + recurse = BooleanSetting(False) + self._settings = { + 'recurse': recurse, + 'output-format': StringSetting('__FILE__(__LINE__): __ERROR__'), + 'lambda_assign_requires_semicolon': DeprecatedSetting(), + 'legacy_control_comments': BooleanSetting(False), + 'jscript_function_extensions': DeprecatedSetting(), + 'always_use_option_explicit': BooleanSetting(True), + 'define': DeclareSetting(), + 'context': BooleanSetting(True), + 'process': ProcessSetting(recurse), + 'default-version': JSVersionSetting(), + # SpiderMonkey warnings + 'no_return_value': BooleanSetting(True), + 'equal_as_assign': BooleanSetting(True), + 'anon_no_return_value': BooleanSetting(True) + } + for name in warnings.warnings: + self._settings[name] = BooleanSetting(True) + self.loadline('-block_without_braces') + + def loadfile(self, path): + path = os.path.abspath(path) + conf = fs.readfile(path) + try: + self.loadtext(conf, dir=os.path.dirname(path)) + except ConfError, error: + error.path = path + raise + + def loadtext(self, conf, dir=None): + lines = conf.splitlines() + for lineno in range(0, len(lines)): + try: + self.loadline(lines[lineno], dir) + except ConfError, error: + error.lineno = lineno + raise + + def loadline(self, line, dir=None): + assert not '\r' in line + assert not '\n' in line + + # Allow comments + if '#' in line: + line = line[:line.find('#')] + line = line.rstrip() + if not line: + return + + # Parse the +/- + if line.startswith('+'): + enabled = True + elif line.startswith('-'): + enabled = False + else: + raise ConfError, 'Expected + or -.' + line = line[1:] + + # Parse the key/parms + name = line.split()[0].lower() + parm = line[len(name):].lstrip() + + # Load the setting + setting = self._settings[name] + args = { + 'enabled': enabled + } + if setting.wants_parm: + args['parm'] = parm + elif parm: + raise ConfError, 'The %s setting does not expect a parameter.' % name + if setting.wants_dir: + args['dir'] = dir + setting.load(**args) + + def __getitem__(self, name): + if name == 'paths': + name = 'process' + elif name == 'declarations': + name = 'define' + return self._settings[name].value + +class TestConf(unittest.TestCase): + def testDefaultConf(self): + # Make sure the string version corresponds with the code. + fromstr = Conf() + fromstr.loadtext(DEFAULT_CONF) + fromcode = Conf() + settings = set(fromcode._settings.keys() + fromstr._settings.keys()) + for setting in settings: + self.assertEquals(fromcode[setting], fromstr[setting], + 'Mismatched defaults for %s' % setting) + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/fs.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/fs.py new file mode 100644 index 0000000..cc05dbb --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/fs.py @@ -0,0 +1,23 @@ +# vim: ts=4 sw=4 expandtab +import codecs +import os + +def readfile(path): + file = codecs.open(path, 'r', 'utf-8') + contents = file.read() + if contents and contents[0] == unicode(codecs.BOM_UTF8, 'utf8'): + contents = contents[1:] + + if contents.startswith('#!'): + idx = contents.find('\n'); + if idx != -1: + contents = '\n' + contents[idx + 1:]; + + return contents + +def normpath(path): + path = os.path.abspath(path) + path = os.path.normcase(path) + path = os.path.normpath(path) + return path + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/htmlparse.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/htmlparse.py new file mode 100644 index 0000000..5d38bce --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/htmlparse.py @@ -0,0 +1,51 @@ +# vim: ts=4 sw=4 expandtab +import HTMLParser +import unittest + +class _Parser(HTMLParser.HTMLParser): + def __init__(self): + HTMLParser.HTMLParser.__init__(self) + self._tags = [] + + def handle_starttag(self, tag, attributes): + if tag.lower() == 'script': + attr = dict(attributes) + self._tags.append({ + 'type': 'start', + 'lineno': self.lineno, + 'offset': self.offset, + 'len': len(self.get_starttag_text()), + 'attr': attr + }) + + def handle_endtag(self, tag): + if tag.lower() == 'script': + self._tags.append({ + 'type': 'end', + 'lineno': self.lineno, + 'offset': self.offset, + }) + + def unknown_decl(self, data): + # Ignore unknown declarations instead of raising an exception. + pass + + def gettags(self): + return self._tags + +def findscripttags(s): + """ Note that the lineno is 1-based. + """ + parser = _Parser() + parser.feed(s) + parser.close() + return parser.gettags() + +class TestHTMLParse(unittest.TestCase): + def testConditionalComments(self): + html = """ + +This is not Internet Explorer +""" + findscripttags(html) + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsl b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsl new file mode 100755 index 0000000..a18794b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsl @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +import os +import sys + +oldcwd = os.getcwd(); +os.chdir(os.path.dirname(sys.argv[0])); +basedir = os.getcwd(); +os.chdir(oldcwd); + +sys.path.insert(0, basedir) + +import javascriptlint +javascriptlint.main() diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsl.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsl.py new file mode 100755 index 0000000..4eee0b6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsl.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# vim: ts=4 sw=4 expandtab +import codecs +import fnmatch +import glob +import os +import sys +import unittest +from optparse import OptionParser + +import conf +import htmlparse +import jsparse +import lint +import util + +_lint_results = { + 'warnings': 0, + 'errors': 0 +} + +def _dump(paths): + for path in paths: + script = util.readfile(path) + jsparse.dump_tree(script) + +def _lint(paths, conf_, printpaths): + def lint_error(path, line, col, errname, errdesc): + _lint_results['warnings'] = _lint_results['warnings'] + 1 + print util.format_error(conf_['output-format'], path, line, col, + errname, errdesc) + lint.lint_files(paths, lint_error, conf=conf_, printpaths=printpaths) + +def _resolve_paths(path, recurse): + # Build a list of directories + paths = [] + + dir, pattern = os.path.split(path) + for cur_root, cur_dirs, cur_files in os.walk(dir): + paths.extend(os.path.join(cur_root, file) for file in \ + fnmatch.filter(cur_files, pattern)) + if not recurse: + break + + # If no files have been found, return the original path/pattern. This will + # force an error to be thrown if no matching files were found. + return paths or [path] + +def printlogo(): + # TODO: Print version number. + print "JavaScript Lint" + print "Developed by Matthias Miller (http://www.JavaScriptLint.com)" + +def _profile_enabled(func, *args, **kwargs): + import tempfile + import hotshot + import hotshot.stats + handle, filename = tempfile.mkstemp() + profile = hotshot.Profile(filename) + profile.runcall(func, *args, **kwargs) + profile.close() + stats = hotshot.stats.load(filename) + stats = stats.sort_stats("time") + stats.print_stats() +def _profile_disabled(func, *args, **kwargs): + func(*args, **kwargs) + +def main(): + parser = OptionParser(usage="%prog [options] [files]") + add = parser.add_option + add("--conf", dest="conf", metavar="CONF", + help="set the conf file") + add("--profile", dest="profile", action="store_true", default=False, + help="turn on hotshot profiling") + add("--recurse", dest="recurse", action="store_true", default=False, + help="recursively search directories on the command line") + if os.name == 'nt': + add("--disable-wildcards", dest="wildcards", action="store_false", + default=True, help="do not resolve wildcards in the command line") + else: + add("--enable-wildcards", dest="wildcards", action="store_true", + default=False, help="resolve wildcards in the command line") + add("--dump", dest="dump", action="store_true", default=False, + help="dump this script") + add("--unittest", dest="unittest", action="store_true", default=False, + help="run the python unittests") + add("--quiet", dest="verbosity", action="store_const", const=0, + help="minimal output") + add("--verbose", dest="verbosity", action="store_const", const=2, + help="verbose output") + add("--nologo", dest="printlogo", action="store_false", default=True, + help="suppress version information") + add("--nofilelisting", dest="printlisting", action="store_false", + default=True, help="suppress file names") + add("--nosummary", dest="printsummary", action="store_false", default=True, + help="suppress lint summary") + add("--help:conf", dest="showdefaultconf", action="store_true", default=False, + help="display the default configuration file") + parser.set_defaults(verbosity=1) + options, args = parser.parse_args() + + if len(sys.argv) == 1: + parser.print_help() + sys.exit() + + if options.showdefaultconf: + print conf.DEFAULT_CONF + sys.exit() + + if options.printlogo: + printlogo() + + conf_ = conf.Conf() + if options.conf: + conf_.loadfile(options.conf) + + profile_func = _profile_disabled + if options.profile: + profile_func = _profile_enabled + + if options.unittest: + suite = unittest.TestSuite(); + for module in [conf, htmlparse, jsparse, lint, util]: + suite.addTest(unittest.findTestCases(module)) + + runner = unittest.TextTestRunner(verbosity=options.verbosity) + runner.run(suite) + + paths = [] + for recurse, path in conf_['paths']: + paths.extend(_resolve_paths(path, recurse)) + for arg in args: + if options.wildcards: + paths.extend(_resolve_paths(arg, options.recurse)) + elif options.recurse and os.path.isdir(arg): + paths.extend(_resolve_paths(os.path.join(arg, '*'), True)) + else: + paths.append(arg) + if options.dump: + profile_func(_dump, paths) + else: + profile_func(_lint, paths, conf_, options.printlisting) + + if options.printsummary: + print '\n%i error(s), %i warnings(s)' % (_lint_results['errors'], + _lint_results['warnings']) + + if _lint_results['errors']: + sys.exit(3) + if _lint_results['warnings']: + sys.exit(1) + sys.exit(0) + +if __name__ == '__main__': + main() + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsparse.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsparse.py new file mode 100644 index 0000000..eec82a4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/jsparse.py @@ -0,0 +1,426 @@ +#!/usr/bin/env python +# vim: ts=4 sw=4 expandtab +""" Parses a script into nodes. """ +import bisect +import re +import unittest + +import spidermonkey +from spidermonkey import tok, op +from util import JSVersion + +_tok_names = dict(zip( + [getattr(tok, prop) for prop in dir(tok)], + ['tok.%s' % prop for prop in dir(tok)] +)) +_op_names = dict(zip( + [getattr(op, prop) for prop in dir(op)], + ['op.%s' % prop for prop in dir(op)] +)) + +NodePos = spidermonkey.NodePos + +class NodePositions: + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text, start_pos=None): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._start_pos = start_pos + self._lines = text.splitlines(True) + lines = [0] + [len(x) for x in self._lines] + for x in range(1, len(lines)): + lines[x] += lines[x-1] + self._line_offsets = lines + def from_offset(self, offset): + line = bisect.bisect(self._line_offsets, offset)-1 + col = offset - self._line_offsets[line] + if self._start_pos: + if line == 0: + col += self._start_pos.col + line += self._start_pos.line + return NodePos(line, col) + def to_offset(self, pos): + pos = self._to_rel_pos(pos) + offset = self._line_offsets[pos.line] + pos.col + assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num + return offset + def text(self, start, end): + assert start <= end + start, end = self._to_rel_pos(start), self._to_rel_pos(end) + # Trim the ending first in case it's a single line. + lines = self._lines[start.line:end.line+1] + lines[-1] = lines[-1][:end.col+1] + lines[0] = lines[0][start.col:] + return ''.join(lines) + def _to_rel_pos(self, pos): + " converts a position to a position relative to self._start_pos " + if not self._start_pos: + return pos + line, col = pos.line, pos.col + line -= self._start_pos.line + if line == 0: + col -= self._start_pos.col + assert line >= 0 and col >= 0 # out-of-bounds node position + return NodePos(line, col) + +class NodeRanges: + def __init__(self): + self._offsets = [] + def add(self, start, end): + i = bisect.bisect_left(self._offsets, start) + if i % 2 == 1: + i -= 1 + start = self._offsets[i] + + end = end + 1 + j = bisect.bisect_left(self._offsets, end) + if j % 2 == 1: + end = self._offsets[j] + j += 1 + + self._offsets[i:j] = [start,end] + def has(self, pos): + return bisect.bisect_right(self._offsets, pos) % 2 == 1 + +class _Node: + def add_child(self, node): + if node: + node.node_index = len(self.kids) + node.parent = self + self.kids.append(node) + + def start_pos(self): + try: + return self._start_pos + except AttributeError: + self._start_pos = NodePos(self._start_line, self._start_col) + return self._start_pos + + def end_pos(self): + try: + return self._end_pos + except AttributeError: + self._end_pos = NodePos(self._end_line, self._end_col) + return self._end_pos + + def __str__(self): + kind = self.kind + if not kind: + kind = '(none)' + return '%s>%s' % (_tok_names[kind], str(self.kids)) + + def is_equivalent(self, other, are_functions_equiv=False): + if not other: + return False + + # Bail out for functions + if not are_functions_equiv: + if self.kind == tok.FUNCTION: + return False + if self.kind == tok.LP and self.opcode == op.CALL: + return False + + if self.kind != other.kind: + return False + if self.opcode != other.opcode: + return False + + # Check atoms on names, properties, and string constants + if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom: + return False + + # Check values on numbers + if self.kind == tok.NUMBER and self.dval != other.dval: + return False + + # Compare child nodes + if len(self.kids) != len(other.kids): + return False + for i in range(0, len(self.kids)): + # Watch for dead nodes + if not self.kids[i]: + if not other.kids[i]: return True + else: return False + if not self.kids[i].is_equivalent(other.kids[i]): + return False + + return True + +def isvalidversion(jsversion): + if jsversion is None: + return True + return spidermonkey.is_valid_version(jsversion.version) + +def findpossiblecomments(script, node_positions): + pos = 0 + single_line_re = r"//[^\r\n]*" + multi_line_re = r"/\*(.*?)\*/" + full_re = "(%s)|(%s)" % (single_line_re, multi_line_re) + comment_re = re.compile(full_re, re.DOTALL) + + comments = [] + while True: + match = comment_re.search(script, pos) + if not match: + return comments + + # Get the comment text + comment_text = script[match.start():match.end()] + if comment_text.startswith('/*'): + comment_text = comment_text[2:-2] + opcode = 'JSOP_C_COMMENT' + else: + comment_text = comment_text[2:] + opcode = 'JSOP_CPP_COMMENT' + opcode = opcode[5:].lower() + + start_offset = match.start() + end_offset = match.end()-1 + + start_pos = node_positions.from_offset(start_offset) + end_pos = node_positions.from_offset(end_offset) + kwargs = { + 'kind': 'COMMENT', + 'atom': comment_text, + 'opcode': opcode, + '_start_line': start_pos.line, + '_start_col': start_pos.col, + '_end_line': end_pos.line, + '_end_col': end_pos.col, + 'parent': None, + 'kids': [], + 'node_index': None + } + comment_node = _Node() + comment_node.__dict__.update(kwargs) + comments.append(comment_node) + + # Start searching immediately after the start of the comment in case + # this one was within a string or a regexp. + pos = match.start()+1 + +def parse(script, jsversion, error_callback, startpos=None): + """ All node positions will be relative to startpos. This allows scripts + to be embedded in a file (for example, HTML). + """ + def _wrapped_callback(line, col, msg): + assert msg.startswith('JSMSG_') + msg = msg[6:].lower() + error_callback(line, col, msg) + + startpos = startpos or NodePos(0,0) + jsversion = jsversion or JSVersion.default() + assert isvalidversion(jsversion) + return spidermonkey.parse(script, jsversion.version, jsversion.e4x, + _Node, _wrapped_callback, + startpos.line, startpos.col) + +def filtercomments(possible_comments, node_positions, root_node): + comment_ignore_ranges = NodeRanges() + + def process(node): + if node.kind == tok.NUMBER: + node.atom = node_positions.text(node.start_pos(), node.end_pos()) + elif node.kind == tok.STRING or \ + (node.kind == tok.OBJECT and node.opcode == op.REGEXP): + start_offset = node_positions.to_offset(node.start_pos()) + end_offset = node_positions.to_offset(node.end_pos()) - 1 + comment_ignore_ranges.add(start_offset, end_offset) + for kid in node.kids: + if kid: + process(kid) + process(root_node) + + comments = [] + for comment in possible_comments: + start_offset = node_positions.to_offset(comment.start_pos()) + end_offset = node_positions.to_offset(comment.end_pos()) + if comment_ignore_ranges.has(start_offset): + continue + comment_ignore_ranges.add(start_offset, end_offset) + comments.append(comment) + return comments + +def findcomments(script, root_node, start_pos=None): + node_positions = NodePositions(script, start_pos) + possible_comments = findpossiblecomments(script, node_positions) + return filtercomments(possible_comments, node_positions, root_node) + +def is_compilable_unit(script, jsversion): + jsversion = jsversion or JSVersion.default() + assert isvalidversion(jsversion) + return spidermonkey.is_compilable_unit(script, jsversion.version, jsversion.e4x) + +def _dump_node(node, depth=0): + if node is None: + print ' '*depth, + print '(None)' + print + else: + print ' '*depth, + print '%s, %s' % (_tok_names[node.kind], _op_names[node.opcode]) + print ' '*depth, + print '%s - %s' % (node.start_pos(), node.end_pos()) + if hasattr(node, 'atom'): + print ' '*depth, + print 'atom: %s' % node.atom + if node.no_semi: + print ' '*depth, + print '(no semicolon)' + print + for node in node.kids: + _dump_node(node, depth+1) + +def dump_tree(script): + def error_callback(line, col, msg): + print '(%i, %i): %s', (line, col, msg) + node = parse(script, None, error_callback) + _dump_node(node) + +class TestComments(unittest.TestCase): + def _test(self, script, expected_comments): + root = parse(script, None, lambda line, col, msg: None) + comments = findcomments(script, root) + encountered_comments = [node.atom for node in comments] + self.assertEquals(encountered_comments, list(expected_comments)) + def testSimpleComments(self): + self._test('re = /\//g', ()) + self._test('re = /\///g', ()) + self._test('re = /\////g', ('g',)) + def testCComments(self): + self._test('/*a*//*b*/', ('a', 'b')) + self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb')) + self._test('a//*b*/c', ('*b*/c',)) + self._test('a///*b*/c', ('/*b*/c',)) + self._test('a/*//*/;', ('//',)) + self._test('a/*b*/+/*c*/d', ('b', 'c')) + +class TestNodePositions(unittest.TestCase): + def _test(self, text, expected_lines, expected_cols): + # Get a NodePos list + positions = NodePositions(text) + positions = [positions.from_offset(i) for i in range(0, len(text))] + encountered_lines = ''.join([str(x.line) for x in positions]) + encountered_cols = ''.join([str(x.col) for x in positions]) + self.assertEquals(encountered_lines, expected_lines.replace(' ', '')) + self.assertEquals(encountered_cols, expected_cols.replace(' ', '')) + def testSimple(self): + self._test( + 'abc\r\ndef\nghi\n\nj', + '0000 0 1111 2222 3 4', + '0123 4 0123 0123 0 0' + ) + self._test( + '\rabc', + '0 111', + '0 012' + ) + def testText(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a') + self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc') + self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef') + def testOffset(self): + pos = NodePositions('abc\r\ndef\n\nghi') + self.assertEquals(pos.to_offset(NodePos(0, 2)), 2) + self.assertEquals(pos.to_offset(NodePos(1, 0)), 5) + self.assertEquals(pos.to_offset(NodePos(3, 1)), 11) + def testStartPos(self): + pos = NodePositions('abc\r\ndef\n\nghi', NodePos(3,4)) + self.assertEquals(pos.to_offset(NodePos(3, 4)), 0) + self.assertEquals(pos.to_offset(NodePos(3, 5)), 1) + self.assertEquals(pos.from_offset(0), NodePos(3, 4)) + self.assertEquals(pos.text(NodePos(3, 4), NodePos(3, 4)), 'a') + self.assertEquals(pos.text(NodePos(3, 4), NodePos(3, 6)), 'abc') + self.assertEquals(pos.text(NodePos(3, 6), NodePos(4, 2)), 'c\r\ndef') + +class TestNodeRanges(unittest.TestCase): + def testAdd(self): + r = NodeRanges() + r.add(5, 10) + self.assertEquals(r._offsets, [5,11]) + r.add(15, 20) + self.assertEquals(r._offsets, [5,11,15,21]) + r.add(21,22) + self.assertEquals(r._offsets, [5,11,15,23]) + r.add(4,5) + self.assertEquals(r._offsets, [4,11,15,23]) + r.add(9,11) + self.assertEquals(r._offsets, [4,12,15,23]) + r.add(10,20) + self.assertEquals(r._offsets, [4,23]) + r.add(4,22) + self.assertEquals(r._offsets, [4,23]) + r.add(30,30) + self.assertEquals(r._offsets, [4,23,30,31]) + def testHas(self): + r = NodeRanges() + r.add(5, 10) + r.add(15, 15) + assert not r.has(4) + assert r.has(5) + assert r.has(6) + assert r.has(9) + assert r.has(10) + assert not r.has(14) + assert r.has(15) + assert not r.has(16) + +class TestCompilableUnit(unittest.TestCase): + def test(self): + tests = ( + ('var s = "', False), + ('bogon()', True), + ('int syntax_error;', True), + ('a /* b', False), + ('re = /.*', False), + ('{ // missing curly', False) + ) + for text, expected in tests: + encountered = is_compilable_unit(text, JSVersion.default()) + self.assertEquals(encountered, expected) + # NOTE: This seems like a bug. + self.assert_(is_compilable_unit("/* test", JSVersion.default())) + +class TestLineOffset(unittest.TestCase): + def testErrorPos(self): + def geterror(script, startpos): + errors = [] + def onerror(line, col, msg): + errors.append((line, col, msg)) + parse(script, None, onerror, startpos) + self.assertEquals(len(errors), 1) + return errors[0] + self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error')) + self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error')) + self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error')) + self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error')) + def testNodePos(self): + def getnodepos(script, startpos): + root = parse(script, None, None, startpos) + self.assertEquals(root.kind, tok.LC) + var, = root.kids + self.assertEquals(var.kind, tok.VAR) + return var.start_pos() + self.assertEquals(getnodepos('var x;', None), NodePos(0,0)) + self.assertEquals(getnodepos(' var x;', None), NodePos(0,1)) + self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2,1)) + self.assertEquals(getnodepos('var x;', NodePos(3,4)), NodePos(3,4)) + self.assertEquals(getnodepos(' var x;', NodePos(3,4)), NodePos(3,5)) + self.assertEquals(getnodepos('\n\n var x;', NodePos(3,4)), NodePos(5,1)) + def testComments(self): + def testcomment(comment, startpos, expectedpos): + root = parse(comment, None, None, startpos) + comment, = findcomments(comment, root, startpos) + self.assertEquals(comment.start_pos(), expectedpos) + for comment in ('/*comment*/', '//comment'): + testcomment(comment, None, NodePos(0,0)) + testcomment(' %s' % comment, None, NodePos(0,1)) + testcomment('\n\n %s' % comment, None, NodePos(2,1)) + testcomment('%s' % comment, NodePos(3,4), NodePos(3,4)) + testcomment(' %s' % comment, NodePos(3,4), NodePos(3,5)) + testcomment('\n\n %s' % comment, NodePos(3,4), NodePos(5,1)) + +if __name__ == '__main__': + unittest.main() + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/lint.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/lint.py new file mode 100644 index 0000000..f2967a4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/javascriptlint/lint.py @@ -0,0 +1,713 @@ +#!/usr/bin/env python +# vim: ts=4 sw=4 expandtab +import os.path +import re + +import conf +import fs +import htmlparse +import jsparse +import visitation +import warnings +import unittest +import util + +from spidermonkey import tok, op + +_newline_kinds = ( + 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', + 'relop', 'hook', 'plus', 'minus', 'star', 'divop', 'eqop', 'shop', 'or', + 'and', 'bitor', 'bitxor', 'bitand', 'else', 'try' +) + +_globals = frozenset([ + 'Array', 'Boolean', 'Math', 'Number', 'String', 'RegExp', 'Script', 'Date', + 'isNaN', 'isFinite', 'parseFloat', 'parseInt', + 'eval', 'NaN', 'Infinity', + 'escape', 'unescape', 'uneval', + 'decodeURI', 'encodeURI', 'decodeURIComponent', 'encodeURIComponent', + 'Function', 'Object', + 'Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError', + 'SyntaxError', 'TypeError', 'URIError', + 'arguments', 'undefined' +]) + +def _find_function(node): + while node and node.kind != tok.FUNCTION: + node = node.parent + return node + +def _find_functions(node): + functions = [] + while node: + if node.kind == tok.FUNCTION: + functions.append(node) + node = node.parent + return functions + +def _parse_control_comment(comment): + """ Returns None or (keyword, parms) """ + atom = comment.atom.strip() + atom_lower = atom.lower() + if atom_lower.startswith('jsl:'): + control_comment = atom[4:] + elif atom.startswith('@') and atom.endswith('@'): + control_comment = atom[1:-1] + else: + return None + + control_comments = { + 'ignoreall': (False), + 'ignore': (False), + 'end': (False), + 'option explicit': (False), + 'import': (True), + 'fallthru': (False), + 'pass': (False), + 'declare': (True), + 'unused': (True), + 'content-type': (True), + } + if control_comment.lower() in control_comments: + keyword = control_comment.lower() + else: + keyword = control_comment.lower().split()[0] + if not keyword in control_comments: + return None + + parms = control_comment[len(keyword):].strip() + return (comment, keyword, parms) + +class Scope: + """ Outer-level scopes will never be associated with a node. + Inner-level scopes will always be associated with a node. + """ + def __init__(self): + self._parent = None + self._kids = [] + self._identifiers = {} + self._references = [] + self._unused = [] + self._node = None + def add_scope(self, node): + assert not node is None + self._kids.append(Scope()) + self._kids[-1]._parent = self + self._kids[-1]._node = node + return self._kids[-1] + def add_declaration(self, name, node, type_): + assert type_ in ('arg', 'function', 'var'), \ + 'Unrecognized identifier type: %s' % type_ + self._identifiers[name] = { + 'node': node, + 'type': type_ + } + def add_reference(self, name, node): + self._references.append((name, node)) + def set_unused(self, name, node): + self._unused.append((name, node)) + def get_identifier(self, name): + if name in self._identifiers: + return self._identifiers[name]['node'] + else: + return None + def get_identifier_type(self, name): + if name in self._identifiers: + return self._identifiers[name]['type'] + else: + return None + def get_identifiers(self): + "returns a list of names" + return self._identifiers.keys() + def resolve_identifier(self, name): + if name in self._identifiers: + return self, self._identifiers[name]['node'] + if self._parent: + return self._parent.resolve_identifier(name) + return None + def get_identifier_warnings(self): + """ Returns a tuple of unreferenced and undeclared, where each is a list + of (scope, name, node) tuples. + """ + unreferenced = {} + undeclared = [] + obstructive = [] + self._find_warnings(unreferenced, undeclared, obstructive, False) + + # Convert "unreferenced" from a dictionary of: + # { (scope, name): node } + # to a list of: + # [ (scope, name, node) ] + # sorted by node position. + unreferenced = [(key[0], key[1], node) for key, node + in unreferenced.items()] + unreferenced.sort(key=lambda x: x[2].start_pos()) + + return { + 'unreferenced': unreferenced, + 'undeclared': undeclared, + 'obstructive': obstructive, + } + def _find_warnings(self, unreferenced, undeclared, obstructive, + is_in_with_scope): + """ unreferenced is a dictionary, such that: + (scope, name): node + } + undeclared is a list, such that: [ + (scope, name, node) + ] + obstructive is a list, such that: [ + (scope, name, node) + ] + """ + if self._node and self._node.kind == tok.WITH: + is_in_with_scope = True + + # Add all identifiers as unreferenced. Children scopes will remove + # them if they are referenced. Variables need to be keyed by name + # instead of node, because function parameters share the same node. + for name, info in self._identifiers.items(): + unreferenced[(self, name)] = info['node'] + + # Check for variables that hide an identifier in a parent scope. + if self._parent: + for name, info in self._identifiers.items(): + if self._parent.resolve_identifier(name): + obstructive.append((self, name, info['node'])) + + # Remove all declared variables from the "unreferenced" set; add all + # undeclared variables to the "undeclared" list. + for name, node in self._references: + resolved = self.resolve_identifier(name) + if resolved: + # Make sure this isn't an assignment. + if node.parent.kind in (tok.ASSIGN, tok.INC, tok.DEC) and \ + node.node_index == 0 and \ + node.parent.parent.kind == tok.SEMI: + continue + unreferenced.pop((resolved[0], name), None) + else: + # with statements cannot have undeclared identifiers. + if not is_in_with_scope: + undeclared.append((self, name, node)) + + # Remove all variables that have been set as "unused". + for name, node in self._unused: + resolved = self.resolve_identifier(name) + if resolved: + unreferenced.pop((resolved[0], name), None) + else: + undeclared.append((self, name, node)) + + for child in self._kids: + child._find_warnings(unreferenced, undeclared, obstructive, + is_in_with_scope) + def find_scope(self, node): + for kid in self._kids: + scope = kid.find_scope(node) + if scope: + return scope + + # Always add it to the outer scope. + if not self._parent: + assert not self._node + return self + + # Conditionally add it to an inner scope. + assert self._node + if (node.start_pos() >= self._node.start_pos() and \ + node.end_pos() <= self._node.end_pos()): + return self + +class _Script: + def __init__(self): + self._imports = set() + self.scope = Scope() + def importscript(self, script): + self._imports.add(script) + def hasglobal(self, name): + return not self._findglobal(name, set()) is None + def _findglobal(self, name, searched): + """ searched is a set of all searched scripts """ + # Avoid recursion. + if self in searched: + return + + # Check this scope. + if self.scope.get_identifier(name): + return self + searched.add(self) + + # Search imported scopes. + for script in self._imports: + global_ = script._findglobal(name, searched) + if global_: + return global_ + +def _findhtmlscripts(contents, default_version): + starttag = None + nodepos = jsparse.NodePositions(contents) + for tag in htmlparse.findscripttags(contents): + if tag['type'] == 'start': + # Ignore nested start tags. + if not starttag: + jsversion = util.JSVersion.fromattr(tag['attr'], default_version) + starttag = dict(tag, jsversion=jsversion) + src = tag['attr'].get('src') + if src: + yield { + 'type': 'external', + 'jsversion': jsversion, + 'src': src, + } + elif tag['type'] == 'end': + if not starttag: + continue + + # htmlparse returns 1-based line numbers. Calculate the + # position of the script's contents. + tagpos = jsparse.NodePos(starttag['lineno']-1, starttag['offset']) + tagoffset = nodepos.to_offset(tagpos) + startoffset = tagoffset + starttag['len'] + startpos = nodepos.from_offset(startoffset) + endpos = jsparse.NodePos(tag['lineno']-1, tag['offset']) + endoffset = nodepos.to_offset(endpos) + script = contents[startoffset:endoffset] + + if not jsparse.isvalidversion(starttag['jsversion']) or \ + jsparse.is_compilable_unit(script, starttag['jsversion']): + if script.strip(): + yield { + 'type': 'inline', + 'jsversion': starttag['jsversion'], + 'pos': startpos, + 'contents': script, + } + starttag = None + else: + assert False, 'Invalid internal tag type %s' % tag['type'] + +def lint_files(paths, lint_error, conf=conf.Conf(), printpaths=True): + def lint_file(path, kind, jsversion): + def import_script(import_path, jsversion): + # The user can specify paths using backslashes (such as when + # linting Windows scripts on a posix environment. + import_path = import_path.replace('\\', os.sep) + import_path = os.path.join(os.path.dirname(path), import_path) + return lint_file(import_path, 'js', jsversion) + def _lint_error(*args): + return lint_error(normpath, *args) + + normpath = fs.normpath(path) + if normpath in lint_cache: + return lint_cache[normpath] + if printpaths: + print normpath + contents = fs.readfile(path) + lint_cache[normpath] = _Script() + + script_parts = [] + if kind == 'js': + script_parts.append((None, jsversion or conf['default-version'], contents)) + elif kind == 'html': + assert jsversion is None + for script in _findhtmlscripts(contents, conf['default-version']): + # TODO: Warn about foreign languages. + if not script['jsversion']: + continue + + if script['type'] == 'external': + other = import_script(script['src'], script['jsversion']) + lint_cache[normpath].importscript(other) + elif script['type'] == 'inline': + script_parts.append((script['pos'], script['jsversion'], + script['contents'])) + else: + assert False, 'Invalid internal script type %s' % \ + script['type'] + else: + assert False, 'Unsupported file kind: %s' % kind + + _lint_script_parts(script_parts, lint_cache[normpath], _lint_error, conf, import_script) + return lint_cache[normpath] + + lint_cache = {} + for path in paths: + ext = os.path.splitext(path)[1] + if ext.lower() in ['.htm', '.html']: + lint_file(path, 'html', None) + else: + lint_file(path, 'js', None) + +def _lint_script_part(scriptpos, jsversion, script, script_cache, conf, + ignores, report_native, report_lint, import_callback): + def parse_error(row, col, msg): + if not msg in ('anon_no_return_value', 'no_return_value', + 'redeclared_var', 'var_hides_arg'): + parse_errors.append((jsparse.NodePos(row, col), msg)) + + def report(node, errname, pos=None, **errargs): + if errname == 'empty_statement' and node.kind == tok.LC: + for pass_ in passes: + if pass_.start_pos() > node.start_pos() and \ + pass_.end_pos() < node.end_pos(): + passes.remove(pass_) + return + + if errname == 'missing_break': + # Find the end of the previous case/default and the beginning of + # the next case/default. + assert node.kind in (tok.CASE, tok.DEFAULT) + prevnode = node.parent.kids[node.node_index-1] + expectedfallthru = prevnode.end_pos(), node.start_pos() + elif errname == 'missing_break_for_last_case': + # Find the end of the current case/default and the end of the + # switch. + assert node.parent.kind == tok.LC + expectedfallthru = node.end_pos(), node.parent.end_pos() + else: + expectedfallthru = None + + if expectedfallthru: + start, end = expectedfallthru + for fallthru in fallthrus: + # Look for a fallthru between the end of the current case or + # default statement and the beginning of the next token. + if fallthru.start_pos() > start and fallthru.end_pos() < end: + fallthrus.remove(fallthru) + return + + report_lint(node, errname, pos, **errargs) + + parse_errors = [] + declares = [] + unused_identifiers = [] + import_paths = [] + fallthrus = [] + passes = [] + + node_positions = jsparse.NodePositions(script, scriptpos) + possible_comments = jsparse.findpossiblecomments(script, node_positions) + + # Check control comments for the correct version. It may be this comment + # isn't a valid comment (for example, it might be inside a string literal) + # After parsing, validate that it's legitimate. + jsversionnode = None + for comment in possible_comments: + cc = _parse_control_comment(comment) + if cc: + node, keyword, parms = cc + if keyword == 'content-type': + ccversion = util.JSVersion.fromtype(parms) + if ccversion: + jsversion = ccversion + jsversionnode = node + else: + report(node, 'unsupported_version', version=parms) + + if not jsparse.isvalidversion(jsversion): + report_lint(jsversionnode, 'unsupported_version', scriptpos, + version=jsversion.version) + return + + root = jsparse.parse(script, jsversion, parse_error, scriptpos) + if not root: + # Report errors and quit. + for pos, msg in parse_errors: + report_native(pos, msg) + return + + comments = jsparse.filtercomments(possible_comments, node_positions, root) + + if jsversionnode is not None and jsversionnode not in comments: + # TODO + report(jsversionnode, 'incorrect_version') + + start_ignore = None + for comment in comments: + cc = _parse_control_comment(comment) + if cc: + node, keyword, parms = cc + if keyword == 'declare': + if not util.isidentifier(parms): + report(node, 'jsl_cc_not_understood') + else: + declares.append((parms, node)) + elif keyword == 'unused': + if not util.isidentifier(parms): + report(node, 'jsl_cc_not_understood') + else: + unused_identifiers.append((parms, node)) + elif keyword == 'ignore': + if start_ignore: + report(node, 'mismatch_ctrl_comments') + else: + start_ignore = node + elif keyword == 'end': + if start_ignore: + ignores.append((start_ignore.start_pos(), node.end_pos())) + start_ignore = None + else: + report(node, 'mismatch_ctrl_comments') + elif keyword == 'import': + if not parms: + report(node, 'jsl_cc_not_understood') + else: + import_paths.append(parms) + elif keyword == 'fallthru': + fallthrus.append(node) + elif keyword == 'pass': + passes.append(node) + else: + if comment.opcode == 'c_comment': + # Look for nested C-style comments. + nested_comment = comment.atom.find('/*') + if nested_comment < 0 and comment.atom.endswith('/'): + nested_comment = len(comment.atom) - 1 + # Report at the actual error of the location. Add two + # characters for the opening two characters. + if nested_comment >= 0: + pos = node_positions.from_offset(node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) + report(comment, 'nested_comment', pos=pos) + if comment.atom.lower().startswith('jsl:'): + report(comment, 'jsl_cc_not_understood') + elif comment.atom.startswith('@'): + report(comment, 'legacy_cc_not_understood') + if start_ignore: + report(start_ignore, 'mismatch_ctrl_comments') + + # Wait to report parse errors until loading jsl:ignore directives. + for pos, msg in parse_errors: + report_native(pos, msg) + + # Find all visitors and convert them into "onpush" callbacks that call "report" + visitors = { + 'push': warnings.make_visitors() + } + for event in visitors: + for kind, callbacks in visitors[event].items(): + visitors[event][kind] = [_getreporter(callback, report) for callback in callbacks] + + # Push the scope/variable checks. + visitation.make_visitors(visitors, [_get_scope_checks(script_cache.scope, report)]) + + # kickoff! + _lint_node(root, visitors) + + for fallthru in fallthrus: + report(fallthru, 'invalid_fallthru') + for fallthru in passes: + report(fallthru, 'invalid_pass') + + # Process imports by copying global declarations into the universal scope. + for path in import_paths: + script_cache.importscript(import_callback(path, jsversion)) + + for name, node in declares: + declare_scope = script_cache.scope.find_scope(node) + _warn_or_declare(declare_scope, name, 'var', node, report) + + for name, node in unused_identifiers: + unused_scope = script_cache.scope.find_scope(node) + unused_scope.set_unused(name, node) + +def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): + def report_lint(node, errname, pos=None, **errargs): + errdesc = warnings.format_error(errname, **errargs) + _report(pos or node.start_pos(), errname, errdesc, True) + + def report_native(pos, errname): + # TODO: Format the error. + _report(pos, errname, errname, False) + + def _report(pos, errname, errdesc, require_key): + try: + if not conf[errname]: + return + except KeyError, err: + if require_key: + raise + + for start, end in ignores: + if pos >= start and pos <= end: + return + + return lint_error(pos.line, pos.col, errname, errdesc) + + for scriptpos, jsversion, script in script_parts: + ignores = [] + _lint_script_part(scriptpos, jsversion, script, script_cache, conf, ignores, + report_native, report_lint, import_callback) + + scope = script_cache.scope + identifier_warnings = scope.get_identifier_warnings() + for decl_scope, name, node in identifier_warnings['undeclared']: + if name in conf['declarations']: + continue + if name in _globals: + continue + if not script_cache.hasglobal(name): + report_lint(node, 'undeclared_identifier', name=name) + for ref_scope, name, node in identifier_warnings['unreferenced']: + # Ignore the outer scope. + if ref_scope != scope: + type_ = ref_scope.get_identifier_type(name) + if type_ == 'arg': + report_lint(node, 'unreferenced_argument', name=name) + elif type_ == 'function': + report_lint(node, 'unreferenced_function', name=name) + elif type_ == 'var': + report_lint(node, 'unreferenced_variable', name=name) + else: + assert False, 'Unrecognized identifier type: %s' % type_ + for ref_scope, name, node in identifier_warnings['obstructive']: + report_lint(node, 'identifier_hides_another', name=name) + +def _getreporter(visitor, report): + def onpush(node): + try: + ret = visitor(node) + assert ret is None, 'visitor should raise an exception, not return a value' + except warnings.LintWarning, warning: + # TODO: This is ugly hardcoding to improve the error positioning of + # "missing_semicolon" errors. + if visitor.warning in ('missing_semicolon', 'missing_semicolon_for_lambda'): + pos = warning.node.end_pos() + else: + pos = None + report(warning.node, visitor.warning, pos=pos, **warning.errargs) + return onpush + +def _warn_or_declare(scope, name, type_, node, report): + parent_scope, other = scope.resolve_identifier(name) or (None, None) + if other and parent_scope == scope: + # Only warn about duplications in this scope. + # Other scopes will be checked later. + if other.kind == tok.FUNCTION and name in other.fn_args: + report(node, 'var_hides_arg', name=name) + else: + report(node, 'redeclared_var', name=name) + else: + scope.add_declaration(name, node, type_) + +def _get_scope_checks(scope, report): + scopes = [scope] + + class scope_checks: + """ This is a non-standard visitation class to track scopes. The + docstring is unused since this class never throws lint errors. + """ + @visitation.visit('push', tok.NAME) + def _name(self, node): + if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC: + return # left side of object literal + if node.parent.kind == tok.VAR: + _warn_or_declare(scopes[-1], node.atom, 'var', node, report) + return + if node.parent.kind == tok.CATCH: + scopes[-1].add_declaration(node.atom, node, 'var') + scopes[-1].add_reference(node.atom, node) + + @visitation.visit('push', tok.FUNCTION) + def _push_func(self, node): + if node.fn_name: + _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) + self._push_scope(node) + for var_name in node.fn_args: + scopes[-1].add_declaration(var_name, node, 'arg') + + @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH) + def _push_scope(self, node): + scopes.append(scopes[-1].add_scope(node)) + + @visitation.visit('pop', tok.FUNCTION, tok.LEXICALSCOPE, tok.WITH) + def _pop_scope(self, node): + scopes.pop() + + return scope_checks + + +def _lint_node(node, visitors): + + for kind in (node.kind, (node.kind, node.opcode)): + if kind in visitors['push']: + for visitor in visitors['push'][kind]: + visitor(node) + + for child in node.kids: + if child: + _lint_node(child, visitors) + + for kind in (node.kind, (node.kind, node.opcode)): + if kind in visitors['pop']: + for visitor in visitors['pop'][kind]: + visitor(node) + + +class TestLint(unittest.TestCase): + def testFindScript(self): + html = """ + + +hi&b +a +ok& +.. +ok& + + +""" + scripts = [(x.get('src'), x.get('contents')) + for x in _findhtmlscripts(html, util.JSVersion.default())] + self.assertEquals(scripts, [ + ('test.js', None), + (None, "") + ]) + def testJSVersion(self): + def parsetag(starttag, default_version=None): + script, = _findhtmlscripts(starttag + '/**/', \ + default_version) + return script + + script = parsetag(' + * + * It does not cope with malformed comment hiding hacks where --> is hidden + * by C-style comments, or on a dirty line. Such cases are already broken. + */ +#define TSF_IN_HTML_COMMENT 0x2000 + +/* Ignore keywords and return TOK_NAME instead to the parser. */ +#define TSF_KEYWORD_IS_NAME 0x4000 + +/* Unicode separators that are treated as line terminators, in addition to \n, \r */ +#define LINE_SEPARATOR 0x2028 +#define PARA_SEPARATOR 0x2029 + +/* + * Create a new token stream, either from an input buffer or from a file. + * Return null on file-open or memory-allocation failure. + * + * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient + * memory in the current context's temp pool. This memory is deallocated via + * JS_ARENA_RELEASE() after parsing is finished. + */ +extern JSTokenStream * +js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, + const char *filename, uintN lineno, JSPrincipals *principals); + +extern JS_FRIEND_API(JSTokenStream *) +js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length); + +extern JS_FRIEND_API(JSTokenStream *) +js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); + +extern JS_FRIEND_API(JSBool) +js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); + +extern JS_FRIEND_API(int) +js_fgets(char *buf, int size, FILE *file); + +/* + * If the given char array forms JavaScript keyword, return corresponding + * token. Otherwise return TOK_EOF. + */ +extern JSTokenType +js_CheckKeyword(const jschar *chars, size_t length); + +#define js_IsKeyword(chars, length) \ + (js_CheckKeyword(chars, length) != TOK_EOF) + +/* + * Friend-exported API entry point to call a mapping function on each reserved + * identifier in the scanner's keyword table. + */ +extern JS_FRIEND_API(void) +js_MapKeywords(void (*mapfun)(const char *)); + +/* + * Report a compile-time error by its number, using ts or cg to show context. + * Return true for a warning, false for an error. + */ +extern JSBool +js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...); + +extern JSBool +js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, + uintN errorNumber, ...); + +/* Steal some JSREPORT_* bits (see jsapi.h) to tell handle's type. */ +#define JSREPORT_HANDLE 0x300 +#define JSREPORT_TS 0x000 +#define JSREPORT_CG 0x100 +#define JSREPORT_PN 0x200 + +/* + * Look ahead one token and return its type. + */ +extern JSTokenType +js_PeekToken(JSContext *cx, JSTokenStream *ts); + +extern JSTokenType +js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts); + +/* + * Get the next token from ts. + */ +extern JSTokenType +js_GetToken(JSContext *cx, JSTokenStream *ts); + +/* + * Push back the last scanned token onto ts. + */ +extern void +js_UngetToken(JSTokenStream *ts); + +/* + * Get the next token from ts if its type is tt. + */ +extern JSBool +js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt); + +JS_END_EXTERN_C + +#endif /* jsscan_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscope.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscope.c new file mode 100644 index 0000000..49b55a6 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscope.c @@ -0,0 +1,1776 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS symbol tables. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsarena.h" +#include "jsbit.h" +#include "jsclist.h" +#include "jsdhash.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsscope.h" +#include "jsstr.h" + +JSScope * +js_GetMutableScope(JSContext *cx, JSObject *obj) +{ + JSScope *scope, *newscope; + + scope = OBJ_SCOPE(obj); + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + if (scope->object == obj) + return scope; + newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), + obj); + if (!newscope) + return NULL; + JS_LOCK_SCOPE(cx, newscope); + obj->map = js_HoldObjectMap(cx, &newscope->map); + scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); + JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); + return newscope; +} + +/* + * JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized + * to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD + * entries, we use linear search and avoid allocating scope->table. + */ +#define SCOPE_HASH_THRESHOLD 6 +#define MIN_SCOPE_SIZE_LOG2 4 +#define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2) +#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) + +static void +InitMinimalScope(JSScope *scope) +{ + scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; + scope->entryCount = scope->removedCount = 0; + scope->table = NULL; + scope->lastProp = NULL; +} + +static JSBool +CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) +{ + int sizeLog2; + JSScopeProperty *sprop, **spp; + + JS_ASSERT(!scope->table); + JS_ASSERT(scope->lastProp); + + if (scope->entryCount > SCOPE_HASH_THRESHOLD) { + /* + * Ouch: calloc failed at least once already -- let's try again, + * overallocating to hold at least twice the current population. + */ + sizeLog2 = JS_CeilingLog2(2 * scope->entryCount); + scope->hashShift = JS_DHASH_BITS - sizeLog2; + } else { + JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2); + sizeLog2 = MIN_SCOPE_SIZE_LOG2; + } + + scope->table = (JSScopeProperty **) + calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *)); + if (!scope->table) { + if (report) + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); + + scope->hashShift = JS_DHASH_BITS - sizeLog2; + for (sprop = scope->lastProp; sprop; sprop = sprop->parent) { + spp = js_SearchScope(scope, sprop->id, JS_TRUE); + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + } + return JS_TRUE; +} + +JSScope * +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj) +{ + JSScope *scope; + + scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); + if (!scope) + return NULL; + + js_InitObjectMap(&scope->map, nrefs, ops, clasp); + scope->object = obj; + scope->flags = 0; + InitMinimalScope(scope); + +#ifdef JS_THREADSAFE + scope->ownercx = cx; + memset(&scope->lock, 0, sizeof scope->lock); + + /* + * Set u.link = NULL, not u.count = 0, in case the target architecture's + * null pointer has a non-zero integer representation. + */ + scope->u.link = NULL; + +#ifdef DEBUG + scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL; + scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0; +#endif +#endif + + JS_RUNTIME_METER(cx->runtime, liveScopes); + JS_RUNTIME_METER(cx->runtime, totalScopes); + return scope; +} + +#ifdef DEBUG_SCOPE_COUNT +extern void +js_unlog_scope(JSScope *scope); +#endif + +void +js_DestroyScope(JSContext *cx, JSScope *scope) +{ +#ifdef DEBUG_SCOPE_COUNT + js_unlog_scope(scope); +#endif + +#ifdef JS_THREADSAFE + /* Scope must be single-threaded at this point, so set scope->ownercx. */ + JS_ASSERT(scope->u.count == 0); + scope->ownercx = cx; + js_FinishLock(&scope->lock); +#endif + if (scope->table) + JS_free(cx, scope->table); + +#ifdef DEBUG + JS_LOCK_RUNTIME_VOID(cx->runtime, + cx->runtime->liveScopeProps -= scope->entryCount); +#endif + JS_RUNTIME_UNMETER(cx->runtime, liveScopes); + JS_free(cx, scope); +} + +#ifdef DUMP_SCOPE_STATS +typedef struct JSScopeStats { + jsrefcount searches; + jsrefcount steps; + jsrefcount hits; + jsrefcount misses; + jsrefcount stepHits; + jsrefcount stepMisses; + jsrefcount adds; + jsrefcount redundantAdds; + jsrefcount addFailures; + jsrefcount changeFailures; + jsrefcount compresses; + jsrefcount grows; + jsrefcount removes; + jsrefcount removeFrees; + jsrefcount uselessRemoves; + jsrefcount shrinks; +} JSScopeStats; + +JS_FRIEND_DATA(JSScopeStats) js_scope_stats; + +# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) +#else +# define METER(x) /* nothing */ +#endif + +/* + * Double hashing needs the second hash code to be relatively prime to table + * size, so we simply make hash2 odd. The inputs to multiplicative hash are + * the golden ratio, expressed as a fixed-point 32 bit fraction, and the int + * property index or named property's atom number (observe that most objects + * have either no indexed properties, or almost all indexed and a few names, + * so collisions between index and atom number are unlikely). + */ +#define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO) +#define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift)) +#define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) + +JS_FRIEND_API(JSScopeProperty **) +js_SearchScope(JSScope *scope, jsid id, JSBool adding) +{ + JSHashNumber hash0, hash1, hash2; + int hashShift, sizeLog2; + JSScopeProperty *stored, *sprop, **spp, **firstRemoved; + uint32 sizeMask; + + METER(searches); + if (!scope->table) { + /* Not enough properties to justify hashing: search from lastProp. */ + JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); + for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) { + if (sprop->id == id) { + METER(hits); + return spp; + } + } + METER(misses); + return spp; + } + + /* Compute the primary hash address. */ + hash0 = SCOPE_HASH0(id); + hashShift = scope->hashShift; + hash1 = SCOPE_HASH1(hash0, hashShift); + spp = scope->table + hash1; + + /* Miss: return space for a new entry. */ + stored = *spp; + if (SPROP_IS_FREE(stored)) { + METER(misses); + return spp; + } + + /* Hit: return entry. */ + sprop = SPROP_CLEAR_COLLISION(stored); + if (sprop && sprop->id == id) { + METER(hits); + return spp; + } + + /* Collision: double hash. */ + sizeLog2 = JS_DHASH_BITS - hashShift; + hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift); + sizeMask = JS_BITMASK(sizeLog2); + + /* Save the first removed entry pointer so we can recycle it if adding. */ + if (SPROP_IS_REMOVED(stored)) { + firstRemoved = spp; + } else { + firstRemoved = NULL; + if (adding && !SPROP_HAD_COLLISION(stored)) + SPROP_FLAG_COLLISION(spp, sprop); + } + + for (;;) { + METER(steps); + hash1 -= hash2; + hash1 &= sizeMask; + spp = scope->table + hash1; + + stored = *spp; + if (SPROP_IS_FREE(stored)) { + METER(stepMisses); + return (adding && firstRemoved) ? firstRemoved : spp; + } + + sprop = SPROP_CLEAR_COLLISION(stored); + if (sprop && sprop->id == id) { + METER(stepHits); + return spp; + } + + if (SPROP_IS_REMOVED(stored)) { + if (!firstRemoved) + firstRemoved = spp; + } else { + if (adding && !SPROP_HAD_COLLISION(stored)) + SPROP_FLAG_COLLISION(spp, sprop); + } + } + + /* NOTREACHED */ + return NULL; +} + +static JSBool +ChangeScope(JSContext *cx, JSScope *scope, int change) +{ + int oldlog2, newlog2; + uint32 oldsize, newsize, nbytes; + JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop; + + /* Grow, shrink, or compress by changing scope->table. */ + oldlog2 = JS_DHASH_BITS - scope->hashShift; + newlog2 = oldlog2 + change; + oldsize = JS_BIT(oldlog2); + newsize = JS_BIT(newlog2); + nbytes = SCOPE_TABLE_NBYTES(newsize); + table = (JSScopeProperty **) calloc(nbytes, 1); + if (!table) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + /* Now that we have a new table allocated, update scope members. */ + scope->hashShift = JS_DHASH_BITS - newlog2; + scope->removedCount = 0; + oldtable = scope->table; + scope->table = table; + + /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ + cx->runtime->gcMallocBytes += nbytes; + + /* Copy only live entries, leaving removed and free ones behind. */ + for (oldspp = oldtable; oldsize != 0; oldspp++) { + sprop = SPROP_FETCH(oldspp); + if (sprop) { + spp = js_SearchScope(scope, sprop->id, JS_TRUE); + JS_ASSERT(SPROP_IS_FREE(*spp)); + *spp = sprop; + } + oldsize--; + } + + /* Finally, free the old table storage. */ + JS_free(cx, oldtable); + return JS_TRUE; +} + +/* + * Take care to exclude the mark and duplicate bits, in case we're called from + * the GC, or we are searching for a property that has not yet been flagged as + * a duplicate when making a duplicate formal parameter. + */ +#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE) + +JS_STATIC_DLL_CALLBACK(JSDHashNumber) +js_HashScopeProperty(JSDHashTable *table, const void *key) +{ + const JSScopeProperty *sprop = (const JSScopeProperty *)key; + JSDHashNumber hash; + JSPropertyOp gsop; + + /* Accumulate from least to most random so the low bits are most random. */ + hash = 0; + gsop = sprop->getter; + if (gsop) + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; + gsop = sprop->setter; + if (gsop) + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; + + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) + ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); + + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->attrs; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->shortid; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->slot; + hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->id; + return hash; +} + +#define SPROP_MATCH(sprop, child) \ + SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \ + (child)->slot, (child)->attrs, (child)->flags, \ + (child)->shortid) + +#define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \ + aflags, ashortid) \ + ((sprop)->id == (aid) && \ + SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ + aflags, ashortid)) + +#define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ + aflags, ashortid) \ + ((sprop)->getter == (agetter) && \ + (sprop)->setter == (asetter) && \ + (sprop)->slot == (aslot) && \ + (sprop)->attrs == (aattrs) && \ + (((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \ + (sprop)->shortid == (ashortid)) + +JS_STATIC_DLL_CALLBACK(JSBool) +js_MatchScopeProperty(JSDHashTable *table, + const JSDHashEntryHdr *hdr, + const void *key) +{ + const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr; + const JSScopeProperty *sprop = entry->child; + const JSScopeProperty *kprop = (const JSScopeProperty *)key; + + return SPROP_MATCH(sprop, kprop); +} + +static const JSDHashTableOps PropertyTreeHashOps = { + JS_DHashAllocTable, + JS_DHashFreeTable, + JS_DHashGetKeyStub, + js_HashScopeProperty, + js_MatchScopeProperty, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +/* + * A property tree node on rt->propertyFreeList overlays the following prefix + * struct on JSScopeProperty. + */ +typedef struct FreeNode { + jsid id; + JSScopeProperty *next; + JSScopeProperty **prevp; +} FreeNode; + +#define FREENODE(sprop) ((FreeNode *) (sprop)) + +#define FREENODE_INSERT(list, sprop) \ + JS_BEGIN_MACRO \ + FREENODE(sprop)->next = (list); \ + FREENODE(sprop)->prevp = &(list); \ + if (list) \ + FREENODE(list)->prevp = &FREENODE(sprop)->next; \ + (list) = (sprop); \ + JS_END_MACRO + +#define FREENODE_REMOVE(sprop) \ + JS_BEGIN_MACRO \ + *FREENODE(sprop)->prevp = FREENODE(sprop)->next; \ + if (FREENODE(sprop)->next) \ + FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \ + JS_END_MACRO + +/* NB: Called with the runtime lock held. */ +static JSScopeProperty * +NewScopeProperty(JSRuntime *rt) +{ + JSScopeProperty *sprop; + + sprop = rt->propertyFreeList; + if (sprop) { + FREENODE_REMOVE(sprop); + } else { + JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *, + &rt->propertyArenaPool, + sizeof(JSScopeProperty)); + if (!sprop) + return NULL; + } + + JS_RUNTIME_METER(rt, livePropTreeNodes); + JS_RUNTIME_METER(rt, totalPropTreeNodes); + return sprop; +} + +#define CHUNKY_KIDS_TAG ((jsuword)1) +#define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG) +#define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \ + ((jsuword)(kids) & ~CHUNKY_KIDS_TAG)) +#define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \ + ((jsuword)(chunk) | CHUNKY_KIDS_TAG)) +#define MAX_KIDS_PER_CHUNK 10 + +typedef struct PropTreeKidsChunk PropTreeKidsChunk; + +struct PropTreeKidsChunk { + JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; + PropTreeKidsChunk *next; +}; + +static PropTreeKidsChunk * +NewPropTreeKidsChunk(JSRuntime *rt) +{ + PropTreeKidsChunk *chunk; + + chunk = calloc(1, sizeof *chunk); + if (!chunk) + return NULL; + JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0); + JS_RUNTIME_METER(rt, propTreeKidsChunks); + return chunk; +} + +static void +DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) +{ + JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); + free(chunk); +} + +/* NB: Called with the runtime lock held. */ +static JSBool +InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, + JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) +{ + JSPropertyTreeEntry *entry; + JSScopeProperty **childp, *kids, *sprop; + PropTreeKidsChunk *chunk, **chunkp; + uintN i; + + JS_ASSERT(!parent || child->parent != parent); + + if (!parent) { + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + if (!entry) + return JS_FALSE; + childp = &entry->child; + sprop = *childp; + if (!sprop) { + *childp = child; + } else { + /* + * A "Duplicate child" case. + * + * We can't do away with child, as at least one live scope entry + * still points at it. What's more, that scope's lastProp chains + * through an ancestor line to reach child, and js_Enumerate and + * others count on this linkage. We must leave child out of the + * hash table, and not require it to be there when we eventually + * GC it (see RemovePropertyTreeChild, below). + * + * It is necessary to leave the duplicate child out of the hash + * table to preserve entry uniqueness. It is safe to leave the + * child out of the hash table (unlike the duplicate child cases + * below), because the child's parent link will be null, which + * can't dangle. + */ + JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child)); + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + } else { + childp = &parent->kids; + kids = *childp; + if (kids) { + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + childp = &chunk->kids[i]; + sprop = *childp; + if (!sprop) + goto insert; + + JS_ASSERT(sprop != child); + if (SPROP_MATCH(sprop, child)) { + /* + * Duplicate child, see comment above. In this + * case, we must let the duplicate be inserted at + * this level in the tree, so we keep iterating, + * looking for an empty slot in which to insert. + */ + JS_ASSERT(sprop != child); + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + } + chunkp = &chunk->next; + } while ((chunk = *chunkp) != NULL); + + if (sweptChunk) { + chunk = sweptChunk; + } else { + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_FALSE; + } + *chunkp = chunk; + childp = &chunk->kids[0]; + } else { + sprop = kids; + JS_ASSERT(sprop != child); + if (SPROP_MATCH(sprop, child)) { + /* + * Duplicate child, see comment above. Once again, we + * must let duplicates created by deletion pile up in a + * kids-chunk-list, in order to find them when sweeping + * and thereby avoid dangling parent pointers. + */ + JS_RUNTIME_METER(rt, duplicatePropTreeNodes); + } + if (sweptChunk) { + chunk = sweptChunk; + } else { + chunk = NewPropTreeKidsChunk(rt); + if (!chunk) + return JS_FALSE; + } + parent->kids = CHUNK_TO_KIDS(chunk); + chunk->kids[0] = sprop; + childp = &chunk->kids[1]; + } + } + insert: + *childp = child; + } + + child->parent = parent; + return JS_TRUE; +} + +/* NB: Called with the runtime lock held. */ +static PropTreeKidsChunk * +RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) +{ + JSPropertyTreeEntry *entry; + JSScopeProperty *parent, *kids, *kid; + PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; + uintN i, j; + + parent = child->parent; + if (!parent) { + /* + * Don't remove child if it is not in rt->propertyTreeHash, but only + * matches a root child in the table that has compatible members. See + * the "Duplicate child" comments in InsertPropertyTreeChild, above. + */ + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP); + + if (entry->child == child) + JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr); + } else { + kids = parent->kids; + if (KIDS_IS_CHUNKY(kids)) { + list = chunk = KIDS_TO_CHUNK(kids); + chunkp = &list; + + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + if (chunk->kids[i] == child) { + lastChunk = chunk; + if (!lastChunk->next) { + j = i + 1; + } else { + j = 0; + do { + chunkp = &lastChunk->next; + lastChunk = *chunkp; + } while (lastChunk->next); + } + for (; j < MAX_KIDS_PER_CHUNK; j++) { + if (!lastChunk->kids[j]) + break; + } + --j; + if (chunk != lastChunk || j > i) + chunk->kids[i] = lastChunk->kids[j]; + lastChunk->kids[j] = NULL; + if (j == 0) { + *chunkp = NULL; + if (!list) + parent->kids = NULL; + return lastChunk; + } + return NULL; + } + } + + chunkp = &chunk->next; + } while ((chunk = *chunkp) != NULL); + } else { + kid = kids; + if (kid == child) + parent->kids = NULL; + } + } + return NULL; +} + +/* + * Called *without* the runtime lock held, this function acquires that lock + * only when inserting a new child. Thus there may be races to find or add + * a node that result in duplicates. We expect such races to be rare! + */ +static JSScopeProperty * +GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, + JSScopeProperty *child) +{ + JSRuntime *rt; + JSPropertyTreeEntry *entry; + JSScopeProperty *sprop; + PropTreeKidsChunk *chunk; + uintN i; + + rt = cx->runtime; + if (!parent) { + JS_LOCK_RUNTIME(rt); + + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + if (!entry) + goto out_of_memory; + + sprop = entry->child; + if (sprop) + goto out; + } else { + /* + * Because chunks are appended at the end and never deleted except by + * the GC, we can search without taking the runtime lock. We may miss + * a matching sprop added by another thread, and make a duplicate one, + * but that is an unlikely, therefore small, cost. The property tree + * has extremely low fan-out below its root in popular embeddings with + * real-world workloads. + * + * If workload changes so as to increase fan-out significantly below + * the property tree root, we'll want to add another tag bit stored in + * parent->kids that indicates a JSDHashTable pointer. + */ + entry = NULL; + sprop = parent->kids; + if (sprop) { + if (KIDS_IS_CHUNKY(sprop)) { + chunk = KIDS_TO_CHUNK(sprop); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + sprop = chunk->kids[i]; + if (!sprop) + goto not_found; + + if (SPROP_MATCH(sprop, child)) + return sprop; + } + } while ((chunk = chunk->next) != NULL); + } else { + if (SPROP_MATCH(sprop, child)) + return sprop; + } + } + + not_found: + JS_LOCK_RUNTIME(rt); + } + + sprop = NewScopeProperty(rt); + if (!sprop) + goto out_of_memory; + + sprop->id = child->id; + sprop->getter = child->getter; + sprop->setter = child->setter; + sprop->slot = child->slot; + sprop->attrs = child->attrs; + sprop->flags = child->flags; + sprop->shortid = child->shortid; + sprop->parent = sprop->kids = NULL; + if (!parent) { + entry->child = sprop; + } else { + if (!InsertPropertyTreeChild(rt, parent, sprop, NULL)) + goto out_of_memory; + } + +out: + JS_UNLOCK_RUNTIME(rt); + return sprop; + +out_of_memory: + JS_UNLOCK_RUNTIME(rt); + JS_ReportOutOfMemory(cx); + return NULL; +} + +#ifdef DEBUG_notbrendan +#define CHECK_ANCESTOR_LINE(scope, sparse) \ + JS_BEGIN_MACRO \ + if ((scope)->table) CheckAncestorLine(scope, sparse); \ + JS_END_MACRO + +static void +CheckAncestorLine(JSScope *scope, JSBool sparse) +{ + uint32 size; + JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; + uint32 entryCount, ancestorCount; + + ancestorLine = SCOPE_LAST_PROP(scope); + if (ancestorLine) + JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine)); + + entryCount = 0; + size = SCOPE_CAPACITY(scope); + start = scope->table; + for (spp = start, end = start + size; spp < end; spp++) { + sprop = SPROP_FETCH(spp); + if (sprop) { + entryCount++; + for (aprop = ancestorLine; aprop; aprop = aprop->parent) { + if (aprop == sprop) + break; + } + JS_ASSERT(aprop); + } + } + JS_ASSERT(entryCount == scope->entryCount); + + ancestorCount = 0; + for (sprop = ancestorLine; sprop; sprop = sprop->parent) { + if (SCOPE_HAD_MIDDLE_DELETE(scope) && + !SCOPE_HAS_PROPERTY(scope, sprop)) { + JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); + continue; + } + ancestorCount++; + } + JS_ASSERT(ancestorCount == scope->entryCount); +} +#else +#define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */ +#endif + +static void +ReportReadOnlyScope(JSContext *cx, JSScope *scope) +{ + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object)); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, + str + ? JS_GetStringBytes(str) + : LOCKED_OBJ_GET_CLASS(scope->object)->name); +} + +JSScopeProperty * +js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid) +{ + JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; + uint32 size, splen, i; + int change; + JSTempValueRooter tvr; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* + * You can't add properties to a sealed scope. But note well that you can + * change property attributes in a sealed scope, even though that replaces + * a JSScopeProperty * in the scope's hash table -- but no id is added, so + * the scope remains sealed. + */ + if (SCOPE_IS_SEALED(scope)) { + ReportReadOnlyScope(cx, scope); + return NULL; + } + + /* + * Normalize stub getter and setter values for faster is-stub testing in + * the SPROP_CALL_[GS]ETTER macros. + */ + if (getter == JS_PropertyStub) + getter = NULL; + if (setter == JS_PropertyStub) + setter = NULL; + + /* + * Search for id in order to claim its entry, allocating a property tree + * node if one doesn't already exist for our parameters. + */ + spp = js_SearchScope(scope, id, JS_TRUE); + sprop = overwriting = SPROP_FETCH(spp); + if (!sprop) { + /* Check whether we need to grow, if the load factor is >= .75. */ + size = SCOPE_CAPACITY(scope); + if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { + if (scope->removedCount >= size >> 2) { + METER(compresses); + change = 0; + } else { + METER(grows); + change = 1; + } + if (!ChangeScope(cx, scope, change) && + scope->entryCount + scope->removedCount == size - 1) { + METER(addFailures); + return NULL; + } + spp = js_SearchScope(scope, id, JS_TRUE); + JS_ASSERT(!SPROP_FETCH(spp)); + } + } else { + /* Property exists: js_SearchScope must have returned a valid entry. */ + JS_ASSERT(!SPROP_IS_REMOVED(*spp)); + + /* + * If all property members match, this is a redundant add and we can + * return early. If the caller wants to allocate a slot, but doesn't + * care which slot, copy sprop->slot into slot so we can match sprop, + * if all other members match. + */ + if (!(attrs & JSPROP_SHARED) && + slot == SPROP_INVALID_SLOT && + SPROP_HAS_VALID_SLOT(sprop, scope)) { + slot = sprop->slot; + } + if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, + flags, shortid)) { + METER(redundantAdds); + return sprop; + } + + /* + * Duplicate formal parameters require us to leave the old property + * on the ancestor line, so the decompiler can find it, even though + * its entry in scope->table is overwritten to point at a new property + * descending from the old one. The SPROP_IS_DUPLICATE flag helps us + * cope with the consequent disparity between ancestor line height and + * scope->entryCount. + */ + if (flags & SPROP_IS_DUPLICATE) { + sprop->flags |= SPROP_IS_DUPLICATE; + } else { + /* + * If we are clearing sprop to force an existing property to be + * overwritten (apart from a duplicate formal parameter), we must + * unlink it from the ancestor line at scope->lastProp, lazily if + * sprop is not lastProp. And we must remove the entry at *spp, + * precisely so the lazy "middle delete" fixup code further below + * won't find sprop in scope->table, in spite of sprop being on + * the ancestor line. + * + * When we finally succeed in finding or creating a new sprop + * and storing its pointer at *spp, we'll use the |overwriting| + * local saved when we first looked up id to decide whether we're + * indeed creating a new entry, or merely overwriting an existing + * property. + */ + if (sprop == SCOPE_LAST_PROP(scope)) { + do { + SCOPE_REMOVE_LAST_PROP(scope); + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + sprop = SCOPE_LAST_PROP(scope); + } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); + } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { + /* + * If we have no hash table yet, we need one now. The middle + * delete code is simple-minded that way! + */ + if (!scope->table) { + if (!CreateScopeTable(cx, scope, JS_TRUE)) + return NULL; + spp = js_SearchScope(scope, id, JS_TRUE); + sprop = overwriting = SPROP_FETCH(spp); + } + SCOPE_SET_MIDDLE_DELETE(scope); + } + } + + /* + * If we fail later on trying to find or create a new sprop, we will + * goto fail_overwrite and restore *spp from |overwriting|. Note that + * we don't bother to keep scope->removedCount in sync, because we'll + * fix up *spp and scope->entryCount shortly, no matter how control + * flow returns from this function. + */ + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, NULL); + scope->entryCount--; + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + sprop = NULL; + } + + if (!sprop) { + /* + * If properties were deleted from the middle of the list starting at + * scope->lastProp, we may need to fork the property tree and squeeze + * all deleted properties out of scope's ancestor line. Otherwise we + * risk adding a node with the same id as a "middle" node, violating + * the rule that properties along an ancestor line have distinct ids + * (unless flagged SPROP_IS_DUPLICATE). + */ + if (SCOPE_HAD_MIDDLE_DELETE(scope)) { + JS_ASSERT(scope->table); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + splen = scope->entryCount; + if (splen == 0) { + JS_ASSERT(scope->lastProp == NULL); + } else { + /* + * Enumerate live entries in scope->table using a temporary + * vector, by walking the (possibly sparse, due to deletions) + * ancestor line from scope->lastProp. + */ + spvec = (JSScopeProperty **) + JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); + if (!spvec) + goto fail_overwrite; + i = splen; + sprop = SCOPE_LAST_PROP(scope); + JS_ASSERT(sprop); + do { + /* + * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- + * the latter insists that sprop->id maps to sprop, while + * the former simply tests whether sprop->id is bound in + * scope. We must allow for duplicate formal parameters + * along the ancestor line, and fork them as needed. + */ + if (!SCOPE_GET_PROPERTY(scope, sprop->id)) + continue; + + JS_ASSERT(sprop != overwriting); + if (i == 0) { + /* + * If our original splen estimate, scope->entryCount, + * is less than the ancestor line height, there must + * be duplicate formal parameters in this (function + * object) scope. Count remaining ancestors in order + * to realloc spvec. + */ + JSScopeProperty *tmp = sprop; + do { + if (SCOPE_GET_PROPERTY(scope, tmp->id)) + i++; + } while ((tmp = tmp->parent) != NULL); + spp2 = (JSScopeProperty **) + JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i)); + if (!spp2) { + JS_free(cx, spvec); + goto fail_overwrite; + } + + spvec = spp2; + memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); + splen += i; + } + + spvec[--i] = sprop; + } while ((sprop = sprop->parent) != NULL); + JS_ASSERT(i == 0); + + /* + * Now loop forward through spvec, forking the property tree + * whenever we see a "parent gap" due to deletions from scope. + * NB: sprop is null on first entry to the loop body. + */ + do { + if (spvec[i]->parent == sprop) { + sprop = spvec[i]; + } else { + sprop = GetPropertyTreeChild(cx, sprop, spvec[i]); + if (!sprop) { + JS_free(cx, spvec); + goto fail_overwrite; + } + + spp2 = js_SearchScope(scope, sprop->id, JS_FALSE); + JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]); + SPROP_STORE_PRESERVING_COLLISION(spp2, sprop); + } + } while (++i < splen); + JS_free(cx, spvec); + + /* + * Now sprop points to the last property in scope, where the + * ancestor line from sprop to the root is dense w.r.t. scope: + * it contains no nodes not mapped by scope->table, apart from + * any stinking ECMA-mandated duplicate formal parameters. + */ + scope->lastProp = sprop; + CHECK_ANCESTOR_LINE(scope, JS_FALSE); + JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); + } + + SCOPE_CLR_MIDDLE_DELETE(scope); + } + + /* + * Aliases share another property's slot, passed in the |slot| param. + * Shared properties have no slot. Unshared properties that do not + * alias another property's slot get one here, but may lose it due to + * a JS_ClearScope call. + */ + if (!(flags & SPROP_IS_ALIAS)) { + if (attrs & JSPROP_SHARED) { + slot = SPROP_INVALID_SLOT; + } else { + /* + * We may have set slot from a nearly-matching sprop, above. + * If so, we're overwriting that nearly-matching sprop, so we + * can reuse its slot -- we don't need to allocate a new one. + * Callers should therefore pass SPROP_INVALID_SLOT for all + * non-alias, unshared property adds. + */ + if (slot != SPROP_INVALID_SLOT) + JS_ASSERT(overwriting); + else if (!js_AllocSlot(cx, scope->object, &slot)) + goto fail_overwrite; + } + } + + /* + * Check for a watchpoint on a deleted property; if one exists, change + * setter to js_watch_set. + * XXXbe this could get expensive with lots of watchpoints... + */ + if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && + js_FindWatchPoint(cx->runtime, scope, id)) { + JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); + setter = js_WrapWatchedSetter(cx, id, attrs, setter); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!setter) + goto fail_overwrite; + } + + /* Find or create a property tree node labeled by our arguments. */ + child.id = id; + child.getter = getter; + child.setter = setter; + child.slot = slot; + child.attrs = attrs; + child.flags = flags; + child.shortid = shortid; + sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); + if (!sprop) + goto fail_overwrite; + + /* Store the tree node pointer in the table entry for id. */ + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, sprop); + scope->entryCount++; + scope->lastProp = sprop; + CHECK_ANCESTOR_LINE(scope, JS_FALSE); + if (!overwriting) { + JS_RUNTIME_METER(cx->runtime, liveScopeProps); + JS_RUNTIME_METER(cx->runtime, totalScopeProps); + } + + /* + * If we reach the hashing threshold, try to allocate scope->table. + * If we can't (a rare event, preceded by swapping to death on most + * modern OSes), stick with linear search rather than whining about + * this little set-back. Therefore we must test !scope->table and + * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the + * entry count just reached the threshold. + */ + if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) + (void) CreateScopeTable(cx, scope, JS_FALSE); + } + + METER(adds); + return sprop; + +fail_overwrite: + if (overwriting) { + /* + * We may or may not have forked overwriting out of scope's ancestor + * line, so we must check (the alternative is to set a flag above, but + * that hurts the common, non-error case). If we did fork overwriting + * out, we'll add it back at scope->lastProp. This means enumeration + * order can change due to a failure to overwrite an id. + * XXXbe very minor incompatibility + */ + for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) { + if (!sprop) { + sprop = SCOPE_LAST_PROP(scope); + if (overwriting->parent == sprop) { + scope->lastProp = overwriting; + } else { + sprop = GetPropertyTreeChild(cx, sprop, overwriting); + if (sprop) { + JS_ASSERT(sprop != overwriting); + scope->lastProp = sprop; + } + overwriting = sprop; + } + break; + } + if (sprop == overwriting) + break; + } + if (overwriting) { + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); + scope->entryCount++; + } + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + } + METER(addFailures); + return NULL; +} + +JSScopeProperty * +js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter) +{ + JSScopeProperty child, *newsprop, **spp; + + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* Allow only shared (slot-less) => unshared (slot-full) transition. */ + attrs |= sprop->attrs & mask; + JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || + !(attrs & JSPROP_SHARED)); + if (getter == JS_PropertyStub) + getter = NULL; + if (setter == JS_PropertyStub) + setter = NULL; + if (sprop->attrs == attrs && + sprop->getter == getter && + sprop->setter == setter) { + return sprop; + } + + child.id = sprop->id; + child.getter = getter; + child.setter = setter; + child.slot = sprop->slot; + child.attrs = attrs; + child.flags = sprop->flags; + child.shortid = sprop->shortid; + + if (SCOPE_LAST_PROP(scope) == sprop) { + /* + * Optimize the case where the last property added to scope is changed + * to have a different attrs, getter, or setter. In the last property + * case, we need not fork the property tree. But since we do not call + * js_AddScopeProperty, we may need to allocate a new slot directly. + */ + if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { + JS_ASSERT(child.slot == SPROP_INVALID_SLOT); + if (!js_AllocSlot(cx, scope->object, &child.slot)) + return NULL; + } + + newsprop = GetPropertyTreeChild(cx, sprop->parent, &child); + if (newsprop) { + spp = js_SearchScope(scope, sprop->id, JS_FALSE); + JS_ASSERT(SPROP_FETCH(spp) == sprop); + + if (scope->table) + SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); + scope->lastProp = newsprop; + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + } + } else { + /* + * Let js_AddScopeProperty handle this |overwriting| case, including + * the conservation of sprop->slot (if it's valid). We must not call + * js_RemoveScopeProperty here, it will free a valid sprop->slot and + * js_AddScopeProperty won't re-allocate it. + */ + newsprop = js_AddScopeProperty(cx, scope, child.id, + child.getter, child.setter, child.slot, + child.attrs, child.flags, child.shortid); + } + +#ifdef DUMP_SCOPE_STATS + if (!newsprop) + METER(changeFailures); +#endif + return newsprop; +} + +JSBool +js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) +{ + JSScopeProperty **spp, *stored, *sprop; + uint32 size; + + JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + if (SCOPE_IS_SEALED(scope)) { + ReportReadOnlyScope(cx, scope); + return JS_FALSE; + } + METER(removes); + + spp = js_SearchScope(scope, id, JS_FALSE); + stored = *spp; + sprop = SPROP_CLEAR_COLLISION(stored); + if (!sprop) { + METER(uselessRemoves); + return JS_TRUE; + } + + /* Convert from a list to a hash so we can handle "middle deletes". */ + if (!scope->table && sprop != scope->lastProp) { + if (!CreateScopeTable(cx, scope, JS_TRUE)) + return JS_FALSE; + spp = js_SearchScope(scope, id, JS_FALSE); + stored = *spp; + sprop = SPROP_CLEAR_COLLISION(stored); + } + + /* First, if sprop is unshared and not cleared, free its slot number. */ + if (SPROP_HAS_VALID_SLOT(sprop, scope)) { + js_FreeSlot(cx, scope->object, sprop->slot); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); + } + + /* Next, remove id by setting its entry to a removed or free sentinel. */ + if (SPROP_HAD_COLLISION(stored)) { + JS_ASSERT(scope->table); + *spp = SPROP_REMOVED; + scope->removedCount++; + } else { + METER(removeFrees); + if (scope->table) + *spp = NULL; + } + scope->entryCount--; + JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps); + + /* Update scope->lastProp directly, or set its deferred update flag. */ + if (sprop == SCOPE_LAST_PROP(scope)) { + do { + SCOPE_REMOVE_LAST_PROP(scope); + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + sprop = SCOPE_LAST_PROP(scope); + } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); + } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { + SCOPE_SET_MIDDLE_DELETE(scope); + } + CHECK_ANCESTOR_LINE(scope, JS_TRUE); + + /* Last, consider shrinking scope's table if its load factor is <= .25. */ + size = SCOPE_CAPACITY(scope); + if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) { + METER(shrinks); + (void) ChangeScope(cx, scope, -1); + } + + return JS_TRUE; +} + +void +js_ClearScope(JSContext *cx, JSScope *scope) +{ + CHECK_ANCESTOR_LINE(scope, JS_TRUE); +#ifdef DEBUG + JS_LOCK_RUNTIME_VOID(cx->runtime, + cx->runtime->liveScopeProps -= scope->entryCount); +#endif + + if (scope->table) + free(scope->table); + SCOPE_CLR_MIDDLE_DELETE(scope); + InitMinimalScope(scope); + JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); +} + +void +js_MarkId(JSContext *cx, jsid id) +{ + if (JSID_IS_ATOM(id)) + GC_MARK_ATOM(cx, JSID_TO_ATOM(id)); + else if (JSID_IS_OBJECT(id)) + GC_MARK(cx, JSID_TO_OBJECT(id), "id"); + else + JS_ASSERT(JSID_IS_INT(id)); +} + +#if defined GC_MARK_DEBUG || defined DUMP_SCOPE_STATS +# include "jsprf.h" +#endif + +void +js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop) +{ + sprop->flags |= SPROP_MARK; + MARK_ID(cx, sprop->id); + +#if JS_HAS_GETTER_SETTER + if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { +#ifdef GC_MARK_DEBUG + char buf[64]; + char buf2[11]; + const char *id; + + if (JSID_IS_ATOM(sprop->id)) { + JSAtom *atom = JSID_TO_ATOM(sprop->id); + + id = (atom && ATOM_IS_STRING(atom)) + ? JS_GetStringBytes(ATOM_TO_STRING(atom)) + : "unknown"; + } else if (JSID_IS_INT(sprop->id)) { + JS_snprintf(buf2, sizeof buf2, "%d", JSID_TO_INT(sprop->id)); + id = buf2; + } else { + id = ""; + } +#endif + + if (sprop->attrs & JSPROP_GETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_getter_str); +#endif + GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->getter), buf); + } + if (sprop->attrs & JSPROP_SETTER) { +#ifdef GC_MARK_DEBUG + JS_snprintf(buf, sizeof buf, "%s %s", + id, js_setter_str); +#endif + GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->setter), buf); + } + } +#endif /* JS_HAS_GETTER_SETTER */ +} + +#ifdef DUMP_SCOPE_STATS + +#include +#include + +uint32 js_nkids_max; +uint32 js_nkids_sum; +double js_nkids_sqsum; +uint32 js_nkids_hist[11]; + +static void +MeterKidCount(uintN nkids) +{ + if (nkids) { + js_nkids_sum += nkids; + js_nkids_sqsum += (double)nkids * nkids; + if (nkids > js_nkids_max) + js_nkids_max = nkids; + } + js_nkids_hist[JS_MIN(nkids, 10)]++; +} + +static void +MeterPropertyTree(JSScopeProperty *node) +{ + uintN i, nkids; + JSScopeProperty *kids, *kid; + PropTreeKidsChunk *chunk; + + nkids = 0; + kids = node->kids; + if (kids) { + if (KIDS_IS_CHUNKY(kids)) { + for (chunk = KIDS_TO_CHUNK(kids); chunk; chunk = chunk->next) { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + MeterPropertyTree(kid); + nkids++; + } + } + } else { + MeterPropertyTree(kids); + nkids = 1; + } + } + + MeterKidCount(nkids); +} + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr; + + MeterPropertyTree(entry->child); + return JS_DHASH_NEXT; +} + +static void +DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) +{ + char buf[10]; + JSScopeProperty *kids, *kid; + PropTreeKidsChunk *chunk; + uintN i; + + fprintf(fp, "%*sid %s g/s %p/%p slot %lu attrs %x flags %x shortid %d\n", + level, "", + JSID_IS_ATOM(sprop->id) + ? JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))) + : JSID_IS_OBJECT(sprop->id) + ? js_ValueToPrintableString(cx, OBJECT_JSID_TO_JSVAL(sprop->id)) + : (JS_snprintf(buf, sizeof buf, "%ld", JSVAL_TO_INT(sprop->id)), + buf) + (void *) sprop->getter, (void *) sprop->setter, + (unsigned long) sprop->slot, sprop->attrs, sprop->flags, + sprop->shortid); + kids = sprop->kids; + if (kids) { + ++level; + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + JS_ASSERT(kid->parent == sprop); + DumpSubtree(kid, level, fp); + } + } while ((chunk = chunk->next) != NULL); + } else { + kid = kids; + DumpSubtree(kid, level, fp); + } + } +} + +#endif /* DUMP_SCOPE_STATS */ + +void +js_SweepScopeProperties(JSRuntime *rt) +{ + JSArena **ap, *a; + JSScopeProperty *limit, *sprop, *parent, *kids, *kid; + uintN liveCount; + PropTreeKidsChunk *chunk, *nextChunk, *freeChunk; + uintN i; + +#ifdef DUMP_SCOPE_STATS + uint32 livePropCapacity = 0, totalLiveCount = 0; + static FILE *logfp; + if (!logfp) + logfp = fopen("/tmp/proptree.stats", "a"); + + MeterKidCount(rt->propertyTreeHash.entryCount); + JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL); + + { + double mean = 0.0, var = 0.0, sigma = 0.0; + double nodesum = rt->livePropTreeNodes; + double kidsum = js_nkids_sum; + if (nodesum > 0 && kidsum >= 0) { + mean = kidsum / nodesum; + var = nodesum * js_nkids_sqsum - kidsum * kidsum; + if (var < 0.0 || nodesum <= 1) + var = 0.0; + else + var /= nodesum * (nodesum - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.0) ? sqrt(var) : 0.0; + } + + fprintf(logfp, + "props %u nodes %g beta %g meankids %g sigma %g max %u", + rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps, + mean, sigma, js_nkids_max); + } + + fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u", + js_nkids_hist[0], js_nkids_hist[1], + js_nkids_hist[2], js_nkids_hist[3], + js_nkids_hist[4], js_nkids_hist[5], + js_nkids_hist[6], js_nkids_hist[7], + js_nkids_hist[8], js_nkids_hist[9], + js_nkids_hist[10]); + js_nkids_sum = js_nkids_max = 0; + js_nkids_sqsum = 0; + memset(js_nkids_hist, 0, sizeof js_nkids_hist); +#endif + + ap = &rt->propertyArenaPool.first.next; + while ((a = *ap) != NULL) { + limit = (JSScopeProperty *) a->avail; + liveCount = 0; + for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) { + /* If the id is null, sprop is already on the freelist. */ + if (sprop->id == JSVAL_NULL) + continue; + + /* If the mark bit is set, sprop is alive, so we skip it. */ + if (sprop->flags & SPROP_MARK) { + sprop->flags &= ~SPROP_MARK; + liveCount++; + continue; + } + + /* Ok, sprop is garbage to collect: unlink it from its parent. */ + freeChunk = RemovePropertyTreeChild(rt, sprop); + + /* + * Take care to reparent all sprop's kids to their grandparent. + * InsertPropertyTreeChild can potentially fail for two reasons: + * + * 1. If parent is null, insertion into the root property hash + * table may fail. We are forced to leave the kid out of the + * table (as can already happen with duplicates) but ensure + * that the kid's parent pointer is set to null. + * + * 2. If parent is non-null, allocation of a new KidsChunk can + * fail. To prevent this from happening, we allow sprops's own + * chunks to be reused by the grandparent, which removes the + * need for InsertPropertyTreeChild to malloc a new KidsChunk. + * + * If sprop does not have chunky kids, then we rely on the + * RemovePropertyTreeChild call above (which removed sprop from + * its parent) either leaving one free entry, or else returning + * the now-unused chunk to us so we can reuse it. + * + * We also require the grandparent to have either no kids or else + * chunky kids. A single non-chunky kid would force a new chunk to + * be malloced in some cases (if sprop had a single non-chunky + * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that + * RemovePropertyTreeChild never converts a single-entry chunky + * kid back to a non-chunky kid, so we are assured of correct + * behaviour. + */ + kids = sprop->kids; + if (kids) { + sprop->kids = NULL; + parent = sprop->parent; + /* Validate that grandparent has no kids or chunky kids. */ + JS_ASSERT(!parent || !parent->kids || + KIDS_IS_CHUNKY(parent->kids)); + if (KIDS_IS_CHUNKY(kids)) { + chunk = KIDS_TO_CHUNK(kids); + do { + nextChunk = chunk->next; + chunk->next = NULL; + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + kid = chunk->kids[i]; + if (!kid) + break; + JS_ASSERT(kid->parent == sprop); + + /* + * Clear a space in the kids array for possible + * re-use by InsertPropertyTreeChild. + */ + chunk->kids[i] = NULL; + if (!InsertPropertyTreeChild(rt, parent, kid, + chunk)) { + /* + * This can happen only if we failed to add an + * entry to the root property hash table. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } + } + if (!chunk->kids[0]) { + /* The chunk wasn't reused, so we must free it. */ + DestroyPropTreeKidsChunk(rt, chunk); + } + } while ((chunk = nextChunk) != NULL); + } else { + kid = kids; + if (!InsertPropertyTreeChild(rt, parent, kid, freeChunk)) { + /* + * This can happen only if we failed to add an entry + * to the root property hash table. + */ + JS_ASSERT(!parent); + kid->parent = NULL; + } + } + } + + if (freeChunk && !freeChunk->kids[0]) { + /* The chunk wasn't reused, so we must free it. */ + DestroyPropTreeKidsChunk(rt, freeChunk); + } + + /* Clear id so we know (above) that sprop is on the freelist. */ + sprop->id = JSVAL_NULL; + FREENODE_INSERT(rt->propertyFreeList, sprop); + JS_RUNTIME_UNMETER(rt, livePropTreeNodes); + } + + /* If a contains no live properties, return it to the malloc heap. */ + if (liveCount == 0) { + for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) + FREENODE_REMOVE(sprop); + JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap); + } else { +#ifdef DUMP_SCOPE_STATS + livePropCapacity += limit - (JSScopeProperty *) a->base; + totalLiveCount += liveCount; +#endif + ap = &a->next; + } + } + +#ifdef DUMP_SCOPE_STATS + fprintf(logfp, " arenautil %g%%\n", + (totalLiveCount * 100.0) / livePropCapacity); + fflush(logfp); +#endif + +#ifdef DUMP_PROPERTY_TREE + { + FILE *dumpfp = fopen("/tmp/proptree.dump", "w"); + if (dumpfp) { + JSPropertyTreeEntry *pte, *end; + + pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore; + end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash); + while (pte < end) { + if (pte->child) + DumpSubtree(pte->child, 0, dumpfp); + pte++; + } + fclose(dumpfp); + } + } +#endif +} + +JSBool +js_InitPropertyTree(JSRuntime *rt) +{ + if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL, + sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) { + rt->propertyTreeHash.ops = NULL; + return JS_FALSE; + } + JS_InitArenaPool(&rt->propertyArenaPool, "properties", + 256 * sizeof(JSScopeProperty), sizeof(void *)); + return JS_TRUE; +} + +void +js_FinishPropertyTree(JSRuntime *rt) +{ + if (rt->propertyTreeHash.ops) { + JS_DHashTableFinish(&rt->propertyTreeHash); + rt->propertyTreeHash.ops = NULL; + } + JS_FinishArenaPool(&rt->propertyArenaPool); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscope.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscope.h new file mode 100644 index 0000000..0565d4d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscope.h @@ -0,0 +1,407 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscope_h___ +#define jsscope_h___ +/* + * JS symbol tables. + */ +#include "jstypes.h" +#include "jsobj.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +#ifdef JS_THREADSAFE +# include "jslock.h" +#endif + +/* + * Given P independent, non-unique properties each of size S words mapped by + * all scopes in a runtime, construct a property tree of N nodes each of size + * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child + * and right-sibling links. We hope that the N < P by enough that the space + * overhead of L, and the overhead of scope entries pointing at property tree + * nodes, is worth it. + * + * The tree construction goes as follows. If any empty scope in the runtime + * has a property X added to it, find or create a node under the tree root + * labeled X, and set scope->lastProp to point at that node. If any non-empty + * scope whose most recently added property is labeled Y has another property + * labeled Z added, find or create a node for Z under the node that was added + * for Y, and set scope->lastProp to point at that node. + * + * A property is labeled by its members' values: id, getter, setter, slot, + * attributes, tiny or short id, and a field telling for..in order. Note that + * labels are not unique in the tree, but they are unique among a node's kids + * (barring rare and benign multi-threaded race condition outcomes, see below) + * and along any ancestor line from the tree root to a given leaf node (except + * for the hard case of duplicate formal parameters to a function). + * + * Thus the root of the tree represents all empty scopes, and the first ply + * of the tree represents all scopes containing one property, etc. Each node + * in the tree can stand for any number of scopes having the same ordered set + * of properties, where that node was the last added to the scope. (We need + * not store the root of the tree as a node, and do not -- all we need are + * links to its kids.) + * + * Sidebar on for..in loop order: ECMA requires no particular order, but this + * implementation has promised and delivered property definition order, and + * compatibility is king. We could use an order number per property, which + * would require a sort in js_Enumerate, and an entry order generation number + * per scope. An order number beats a list, which should be doubly-linked for + * O(1) delete. An even better scheme is to use a parent link in the property + * tree, so that the ancestor line can be iterated from scope->lastProp when + * filling in a JSIdArray from back to front. This parent link also helps the + * GC to sweep properties iteratively. + * + * What if a property Y is deleted from a scope? If Y is the last property in + * the scope, we simply adjust the scope's lastProp member after we remove the + * scope's hash-table entry pointing at that property node. The parent link + * mentioned in the for..in sidebar above makes this adjustment O(1). But if + * Y comes between X and Z in the scope, then we might have to "fork" the tree + * at X, leaving X->Y->Z in case other scopes have those properties added in + * that order; and to finish the fork, we'd add a node labeled Z with the path + * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to + * O(n^2) growth when deleting lots of properties. + * + * Rather, for O(1) growth all around, we should share the path X->Y->Z among + * scopes having those three properties added in that order, and among scopes + * having only X->Z where Y was deleted. All such scopes have a lastProp that + * points to the Z child of Y. But a scope in which Y was deleted does not + * have a table entry for Y, and when iterating that scope by traversing the + * ancestor line from Z, we will have to test for a table entry for each node, + * skipping nodes that lack entries. + * + * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice. + * Therefore we must fork in such a case, if not earlier. Because delete is + * "bursty", we should not fork eagerly. Delaying a fork till we are at risk + * of adding Y after it was deleted already requires a flag in the JSScope, to + * wit, SCOPE_MIDDLE_DELETE. + * + * What about thread safety? If the property tree operations done by requests + * are find-node and insert-node, then the only hazard is duplicate insertion. + * This is harmless except for minor bloat. When all requests have ended or + * been suspended, the GC is free to sweep the tree after marking all nodes + * reachable from scopes, performing remove-node operations as needed. Note + * also that the stable storage of the property nodes during active requests + * permits the property cache (see jsinterp.h) to dereference JSScopeProperty + * weak references safely. + * + * Is the property tree worth it compared to property storage in each table's + * entries? To decide, we must find the relation <> between the words used + * with a property tree and the words required without a tree. + * + * Model all scopes as one super-scope of capacity T entries (T a power of 2). + * Let alpha be the load factor of this double hash-table. With the property + * tree, each entry in the table is a word-sized pointer to a node that can be + * shared by many scopes. But all such pointers are overhead compared to the + * situation without the property tree, where the table stores property nodes + * directly, as entries each of size S words. With the property tree, we need + * L=2 extra words per node for siblings and kids pointers. Without the tree, + * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required + * by double hashing. + * + * Therefore, + * + * (property tree) <> (no property tree) + * N*(S+L) + T <> S*T + * N*(S+L) + T <> P*S + (1-alpha)*S*T + * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T + * + * Note that P is alpha*T by definition, so + * + * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T + * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T + * N*(S+L) <> (P + (1-alpha)*T) * (S-1) + * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1) + * N*(S+L) <> P * (1/alpha) * (S-1) + * + * Let N = P*beta for a compression ratio beta, beta <= 1: + * + * P*beta*(S+L) <> P * (1/alpha) * (S-1) + * beta*(S+L) <> (S-1)/alpha + * beta <> (S-1)/((S+L)*alpha) + * + * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff + * + * beta < 5/(8*alpha) + * + * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An + * average beta from recent Mozilla browser startups was around .6. + * + * Can we reduce L? Observe that the property tree degenerates into a list of + * lists if at most one property Y follows X in all scopes. In or near such a + * case, we waste a word on the right-sibling link outside of the root ply of + * the tree. Note also that the root ply tends to be large, so O(n^2) growth + * searching it is likely, indicating the need for hashing (but with increased + * thread safety costs). + * + * If only K out of N nodes in the property tree have more than one child, we + * could eliminate the sibling link and overlay a children list or hash-table + * pointer on the leftmost-child link (which would then be either null or an + * only-child link; the overlay could be tagged in the low bit of the pointer, + * or flagged elsewhere in the property tree node, although such a flag must + * not be considered when comparing node labels during tree search). + * + * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2. + * If K << N, L approaches 1 and the property tree wins if beta < .95. + * + * We observe that fan-out below the root ply of the property tree appears to + * have extremely low degree (see the MeterPropertyTree code that histograms + * child-counts in jsscope.c), so instead of a hash-table we use a linked list + * of child node pointer arrays ("kid chunks"). The details are isolated in + * jsscope.c; others must treat JSScopeProperty.kids as opaque. We leave it + * strongly typed for debug-ability of the common (null or one-kid) cases. + * + * One final twist (can you stand it?): the mean number of entries per scope + * in Mozilla is < 5, with a large standard deviation (~8). Instead of always + * allocating scope->table, we leave it null while initializing all the other + * scope members as if it were non-null and minimal-length. Until a property + * is added that crosses the threshold of 6 or more entries for hashing, or + * until a "middle delete" occurs, we use linear search from scope->lastProp + * to find a given id, and save on the space overhead of a hash table. + */ + +struct JSScope { + JSObjectMap map; /* base class state */ + JSObject *object; /* object that owns this scope */ + uint8 flags; /* flags, see below */ + int8 hashShift; /* multiplicative hash shift */ + uint16 spare; /* reserved */ + uint32 entryCount; /* number of entries in table */ + uint32 removedCount; /* removed entry sentinels in table */ + JSScopeProperty **table; /* table of ptrs to shared tree nodes */ + JSScopeProperty *lastProp; /* pointer to last property added */ +#ifdef JS_THREADSAFE + JSContext *ownercx; /* creating context, NULL if shared */ + JSThinLock lock; /* binary semaphore protecting scope */ + union { /* union lockful and lock-free state: */ + jsrefcount count; /* lock entry count for reentrancy */ + JSScope *link; /* next link in rt->scopeSharingTodo */ + } u; +#ifdef DEBUG + const char *file[4]; /* file where lock was (re-)taken */ + unsigned int line[4]; /* line where lock was (re-)taken */ +#endif +#endif +}; + +#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) + +/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ +#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift) + +/* Scope flags and some macros to hide them from other files than jsscope.c. */ +#define SCOPE_MIDDLE_DELETE 0x0001 +#define SCOPE_SEALED 0x0002 + +#define SCOPE_HAD_MIDDLE_DELETE(scope) ((scope)->flags & SCOPE_MIDDLE_DELETE) +#define SCOPE_SET_MIDDLE_DELETE(scope) ((scope)->flags |= SCOPE_MIDDLE_DELETE) +#define SCOPE_CLR_MIDDLE_DELETE(scope) ((scope)->flags &= ~SCOPE_MIDDLE_DELETE) + +#define SCOPE_IS_SEALED(scope) ((scope)->flags & SCOPE_SEALED) +#define SCOPE_SET_SEALED(scope) ((scope)->flags |= SCOPE_SEALED) +#if 0 +/* + * Don't define this, it can't be done safely because JS_LOCK_OBJ will avoid + * taking the lock if the object owns its scope and the scope is sealed. + */ +#define SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED) +#endif + +/* + * A little information hiding for scope->lastProp, in case it ever becomes + * a tagged pointer again. + */ +#define SCOPE_LAST_PROP(scope) ((scope)->lastProp) +#define SCOPE_REMOVE_LAST_PROP(scope) ((scope)->lastProp = \ + (scope)->lastProp->parent) + +struct JSScopeProperty { + jsid id; /* int-tagged jsval/untagged JSAtom* */ + JSPropertyOp getter; /* getter and setter hooks or objects */ + JSPropertyOp setter; + uint32 slot; /* index in obj->slots vector */ + uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ + uint8 flags; /* flags, see below for defines */ + int16 shortid; /* tinyid, or local arg/var index */ + JSScopeProperty *parent; /* parent node, reverse for..in order */ + JSScopeProperty *kids; /* null, single child, or a tagged ptr + to many-kids data structure */ +}; + +/* JSScopeProperty pointer tag bit indicating a collision. */ +#define SPROP_COLLISION ((jsuword)1) +#define SPROP_REMOVED ((JSScopeProperty *) SPROP_COLLISION) + +/* Macros to get and set sprop pointer values and collision flags. */ +#define SPROP_IS_FREE(sprop) ((sprop) == NULL) +#define SPROP_IS_REMOVED(sprop) ((sprop) == SPROP_REMOVED) +#define SPROP_IS_LIVE(sprop) ((sprop) > SPROP_REMOVED) +#define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *) \ + ((jsuword)(sprop) | SPROP_COLLISION)) +#define SPROP_HAD_COLLISION(sprop) ((jsuword)(sprop) & SPROP_COLLISION) +#define SPROP_FETCH(spp) SPROP_CLEAR_COLLISION(*(spp)) + +#define SPROP_CLEAR_COLLISION(sprop) \ + ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION)) + +#define SPROP_STORE_PRESERVING_COLLISION(spp, sprop) \ + (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \ + | SPROP_HAD_COLLISION(*(spp)))) + +/* Bits stored in sprop->flags. */ +#define SPROP_MARK 0x01 +#define SPROP_IS_DUPLICATE 0x02 +#define SPROP_IS_ALIAS 0x04 +#define SPROP_HAS_SHORTID 0x08 +#define SPROP_IS_HIDDEN 0x10 /* a normally-hidden property, + e.g., function arg or var */ + +/* + * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather + * than id when calling sprop's getter or setter. + */ +#define SPROP_USERID(sprop) \ + (((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid) \ + : ID_TO_VALUE((sprop)->id)) + +#define SPROP_INVALID_SLOT 0xffffffff + +#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) +#define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) + +#define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) +#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) + +/* + * NB: SPROP_GET must not be called if SPROP_HAS_STUB_GETTER(sprop). + */ +#define SPROP_GET(cx,sprop,obj,obj2,vp) \ + (((sprop)->attrs & JSPROP_GETTER) \ + ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ + OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ, \ + 0, 0, vp) \ + : (sprop)->getter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + +/* + * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) && + * !(sprop->attrs & JSPROP_GETTER)). + */ +#define SPROP_SET(cx,sprop,obj,obj2,vp) \ + (((sprop)->attrs & JSPROP_SETTER) \ + ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ + OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE, \ + 1, vp, vp) \ + : ((sprop)->attrs & JSPROP_GETTER) \ + ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, \ + JSMSG_GETTER_ONLY, NULL), JS_FALSE) \ + : (sprop)->setter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) + +/* Macro for common expression to test for shared permanent attributes. */ +#define SPROP_IS_SHARED_PERMANENT(sprop) \ + ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0) + +extern JSScope * +js_GetMutableScope(JSContext *cx, JSObject *obj); + +extern JSScope * +js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, + JSObject *obj); + +extern void +js_DestroyScope(JSContext *cx, JSScope *scope); + +#define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_JSID_TO_JSVAL(id) : \ + JSID_IS_OBJECT(id) ? OBJECT_JSID_TO_JSVAL(id) : \ + (jsval)(id)) +#define HASH_ID(id) (JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->number : \ + JSID_IS_OBJECT(id) ? (jsatomid) JSID_CLRTAG(id) : \ + (jsatomid) JSID_TO_INT(id)) + +extern JS_FRIEND_API(JSScopeProperty **) +js_SearchScope(JSScope *scope, jsid id, JSBool adding); + +#define SCOPE_GET_PROPERTY(scope, id) \ + SPROP_FETCH(js_SearchScope(scope, id, JS_FALSE)) + +#define SCOPE_HAS_PROPERTY(scope, sprop) \ + (SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop)) + +extern JSScopeProperty * +js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, + JSPropertyOp getter, JSPropertyOp setter, uint32 slot, + uintN attrs, uintN flags, intN shortid); + +extern JSScopeProperty * +js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, + JSScopeProperty *sprop, uintN attrs, uintN mask, + JSPropertyOp getter, JSPropertyOp setter); + +extern JSBool +js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id); + +extern void +js_ClearScope(JSContext *cx, JSScope *scope); + +/* + * These macros used to inline short code sequences, but they grew over time. + * We retain them for internal backward compatibility, and in case one or both + * ever shrink to inline-able size. + */ +#define MARK_ID(cx,id) js_MarkId(cx, id) +#define MARK_SCOPE_PROPERTY(cx,sprop) js_MarkScopeProperty(cx, sprop) + +extern void +js_MarkId(JSContext *cx, jsid id); + +extern void +js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop); + +extern void +js_SweepScopeProperties(JSRuntime *rt); + +extern JSBool +js_InitPropertyTree(JSRuntime *rt); + +extern void +js_FinishPropertyTree(JSRuntime *rt); + +#endif /* jsscope_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscript.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscript.c new file mode 100644 index 0000000..73298a4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscript.c @@ -0,0 +1,1717 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS script operations. + */ +#include "jsstddef.h" +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsopcode.h" +#include "jsscript.h" +#if JS_HAS_XDR +#include "jsxdrapi.h" +#endif + +#if JS_HAS_SCRIPT_OBJECT + +static const char js_script_exec[] = "Script.prototype.exec"; +static const char js_script_compile[] = "Script.prototype.compile"; + +/* + * This routine requires that obj has been locked previously. + */ +static jsint +GetScriptExecDepth(JSContext *cx, JSObject *obj) +{ + jsval v; + + JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); + v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); + return JSVAL_TO_INT(v); +} + +static void +AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) +{ + jsint execDepth; + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), + INT_TO_JSVAL(execDepth + delta)); + JS_UNLOCK_OBJ(cx, obj); +} + +#if JS_HAS_TOSOURCE +static JSBool +script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + uint32 indent; + JSScript *script; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + + script = (JSScript *) JS_GetPrivate(cx, obj); + + /* Let n count the source string length, j the "front porch" length. */ + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); + n = j + 2; + if (!script) { + /* Let k count the constructor argument string length. */ + k = 0; + s = NULL; /* quell GCC overwarning */ + } else { + str = JS_DecompileScript(cx, script, "Script.prototype.toSource", + (uintN)indent); + if (!str) + return JS_FALSE; + str = js_QuoteString(cx, str, '\''); + if (!str) + return JS_FALSE; + s = JSSTRING_CHARS(str); + k = JSSTRING_LENGTH(str); + n += k; + } + + /* Allocate the source string and copy into it. */ + t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + + /* Create and return a JS string for t. */ + str = JS_NewUCString(cx, t, n); + if (!str) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + uint32 indent; + JSScript *script; + JSString *str; + + indent = 0; + if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) + return JS_FALSE; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + str = JS_DecompileScript(cx, script, "Script.prototype.toString", + (uintN)indent); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + JSObject *scopeobj; + jsval v; + JSScript *script, *oldscript; + JSStackFrame *fp, *caller; + const char *file; + uintN line; + JSPrincipals *principals; + jsint execDepth; + + /* Make sure obj is a Script object. */ + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + /* If no args, leave private undefined and return early. */ + if (argc == 0) + goto out; + + /* Otherwise, the first arg is the script source to compile. */ + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + scopeobj = NULL; + if (argc >= 2) { + if (!js_ValueToObject(cx, argv[1], &scopeobj)) + return JS_FALSE; + argv[1] = OBJECT_TO_JSVAL(scopeobj); + } + + /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ + fp = cx->fp; + caller = JS_GetScriptedCaller(cx, fp); + JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); + + if (caller) { + if (!scopeobj) { + scopeobj = js_GetScopeChain(cx, caller); + if (!scopeobj) + return JS_FALSE; + fp->scopeChain = scopeobj; /* for the compiler's benefit */ + } + + principals = JS_EvalFramePrincipals(cx, fp, caller); + if (principals == caller->script->principals) { + file = caller->script->filename; + line = js_PCToLineNumber(cx, caller->script, caller->pc); + } else { + file = principals->codebase; + line = 0; + } + } else { + file = NULL; + line = 0; + principals = NULL; + } + + /* Ensure we compile this script with the right (inner) principals. */ + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile); + if (!scopeobj) + return JS_FALSE; + + /* + * Compile the new script using the caller's scope chain, a la eval(). + * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's + * flags, because compilation is here separated from execution, and the + * run-time scope chain may not match the compile-time. JSFRAME_EVAL is + * tested in jsemit.c and jsscan.c to optimize based on identity of run- + * and compile-time scope. + */ + fp->flags |= JSFRAME_SCRIPT_OBJECT; + script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, + JSSTRING_CHARS(str), + JSSTRING_LENGTH(str), + file, line); + if (!script) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); + return JS_FALSE; + } + + /* Swap script for obj's old script, if any. */ + v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); + oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; + LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); + JS_UNLOCK_OBJ(cx, obj); + + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->object = obj; + js_CallNewScriptHook(cx, script, NULL); + +out: + /* Return the object. */ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *scopeobj, *parent; + JSStackFrame *fp, *caller; + JSScript *script; + JSBool ok; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + scopeobj = NULL; + if (argc) { + if (!js_ValueToObject(cx, argv[0], &scopeobj)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(scopeobj); + } + + /* + * Emulate eval() by using caller's this, var object, sharp array, etc., + * all propagated by js_Execute via a non-null fourth (down) argument to + * js_Execute. If there is no scripted caller, js_Execute uses its second + * (chain) argument to set the exec frame's varobj, thisp, and scopeChain. + * + * Unlike eval, which the compiler detects, Script.prototype.exec may be + * called from a lightweight function, or even from native code (in which + * case fp->varobj and fp->scopeChain are null). If exec is called from + * a lightweight function, we will need to get a Call object representing + * its frame, to act as the var object and scope chain head. + */ + fp = cx->fp; + caller = JS_GetScriptedCaller(cx, fp); + if (caller && !caller->varobj) { + /* Called from a lightweight function. */ + JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); + + /* Scope chain links from Call object to callee's parent. */ + parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2])); + if (!js_GetCallObject(cx, caller, parent)) + return JS_FALSE; + } + + if (!scopeobj) { + /* No scope object passed in: try to use the caller's scope chain. */ + if (caller) { + /* + * Load caller->scopeChain after the conditional js_GetCallObject + * call above, which resets scopeChain as well as varobj. + */ + scopeobj = js_GetScopeChain(cx, caller); + if (!scopeobj) + return JS_FALSE; + } else { + /* + * Called from native code, so we don't know what scope object to + * use. We could use parent (see above), but Script.prototype.exec + * might be a shared/sealed "superglobal" method. A more general + * approach would use cx->globalObject, which will be the same as + * exec.__parent__ in the non-superglobal case. In the superglobal + * case it's the right object: the global, not the superglobal. + */ + scopeobj = cx->globalObject; + } + } + + scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec); + if (!scopeobj) + return JS_FALSE; + + /* Keep track of nesting depth for the script. */ + AdjustScriptExecDepth(cx, obj, 1); + + /* Must get to out label after this */ + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) { + ok = JS_FALSE; + goto out; + } + + /* Belt-and-braces: check that this script object has access to scopeobj. */ + ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals, + CLASS_ATOM(cx, Script)); + if (!ok) + goto out; + + ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); + +out: + AdjustScriptExecDepth(cx, obj, -1); + return ok; +} + +#if JS_HAS_XDR + +static JSBool +XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) +{ + JSContext *cx; + uint32 natoms, i, index; + JSAtom **atoms; + + cx = xdr->cx; + + if (xdr->mode == JSXDR_ENCODE) + natoms = (uint32)map->length; + + if (!JS_XDRUint32(xdr, &natoms)) + return JS_FALSE; + + if (xdr->mode == JSXDR_ENCODE) { + atoms = map->vector; + } else { + if (natoms == 0) { + atoms = NULL; + } else { + atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms); + if (!atoms) + return JS_FALSE; +#ifdef DEBUG + memset(atoms, 0, (size_t)natoms * sizeof *atoms); +#endif + } + + map->vector = atoms; + map->length = natoms; + } + + for (i = 0; i != natoms; ++i) { + if (xdr->mode == JSXDR_ENCODE) + index = i; + if (!JS_XDRUint32(xdr, &index)) + goto bad; + + /* + * Assert that, when decoding, the read index is valid and points to + * an unoccupied element of atoms array. + */ + JS_ASSERT(index < natoms); + JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]); + if (!js_XDRAtom(xdr, &atoms[index])) + goto bad; + } + + return JS_TRUE; + + bad: + if (xdr->mode == JSXDR_DECODE) { + JS_free(cx, atoms); + map->vector = NULL; + map->length = 0; + } + + return JS_FALSE; +} + +JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) +{ + JSContext *cx; + JSScript *script, *newscript, *oldscript; + uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes; + uint32 prologLength, version; + JSBool filenameWasSaved; + jssrcnote *notes, *sn; + + cx = xdr->cx; + script = *scriptp; + nsrcnotes = ntrynotes = 0; + filenameWasSaved = JS_FALSE; + notes = NULL; + + /* + * Encode prologLength and version after script->length (_2 or greater), + * but decode both new (>= _2) and old, prolog&version-free (_1) scripts. + * Version _3 supports principals serialization. Version _4 reorders the + * nsrcnotes and ntrynotes fields to come before everything except magic, + * length, prologLength, and version, so that srcnote and trynote storage + * can be allocated as part of the JSScript (along with bytecode storage). + * + * So far, the magic number has not changed for every jsopcode.tbl change. + * We stipulate forward compatibility by requiring old bytecodes never to + * change or go away (modulo a few exceptions before the XDR interfaces + * evolved, and a few exceptions during active trunk development). With + * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new + * magic number (_5) so that we know to append JSOP_STOP to old scripts + * when deserializing. + */ + if (xdr->mode == JSXDR_ENCODE) + magic = JSXDR_MAGIC_SCRIPT_CURRENT; + if (!JS_XDRUint32(xdr, &magic)) + return JS_FALSE; + JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4); + if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) { + if (!hasMagic) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SCRIPT_MAGIC); + return JS_FALSE; + } + *hasMagic = JS_FALSE; + return JS_TRUE; + } + if (hasMagic) + *hasMagic = JS_TRUE; + + if (xdr->mode == JSXDR_ENCODE) { + length = script->length; + prologLength = PTRDIFF(script->main, script->code, jsbytecode); + JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); + version = (uint32)script->version | (script->numGlobalVars << 16); + lineno = (uint32)script->lineno; + depth = (uint32)script->depth; + + /* Count the srcnotes, keeping notes pointing at the first one. */ + notes = SCRIPT_NOTES(script); + for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) + continue; + nsrcnotes = PTRDIFF(sn, notes, jssrcnote); + nsrcnotes++; /* room for the terminator */ + + /* Count the trynotes. */ + if (script->trynotes) { + while (script->trynotes[ntrynotes].catchStart) + ntrynotes++; + ntrynotes++; /* room for the end marker */ + } + } + + if (!JS_XDRUint32(xdr, &length)) + return JS_FALSE; + if (magic >= JSXDR_MAGIC_SCRIPT_2) { + if (!JS_XDRUint32(xdr, &prologLength)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &version)) + return JS_FALSE; + + /* To fuse allocations, we need srcnote and trynote counts early. */ + if (magic >= JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) + return JS_FALSE; + if (!JS_XDRUint32(xdr, &ntrynotes)) + return JS_FALSE; + } + } + + if (xdr->mode == JSXDR_DECODE) { + size_t alloclength = length; + if (magic < JSXDR_MAGIC_SCRIPT_5) + ++alloclength; /* add a byte for JSOP_STOP */ + + script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes); + if (!script) + return JS_FALSE; + if (magic >= JSXDR_MAGIC_SCRIPT_2) { + script->main += prologLength; + script->version = (JSVersion) (version & 0xffff); + script->numGlobalVars = (uint16) (version >> 16); + + /* If we know nsrcnotes, we allocated space for notes in script. */ + if (magic >= JSXDR_MAGIC_SCRIPT_4) + notes = SCRIPT_NOTES(script); + } + *scriptp = script; + } + + /* + * Control hereafter must goto error on failure, in order for the DECODE + * case to destroy script and conditionally free notes, which if non-null + * in the (DECODE and magic < _4) case must point at a temporary vector + * allocated just below. + */ + oldscript = xdr->script; + xdr->script = script; + if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || + !XDRAtomMap(xdr, &script->atomMap)) { + goto error; + } + + if (magic < JSXDR_MAGIC_SCRIPT_5) { + if (xdr->mode == JSXDR_DECODE) { + /* + * Append JSOP_STOP to old scripts, to relieve the interpreter + * from having to bounds-check pc. Also take care to increment + * length, as it is used below and must count all bytecode. + */ + script->code[length++] = JSOP_STOP; + } + + if (magic < JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) + goto error; + if (xdr->mode == JSXDR_DECODE) { + notes = (jssrcnote *) + JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); + if (!notes) + goto error; + } + } + } + + if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || + !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || + !JS_XDRUint32(xdr, &lineno) || + !JS_XDRUint32(xdr, &depth) || + (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) { + goto error; + } + + /* Script principals transcoding support comes with versions >= _3. */ + if (magic >= JSXDR_MAGIC_SCRIPT_3) { + JSPrincipals *principals; + uint32 encodeable; + + if (xdr->mode == JSXDR_ENCODE) { + principals = script->principals; + encodeable = (cx->runtime->principalsTranscoder != NULL); + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable && + !cx->runtime->principalsTranscoder(xdr, &principals)) { + goto error; + } + } else { + if (!JS_XDRUint32(xdr, &encodeable)) + goto error; + if (encodeable) { + if (!cx->runtime->principalsTranscoder) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_DECODE_PRINCIPALS); + goto error; + } + if (!cx->runtime->principalsTranscoder(xdr, &principals)) + goto error; + script->principals = principals; + } + } + } + + if (xdr->mode == JSXDR_DECODE) { + const char *filename = script->filename; + if (filename) { + filename = js_SaveScriptFilename(cx, filename); + if (!filename) + goto error; + JS_free(cx, (void *) script->filename); + script->filename = filename; + filenameWasSaved = JS_TRUE; + } + script->lineno = (uintN)lineno; + script->depth = (uintN)depth; + + if (magic < JSXDR_MAGIC_SCRIPT_4) { + /* + * Argh, we have to reallocate script, copy notes into the extra + * space after the bytecodes, and free the temporary notes vector. + * First, add enough slop to nsrcnotes so we can align the address + * after the srcnotes of the first trynote. + */ + uint32 osrcnotes = nsrcnotes; + + if (ntrynotes) + nsrcnotes += JSTRYNOTE_ALIGNMASK; + newscript = (JSScript *) JS_realloc(cx, script, + sizeof(JSScript) + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote) + + ntrynotes * sizeof(JSTryNote)); + if (!newscript) + goto error; + + *scriptp = script = newscript; + script->code = (jsbytecode *)(script + 1); + script->main = script->code + prologLength; + memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote)); + JS_free(cx, (void *) notes); + notes = NULL; + if (ntrynotes) { + script->trynotes = (JSTryNote *) + ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & + ~(jsword)JSTRYNOTE_ALIGNMASK); + memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); + } + } + } + + while (ntrynotes) { + JSTryNote *tn = &script->trynotes[--ntrynotes]; + uint32 start = (uint32) tn->start, + catchLength = (uint32) tn->length, + catchStart = (uint32) tn->catchStart; + + if (!JS_XDRUint32(xdr, &start) || + !JS_XDRUint32(xdr, &catchLength) || + !JS_XDRUint32(xdr, &catchStart)) { + goto error; + } + tn->start = (ptrdiff_t) start; + tn->length = (ptrdiff_t) catchLength; + tn->catchStart = (ptrdiff_t) catchStart; + } + + xdr->script = oldscript; + return JS_TRUE; + + error: + if (xdr->mode == JSXDR_DECODE) { + if (script->filename && !filenameWasSaved) { + JS_free(cx, (void *) script->filename); + script->filename = NULL; + } + if (notes && magic < JSXDR_MAGIC_SCRIPT_4) + JS_free(cx, (void *) notes); + js_DestroyScript(cx, script); + *scriptp = NULL; + } + return JS_FALSE; +} + +#if JS_HAS_XDR_FREEZE_THAW +/* + * These cannot be exposed to web content, and chrome does not need them, so + * we take them out of the Mozilla client altogether. Fortunately, there is + * no way to serialize a native function (see fun_xdrObject in jsfun.c). + */ + +static JSBool +script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSScript *script; + JSBool ok, hasMagic; + uint32 len; + void *buf; + JSString *str; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + script = (JSScript *) JS_GetPrivate(cx, obj); + if (!script) + return JS_TRUE; + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); + if (!xdr) + return JS_FALSE; + + /* write */ + ok = js_XDRScript(xdr, &script, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *rval = JSVAL_VOID; + goto out; + } + + buf = JS_XDRMemGetData(xdr, &len); + if (!buf) { + ok = JS_FALSE; + goto out; + } + + JS_ASSERT((jsword)buf % sizeof(jschar) == 0); + len /= sizeof(jschar); + str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); + if (!str) { + ok = JS_FALSE; + goto out; + } + +#if IS_BIG_ENDIAN + { + jschar *chars; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + chars = JS_GetStringChars(str); + for (i = 0; i < len; i++) + chars[i] = JSXDR_SWAB16(chars[i]); + } +#endif + *rval = STRING_TO_JSVAL(str); + +out: + JS_XDRDestroy(xdr); + return ok; +} + +static JSBool +script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXDRState *xdr; + JSString *str; + void *buf; + uint32 len; + jsval v; + JSScript *script, *oldscript; + JSBool ok, hasMagic; + + if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) + return JS_FALSE; + + if (argc == 0) + return JS_TRUE; + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + /* create new XDR */ + xdr = JS_XDRNewMem(cx, JSXDR_DECODE); + if (!xdr) + return JS_FALSE; + + buf = JS_GetStringChars(str); + len = JS_GetStringLength(str); +#if IS_BIG_ENDIAN + { + jschar *from, *to; + uint32 i; + + /* Swap bytes in Unichars to keep frozen strings machine-independent. */ + from = (jschar *)buf; + to = (jschar *) JS_malloc(cx, len * sizeof(jschar)); + if (!to) { + JS_XDRDestroy(xdr); + return JS_FALSE; + } + for (i = 0; i < len; i++) + to[i] = JSXDR_SWAB16(from[i]); + buf = (char *)to; + } +#endif + len *= sizeof(jschar); + JS_XDRMemSetData(xdr, buf, len); + + /* XXXbe should magic mismatch be error, or false return value? */ + ok = js_XDRScript(xdr, &script, &hasMagic); + if (!ok) + goto out; + if (!hasMagic) { + *rval = JSVAL_FALSE; + goto out; + } + + JS_LOCK_OBJ(cx, obj); + execDepth = GetScriptExecDepth(cx, obj); + + /* + * execDepth must be 0 to allow compilation here, otherwise the JSScript + * struct can be released while running. + */ + if (execDepth > 0) { + JS_UNLOCK_OBJ(cx, obj); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_COMPILE_EXECED_SCRIPT); + goto out; + } + + /* Swap script for obj's old script, if any. */ + v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; + LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); + JS_UNLOCK_OBJ(cx, obj); + + if (oldscript) + js_DestroyScript(cx, oldscript); + + script->object = obj; + js_CallNewScriptHook(cx, script, NULL); + +out: + /* + * We reset the buffer to be NULL so that it doesn't free the chars + * memory owned by str (argv[0]). + */ + JS_XDRMemSetData(xdr, NULL, 0); + JS_XDRDestroy(xdr); +#if IS_BIG_ENDIAN + JS_free(cx, buf); +#endif + *rval = JSVAL_TRUE; + return ok; +} + +static const char js_thaw_str[] = "thaw"; + +#endif /* JS_HAS_XDR_FREEZE_THAW */ +#endif /* JS_HAS_XDR */ + +static JSFunctionSpec script_methods[] = { +#if JS_HAS_TOSOURCE + {js_toSource_str, script_toSource, 0,0,0}, +#endif + {js_toString_str, script_toString, 0,0,0}, + {"compile", script_compile, 2,0,0}, + {"exec", script_exec, 1,0,0}, +#if JS_HAS_XDR_FREEZE_THAW + {"freeze", script_freeze, 0,0,0}, + {js_thaw_str, script_thaw, 1,0,0}, +#endif /* JS_HAS_XDR_FREEZE_THAW */ + {0,0,0,0,0} +}; + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +static void +script_finalize(JSContext *cx, JSObject *obj) +{ + JSScript *script; + + script = (JSScript *) JS_GetPrivate(cx, obj); + if (script) + js_DestroyScript(cx, script); +} + +static JSBool +script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ +#if JS_HAS_SCRIPT_OBJECT + return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); +#else + return JS_FALSE; +#endif +} + +static uint32 +script_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSScript *script; + + script = (JSScript *) JS_GetPrivate(cx, obj); + if (script) + js_MarkScript(cx, script); + return 0; +} + +#if !JS_HAS_SCRIPT_OBJECT +const char js_Script_str[] = "Script"; + +#define JSProto_Script JSProto_Object +#endif + +JS_FRIEND_DATA(JSClass) js_ScriptClass = { + js_Script_str, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) | + JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, + NULL, NULL, script_call, NULL,/*XXXbe xdr*/ + NULL, NULL, script_mark, 0 +}; + +#if JS_HAS_SCRIPT_OBJECT + +static JSBool +Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + /* If not constructing, replace obj with a new Script object. */ + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + + /* + * script_compile does not use rval to root its temporaries + * so we can use it to root obj. + */ + *rval = OBJECT_TO_JSVAL(obj); + } + + if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) + return JS_FALSE; + + return script_compile(cx, obj, argc, argv, rval); +} + +#if JS_HAS_XDR_FREEZE_THAW + +static JSBool +script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return JS_FALSE; + if (!script_thaw(cx, obj, argc, argv, rval)) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec script_static_methods[] = { + {js_thaw_str, script_static_thaw, 1,0,0}, + {0,0,0,0,0} +}; + +#else /* !JS_HAS_XDR_FREEZE_THAW */ + +#define script_static_methods NULL + +#endif /* !JS_HAS_XDR_FREEZE_THAW */ + +JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, + NULL, script_methods, NULL, script_static_methods); +} + +#endif /* JS_HAS_SCRIPT_OBJECT */ + +/* + * Shared script filename management. + */ +JS_STATIC_DLL_CALLBACK(int) +js_compare_strings(const void *k1, const void *k2) +{ + return strcmp(k1, k2) == 0; +} + +/* Shared with jsatom.c to save code space. */ +extern void * JS_DLL_CALLBACK +js_alloc_table_space(void *priv, size_t size); + +extern void JS_DLL_CALLBACK +js_free_table_space(void *priv, void *item); + +/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ +typedef struct ScriptFilenameEntry { + JSHashEntry *next; /* hash chain linkage */ + JSHashNumber keyHash; /* key hash function result */ + const void *key; /* ptr to filename, below */ + uint32 flags; /* user-defined filename prefix flags */ + JSPackedBool mark; /* GC mark flag */ + char filename[3]; /* two or more bytes, NUL-terminated */ +} ScriptFilenameEntry; + +JS_STATIC_DLL_CALLBACK(JSHashEntry *) +js_alloc_sftbl_entry(void *priv, const void *key) +{ + size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1; + + return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); +} + +JS_STATIC_DLL_CALLBACK(void) +js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) +{ + if (flag != HT_FREE_ENTRY) + return; + free(he); +} + +static JSHashAllocOps sftbl_alloc_ops = { + js_alloc_table_space, js_free_table_space, + js_alloc_sftbl_entry, js_free_sftbl_entry +}; + +JSBool +js_InitRuntimeScriptState(JSRuntime *rt) +{ +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = JS_NEW_LOCK(); + if (!rt->scriptFilenameTableLock) + return JS_FALSE; +#endif + JS_ASSERT(!rt->scriptFilenameTable); + rt->scriptFilenameTable = + JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, + &sftbl_alloc_ops, NULL); + if (!rt->scriptFilenameTable) { + js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */ + return JS_FALSE; + } + JS_INIT_CLIST(&rt->scriptFilenamePrefixes); + return JS_TRUE; +} + +typedef struct ScriptFilenamePrefix { + JSCList links; /* circular list linkage for easy deletion */ + const char *name; /* pointer to pinned ScriptFilenameEntry string */ + size_t length; /* prefix string length, precomputed */ + uint32 flags; /* user-defined flags to inherit from this prefix */ +} ScriptFilenamePrefix; + +void +js_FinishRuntimeScriptState(JSRuntime *rt) +{ + if (rt->scriptFilenameTable) { + JS_HashTableDestroy(rt->scriptFilenameTable); + rt->scriptFilenameTable = NULL; + } +#ifdef JS_THREADSAFE + if (rt->scriptFilenameTableLock) { + JS_DESTROY_LOCK(rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = NULL; + } +#endif +} + +void +js_FreeRuntimeScriptState(JSRuntime *rt) +{ + ScriptFilenamePrefix *sfp; + + if (!rt->scriptFilenameTable) + return; + + while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { + sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; + JS_REMOVE_LINK(&sfp->links); + free(sfp); + } + js_FinishRuntimeScriptState(rt); +} + +#ifdef DEBUG_brendan +#define DEBUG_SFTBL +#endif +#ifdef DEBUG_SFTBL +size_t sftbl_savings = 0; +#endif + +static ScriptFilenameEntry * +SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) +{ + JSHashTable *table; + JSHashNumber hash; + JSHashEntry **hep; + ScriptFilenameEntry *sfe; + size_t length; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + table = rt->scriptFilenameTable; + hash = JS_HashString(filename); + hep = JS_HashTableRawLookup(table, hash, filename); + sfe = (ScriptFilenameEntry *) *hep; +#ifdef DEBUG_SFTBL + if (sfe) + sftbl_savings += strlen(sfe->filename); +#endif + + if (!sfe) { + sfe = (ScriptFilenameEntry *) + JS_HashTableRawAdd(table, hep, hash, filename, NULL); + if (!sfe) + return NULL; + sfe->key = strcpy(sfe->filename, filename); + sfe->flags = 0; + sfe->mark = JS_FALSE; + } + + /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ + if (flags != 0) { + /* Search in case filename was saved already; we must be idempotent. */ + sfp = NULL; + length = strlen(filename); + for (head = link = &rt->scriptFilenamePrefixes; + link->next != head; + link = link->next) { + /* Lag link behind sfp to insert in non-increasing length order. */ + sfp = (ScriptFilenamePrefix *) link->next; + if (!strcmp(sfp->name, filename)) + break; + if (sfp->length <= length) { + sfp = NULL; + break; + } + sfp = NULL; + } + + if (!sfp) { + /* No such prefix: add one now. */ + sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix)); + if (!sfp) + return NULL; + JS_INSERT_AFTER(&sfp->links, link); + sfp->name = sfe->filename; + sfp->length = length; + sfp->flags = 0; + } + + /* + * Accumulate flags in both sfe and sfp: sfe for later access from the + * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer + * filename entries can inherit by prefix. + */ + sfe->flags |= flags; + sfp->flags |= flags; + } + + return sfe; +} + +const char * +js_SaveScriptFilename(JSContext *cx, const char *filename) +{ + JSRuntime *rt; + ScriptFilenameEntry *sfe; + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + rt = cx->runtime; + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, 0); + if (!sfe) { + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + JS_ReportOutOfMemory(cx); + return NULL; + } + + /* + * Try to inherit flags by prefix. We assume there won't be more than a + * few (dozen! ;-) prefixes, so linear search is tolerable. + * XXXbe every time I've assumed that in the JS engine, I've been wrong! + */ + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + if (!strncmp(sfp->name, filename, sfp->length)) { + sfe->flags |= sfp->flags; + break; + } + } + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + return sfe->filename; +} + +const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) +{ + ScriptFilenameEntry *sfe; + + /* This may be called very early, via the jsdbgapi.h entry point. */ + if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) + return NULL; + + JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); + sfe = SaveScriptFilename(rt, filename, flags); + JS_RELEASE_LOCK(rt->scriptFilenameTableLock); + if (!sfe) + return NULL; + + return sfe->filename; +} + +/* + * Back up from a saved filename by its offset within its hash table entry. + */ +#define FILENAME_TO_SFE(fn) \ + ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) + +/* + * The sfe->key member, redundant given sfe->filename but required by the old + * jshash.c code, here gives us a useful sanity check. This assertion will + * very likely botch if someone tries to mark a string that wasn't allocated + * as an sfe->filename. + */ +#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) + +uint32 +js_GetScriptFilenameFlags(const char *filename) +{ + ScriptFilenameEntry *sfe; + + sfe = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); + return sfe->flags; +} + +void +js_MarkScriptFilename(const char *filename) +{ + ScriptFilenameEntry *sfe; + + sfe = FILENAME_TO_SFE(filename); + ASSERT_VALID_SFE(sfe); + sfe->mark = JS_TRUE; +} + +JS_STATIC_DLL_CALLBACK(intN) +js_script_filename_marker(JSHashEntry *he, intN i, void *arg) +{ + ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; + + sfe->mark = JS_TRUE; + return HT_ENUMERATE_NEXT; +} + +void +js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) +{ + JSCList *head, *link; + ScriptFilenamePrefix *sfp; + + if (!rt->scriptFilenameTable) + return; + + if (keepAtoms) { + JS_HashTableEnumerateEntries(rt->scriptFilenameTable, + js_script_filename_marker, + rt); + } + for (head = &rt->scriptFilenamePrefixes, link = head->next; + link != head; + link = link->next) { + sfp = (ScriptFilenamePrefix *) link; + js_MarkScriptFilename(sfp->name); + } +} + +JS_STATIC_DLL_CALLBACK(intN) +js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) +{ + ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; + + if (!sfe->mark) + return HT_ENUMERATE_REMOVE; + sfe->mark = JS_FALSE; + return HT_ENUMERATE_NEXT; +} + +void +js_SweepScriptFilenames(JSRuntime *rt) +{ + if (!rt->scriptFilenameTable) + return; + + JS_HashTableEnumerateEntries(rt->scriptFilenameTable, + js_script_filename_sweeper, + rt); +#ifdef DEBUG_notme +#ifdef DEBUG_SFTBL + printf("script filename table savings so far: %u\n", sftbl_savings); +#endif +#endif +} + +JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes) +{ + JSScript *script; + + /* Round up source note count to align script->trynotes for its type. */ + if (ntrynotes) + nsrcnotes += JSTRYNOTE_ALIGNMASK; + script = (JSScript *) JS_malloc(cx, + sizeof(JSScript) + + length * sizeof(jsbytecode) + + nsrcnotes * sizeof(jssrcnote) + + ntrynotes * sizeof(JSTryNote)); + if (!script) + return NULL; + memset(script, 0, sizeof(JSScript)); + script->code = script->main = (jsbytecode *)(script + 1); + script->length = length; + script->version = cx->version; + if (ntrynotes) { + script->trynotes = (JSTryNote *) + ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & + ~(jsword)JSTRYNOTE_ALIGNMASK); + memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); + } + return script; +} + +JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) +{ + uint32 mainLength, prologLength, nsrcnotes, ntrynotes; + JSScript *script; + const char *filename; + + mainLength = CG_OFFSET(cg); + prologLength = CG_PROLOG_OFFSET(cg); + CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); + CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes); + script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes); + if (!script) + return NULL; + + /* Now that we have script, error control flow must go to label bad. */ + script->main += prologLength; + memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); + memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); + script->numGlobalVars = cg->treeContext.numGlobalVars; + if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) + goto bad; + + filename = cg->filename; + if (filename) { + script->filename = js_SaveScriptFilename(cx, filename); + if (!script->filename) + goto bad; + } + script->lineno = cg->firstLine; + script->depth = cg->maxStackDepth; + if (cg->principals) { + script->principals = cg->principals; + JSPRINCIPALS_HOLD(cx, script->principals); + } + + if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script))) + goto bad; + if (script->trynotes) + js_FinishTakingTryNotes(cx, cg, script->trynotes); + + /* + * We initialize fun->u.script to be the script constructed above + * so that the debugger has a valid FUN_SCRIPT(fun). + */ + if (fun) { + JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); + fun->u.i.script = script; + if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) + fun->flags |= JSFUN_HEAVYWEIGHT; + } + + /* Tell the debugger about this compiled script. */ + js_CallNewScriptHook(cx, script, fun); + return script; + +bad: + js_DestroyScript(cx, script); + return NULL; +} + +JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) +{ + JSRuntime *rt; + JSNewScriptHook hook; + + rt = cx->runtime; + hook = rt->newScriptHook; + if (hook) { + JS_KEEP_ATOMS(rt); + hook(cx, script->filename, script->lineno, script, fun, + rt->newScriptHookData); + JS_UNKEEP_ATOMS(rt); + } +} + +JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script) +{ + JSRuntime *rt; + JSDestroyScriptHook hook; + + rt = cx->runtime; + hook = rt->destroyScriptHook; + if (hook) + hook(cx, script, rt->destroyScriptHookData); +} + +void +js_DestroyScript(JSContext *cx, JSScript *script) +{ + js_CallDestroyScriptHook(cx, script); + + JS_ClearScriptTraps(cx, script); + js_FreeAtomMap(cx, &script->atomMap); + if (script->principals) + JSPRINCIPALS_DROP(cx, script->principals); + if (JS_GSN_CACHE(cx).script == script) + JS_CLEAR_GSN_CACHE(cx); + JS_free(cx, script); +} + +void +js_MarkScript(JSContext *cx, JSScript *script) +{ + JSAtomMap *map; + uintN i, length; + JSAtom **vector; + + map = &script->atomMap; + length = map->length; + vector = map->vector; + for (i = 0; i < length; i++) + GC_MARK_ATOM(cx, vector[i]); + + if (script->filename) + js_MarkScriptFilename(script->filename); +} + +typedef struct GSNCacheEntry { + JSDHashEntryHdr hdr; + jsbytecode *pc; + jssrcnote *sn; +} GSNCacheEntry; + +#define GSN_CACHE_THRESHOLD 100 + +jssrcnote * +js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + ptrdiff_t target, offset; + GSNCacheEntry *entry; + jssrcnote *sn, *result; + uintN nsrcnotes; + + + target = PTRDIFF(pc, script->code, jsbytecode); + if ((uint32)target >= script->length) + return NULL; + + if (JS_GSN_CACHE(cx).script == script) { + JS_METER_GSN_CACHE(cx, hits); + entry = (GSNCacheEntry *) + JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, + JS_DHASH_LOOKUP); + return entry->sn; + } + + JS_METER_GSN_CACHE(cx, misses); + offset = 0; + for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) { + if (SN_IS_TERMINATOR(sn)) { + result = NULL; + break; + } + offset += SN_DELTA(sn); + if (offset == target && SN_IS_GETTABLE(sn)) { + result = sn; + break; + } + } + + if (JS_GSN_CACHE(cx).script != script && + script->length >= GSN_CACHE_THRESHOLD) { + JS_CLEAR_GSN_CACHE(cx); + nsrcnotes = 0; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); + sn = SN_NEXT(sn)) { + if (SN_IS_GETTABLE(sn)) + ++nsrcnotes; + } + if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), + NULL, sizeof(GSNCacheEntry), nsrcnotes)) { + JS_GSN_CACHE(cx).table.ops = NULL; + } else { + pc = script->code; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); + sn = SN_NEXT(sn)) { + pc += SN_DELTA(sn); + if (SN_IS_GETTABLE(sn)) { + entry = (GSNCacheEntry *) + JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, + JS_DHASH_ADD); + entry->pc = pc; + entry->sn = sn; + } + } + JS_GSN_CACHE(cx).script = script; + JS_METER_GSN_CACHE(cx, fills); + } + } + + return result; +} + +uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) +{ + JSAtom *atom; + JSFunction *fun; + uintN lineno; + ptrdiff_t offset, target; + jssrcnote *sn; + JSSrcNoteType type; + + /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ + if (!pc) + return 0; + + /* + * Special case: function definition needs no line number note because + * the function's script contains its starting line number. + */ + if (*pc == JSOP_DEFFUN || + (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) { + atom = js_GetAtom(cx, &script->atomMap, + (*pc == JSOP_DEFFUN) + ? GET_ATOM_INDEX(pc) + : GET_LITERAL_INDEX(pc)); + fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); + JS_ASSERT(FUN_INTERPRETED(fun)); + return fun->u.i.script->lineno; + } + + /* + * General case: walk through source notes accumulating their deltas, + * keeping track of line-number notes, until we pass the note for pc's + * offset within script->code. + */ + lineno = script->lineno; + offset = 0; + target = PTRDIFF(pc, script->code, jsbytecode); + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + offset += SN_DELTA(sn); + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + if (offset <= target) + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + if (offset <= target) + lineno++; + } + if (offset > target) + break; + } + return lineno; +} + +/* The line number limit is the same as the jssrcnote offset limit. */ +#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) + +jsbytecode * +js_LineNumberToPC(JSScript *script, uintN target) +{ + ptrdiff_t offset, best; + uintN lineno, bestdiff, diff; + jssrcnote *sn; + JSSrcNoteType type; + + offset = 0; + best = -1; + lineno = script->lineno; + bestdiff = SN_LINE_LIMIT; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + if (lineno == target) + goto out; + if (lineno > target) { + diff = lineno - target; + if (diff < bestdiff) { + bestdiff = diff; + best = offset; + } + } + offset += SN_DELTA(sn); + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + lineno++; + } + } + if (best >= 0) + offset = best; +out: + return script->code + offset; +} + +JS_FRIEND_API(uintN) +js_GetScriptLineExtent(JSScript *script) +{ + uintN lineno; + jssrcnote *sn; + JSSrcNoteType type; + + lineno = script->lineno; + for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { + type = (JSSrcNoteType) SN_TYPE(sn); + if (type == SRC_SETLINE) { + lineno = (uintN) js_GetSrcNoteOffset(sn, 0); + } else if (type == SRC_NEWLINE) { + lineno++; + } + } + return 1 + lineno - script->lineno; +} + +#if JS_HAS_GENERATORS + +jsbytecode * +js_FindFinallyHandler(JSScript *script, jsbytecode *pc) +{ + JSTryNote *tn; + ptrdiff_t off; + JSOp op2; + + tn = script->trynotes; + if (!tn) + return NULL; + + off = pc - script->main; + if (off < 0) + return NULL; + + JS_ASSERT(tn->catchStart != 0); + do { + if ((jsuword)(off - tn->start) < (jsuword)tn->length) { + /* + * We have a handler: is it the finally one, or a catch handler? + * + * Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK + * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION) + */ + pc = script->main + tn->catchStart; + JS_ASSERT(*pc == JSOP_SETSP); + op2 = pc[JSOP_SETSP_LENGTH]; + if (op2 != JSOP_ENTERBLOCK) { + JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION); + return pc; + } + } + } while ((++tn)->catchStart != 0); + return NULL; +} + +#endif diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscript.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscript.h new file mode 100644 index 0000000..18ad373 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsscript.h @@ -0,0 +1,225 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsscript_h___ +#define jsscript_h___ +/* + * JS script descriptor. + */ +#include "jsatom.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* + * Exception handling runtime information. + * + * All fields except length are code offsets relative to the main entry point + * of the script. If script->trynotes is not null, it points to a vector of + * these structs terminated by one with catchStart == 0. + */ +struct JSTryNote { + ptrdiff_t start; /* start of try statement */ + ptrdiff_t length; /* count of try statement bytecodes */ + ptrdiff_t catchStart; /* start of catch block (0 if end) */ +}; + +#define JSTRYNOTE_GRAIN sizeof(ptrdiff_t) +#define JSTRYNOTE_ALIGNMASK (JSTRYNOTE_GRAIN - 1) + +struct JSScript { + jsbytecode *code; /* bytecodes and their immediate operands */ + uint32 length; /* length of code vector */ + jsbytecode *main; /* main entry point, after predef'ing prolog */ + uint16 version; /* JS version under which script was compiled */ + uint16 numGlobalVars; /* declared global var/const/function count */ + JSAtomMap atomMap; /* maps immediate index to literal struct */ + const char *filename; /* source filename or null */ + uintN lineno; /* base line number of script */ + uintN depth; /* maximum stack depth in slots */ + JSTryNote *trynotes; /* exception table for this script */ + JSPrincipals *principals; /* principals for this script */ + JSObject *object; /* optional Script-class object wrapper */ +}; + +/* No need to store script->notes now that it is allocated right after code. */ +#define SCRIPT_NOTES(script) ((jssrcnote*)((script)->code+(script)->length)) + +#define SCRIPT_FIND_CATCH_START(script, pc, catchpc) \ + JS_BEGIN_MACRO \ + JSTryNote *tn_ = (script)->trynotes; \ + jsbytecode *catchpc_ = NULL; \ + if (tn_) { \ + ptrdiff_t off_ = PTRDIFF(pc, (script)->main, jsbytecode); \ + if (off_ >= 0) { \ + while ((jsuword)(off_ - tn_->start) >= (jsuword)tn_->length) \ + ++tn_; \ + if (tn_->catchStart) \ + catchpc_ = (script)->main + tn_->catchStart; \ + } \ + } \ + catchpc = catchpc_; \ + JS_END_MACRO + +/* + * Find the innermost finally block that handles the given pc. This is a + * version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used + * to implement generator.close(). + */ +jsbytecode * +js_FindFinallyHandler(JSScript *script, jsbytecode *pc); + +extern JS_FRIEND_DATA(JSClass) js_ScriptClass; + +extern JSObject * +js_InitScriptClass(JSContext *cx, JSObject *obj); + +/* + * On first new context in rt, initialize script runtime state, specifically + * the script filename table and its lock. + */ +extern JSBool +js_InitRuntimeScriptState(JSRuntime *rt); + +/* + * On last context destroy for rt, if script filenames are all GC'd, free the + * script filename table and its lock. + */ +extern void +js_FinishRuntimeScriptState(JSRuntime *rt); + +/* + * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any + * script filename table entries that have not been GC'd, the latter using + * js_FinishRuntimeScriptState. + * + * This allows script filename prefixes to outlive any context in rt. + */ +extern void +js_FreeRuntimeScriptState(JSRuntime *rt); + +extern const char * +js_SaveScriptFilename(JSContext *cx, const char *filename); + +extern const char * +js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags); + +extern uint32 +js_GetScriptFilenameFlags(const char *filename); + +extern void +js_MarkScriptFilename(const char *filename); + +extern void +js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms); + +extern void +js_SweepScriptFilenames(JSRuntime *rt); + +/* + * Two successively less primitive ways to make a new JSScript. The first + * does *not* call a non-null cx->runtime->newScriptHook -- only the second, + * js_NewScriptFromCG, calls this optional debugger hook. + * + * The js_NewScript function can't know whether the script it creates belongs + * to a function, or is top-level or eval code, but the debugger wants access + * to the newly made script's function, if any -- so callers of js_NewScript + * are responsible for notifying the debugger after successfully creating any + * kind (function or other) of new JSScript. + */ +extern JSScript * +js_NewScript(JSContext *cx, uint32 length, uint32 snlength, uint32 tnlength); + +extern JS_FRIEND_API(JSScript *) +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); + +/* + * New-script-hook calling is factored from js_NewScriptFromCG so that it + * and callers of js_XDRScript can share this code. In the case of callers + * of js_XDRScript, the hook should be invoked only after successful decode + * of any owning function (the fun parameter) or script object (null fun). + */ +extern JS_FRIEND_API(void) +js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); + +extern JS_FRIEND_API(void) +js_CallDestroyScriptHook(JSContext *cx, JSScript *script); + +extern void +js_DestroyScript(JSContext *cx, JSScript *script); + +extern void +js_MarkScript(JSContext *cx, JSScript *script); + +/* + * To perturb as little code as possible, we introduce a js_GetSrcNote lookup + * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes + * a macro that uses cx from its calls' lexical environments. + */ +#define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc) + +extern jssrcnote * +js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); + +/* XXX need cx to lock function objects declared by prolog bytecodes. */ +extern uintN +js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); + +extern jsbytecode * +js_LineNumberToPC(JSScript *script, uintN lineno); + +extern JS_FRIEND_API(uintN) +js_GetScriptLineExtent(JSScript *script); + +/* + * If magic is non-null, js_XDRScript succeeds on magic number mismatch but + * returns false in *magic; it reflects a match via a true *magic out param. + * If magic is null, js_XDRScript returns false on bad magic number errors, + * which it reports. + * + * NB: callers must call js_CallNewScriptHook after successful JSXDR_DECODE + * and subsequent set-up of owning function or script object, if any. + */ +extern JSBool +js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic); + +JS_END_EXTERN_C + +#endif /* jsscript_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsshell.msg b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsshell.msg new file mode 100644 index 0000000..4b811ac --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsshell.msg @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + Error messages for JSShell. See js.msg for format. +*/ + +MSG_DEF(JSSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") +MSG_DEF(JSSMSG_CANT_OPEN, 1, 2, JSEXN_NONE, "can't open {0}: {1}") +MSG_DEF(JSSMSG_TRAP_USAGE, 2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") +MSG_DEF(JSSMSG_LINE2PC_USAGE, 3, 0, JSEXN_NONE, "usage: line2pc [fun] line") +MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scripts read from files") +MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}") +MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id") diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstddef.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstddef.h new file mode 100644 index 0000000..addaa88 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstddef.h @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * stddef inclusion here to first declare ptrdif as a signed long instead of a + * signed int. + */ + +#ifdef _WINDOWS +# ifndef XP_WIN +# define XP_WIN +# endif +#if defined(_WIN32) || defined(WIN32) +# ifndef XP_WIN32 +# define XP_WIN32 +# endif +#else +# ifndef XP_WIN16 +# define XP_WIN16 +# endif +#endif +#endif + +#ifdef XP_WIN16 +#ifndef _PTRDIFF_T_DEFINED +typedef long ptrdiff_t; + +/* + * The Win16 compiler treats pointer differences as 16-bit signed values. + * This macro allows us to treat them as 17-bit signed values, stored in + * a 32-bit type. + */ +#define PTRDIFF(p1, p2, type) \ + ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type)) + +#define _PTRDIFF_T_DEFINED +#endif /*_PTRDIFF_T_DEFINED*/ +#else /*WIN16*/ + +#define PTRDIFF(p1, p2, type) \ + ((p1) - (p2)) + +#endif + +#include + + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstr.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstr.c new file mode 100644 index 0000000..e38f652 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstr.c @@ -0,0 +1,4818 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * JS string type implementation. + * + * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these + * native methods store strings (possibly newborn) converted from their 'this' + * parameter and arguments on the stack: 'this' conversions at argv[-1], arg + * conversions at their index (argv[0], argv[1]). This is a legitimate method + * of rooting things that might lose their newborn root due to subsequent GC + * allocations in the same native method. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jshash.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsregexp.h" +#include "jsstr.h" + +#define JSSTRDEP_RECURSION_LIMIT 100 + +size_t +js_MinimizeDependentStrings(JSString *str, int level, JSString **basep) +{ + JSString *base; + size_t start, length; + + JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); + base = JSSTRDEP_BASE(str); + start = JSSTRDEP_START(str); + if (JSSTRING_IS_DEPENDENT(base)) { + if (level < JSSTRDEP_RECURSION_LIMIT) { + start += js_MinimizeDependentStrings(base, level + 1, &base); + } else { + do { + start += JSSTRDEP_START(base); + base = JSSTRDEP_BASE(base); + } while (JSSTRING_IS_DEPENDENT(base)); + } + if (start == 0) { + JS_ASSERT(JSSTRING_IS_PREFIX(str)); + JSPREFIX_SET_BASE(str, base); + } else if (start <= JSSTRDEP_START_MASK) { + length = JSSTRDEP_LENGTH(str); + JSSTRDEP_SET_START_AND_LENGTH(str, start, length); + JSSTRDEP_SET_BASE(str, base); + } + } + *basep = base; + return start; +} + +jschar * +js_GetDependentStringChars(JSString *str) +{ + size_t start; + JSString *base; + + start = js_MinimizeDependentStrings(str, 0, &base); + JS_ASSERT(!JSSTRING_IS_DEPENDENT(base)); + JS_ASSERT(start < base->length); + return base->chars + start; +} + +jschar * +js_GetStringChars(JSString *str) +{ + if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str)) + return NULL; + + *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; + return str->chars; +} + +JSString * +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) +{ + size_t rn, ln, lrdist, n; + jschar *rs, *ls, *s; + JSDependentString *ldep; /* non-null if left should become dependent */ + JSString *str; + + if (JSSTRING_IS_DEPENDENT(right)) { + rn = JSSTRDEP_LENGTH(right); + rs = JSSTRDEP_CHARS(right); + } else { + rn = right->length; + rs = right->chars; + } + if (rn == 0) + return left; + + if (JSSTRING_IS_DEPENDENT(left) || + !(*js_GetGCThingFlags(left) & GCF_MUTABLE)) { + /* We must copy if left does not own a buffer to realloc. */ + ln = JSSTRING_LENGTH(left); + if (ln == 0) + return right; + ls = JSSTRING_CHARS(left); + s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar)); + if (!s) + return NULL; + js_strncpy(s, ls, ln); + ldep = NULL; + } else { + /* We can realloc left's space and make it depend on our result. */ + ln = left->length; + if (ln == 0) + return right; + ls = left->chars; + s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar)); + if (!s) + return NULL; + + /* Take care: right could depend on left! */ + lrdist = (size_t)(rs - ls); + if (lrdist < ln) + rs = s + lrdist; + left->chars = ls = s; + ldep = JSSTRDEP(left); + } + + js_strncpy(s + ln, rs, rn); + n = ln + rn; + s[n] = 0; + str = js_NewString(cx, s, n, GCF_MUTABLE); + if (!str) { + /* Out of memory: clean up any space we (re-)allocated. */ + if (!ldep) { + JS_free(cx, s); + } else { + s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar)); + if (s) + left->chars = s; + } + } else { + /* Morph left into a dependent prefix if we realloc'd its buffer. */ + if (ldep) { + JSPREFIX_SET_LENGTH(ldep, ln); + JSPREFIX_SET_BASE(ldep, str); +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveDependentStrings); + JS_RUNTIME_METER(rt, totalDependentStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum += (double)ln, + rt->strdepLengthSquaredSum += (double)ln * (double)ln)); + } +#endif + } + } + + return str; +} + +/* + * May be called with null cx by js_GetStringChars, above; and by the jslock.c + * MAKE_STRING_IMMUTABLE file-local macro. + */ +const jschar * +js_UndependString(JSContext *cx, JSString *str) +{ + size_t n, size; + jschar *s; + + if (JSSTRING_IS_DEPENDENT(str)) { + n = JSSTRDEP_LENGTH(str); + size = (n + 1) * sizeof(jschar); + s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size)); + if (!s) + return NULL; + + js_strncpy(s, JSSTRDEP_CHARS(str), n); + s[n] = 0; + str->length = n; + str->chars = s; + +#ifdef DEBUG + if (cx) { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_UNMETER(rt, liveDependentStrings); + JS_RUNTIME_UNMETER(rt, totalDependentStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum -= (double)n, + rt->strdepLengthSquaredSum -= (double)n * (double)n)); + } +#endif + } + + return str->chars; +} + +/* + * Forward declarations for URI encode/decode and helper routines + */ +static JSBool +str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static JSBool +str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +static uint32 +Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length); + +/* + * Contributions from the String class to the set of methods defined for the + * global object. escape and unescape used to be defined in the Mocha library, + * but as ECMA decided to spec them, they've been moved to the core engine + * and made ECMA-compliant. (Incomplete escapes are interpreted as literal + * characters by unescape.) + */ + +/* + * Stuff to emulate the old libmocha escape, which took a second argument + * giving the type of escape to perform. Retained for compatibility, and + * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. + */ + +#define URL_XALPHAS ((uint8) 1) +#define URL_XPALPHAS ((uint8) 2) +#define URL_PATH ((uint8) 4) + +static const uint8 urlCharType[256] = +/* Bit 0 xalpha -- the alphas + * Bit 1 xpalpha -- as xalpha but + * converts spaces to plus and plus to %20 + * Bit 2 ... path -- as xalphas but doesn't escape '/' + */ + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ + 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ + 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ + 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ + 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ + 0, }; + +/* This matches the ECMA escape set when mask is 7 (default.) */ + +#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask)) + +/* See ECMA-262 15.1.2.4. */ +JSBool +js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + size_t i, ni, length, newlength; + const jschar *chars; + jschar *newchars; + jschar ch; + jsint mask; + jsdouble d; + const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + if (!JSDOUBLE_IS_FINITE(d) || + (mask = (jsint)d) != d || + mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) + { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_STRING_MASK, numBuf); + return JS_FALSE; + } + } + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + chars = JSSTRING_CHARS(str); + length = newlength = JSSTRING_LENGTH(str); + + /* Take a first pass and see how big the result string will need to be. */ + for (i = 0; i < length; i++) { + if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) + continue; + if (ch < 256) { + if (mask == URL_XPALPHAS && ch == ' ') + continue; /* The character will be encoded as '+' */ + newlength += 2; /* The character will be encoded as %XX */ + } else { + newlength += 5; /* The character will be encoded as %uXXXX */ + } + + /* + * This overflow test works because newlength is incremented by at + * most 5 on each iteration. + */ + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + + if (newlength >= ~(size_t)0 / sizeof(jschar)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!newchars) + return JS_FALSE; + for (i = 0, ni = 0; i < length; i++) { + if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { + newchars[ni++] = ch; + } else if (ch < 256) { + if (mask == URL_XPALPHAS && ch == ' ') { + newchars[ni++] = '+'; /* convert spaces to pluses */ + } else { + newchars[ni++] = '%'; + newchars[ni++] = digits[ch >> 4]; + newchars[ni++] = digits[ch & 0xF]; + } + } else { + newchars[ni++] = '%'; + newchars[ni++] = 'u'; + newchars[ni++] = digits[ch >> 12]; + newchars[ni++] = digits[(ch & 0xF00) >> 8]; + newchars[ni++] = digits[(ch & 0xF0) >> 4]; + newchars[ni++] = digits[ch & 0xF]; + } + } + JS_ASSERT(ni == newlength); + newchars[newlength] = 0; + + str = js_NewString(cx, newchars, newlength, 0); + if (!str) { + JS_free(cx, newchars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#undef IS_OK + +/* See ECMA-262 15.1.2.5 */ +static JSBool +str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + size_t i, ni, length; + const jschar *chars; + jschar *newchars; + jschar ch; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + + chars = JSSTRING_CHARS(str); + length = JSSTRING_LENGTH(str); + + /* Don't bother allocating less space for the new string. */ + newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!newchars) + return JS_FALSE; + ni = i = 0; + while (i < length) { + ch = chars[i++]; + if (ch == '%') { + if (i + 1 < length && + JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) + { + ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]); + i += 2; + } else if (i + 4 < length && chars[i] == 'u' && + JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) && + JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4])) + { + ch = (((((JS7_UNHEX(chars[i + 1]) << 4) + + JS7_UNHEX(chars[i + 2])) << 4) + + JS7_UNHEX(chars[i + 3])) << 4) + + JS7_UNHEX(chars[i + 4]); + i += 5; + } + } + newchars[ni++] = ch; + } + newchars[ni] = 0; + + str = js_NewString(cx, newchars, ni, 0); + if (!str) { + JS_free(cx, newchars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#if JS_HAS_UNEVAL +static JSBool +str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = js_ValueToSource(cx, argv[0]); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif + +const char js_escape_str[] = "escape"; +const char js_unescape_str[] = "unescape"; +#if JS_HAS_UNEVAL +const char js_uneval_str[] = "uneval"; +#endif +const char js_decodeURI_str[] = "decodeURI"; +const char js_encodeURI_str[] = "encodeURI"; +const char js_decodeURIComponent_str[] = "decodeURIComponent"; +const char js_encodeURIComponent_str[] = "encodeURIComponent"; + +static JSFunctionSpec string_functions[] = { + {js_escape_str, js_str_escape, 1,0,0}, + {js_unescape_str, str_unescape, 1,0,0}, +#if JS_HAS_UNEVAL + {js_uneval_str, str_uneval, 1,0,0}, +#endif + {js_decodeURI_str, str_decodeURI, 1,0,0}, + {js_encodeURI_str, str_encodeURI, 1,0,0}, + {js_decodeURIComponent_str, str_decodeURI_Component, 1,0,0}, + {js_encodeURIComponent_str, str_encodeURI_Component, 1,0,0}, + + {0,0,0,0,0} +}; + +jschar js_empty_ucstr[] = {0}; +JSSubString js_EmptySubString = {0, js_empty_ucstr}; + +enum string_tinyid { + STRING_LENGTH = -1 +}; + +static JSPropertySpec string_props[] = { + {js_length_str, STRING_LENGTH, + JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0}, + {0,0,0,0,0} +}; + +static JSBool +str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + jsval v; + JSString *str; + jsint slot; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + slot = JSVAL_TO_INT(id); + if (slot == STRING_LENGTH) { + if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { + /* Follow ECMA-262 by fetching intrinsic length of our string. */ + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + } else { + /* Preserve compatibility: convert obj to a string primitive. */ + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + } + + *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str)); + } + return JS_TRUE; +} + +#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) + +static JSBool +str_enumerate(JSContext *cx, JSObject *obj) +{ + jsval v; + JSString *str, *str1; + size_t i, length; + + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + + length = JSSTRING_LENGTH(str); + for (i = 0; i < length; i++) { + str1 = js_NewDependentString(cx, str, i, 1, 0); + if (!str1) + return JS_FALSE; + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i), + STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS, NULL)) { + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + jsval v; + JSString *str, *str1; + jsint slot; + + if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) + return JS_TRUE; + + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(JSVAL_IS_STRING(v)); + str = JSVAL_TO_STRING(v); + + slot = JSVAL_TO_INT(id); + if ((size_t)slot < JSSTRING_LENGTH(str)) { + str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0); + if (!str1) + return JS_FALSE; + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot), + STRING_TO_JSVAL(str1), NULL, NULL, + STRING_ELEMENT_ATTRS, NULL)) { + return JS_FALSE; + } + *objp = obj; + } + return JS_TRUE; +} + +JSClass js_StringClass = { + js_String_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | + JSCLASS_HAS_CACHED_PROTO(JSProto_String), + JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, + str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +#if JS_HAS_TOSOURCE + +/* + * String.prototype.quote is generic (as are most string methods), unlike + * toSource, toString, and valueOf. + */ +static JSBool +str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + str = js_QuoteString(cx, str, '"'); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSString *str; + size_t i, j, k, n; + char buf[16]; + jschar *s, *t; + + if (JSVAL_IS_STRING((jsval)obj)) { + v = (jsval)obj; + } else { + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toSource(cx, obj, argc, argv, rval); + } + str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (!str) + return JS_FALSE; + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); + s = JSSTRING_CHARS(str); + k = JSSTRING_LENGTH(str); + n = j + k + 2; + t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!t) + return JS_FALSE; + for (i = 0; i < j; i++) + t[i] = buf[i]; + for (j = 0; j < k; i++, j++) + t[i] = s[j]; + t[i++] = ')'; + t[i++] = ')'; + t[i] = 0; + str = js_NewString(cx, t, n, 0); + if (!str) { + JS_free(cx, t); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#endif /* JS_HAS_TOSOURCE */ + +static JSBool +str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + + if (JSVAL_IS_STRING((jsval)obj)) { + *rval = (jsval)obj; + return JS_TRUE; + } + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) + return JS_FALSE; + v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_STRING(v)) + return js_obj_toString(cx, obj, argc, argv, rval); + *rval = v; + return JS_TRUE; +} + +static JSBool +str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + if (JSVAL_IS_STRING((jsval)obj)) { + *rval = (jsval)obj; + return JS_TRUE; + } + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) + return JS_FALSE; + *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + return JS_TRUE; +} + +/* + * Java-like string native methods. + */ +static JSBool +str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + begin = js_DoubleToInteger(d); + if (begin < 0) + begin = 0; + else if (begin > length) + begin = length; + + if (argc == 1) { + end = length; + } else { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) + end = 0; + else if (end > length) + end = length; + if (end < begin) { + /* ECMA emulates old JDK1.0 java.lang.String.substring. */ + jsdouble tmp = begin; + begin = end; + end = tmp; + } + } + + str = js_NewDependentString(cx, str, (size_t)begin, + (size_t)(end - begin), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + size_t i, n; + jschar *s, *news; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + n = JSSTRING_LENGTH(str); + news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return JS_FALSE; + s = JSSTRING_CHARS(str); + for (i = 0; i < n; i++) + news[i] = JS_TOLOWER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n, 0); + if (!str) { + JS_free(cx, news); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + /* + * Forcefully ignore the first (or any) argument and return toLowerCase(), + * ECMA has reserved that argument, presumably for defining the locale. + */ + if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + return cx->localeCallbacks->localeToLowerCase(cx, str, rval); + } + return str_toLowerCase(cx, obj, 0, argv, rval); +} + +static JSBool +str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + size_t i, n; + jschar *s, *news; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + n = JSSTRING_LENGTH(str); + news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return JS_FALSE; + s = JSSTRING_CHARS(str); + for (i = 0; i < n; i++) + news[i] = JS_TOUPPER(s[i]); + news[n] = 0; + str = js_NewString(cx, news, n, 0); + if (!str) { + JS_free(cx, news); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + /* + * Forcefully ignore the first (or any) argument and return toUpperCase(), + * ECMA has reserved that argument, presumbaly for defining the locale. + */ + if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + return cx->localeCallbacks->localeToUpperCase(cx, str, rval); + } + return str_toUpperCase(cx, obj, 0, argv, rval); +} + +static JSBool +str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str, *thatStr; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + *rval = JSVAL_ZERO; + } else { + thatStr = js_ValueToString(cx, argv[0]); + if (!thatStr) + return JS_FALSE; + if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { + argv[0] = STRING_TO_JSVAL(thatStr); + return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval); + } + *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); + } + return JS_TRUE; +} + +static JSBool +str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + size_t index; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + d = 0.0; + } else { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || JSSTRING_LENGTH(str) <= d) { + *rval = JS_GetEmptyStringValue(cx); + } else { + index = (size_t)d; + str = js_NewDependentString(cx, str, index, 1, 0); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +static JSBool +str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + jsdouble d; + size_t index; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc == 0) { + d = 0.0; + } else { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + } + + if (d < 0 || JSSTRING_LENGTH(str) <= d) { + *rval = JS_GetNaNValue(cx); + } else { + index = (size_t)d; + *rval = INT_TO_JSVAL((jsint) JSSTRING_CHARS(str)[index]); + } + return JS_TRUE; +} + +jsint +js_BoyerMooreHorspool(const jschar *text, jsint textlen, + const jschar *pat, jsint patlen, + jsint start) +{ + jsint i, j, k, m; + uint8 skip[BMH_CHARSET_SIZE]; + jschar c; + + JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX); + for (i = 0; i < BMH_CHARSET_SIZE; i++) + skip[i] = (uint8)patlen; + m = patlen - 1; + for (i = 0; i < m; i++) { + c = pat[i]; + if (c >= BMH_CHARSET_SIZE) + return BMH_BAD_PATTERN; + skip[c] = (uint8)(m - i); + } + for (k = start + m; + k < textlen; + k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) { + for (i = k, j = m; ; i--, j--) { + if (j < 0) + return i + 1; + if (text[i] != pat[j]) + break; + } + } + return -1; +} + +static JSBool +str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *str2; + jsint i, j, index, textlen, patlen; + const jschar *text, *pat; + jsdouble d; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + text = JSSTRING_CHARS(str); + textlen = (jsint) JSSTRING_LENGTH(str); + + str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + pat = JSSTRING_CHARS(str2); + patlen = (jsint) JSSTRING_LENGTH(str2); + + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + d = js_DoubleToInteger(d); + if (d < 0) + i = 0; + else if (d > textlen) + i = textlen; + else + i = (jsint)d; + } else { + i = 0; + } + if (patlen == 0) { + *rval = INT_TO_JSVAL(i); + return JS_TRUE; + } + + /* XXX tune the BMH threshold (512) */ + if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) { + index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i); + if (index != BMH_BAD_PATTERN) + goto out; + } + + index = -1; + j = 0; + while (i + j < textlen) { + if (text[i + j] == pat[j]) { + if (++j == patlen) { + index = i; + break; + } + } else { + i++; + j = 0; + } + } + +out: + *rval = INT_TO_JSVAL(index); + return JS_TRUE; +} + +static JSBool +str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str, *str2; + const jschar *text, *pat; + jsint i, j, textlen, patlen; + jsdouble d; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + text = JSSTRING_CHARS(str); + textlen = (jsint) JSSTRING_LENGTH(str); + + str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + pat = JSSTRING_CHARS(str2); + patlen = (jsint) JSSTRING_LENGTH(str2); + + if (argc > 1) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + if (JSDOUBLE_IS_NaN(d)) { + i = textlen; + } else { + d = js_DoubleToInteger(d); + if (d < 0) + i = 0; + else if (d > textlen) + i = textlen; + else + i = (jsint)d; + } + } else { + i = textlen; + } + + if (patlen == 0) { + *rval = INT_TO_JSVAL(i); + return JS_TRUE; + } + + j = 0; + while (i >= 0) { + /* Don't assume that text is NUL-terminated: it could be dependent. */ + if (i + j < textlen && text[i + j] == pat[j]) { + if (++j == patlen) + break; + } else { + i--; + j = 0; + } + } + *rval = INT_TO_JSVAL(i); + return JS_TRUE; +} + +/* + * Perl-inspired string functions. + */ +typedef struct GlobData { + uintN flags; /* inout: mode and flag bits, see below */ + uintN optarg; /* in: index of optional flags argument */ + JSString *str; /* out: 'this' parameter object as string */ + JSRegExp *regexp; /* out: regexp parameter object private data */ +} GlobData; + +/* + * Mode and flag bit definitions for match_or_replace's GlobData.flags field. + */ +#define MODE_MATCH 0x00 /* in: return match array on success */ +#define MODE_REPLACE 0x01 /* in: match and replace */ +#define MODE_SEARCH 0x02 /* in: search only, return match index or -1 */ +#define GET_MODE(f) ((f) & 0x03) +#define FORCE_FLAT 0x04 /* in: force flat (non-regexp) string match */ +#define KEEP_REGEXP 0x08 /* inout: keep GlobData.regexp alive for caller + of match_or_replace; if set on input + but clear on output, regexp ownership + does not pass to caller */ +#define GLOBAL_REGEXP 0x10 /* out: regexp had the 'g' flag */ + +static JSBool +match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + JSBool (*glob)(JSContext *cx, jsint count, GlobData *data), + GlobData *data, jsval *rval) +{ + JSString *str, *src, *opt; + JSObject *reobj; + JSRegExp *re; + size_t index, length; + JSBool ok, test; + jsint count; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + data->str = str; + + if (JSVAL_IS_REGEXP(cx, argv[0])) { + reobj = JSVAL_TO_OBJECT(argv[0]); + re = (JSRegExp *) JS_GetPrivate(cx, reobj); + } else { + src = js_ValueToString(cx, argv[0]); + if (!src) + return JS_FALSE; + if (data->optarg < argc) { + argv[0] = STRING_TO_JSVAL(src); + opt = js_ValueToString(cx, argv[data->optarg]); + if (!opt) + return JS_FALSE; + } else { + opt = NULL; + } + re = js_NewRegExpOpt(cx, NULL, src, opt, + (data->flags & FORCE_FLAT) != 0); + if (!re) + return JS_FALSE; + reobj = NULL; + } + /* From here on, all control flow must reach the matching DROP. */ + data->regexp = re; + HOLD_REGEXP(cx, re); + + if (re->flags & JSREG_GLOB) + data->flags |= GLOBAL_REGEXP; + index = 0; + if (GET_MODE(data->flags) == MODE_SEARCH) { + ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); + if (ok) { + *rval = (*rval == JSVAL_TRUE) + ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length) + : INT_TO_JSVAL(-1); + } + } else if (data->flags & GLOBAL_REGEXP) { + if (reobj) { + /* Set the lastIndex property's reserved slot to 0. */ + ok = js_SetLastIndex(cx, reobj, 0); + } else { + ok = JS_TRUE; + } + if (ok) { + length = JSSTRING_LENGTH(str); + for (count = 0; index <= length; count++) { + ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); + if (!ok || *rval != JSVAL_TRUE) + break; + ok = glob(cx, count, data); + if (!ok) + break; + if (cx->regExpStatics.lastMatch.length == 0) { + if (index == length) + break; + index++; + } + } + } + } else { + if (GET_MODE(data->flags) == MODE_REPLACE) { + test = JS_TRUE; + } else { + /* + * MODE_MATCH implies str_match is being called from a script or a + * scripted function. If the caller cares only about testing null + * vs. non-null return value, optimize away the array object that + * would normally be returned in *rval. + */ + JSStackFrame *fp = cx->fp->down; + + /* Skip Function.prototype.call and .apply frames. */ + while (fp && !fp->pc) { + JS_ASSERT(!fp->script); + fp = fp->down; + } + + /* Assume a full array result is required, then prove otherwise. */ + test = JS_FALSE; + if (fp) { + JS_ASSERT(*fp->pc == JSOP_CALL || *fp->pc == JSOP_NEW); + JS_ASSERT(js_CodeSpec[*fp->pc].length == 3); + switch (fp->pc[3]) { + case JSOP_POP: + case JSOP_IFEQ: + case JSOP_IFNE: + case JSOP_IFEQX: + case JSOP_IFNEX: + test = JS_TRUE; + break; + default:; + } + } + } + ok = js_ExecuteRegExp(cx, re, str, &index, test, rval); + } + + DROP_REGEXP(cx, re); + if (reobj) { + /* Tell our caller that it doesn't need to destroy data->regexp. */ + data->flags &= ~KEEP_REGEXP; + } else if (!(data->flags & KEEP_REGEXP)) { + /* Caller didn't want to keep data->regexp, so null and destroy it. */ + data->regexp = NULL; + js_DestroyRegExp(cx, re); + } + + return ok; +} + +typedef struct MatchData { + GlobData base; + jsval *arrayval; /* NB: local root pointer */ +} MatchData; + +static JSBool +match_glob(JSContext *cx, jsint count, GlobData *data) +{ + MatchData *mdata; + JSObject *arrayobj; + JSSubString *matchsub; + JSString *matchstr; + jsval v; + + mdata = (MatchData *)data; + arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval); + if (!arrayobj) { + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj); + } + matchsub = &cx->regExpStatics.lastMatch; + matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0); + if (!matchstr) + return JS_FALSE; + v = STRING_TO_JSVAL(matchstr); + return js_SetProperty(cx, arrayobj, INT_TO_JSID(count), &v); +} + +static JSBool +str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + MatchData mdata; + JSBool ok; + + mdata.base.flags = MODE_MATCH; + mdata.base.optarg = 1; + mdata.arrayval = &argv[2]; + *mdata.arrayval = JSVAL_NULL; + ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval); + if (ok && !JSVAL_IS_NULL(*mdata.arrayval)) + *rval = *mdata.arrayval; + return ok; +} + +static JSBool +str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + GlobData data; + + data.flags = MODE_SEARCH; + data.optarg = 1; + return match_or_replace(cx, obj, argc, argv, NULL, &data, rval); +} + +typedef struct ReplaceData { + GlobData base; /* base struct state */ + JSObject *lambda; /* replacement function object or null */ + JSString *repstr; /* replacement string */ + jschar *dollar; /* null or pointer to first $ in repstr */ + jschar *dollarEnd; /* limit pointer for js_strchr_limit */ + jschar *chars; /* result chars, null initially */ + size_t length; /* result length, 0 initially */ + jsint index; /* index in result of next replacement */ + jsint leftIndex; /* left context index in base.str->chars */ + JSSubString dollarStr; /* for "$$" interpret_dollar result */ +} ReplaceData; + +static JSSubString * +interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, + size_t *skip) +{ + JSRegExpStatics *res; + jschar dc, *cp; + uintN num, tmp; + + JS_ASSERT(*dp == '$'); + + /* If there is only a dollar, bail now */ + if (dp + 1 >= ep) + return NULL; + + /* Interpret all Perl match-induced dollar variables. */ + res = &cx->regExpStatics; + dc = dp[1]; + if (JS7_ISDEC(dc)) { + /* ECMA-262 Edition 3: 1-9 or 01-99 */ + num = JS7_UNDEC(dc); + if (num > res->parenCount) + return NULL; + + cp = dp + 2; + if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { + tmp = 10 * num + JS7_UNDEC(dc); + if (tmp <= res->parenCount) { + cp++; + num = tmp; + } + } + if (num == 0) + return NULL; + + /* Adjust num from 1 $n-origin to 0 array-index-origin. */ + num--; + *skip = cp - dp; + return REGEXP_PAREN_SUBSTRING(res, num); + } + + *skip = 2; + switch (dc) { + case '$': + rdata->dollarStr.chars = dp; + rdata->dollarStr.length = 1; + return &rdata->dollarStr; + case '&': + return &res->lastMatch; + case '+': + return &res->lastParen; + case '`': + return &res->leftContext; + case '\'': + return &res->rightContext; + } + return NULL; +} + +static JSBool +find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) +{ + JSString *repstr; + size_t replen, skip; + jschar *dp, *ep; + JSSubString *sub; + JSObject *lambda; + + lambda = rdata->lambda; + if (lambda) { + uintN argc, i, j, m, n, p; + jsval *sp, *oldsp, rval; + void *mark; + JSStackFrame *fp; + JSBool ok; + + /* + * Save the regExpStatics from the current regexp, since they may be + * clobbered by a RegExp usage in the lambda function. Note that all + * members of JSRegExpStatics are JSSubStrings, so not GC roots, save + * input, which is rooted otherwise via argv[-1] in str_replace. + */ + JSRegExpStatics save = cx->regExpStatics; + JSBool freeMoreParens = JS_FALSE; + + /* + * In the lambda case, not only do we find the replacement string's + * length, we compute repstr and return it via rdata for use within + * do_replace. The lambda is called with arguments ($&, $1, $2, ..., + * index, input), i.e., all the properties of a regexp match array. + * For $&, etc., we must create string jsvals from cx->regExpStatics. + * We grab up stack space to keep the newborn strings GC-rooted. + */ + p = rdata->base.regexp->parenCount; + argc = 1 + p + 2; + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) + return JS_FALSE; + + /* Push lambda and its 'this' parameter. */ + *sp++ = OBJECT_TO_JSVAL(lambda); + *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); + +#define PUSH_REGEXP_STATIC(sub) \ + JS_BEGIN_MACRO \ + JSString *str = js_NewStringCopyN(cx, \ + cx->regExpStatics.sub.chars, \ + cx->regExpStatics.sub.length, \ + 0); \ + if (!str) { \ + ok = JS_FALSE; \ + goto lambda_out; \ + } \ + *sp++ = STRING_TO_JSVAL(str); \ + JS_END_MACRO + + /* Push $&, $1, $2, ... */ + PUSH_REGEXP_STATIC(lastMatch); + i = 0; + m = cx->regExpStatics.parenCount; + n = JS_MIN(m, 9); + for (j = 0; i < n; i++, j++) + PUSH_REGEXP_STATIC(parens[j]); + for (j = 0; i < m; i++, j++) + PUSH_REGEXP_STATIC(moreParens[j]); + + /* + * We need to clear moreParens in the top-of-stack cx->regExpStatics + * to it won't be possibly realloc'ed, leaving the bottom-of-stack + * moreParens pointing to freed memory. + */ + cx->regExpStatics.moreParens = NULL; + freeMoreParens = JS_TRUE; + +#undef PUSH_REGEXP_STATIC + + /* Make sure to push undefined for any unmatched parens. */ + for (; i < p; i++) + *sp++ = JSVAL_VOID; + + /* Push match index and input string. */ + *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length); + *sp++ = STRING_TO_JSVAL(rdata->base.str); + + /* Lift current frame to include the args and do the call. */ + fp = cx->fp; + oldsp = fp->sp; + fp->sp = sp; + ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL); + rval = fp->sp[-1]; + fp->sp = oldsp; + + if (ok) { + /* + * NB: we count on the newborn string root to hold any string + * created by this js_ValueToString that would otherwise be GC- + * able, until we use rdata->repstr in do_replace. + */ + repstr = js_ValueToString(cx, rval); + if (!repstr) { + ok = JS_FALSE; + } else { + rdata->repstr = repstr; + *sizep = JSSTRING_LENGTH(repstr); + } + } + + lambda_out: + js_FreeStack(cx, mark); + if (freeMoreParens) + JS_free(cx, cx->regExpStatics.moreParens); + cx->regExpStatics = save; + return ok; + } + + repstr = rdata->repstr; + replen = JSSTRING_LENGTH(repstr); + for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { + sub = interpret_dollar(cx, dp, ep, rdata, &skip); + if (sub) { + replen += sub->length - skip; + dp += skip; + } + else + dp++; + } + *sizep = replen; + return JS_TRUE; +} + +static void +do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) +{ + JSString *repstr; + jschar *bp, *cp, *dp, *ep; + size_t len, skip; + JSSubString *sub; + + repstr = rdata->repstr; + bp = cp = JSSTRING_CHARS(repstr); + for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; + dp = js_strchr_limit(dp, '$', ep)) { + len = dp - cp; + js_strncpy(chars, cp, len); + chars += len; + cp = dp; + sub = interpret_dollar(cx, dp, ep, rdata, &skip); + if (sub) { + len = sub->length; + js_strncpy(chars, sub->chars, len); + chars += len; + cp += skip; + dp += skip; + } else { + dp++; + } + } + js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (cp - bp)); +} + +static JSBool +replace_glob(JSContext *cx, jsint count, GlobData *data) +{ + ReplaceData *rdata; + JSString *str; + size_t leftoff, leftlen, replen, growth; + const jschar *left; + jschar *chars; + + rdata = (ReplaceData *)data; + str = data->str; + leftoff = rdata->leftIndex; + left = JSSTRING_CHARS(str) + leftoff; + leftlen = cx->regExpStatics.lastMatch.chars - left; + rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str); + rdata->leftIndex += cx->regExpStatics.lastMatch.length; + if (!find_replen(cx, rdata, &replen)) + return JS_FALSE; + growth = leftlen + replen; + chars = (jschar *) + (rdata->chars + ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1) + * sizeof(jschar)) + : JS_malloc(cx, (growth + 1) * sizeof(jschar))); + if (!chars) { + JS_free(cx, rdata->chars); + rdata->chars = NULL; + return JS_FALSE; + } + rdata->chars = chars; + rdata->length += growth; + chars += rdata->index; + rdata->index += growth; + js_strncpy(chars, left, leftlen); + chars += leftlen; + do_replace(cx, rdata, chars); + return JS_TRUE; +} + +static JSBool +str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *lambda; + JSString *repstr, *str; + ReplaceData rdata; + JSBool ok; + jschar *chars; + size_t leftlen, rightlen, length; + + if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { + lambda = JSVAL_TO_OBJECT(argv[1]); + repstr = NULL; + } else { + if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) + return JS_FALSE; + repstr = JSVAL_TO_STRING(argv[1]); + lambda = NULL; + } + + /* + * For ECMA Edition 3, the first argument is to be converted to a string + * to match in a "flat" sense (without regular expression metachars having + * special meanings) UNLESS the first arg is a RegExp object. + */ + rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT; + rdata.base.optarg = 2; + + rdata.lambda = lambda; + rdata.repstr = repstr; + if (repstr) { + rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr); + rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$', + rdata.dollarEnd); + } else { + rdata.dollar = rdata.dollarEnd = NULL; + } + rdata.chars = NULL; + rdata.length = 0; + rdata.index = 0; + rdata.leftIndex = 0; + + ok = match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval); + if (!ok) + return JS_FALSE; + + if (!rdata.chars) { + if ((rdata.base.flags & GLOBAL_REGEXP) || *rval != JSVAL_TRUE) { + /* Didn't match even once. */ + *rval = STRING_TO_JSVAL(rdata.base.str); + goto out; + } + leftlen = cx->regExpStatics.leftContext.length; + ok = find_replen(cx, &rdata, &length); + if (!ok) + goto out; + length += leftlen; + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) { + ok = JS_FALSE; + goto out; + } + js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen); + do_replace(cx, &rdata, chars + leftlen); + rdata.chars = chars; + rdata.length = length; + } + + rightlen = cx->regExpStatics.rightContext.length; + length = rdata.length + rightlen; + chars = (jschar *) + JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar)); + if (!chars) { + JS_free(cx, rdata.chars); + ok = JS_FALSE; + goto out; + } + js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars, + rightlen); + chars[length] = 0; + + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + ok = JS_FALSE; + goto out; + } + *rval = STRING_TO_JSVAL(str); + +out: + /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */ + if (rdata.base.flags & KEEP_REGEXP) + js_DestroyRegExp(cx, rdata.base.regexp); + return ok; +} + +/* + * Subroutine used by str_split to find the next split point in str, starting + * at offset *ip and looking either for the separator substring given by sep, + * or for the next re match. In the re case, return the matched separator in + * *sep, and the possibly updated offset in *ip. + * + * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next + * separator occurrence if found, or str->length if no separator is found. + */ +static jsint +find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, + JSSubString *sep) +{ + jsint i, j, k; + size_t length; + jschar *chars; + + /* + * Stop if past end of string. If at end of string, we will compare the + * null char stored there (by js_NewString*) to sep->chars[j] in the while + * loop at the end of this function, so that + * + * "ab,".split(',') => ["ab", ""] + * + * and the resulting array converts back to the string "ab," for symmetry. + * However, we ape Perl and do this only if there is a sufficiently large + * limit argument (see str_split). + */ + i = *ip; + length = JSSTRING_LENGTH(str); + if ((size_t)i > length) + return -1; + + chars = JSSTRING_CHARS(str); + + /* + * Match a regular expression against the separator at or above index i. + * Call js_ExecuteRegExp with true for the test argument. On successful + * match, get the separator from cx->regExpStatics.lastMatch. + */ + if (re) { + size_t index; + jsval rval; + + again: + /* JS1.2 deviated from Perl by never matching at end of string. */ + index = (size_t)i; + if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) + return -2; + if (rval != JSVAL_TRUE) { + /* Mismatch: ensure our caller advances i past end of string. */ + sep->length = 1; + return length; + } + i = (jsint)index; + *sep = cx->regExpStatics.lastMatch; + if (sep->length == 0) { + /* + * Empty string match: never split on an empty match at the start + * of a find_split cycle. Same rule as for an empty global match + * in match_or_replace. + */ + if (i == *ip) { + /* + * "Bump-along" to avoid sticking at an empty match, but don't + * bump past end of string -- our caller must do that by adding + * sep->length to our return value. + */ + if ((size_t)i == length) + return -1; + i++; + goto again; + } + if ((size_t)i == length) { + /* + * If there was a trivial zero-length match at the end of the + * split, then we shouldn't output the matched string at the end + * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15. + */ + sep->chars = NULL; + } + } + JS_ASSERT((size_t)i >= sep->length); + return i - sep->length; + } + + /* + * Deviate from ECMA by never splitting an empty string by any separator + * string into a non-empty array (an array of length 1 that contains the + * empty string). + */ + if (!JS_VERSION_IS_ECMA(cx) && length == 0) + return -1; + + /* + * Special case: if sep is the empty string, split str into one character + * substrings. Let our caller worry about whether to split once at end of + * string into an empty substring. + */ + if (sep->length == 0) + return ((size_t)i == length) ? -1 : i + 1; + + /* + * Now that we know sep is non-empty, search starting at i in str for an + * occurrence of all of sep's chars. If we find them, return the index of + * the first separator char. Otherwise, return length. + */ + j = 0; + while ((size_t)(k = i + j) < length) { + if (chars[k] == sep->chars[j]) { + if ((size_t)++j == sep->length) + return i; + } else { + i++; + j = 0; + } + } + return k; +} + +static JSBool +str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *sub; + JSObject *arrayobj; + jsval v; + JSBool ok, limited; + JSRegExp *re; + JSSubString *sep, tmp; + jsdouble d; + jsint i, j; + uint32 len, limit; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + + if (argc == 0) { + v = STRING_TO_JSVAL(str); + ok = JS_SetElement(cx, arrayobj, 0, &v); + } else { + if (JSVAL_IS_REGEXP(cx, argv[0])) { + re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); + sep = &tmp; + + /* Set a magic value so we can detect a successful re match. */ + sep->chars = NULL; + sep->length = 0; + } else { + JSString *str2 = js_ValueToString(cx, argv[0]); + if (!str2) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str2); + + /* + * Point sep at a local copy of str2's header because find_split + * will modify sep->length. + */ + tmp.length = JSSTRING_LENGTH(str2); + tmp.chars = JSSTRING_CHARS(str2); + sep = &tmp; + re = NULL; + } + + /* Use the second argument as the split limit, if given. */ + limited = (argc > 1) && !JSVAL_IS_VOID(argv[1]); + limit = 0; /* Avoid warning. */ + if (limited) { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + + /* Clamp limit between 0 and 1 + string length. */ + if (!js_DoubleToECMAUint32(cx, d, &limit)) + return JS_FALSE; + if (limit > JSSTRING_LENGTH(str)) + limit = 1 + JSSTRING_LENGTH(str); + } + + len = i = 0; + while ((j = find_split(cx, str, re, &i, sep)) >= 0) { + if (limited && len >= limit) + break; + sub = js_NewDependentString(cx, str, i, (size_t)(j - i), 0); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; + + /* + * Imitate perl's feature of including parenthesized substrings + * that matched part of the delimiter in the new array, after the + * split substring that was delimited. + */ + if (re && sep->chars) { + uintN num; + JSSubString *parsub; + + for (num = 0; num < cx->regExpStatics.parenCount; num++) { + if (limited && len >= limit) + break; + parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num); + sub = js_NewStringCopyN(cx, parsub->chars, parsub->length, + 0); + if (!sub) + return JS_FALSE; + v = STRING_TO_JSVAL(sub); + if (!JS_SetElement(cx, arrayobj, len, &v)) + return JS_FALSE; + len++; + } + sep->chars = NULL; + } + + i = j + sep->length; + if (!JS_VERSION_IS_ECMA(cx)) { + /* + * Deviate from ECMA to imitate Perl, which omits a final + * split unless a limit argument is given and big enough. + */ + if (!limited && (size_t)i == JSSTRING_LENGTH(str)) + break; + } + } + ok = (j != -2); + } + return ok; +} + +#if JS_HAS_PERL_SUBSTR +static JSBool +str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + begin = js_DoubleToInteger(d); + if (begin < 0) { + begin += length; + if (begin < 0) + begin = 0; + } else if (begin > length) { + begin = length; + } + + if (argc == 1) { + end = length; + } else { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) + end = 0; + end += begin; + if (end > length) + end = length; + } + + str = js_NewDependentString(cx, str, (size_t)begin, + (size_t)(end - begin), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} +#endif /* JS_HAS_PERL_SUBSTR */ + +/* + * Python-esque sequence operations. + */ +static JSBool +str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str, *str2; + uintN i; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + for (i = 0; i < argc; i++) { + str2 = js_ValueToString(cx, argv[i]); + if (!str2) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str2); + + str = js_ConcatStrings(cx, str, str2); + if (!str) + return JS_FALSE; + } + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + jsdouble d; + jsdouble length, begin, end; + + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + + if (argc != 0) { + if (!js_ValueToNumber(cx, argv[0], &d)) + return JS_FALSE; + length = JSSTRING_LENGTH(str); + begin = js_DoubleToInteger(d); + if (begin < 0) { + begin += length; + if (begin < 0) + begin = 0; + } else if (begin > length) { + begin = length; + } + + if (argc == 1) { + end = length; + } else { + if (!js_ValueToNumber(cx, argv[1], &d)) + return JS_FALSE; + end = js_DoubleToInteger(d); + if (end < 0) { + end += length; + if (end < 0) + end = 0; + } else if (end > length) { + end = length; + } + if (end < begin) + end = begin; + } + + str = js_NewDependentString(cx, str, (size_t)begin, + (size_t)(end - begin), 0); + if (!str) + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +#if JS_HAS_STR_HTML_HELPERS +/* + * HTML composition aids. + */ +static JSBool +tagify(JSContext *cx, JSObject *obj, jsval *argv, + const char *begin, JSString *param, const char *end, + jsval *rval) +{ + JSString *str; + jschar *tagbuf; + size_t beglen, endlen, parlen, taglen; + size_t i, j; + + if (JSVAL_IS_STRING((jsval)obj)) { + str = JSVAL_TO_STRING((jsval)obj); + } else { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + argv[-1] = STRING_TO_JSVAL(str); + } + + if (!end) + end = begin; + + beglen = strlen(begin); + taglen = 1 + beglen + 1; /* '' */ + parlen = 0; /* Avoid warning. */ + if (param) { + parlen = JSSTRING_LENGTH(param); + taglen += 2 + parlen + 1; /* '="param"' */ + } + endlen = strlen(end); + taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1; /* 'str' */ + + if (taglen >= ~(size_t)0 / sizeof(jschar)) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar)); + if (!tagbuf) + return JS_FALSE; + + j = 0; + tagbuf[j++] = '<'; + for (i = 0; i < beglen; i++) + tagbuf[j++] = (jschar)begin[i]; + if (param) { + tagbuf[j++] = '='; + tagbuf[j++] = '"'; + js_strncpy(&tagbuf[j], JSSTRING_CHARS(param), parlen); + j += parlen; + tagbuf[j++] = '"'; + } + tagbuf[j++] = '>'; + js_strncpy(&tagbuf[j], JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); + j += JSSTRING_LENGTH(str); + tagbuf[j++] = '<'; + tagbuf[j++] = '/'; + for (i = 0; i < endlen; i++) + tagbuf[j++] = (jschar)end[i]; + tagbuf[j++] = '>'; + JS_ASSERT(j == taglen); + tagbuf[j] = 0; + + str = js_NewString(cx, tagbuf, taglen, 0); + if (!str) { + free((char *)tagbuf); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +tagify_value(JSContext *cx, JSObject *obj, jsval *argv, + const char *begin, const char *end, + jsval *rval) +{ + JSString *param; + + param = js_ValueToString(cx, argv[0]); + if (!param) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(param); + return tagify(cx, obj, argv, begin, param, end, rval); +} + +static JSBool +str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "b", NULL, NULL, rval); +} + +static JSBool +str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "i", NULL, NULL, rval); +} + +static JSBool +str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "tt", NULL, NULL, rval); +} + +static JSBool +str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "font size", "font", rval); +} + +static JSBool +str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + return tagify_value(cx, obj, argv, "font color", "font", rval); +} + +static JSBool +str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "a href", "a", rval); +} + +static JSBool +str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify_value(cx, obj, argv, "a name", "a", rval); +} + +static JSBool +str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "strike", NULL, NULL, rval); +} + +static JSBool +str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "small", NULL, NULL, rval); +} + +static JSBool +str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "big", NULL, NULL, rval); +} + +static JSBool +str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "blink", NULL, NULL, rval); +} + +static JSBool +str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "sup", NULL, NULL, rval); +} + +static JSBool +str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + return tagify(cx, obj, argv, "sub", NULL, NULL, rval); +} +#endif /* JS_HAS_STR_HTML_HELPERS */ + +static JSFunctionSpec string_methods[] = { +#if JS_HAS_TOSOURCE + {"quote", str_quote, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING,0}, +#endif + + /* Java-like methods. */ + {js_toString_str, str_toString, 0,JSFUN_THISP_STRING,0}, + {js_valueOf_str, str_valueOf, 0,JSFUN_THISP_STRING,0}, + {"substring", str_substring, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + + /* Perl-ish methods (search is actually Python-esque). */ + {"match", str_match, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,2}, + {"search", str_search, 1,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"replace", str_replace, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"split", str_split, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, +#if JS_HAS_PERL_SUBSTR + {"substr", str_substr, 2,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, +#endif + + /* Python-esque sequence methods. */ + {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE| + JSFUN_THISP_PRIMITIVE,0}, + + /* HTML string methods. */ +#if JS_HAS_STR_HTML_HELPERS + {"bold", str_bold, 0,JSFUN_THISP_PRIMITIVE,0}, + {"italics", str_italics, 0,JSFUN_THISP_PRIMITIVE,0}, + {"fixed", str_fixed, 0,JSFUN_THISP_PRIMITIVE,0}, + {"fontsize", str_fontsize, 1,JSFUN_THISP_PRIMITIVE,0}, + {"fontcolor", str_fontcolor, 1,JSFUN_THISP_PRIMITIVE,0}, + {"link", str_link, 1,JSFUN_THISP_PRIMITIVE,0}, + {"anchor", str_anchor, 1,JSFUN_THISP_PRIMITIVE,0}, + {"strike", str_strike, 0,JSFUN_THISP_PRIMITIVE,0}, + {"small", str_small, 0,JSFUN_THISP_PRIMITIVE,0}, + {"big", str_big, 0,JSFUN_THISP_PRIMITIVE,0}, + {"blink", str_blink, 0,JSFUN_THISP_PRIMITIVE,0}, + {"sup", str_sup, 0,JSFUN_THISP_PRIMITIVE,0}, + {"sub", str_sub, 0,JSFUN_THISP_PRIMITIVE,0}, +#endif + + {0,0,0,0,0} +}; + +static JSBool +String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSString *str; + + if (argc > 0) { + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + } else { + str = cx->runtime->emptyString; + } + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + } + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); + return JS_TRUE; +} + +static JSBool +str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jschar *chars; + uintN i; + uint16 code; + JSString *str; + + JS_ASSERT(argc < ARRAY_INIT_LIMIT); + chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + for (i = 0; i < argc; i++) { + if (!js_ValueToUint16(cx, argv[i], &code)) { + JS_free(cx, chars); + return JS_FALSE; + } + chars[i] = (jschar)code; + } + chars[i] = 0; + str = js_NewString(cx, chars, argc, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSFunctionSpec string_static_methods[] = { + {"fromCharCode", str_fromCharCode, 1,0,0}, + {0,0,0,0,0} +}; + +JSBool +js_InitRuntimeStringState(JSContext *cx) +{ + JSRuntime *rt; + JSString *empty; + JSAtom *atom; + + rt = cx->runtime; + + /* Initialize string cache */ +#ifdef JS_THREADSAFE + JS_ASSERT(!rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = JS_NEW_LOCK(); + if (!rt->deflatedStringCacheLock) + return JS_FALSE; +#endif + + /* Make a permanently locked empty string. */ + JS_ASSERT(!rt->emptyString); + empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); + if (!empty) + goto bad; + + /* Atomize it for scripts that use '' + x to convert x to string. */ + atom = js_AtomizeString(cx, empty, ATOM_PINNED); + if (!atom) + goto bad; + + rt->emptyString = empty; + rt->atomState.emptyAtom = atom; + + return JS_TRUE; + + bad: +#ifdef JS_THREADSAFE + JS_DESTROY_LOCK(rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = NULL; +#endif + return JS_FALSE; + +} + +void +js_FinishRuntimeStringState(JSContext *cx) +{ + JSRuntime *rt = cx->runtime; + + js_UnlockGCThingRT(rt, rt->emptyString); + rt->emptyString = NULL; +} + +void +js_FinishDeflatedStringCache(JSRuntime *rt) +{ + if (rt->deflatedStringCache) { + JS_HashTableDestroy(rt->deflatedStringCache); + rt->deflatedStringCache = NULL; + } +#ifdef JS_THREADSAFE + if (rt->deflatedStringCacheLock) { + JS_DESTROY_LOCK(rt->deflatedStringCacheLock); + rt->deflatedStringCacheLock = NULL; + } +#endif +} + +JSObject * +js_InitStringClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + /* Define the escape, unescape functions in the global object. */ + if (!JS_DefineFunctions(cx, obj, string_functions)) + return NULL; + + proto = JS_InitClass(cx, obj, NULL, &js_StringClass, String, 1, + string_props, string_methods, + NULL, string_static_methods); + if (!proto) + return NULL; + OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, + STRING_TO_JSVAL(cx->runtime->emptyString)); + return proto; +} + +JSString * +js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag) +{ + JSString *str; + + if (length > JSSTRING_LENGTH_MASK) { + JS_ReportOutOfMemory(cx); + return NULL; + } + + str = (JSString *) js_NewGCThing(cx, gcflag | GCX_STRING, sizeof(JSString)); + if (!str) + return NULL; + str->length = length; + str->chars = chars; +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveStrings); + JS_RUNTIME_METER(rt, totalStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->lengthSum += (double)length, + rt->lengthSquaredSum += (double)length * (double)length)); + } +#endif + return str; +} + +JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length, uintN gcflag) +{ + JSDependentString *ds; + + if (length == 0) + return cx->runtime->emptyString; + + if (start == 0 && length == JSSTRING_LENGTH(base)) + return base; + + if (start > JSSTRDEP_START_MASK || + (start != 0 && length > JSSTRDEP_LENGTH_MASK)) { + return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length, + gcflag); + } + + ds = (JSDependentString *) + js_NewGCThing(cx, gcflag | GCX_MUTABLE_STRING, sizeof(JSString)); + if (!ds) + return NULL; + if (start == 0) { + JSPREFIX_SET_LENGTH(ds, length); + JSPREFIX_SET_BASE(ds, base); + } else { + JSSTRDEP_SET_START_AND_LENGTH(ds, start, length); + JSSTRDEP_SET_BASE(ds, base); + } +#ifdef DEBUG + { + JSRuntime *rt = cx->runtime; + JS_RUNTIME_METER(rt, liveDependentStrings); + JS_RUNTIME_METER(rt, totalDependentStrings); + JS_RUNTIME_METER(rt, liveStrings); + JS_RUNTIME_METER(rt, totalStrings); + JS_LOCK_RUNTIME_VOID(rt, + (rt->strdepLengthSum += (double)length, + rt->strdepLengthSquaredSum += (double)length * (double)length)); + JS_LOCK_RUNTIME_VOID(rt, + (rt->lengthSum += (double)length, + rt->lengthSquaredSum += (double)length * (double)length)); + } +#endif + return (JSString *)ds; +} + +#ifdef DEBUG +#include + +void printJSStringStats(JSRuntime *rt) { + double mean = 0., var = 0., sigma = 0.; + jsrefcount count = rt->totalStrings; + if (count > 0 && rt->lengthSum >= 0) { + mean = rt->lengthSum / count; + var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum; + if (var < 0.0 || count <= 1) + var = 0.0; + else + var /= count * (count - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", + (unsigned long)count, mean, sigma); + + mean = var = sigma = 0.; + count = rt->totalDependentStrings; + if (count > 0 && rt->strdepLengthSum >= 0) { + mean = rt->strdepLengthSum / count; + var = count * rt->strdepLengthSquaredSum + - rt->strdepLengthSum * rt->strdepLengthSum; + if (var < 0.0 || count <= 1) + var = 0.0; + else + var /= count * (count - 1); + + /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ + sigma = (var != 0.) ? sqrt(var) : 0.; + } + fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", + (unsigned long)count, mean, sigma); +} +#endif + +JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag) +{ + jschar *news; + JSString *str; + + news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar)); + if (!news) + return NULL; + js_strncpy(news, s, n); + news[n] = 0; + str = js_NewString(cx, news, n, gcflag); + if (!str) + JS_free(cx, news); + return str; +} + +JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) +{ + size_t n, m; + jschar *news; + JSString *str; + + n = js_strlen(s); + m = (n + 1) * sizeof(jschar); + news = (jschar *) JS_malloc(cx, m); + if (!news) + return NULL; + memcpy(news, s, m); + str = js_NewString(cx, news, n, gcflag); + if (!str) + JS_free(cx, news); + return str; +} + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_string_pointer(const void *key) +{ + return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; +} + +void +js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str) +{ + JSHashNumber hash; + JSHashEntry *he, **hep; + + if (!rt->deflatedStringCache) + return; + + hash = js_hash_string_pointer(str); + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str); + he = *hep; + if (he) { +#ifdef DEBUG + rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str); +#endif + free(he->value); + JS_HashTableRawRemove(rt->deflatedStringCache, hep, he); + } + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); +} + +void +js_FinalizeString(JSContext *cx, JSString *str) +{ + js_FinalizeStringRT(cx->runtime, str); +} + +void +js_FinalizeStringRT(JSRuntime *rt, JSString *str) +{ + JSBool valid; + + JS_RUNTIME_UNMETER(rt, liveStrings); + if (JSSTRING_IS_DEPENDENT(str)) { + /* If JSSTRFLAG_DEPENDENT is set, this string must be valid. */ + JS_ASSERT(JSSTRDEP_BASE(str)); + JS_RUNTIME_UNMETER(rt, liveDependentStrings); + valid = JS_TRUE; + } else { + /* A stillborn string has null chars, so is not valid. */ + valid = (str->chars != NULL); + if (valid) + free(str->chars); + } + if (valid) { + js_PurgeDeflatedStringCache(rt, str); + str->chars = NULL; + } + str->length = 0; +} + +JSObject * +js_StringToObject(JSContext *cx, JSString *str) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_StringClass, NULL, NULL); + if (!obj) + return NULL; + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); + return obj; +} + +JS_FRIEND_API(const char *) +js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun) +{ + JSString *str; + const char *bytes; + + str = v2sfun(cx, v); + if (!str) + return NULL; + str = js_QuoteString(cx, str, 0); + if (!str) + return NULL; + bytes = js_GetStringBytes(cx->runtime, str); + if (!bytes) + JS_ReportOutOfMemory(cx); + return bytes; +} + +JS_FRIEND_API(JSString *) +js_ValueToString(JSContext *cx, jsval v) +{ + JSObject *obj; + JSString *str; + + if (JSVAL_IS_OBJECT(v)) { + obj = JSVAL_TO_OBJECT(v); + if (!obj) + return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) + return NULL; + } + if (JSVAL_IS_STRING(v)) { + str = JSVAL_TO_STRING(v); + } else if (JSVAL_IS_INT(v)) { + str = js_NumberToString(cx, JSVAL_TO_INT(v)); + } else if (JSVAL_IS_DOUBLE(v)) { + str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v)); + } else if (JSVAL_IS_BOOLEAN(v)) { + str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v)); + } else { + str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); + } + return str; +} + +JS_FRIEND_API(JSString *) +js_ValueToSource(JSContext *cx, jsval v) +{ + JSTempValueRooter tvr; + JSString *str; + + if (JSVAL_IS_STRING(v)) + return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); + if (JSVAL_IS_PRIMITIVE(v)) { + /* Special case to preserve negative zero, _contra_ toString. */ + if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v))) { + /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */ + static const jschar js_negzero_ucNstr[] = {'-', '0'}; + + return js_NewStringCopyN(cx, js_negzero_ucNstr, 2, 0); + } + return js_ValueToString(cx, v); + } + + JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); + if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), + cx->runtime->atomState.toSourceAtom, + 0, NULL, &tvr.u.value)) { + str = NULL; + } else { + str = js_ValueToString(cx, tvr.u.value); + } + JS_POP_TEMP_ROOT(cx, &tvr); + return str; +} + +JSHashNumber +js_HashString(JSString *str) +{ + JSHashNumber h; + const jschar *s; + size_t n; + + h = 0; + for (s = JSSTRING_CHARS(str), n = JSSTRING_LENGTH(str); n; s++, n--) + h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; + return h; +} + +intN +js_CompareStrings(JSString *str1, JSString *str2) +{ + size_t l1, l2, n, i; + const jschar *s1, *s2; + intN cmp; + + JS_ASSERT(str1); + JS_ASSERT(str2); + + /* Fast case: pointer equality could be a quick win. */ + if (str1 == str2) + return 0; + + l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); + s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); + n = JS_MIN(l1, l2); + for (i = 0; i < n; i++) { + cmp = s1[i] - s2[i]; + if (cmp != 0) + return cmp; + } + return (intN)(l1 - l2); +} + +JSBool +js_EqualStrings(JSString *str1, JSString *str2) +{ + size_t n; + const jschar *s1, *s2; + + JS_ASSERT(str1); + JS_ASSERT(str2); + + /* Fast case: pointer equality could be a quick win. */ + if (str1 == str2) + return JS_TRUE; + + n = JSSTRING_LENGTH(str1); + if (n != JSSTRING_LENGTH(str2)) + return JS_FALSE; + + if (n == 0) + return JS_TRUE; + + s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); + do { + if (*s1 != *s2) + return JS_FALSE; + ++s1, ++s2; + } while (--n != 0); + + return JS_TRUE; +} + +size_t +js_strlen(const jschar *s) +{ + const jschar *t; + + for (t = s; *t != 0; t++) + continue; + return (size_t)(t - s); +} + +jschar * +js_strchr(const jschar *s, jschar c) +{ + while (*s != 0) { + if (*s == c) + return (jschar *)s; + s++; + } + return NULL; +} + +jschar * +js_strchr_limit(const jschar *s, jschar c, const jschar *limit) +{ + while (s < limit) { + if (*s == c) + return (jschar *)s; + s++; + } + return NULL; +} + +const jschar * +js_SkipWhiteSpace(const jschar *s) +{ + /* JS_ISSPACE is false on a null. */ + while (JS_ISSPACE(*s)) + s++; + return s; +} + +#ifdef JS_C_STRINGS_ARE_UTF8 + +jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *length) +{ + jschar *chars = NULL; + size_t dstlen = 0; + + if (!js_InflateStringToBuffer(cx, bytes, *length, NULL, &dstlen)) + return NULL; + chars = (jschar *) JS_malloc(cx, (dstlen + 1) * sizeof (jschar)); + if (!chars) + return NULL; + js_InflateStringToBuffer(cx, bytes, *length, chars, &dstlen); + chars[dstlen] = 0; + *length = dstlen; + return chars; +} + +/* + * May be called with null cx by js_GetStringBytes, see below. + */ +char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length) +{ + size_t size = 0; + char *bytes = NULL; + if (!js_DeflateStringToBuffer(cx, chars, length, NULL, &size)) + return NULL; + bytes = (char *) (cx ? JS_malloc(cx, size+1) : malloc(size+1)); + if (!bytes) + return NULL; + js_DeflateStringToBuffer(cx, chars, length, bytes, &size); + bytes[size] = 0; + return bytes; +} + +JSBool +js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, + char *dst, size_t *dstlenp) +{ + size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen; + jschar c, c2; + uint32 v; + uint8 utf8buf[6]; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + c = *src++; + srclen--; + if ((c >= 0xDC00) && (c <= 0xDFFF)) + goto badSurrogate; + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + if (srclen < 1) + goto bufferTooSmall; + c2 = *src++; + srclen--; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { + c = c2; + goto badSurrogate; + } + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + if (v < 0x0080) { + /* no encoding necessary - performance hack */ + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (char) v; + utf8Len = 1; + } else { + utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); + if (utf8Len > dstlen) + goto bufferTooSmall; + if (dst) { + for (i = 0; i < utf8Len; i++) + *dst++ = (char) utf8buf[i]; + } + } + dstlen -= utf8Len; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badSurrogate: + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "0x%x", c); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_BAD_SURROGATE_CHAR, + buffer); + } + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +JSBool +js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, + jschar *dst, size_t *dstlenp) +{ + uint32 v; + size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen; + + if (!dst) + dstlen = origDstlen = (size_t) -1; + + while (srclen) { + v = (uint8) *src; + n = 1; + if (v & 0x80) { + while (v & (0x80 >> n)) + n++; + if (n > srclen) + goto bufferTooSmall; + if (n == 1 || n > 6) + goto badCharacter; + for (j = 1; j < n; j++) { + if ((src[j] & 0xC0) != 0x80) + goto badCharacter; + } + v = Utf8ToOneUcs4Char(src, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF || dstlen < 2) { + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "0x%x", v + 0x10000); + JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UTF8_CHAR_TOO_LARGE, + buffer); + } + return JS_FALSE; + } + if (dstlen < 2) + goto bufferTooSmall; + if (dst) { + *dst++ = (jschar)((v >> 10) + 0xD800); + v = (jschar)((v & 0x3FF) + 0xDC00); + } + dstlen--; + } + } + if (!dstlen) + goto bufferTooSmall; + if (dst) + *dst++ = (jschar) v; + dstlen--; + offset += n; + src += n; + srclen -= n; + } + *dstlenp = (origDstlen - dstlen); + return JS_TRUE; + +badCharacter: + *dstlenp = (origDstlen - dstlen); + if (cx) { + char buffer[10]; + JS_snprintf(buffer, 10, "%d", offset); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_MALFORMED_UTF8_CHAR, + buffer); + } + return JS_FALSE; + +bufferTooSmall: + *dstlenp = (origDstlen - dstlen); + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; +} + +#else + +JSBool +js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, + jschar *chars, size_t* charsLength) +{ + size_t i; + + if (length > *charsLength) { + for (i = 0; i < *charsLength; i++) + chars[i] = (unsigned char) bytes[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < length; i++) + chars[i] = (unsigned char) bytes[i]; + *charsLength = length; + return JS_TRUE; +} + +jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *bytesLength) +{ + jschar *chars; + size_t i, length = *bytesLength; + + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) { + *bytesLength = 0; + return NULL; + } + for (i = 0; i < length; i++) + chars[i] = (unsigned char) bytes[i]; + chars[length] = 0; + *bytesLength = length; + return chars; +} + +JSBool +js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t length, + char *bytes, size_t* bytesLength) +{ + size_t i; + + if (length > *bytesLength) { + for (i = 0; i < *bytesLength; i++) + bytes[i] = (char) chars[i]; + if (cx) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BUFFER_TOO_SMALL); + } + return JS_FALSE; + } + for (i = 0; i < length; i++) + bytes[i] = (char) chars[i]; + *bytesLength = length; + return JS_TRUE; +} + +/* + * May be called with null cx by js_GetStringBytes, see below. + */ +char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length) +{ + size_t i, size; + char *bytes; + + size = (length + 1) * sizeof(char); + bytes = (char *) (cx ? JS_malloc(cx, size) : malloc(size)); + if (!bytes) + return NULL; + + for (i = 0; i < length; i++) + bytes[i] = (char) chars[i]; + + bytes[length] = 0; + return bytes; +} + +#endif + +static JSHashTable * +GetDeflatedStringCache(JSRuntime *rt) +{ + JSHashTable *cache; + + cache = rt->deflatedStringCache; + if (!cache) { + cache = JS_NewHashTable(8, js_hash_string_pointer, + JS_CompareValues, JS_CompareValues, + NULL, NULL); + rt->deflatedStringCache = cache; + } + return cache; +} + +JSBool +js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length) +{ + JSHashTable *cache; + JSBool ok; + JSHashNumber hash; + JSHashEntry **hep; + + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + + cache = GetDeflatedStringCache(rt); + if (!cache) { + ok = JS_FALSE; + } else { + hash = js_hash_string_pointer(str); + hep = JS_HashTableRawLookup(cache, hash, str); + JS_ASSERT(*hep == NULL); + ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL; +#ifdef DEBUG + if (ok) + rt->deflatedStringCacheBytes += length; +#endif + } + + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + return ok; +} + +char * +js_GetStringBytes(JSRuntime *rt, JSString *str) +{ + JSHashTable *cache; + char *bytes; + JSHashNumber hash; + JSHashEntry *he, **hep; + + JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); + + cache = GetDeflatedStringCache(rt); + if (!cache) { + bytes = NULL; + } else { + hash = js_hash_string_pointer(str); + hep = JS_HashTableRawLookup(cache, hash, str); + he = *hep; + if (he) { + bytes = (char *) he->value; + + /* Try to catch failure to JS_ShutDown between runtime epochs. */ + JS_ASSERT((*bytes == '\0' && JSSTRING_LENGTH(str) == 0) || + *bytes == (char) JSSTRING_CHARS(str)[0]); + } else { + bytes = js_DeflateString(NULL, JSSTRING_CHARS(str), + JSSTRING_LENGTH(str)); + if (bytes) { + if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { +#ifdef DEBUG + rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str); +#endif + } else { + free(bytes); + bytes = NULL; + } + } + } + } + + JS_RELEASE_LOCK(rt->deflatedStringCacheLock); + return bytes; +} + +/* + * From java.lang.Character.java: + * + * The character properties are currently encoded into 32 bits in the + * following manner: + * + * 10 bits signed offset used for converting case + * 1 bit if 1, adding the signed offset converts the character to + * lowercase + * 1 bit if 1, subtracting the signed offset converts the character to + * uppercase + * 1 bit if 1, character has a titlecase equivalent (possibly itself) + * 3 bits 0 may not be part of an identifier + * 1 ignorable control; may continue a Unicode identifier or JS + * identifier + * 2 may continue a JS identifier but not a Unicode identifier + * (unused) + * 3 may continue a Unicode identifier or JS identifier + * 4 is a JS whitespace character + * 5 may start or continue a JS identifier; + * may continue but not start a Unicode identifier (_) + * 6 may start or continue a JS identifier but not a Unicode + * identifier ($) + * 7 may start or continue a Unicode identifier or JS identifier + * Thus: + * 5, 6, 7 may start a JS identifier + * 1, 2, 3, 5, 6, 7 may continue a JS identifier + * 7 may start a Unicode identifier + * 1, 3, 5, 7 may continue a Unicode identifier + * 1 is ignorable within an identifier + * 4 is JS whitespace + * 2 bits 0 this character has no numeric property + * 1 adding the digit offset to the character code and then + * masking with 0x1F will produce the desired numeric value + * 2 this character has a "strange" numeric value + * 3 a JS supradecimal digit: adding the digit offset to the + * character code, then masking with 0x1F, then adding 10 + * will produce the desired numeric value + * 5 bits digit offset + * 1 bit XML 1.0 name start character + * 1 bit XML 1.0 name character + * 2 bits reserved for future use + * 5 bits character type + */ + +/* The X table has 1024 entries for a total of 1024 bytes. */ + +const uint8 js_X[] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */ + 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */ + 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */ + 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */ + 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */ + 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */ + 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */ + 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */ + 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */ + 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */ + 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */ + 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */ + 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */ + 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */ + 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */ + 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */ + 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */ + 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */ + 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */ + 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */ +104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */ +105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */ +105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */ +106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */ + 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */ +115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */ +}; + +/* The Y table has 7808 entries for a total of 7808 bytes. */ + +const uint8 js_Y[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ + 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */ + 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */ + 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */ + 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ + 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */ + 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ + 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ + 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */ + 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */ + 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */ + 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */ + 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */ + 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ + 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */ + 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */ + 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */ + 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ + 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */ + 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */ + 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */ + 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */ + 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */ + 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */ + 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */ + 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */ + 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */ + 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */ + 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */ + 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */ + 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ + 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */ + 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ + 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */ + 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */ + 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */ + 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */ + 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */ + 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */ + 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */ + 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ + 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */ + 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */ + 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */ + 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ + 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */ + 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ + 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */ + 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */ + 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ + 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */ + 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */ + 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */ + 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */ + 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */ + 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */ + 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */ + 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */ + 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */ + 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */ + 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */ + 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */ + 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */ + 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */ + 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ + 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ + 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */ + 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ + 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ + 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */ + 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ + 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */ + 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */ + 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ + 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ + 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */ + 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */ + 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ + 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */ + 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ + 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */ + 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */ + 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ + 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ + 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */ + 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */ + 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */ + 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */ + 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */ + 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ + 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */ + 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */ + 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */ + 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */ + 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ + 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ + 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */ + 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */ + 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */ + 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */ + 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */ + 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */ + 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */ + 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */ + 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */ + 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */ + 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */ + 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */ + 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */ + 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */ + 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */ + 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */ + 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */ + 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */ + 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */ + 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */ + 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ + 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */ + 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ + 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */ + 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */ + 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */ + 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */ + 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */ + 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ + 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */ + 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */ + 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */ + 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */ + 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */ + 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */ + 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */ + 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */ + 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */ + 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ + 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */ + 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */ + 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */ + 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ + 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */ + 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */ + 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */ + 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */ + 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ + 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ + 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */ + 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */ + 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */ + 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */ + 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ + 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ + 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */ + 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */ + 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */ + 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */ + 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */ + 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ + 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ + 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */ + 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */ + 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */ + 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ + 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */ + 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */ + 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */ + 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */ + 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */ + 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ + 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */ + 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */ + 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */ + 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */ + 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */ + 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */ + 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */ + 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */ + 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ + 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */ + 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */ + 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */ + 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */ + 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */ + 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */ + 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */ + 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */ + 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */ + 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */ + 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */ + 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ + 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ + 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ + 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */ + 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */ + 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ + 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */ + 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ + 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */ + 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */ + 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */ + 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */ + 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */ + 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ + 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ + 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */ + 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */ + 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */ + 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */ + 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */ + 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */ + 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */ + 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */ + 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ +102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ + 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ + 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ + 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ + 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ +105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ + 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ + 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ + 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ + 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ + 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */ +107, 107, 107, 107, 107, 107, 107, 107, /* 69 */ +107, 107, 7, 7, 7, 5, 6, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */ + 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */ + 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */ + 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ + 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */ + 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */ + 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */ + 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */ + 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */ + 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */ + 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */ + 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ + 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */ + 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */ +109, 109, 109, 109, 109, 109, 109, 109, /* 72 */ +109, 109, 109, 109, 110, 110, 110, 110, /* 72 */ +111, 111, 111, 111, 111, 111, 111, 111, /* 72 */ +111, 111, 111, 111, 112, 112, 112, 112, /* 72 */ +113, 113, 113, 46, 46, 46, 46, 46, /* 73 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */ + 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ + 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */ + 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */ + 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ + 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */ + 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ +114, 114, 114, 114, 82, 82, 82, 82, /* 80 */ + 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */ + 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */ +115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ +115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ +115, 115, 115, 115, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ + 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 81 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ +116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ +117, 117, 118, 46, 46, 46, 46, 46, /* 82 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */ + 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ + 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */ + 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */ + 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */ + 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */ + 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */ +119, 119, 119, 119, 119, 119, 119, 119, /* 88 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 89 */ +114, 114, 83, 83, 83, 83, 83, 83, /* 89 */ + 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */ + 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */ + 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */ + 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */ + 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */ + 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */ +121, 121, 60, 60, 60, 60, 60, 60, /* 90 */ + 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */ + 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */ + 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ + 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */ + 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */ + 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */ + 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */ + 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */ + 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ + 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */ +114, 114, 114, 114, 114, 114, 114, 114, /* 98 */ +114, 114, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ + 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */ + 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ + 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ + 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ + 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ +123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ + 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ + 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */ + 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */ + 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */ + 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */ + 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */ + 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ + 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ + 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ + 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ + 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ + 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */ + 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */ + 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */ + 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */ + 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */ + 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */ + 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */ + 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */ + 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ + 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */ + 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */ + 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */ + 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */ + 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */ + 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ + 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */ + 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ + 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */ + 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ + 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ + 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */ + 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */ + 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */ + 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */ +}; + +/* The A table has 124 entries for a total of 496 bytes. */ + +const uint32 js_A[] = { +0x0001000F, /* 0 Cc, ignorable */ +0x0004000F, /* 1 Cc, whitespace */ +0x0004000C, /* 2 Zs, whitespace */ +0x00000018, /* 3 Po */ +0x0006001A, /* 4 Sc, currency */ +0x00000015, /* 5 Ps */ +0x00000016, /* 6 Pe */ +0x00000019, /* 7 Sm */ +0x00000014, /* 8 Pd */ +0x00036089, /* 9 Nd, identifier part, decimal 16 */ +0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ +0x0000001B, /* 11 Sk */ +0x00050017, /* 12 Pc, underscore */ +0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ +0x0000000C, /* 14 Zs */ +0x0000001C, /* 15 So */ +0x00070182, /* 16 Ll, identifier start */ +0x0000600B, /* 17 No, decimal 16 */ +0x0000500B, /* 18 No, decimal 8 */ +0x0000800B, /* 19 No, strange */ +0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ +0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ +0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ +0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ +0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ +0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ +0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ +0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ +0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ +0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ +0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ +0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ +0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ +0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ +0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ +0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ +0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ +0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ +0x00070181, /* 38 Lu, identifier start */ +0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ +0x00070185, /* 40 Lo, identifier start */ +0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ +0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ +0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ +0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ +0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ +0x00000000, /* 46 unassigned */ +0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ +0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ +0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ +0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ +0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ +0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ +0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ +0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ +0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ +0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ +0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ +0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ +0x00070084, /* 59 Lm, identifier start */ +0x00030086, /* 60 Mn, identifier part */ +0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ +0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ +0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ +0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ +0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ +0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ +0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ +0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ +0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ +0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ +0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ +0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ +0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ +0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ +0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ +0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ +0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ +0x00034089, /* 78 Nd, identifier part, decimal 0 */ +0x00000087, /* 79 Me */ +0x00030088, /* 80 Mc, identifier part */ +0x00037489, /* 81 Nd, identifier part, decimal 26 */ +0x00005A0B, /* 82 No, decimal 13 */ +0x00006E0B, /* 83 No, decimal 23 */ +0x0000740B, /* 84 No, decimal 26 */ +0x0000000B, /* 85 No */ +0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ +0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ +0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ +0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ +0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ +0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ +0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ +0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ +0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ +0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ +0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ +0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ +0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ +0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ +0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ +0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ +0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ +0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ +0x00010010, /* 104 Cf, ignorable */ +0x0004000D, /* 105 Zl, whitespace */ +0x0004000E, /* 106 Zp, whitespace */ +0x0000400B, /* 107 No, decimal 0 */ +0x0000440B, /* 108 No, decimal 2 */ +0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ +0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ +0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ +0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ +0x0007818A, /* 113 Nl, identifier start, strange */ +0x0000420B, /* 114 No, decimal 1 */ +0x0000720B, /* 115 No, decimal 25 */ +0x06A0001C, /* 116 So, hasLower (add 26) */ +0x0690001C, /* 117 So, hasUpper (subtract 26) */ +0x00006C0B, /* 118 No, decimal 22 */ +0x0000560B, /* 119 No, decimal 11 */ +0x0007738A, /* 120 Nl, identifier start, decimal 25 */ +0x0007418A, /* 121 Nl, identifier start, decimal 0 */ +0x00000013, /* 122 Cs */ +0x00000012 /* 123 Co */ +}; + +const jschar js_uriReservedPlusPound_ucstr[] = + {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0}; +const jschar js_uriUnescaped_ucstr[] = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0}; + +#define URI_CHUNK 64U + +/* Concatenate jschars onto an unshared/newborn JSString. */ +static JSBool +AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length) +{ + size_t total; + + JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); + total = str->length + length + 1; + if (!str->chars || + JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(str->length + 1, URI_CHUNK)) { + total = JS_ROUNDUP(total, URI_CHUNK); + str->chars = JS_realloc(cx, str->chars, total * sizeof(jschar)); + if (!str->chars) + return JS_FALSE; + } + js_strncpy(str->chars + str->length, chars, length); + str->length += length; + str->chars[str->length] = 0; + return JS_TRUE; +} + +/* + * ECMA 3, 15.1.3 URI Handling Function Properties + * + * The following are implementations of the algorithms + * given in the ECMA specification for the hidden functions + * 'Encode' and 'Decode'. + */ +static JSBool +Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, + const jschar *unescapedSet2, jsval *rval) +{ + size_t length, j, k, L; + jschar *chars, c, c2; + uint32 v; + uint8 utf8buf[6]; + jschar hexBuf[4]; + static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ + JSString *R; + + length = JSSTRING_LENGTH(str); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + R = js_NewString(cx, NULL, 0, 0); + if (!R) + return JS_FALSE; + + hexBuf[0] = '%'; + hexBuf[3] = 0; + chars = JSSTRING_CHARS(str); + for (k = 0; k < length; k++) { + c = chars[k]; + if (js_strchr(unescapedSet, c) || + (unescapedSet2 && js_strchr(unescapedSet2, c))) { + if (!AddCharsToURI(cx, R, &c, 1)) + return JS_FALSE; + } else { + if ((c >= 0xDC00) && (c <= 0xDFFF)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + if (c < 0xD800 || c > 0xDBFF) { + v = c; + } else { + k++; + if (k == length) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + c2 = chars[k]; + if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_URI, NULL); + return JS_FALSE; + } + v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; + } + L = js_OneUcs4ToUtf8Char(utf8buf, v); + for (j = 0; j < L; j++) { + hexBuf[1] = HexDigits[utf8buf[j] >> 4]; + hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; + if (!AddCharsToURI(cx, R, hexBuf, 3)) + return JS_FALSE; + } + } + } + + /* + * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we + * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 + * more jschars than it needs. + */ + chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); + if (chars) + R->chars = chars; + *rval = STRING_TO_JSVAL(R); + return JS_TRUE; +} + +static JSBool +Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) +{ + size_t length, start, k; + jschar *chars, c, H; + uint32 v; + jsuint B; + uint8 octets[6]; + JSString *R; + intN j, n; + + length = JSSTRING_LENGTH(str); + if (length == 0) { + *rval = STRING_TO_JSVAL(cx->runtime->emptyString); + return JS_TRUE; + } + + R = js_NewString(cx, NULL, 0, 0); + if (!R) + return JS_FALSE; + + chars = JSSTRING_CHARS(str); + for (k = 0; k < length; k++) { + c = chars[k]; + if (c == '%') { + start = k; + if ((k + 2) >= length) + goto bad; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto bad; + B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + k += 2; + if (!(B & 0x80)) { + c = (jschar)B; + } else { + n = 1; + while (B & (0x80 >> n)) + n++; + if (n == 1 || n > 6) + goto bad; + octets[0] = (uint8)B; + if (k + 3 * (n - 1) >= length) + goto bad; + for (j = 1; j < n; j++) { + k++; + if (chars[k] != '%') + goto bad; + if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) + goto bad; + B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + if ((B & 0xC0) != 0x80) + goto bad; + k += 2; + octets[j] = (char)B; + } + v = Utf8ToOneUcs4Char(octets, n); + if (v >= 0x10000) { + v -= 0x10000; + if (v > 0xFFFFF) + goto bad; + c = (jschar)((v & 0x3FF) + 0xDC00); + H = (jschar)((v >> 10) + 0xD800); + if (!AddCharsToURI(cx, R, &H, 1)) + return JS_FALSE; + } else { + c = (jschar)v; + } + } + if (js_strchr(reservedSet, c)) { + if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1))) + return JS_FALSE; + } else { + if (!AddCharsToURI(cx, R, &c, 1)) + return JS_FALSE; + } + } else { + if (!AddCharsToURI(cx, R, &c, 1)) + return JS_FALSE; + } + } + + /* + * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we + * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 + * more jschars than it needs. + */ + chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); + if (chars) + R->chars = chars; + *rval = STRING_TO_JSVAL(R); + return JS_TRUE; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI); + return JS_FALSE; +} + +static JSBool +str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval); +} + +static JSBool +str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Decode(cx, str, js_empty_ucstr, rval); +} + +static JSBool +str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, + rval); +} + +static JSBool +str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = js_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval); +} + +/* + * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at + * least 6 bytes long. Return the number of UTF-8 bytes of data written. + */ +int +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) +{ + int utf8Length = 1; + + JS_ASSERT(ucs4Char <= 0x7FFFFFFF); + if (ucs4Char < 0x80) { + *utf8Buffer = (uint8)ucs4Char; + } else { + int i; + uint32 a = ucs4Char >> 11; + utf8Length = 2; + while (a) { + a >>= 5; + utf8Length++; + } + i = utf8Length; + while (--i) { + utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); + ucs4Char >>= 6; + } + *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); + } + return utf8Length; +} + +/* + * Convert a utf8 character sequence into a UCS-4 character and return that + * character. It is assumed that the caller already checked that the sequence + * is valid. + */ +static uint32 +Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) +{ + uint32 ucs4Char; + uint32 minucs4Char; + /* from Unicode 3.1, non-shortest form is illegal */ + static const uint32 minucs4Table[] = { + 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000 + }; + + JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); + if (utf8Length == 1) { + ucs4Char = *utf8Buffer; + JS_ASSERT(!(ucs4Char & 0x80)); + } else { + JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == + (0x100 - (1 << (8-utf8Length)))); + ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); + minucs4Char = minucs4Table[utf8Length-2]; + while (--utf8Length) { + JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); + ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); + } + if (ucs4Char < minucs4Char || + ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { + ucs4Char = 0xFFFD; + } + } + return ucs4Char; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstr.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstr.h new file mode 100644 index 0000000..708c69a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsstr.h @@ -0,0 +1,500 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsstr_h___ +#define jsstr_h___ +/* + * JS string type implementation. + * + * A JS string is a counted array of unicode characters. To support handoff + * of API client memory, the chars are allocated separately from the length, + * necessitating a pointer after the count, to form a separately allocated + * string descriptor. String descriptors are GC'ed, while their chars are + * allocated from the malloc heap. + * + * When a string is treated as an object (by following it with . or []), the + * runtime wraps it with a JSObject whose valueOf method returns the unwrapped + * string descriptor. + */ +#include +#include "jspubtd.h" +#include "jsprvtd.h" +#include "jshash.h" + +JS_BEGIN_EXTERN_C + +/* + * The original GC-thing "string" type, a flat character string owned by its + * GC-thing descriptor. The chars member points to a vector having byte size + * (length + 1) * sizeof(jschar), terminated at index length by a zero jschar. + * The terminator is purely a backstop, in case the chars pointer flows out to + * native code that requires \u0000 termination. + * + * NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros, + * unless you guard str->member uses with !JSSTRING_IS_DEPENDENT(str). + */ +struct JSString { + size_t length; + jschar *chars; +}; + +/* + * Overlay structure for a string that depends on another string's characters. + * Distinguished by the JSSTRFLAG_DEPENDENT bit being set in length. The base + * member may point to another dependent string if JSSTRING_CHARS has not been + * called yet. The length chars in a dependent string are stored starting at + * base->chars + start, and are not necessarily zero-terminated. If start is + * 0, it is not stored, length is a full size_t (minus the JSSTRFLAG_* bits in + * the high two positions), and the JSSTRFLAG_PREFIX flag is set. + */ +struct JSDependentString { + size_t length; + JSString *base; +}; + +/* Definitions for flags stored in the high order bits of JSString.length. */ +#define JSSTRFLAG_BITS 2 +#define JSSTRFLAG_SHIFT(flg) ((size_t)(flg) << JSSTRING_LENGTH_BITS) +#define JSSTRFLAG_MASK JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS)) +#define JSSTRFLAG_DEPENDENT JSSTRFLAG_SHIFT(1) +#define JSSTRFLAG_PREFIX JSSTRFLAG_SHIFT(2) + +/* Universal JSString type inquiry and accessor macros. */ +#define JSSTRING_BIT(n) ((size_t)1 << (n)) +#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) +#define JSSTRING_HAS_FLAG(str,flg) ((str)->length & (flg)) +#define JSSTRING_IS_DEPENDENT(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT) +#define JSSTRING_IS_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX) +#define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \ + ? JSSTRDEP_CHARS(str) \ + : (str)->chars) +#define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \ + ? JSSTRDEP_LENGTH(str) \ + : (str)->length) +#define JSSTRING_LENGTH_BITS (sizeof(size_t) * JS_BITS_PER_BYTE \ + - JSSTRFLAG_BITS) +#define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS) + +/* Specific JSDependentString shift/mask accessor and mutator macros. */ +#define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS) +#define JSSTRDEP_START_SHIFT JSSTRDEP_LENGTH_BITS +#define JSSTRDEP_START_MASK JSSTRING_BITMASK(JSSTRDEP_START_BITS) +#define JSSTRDEP_LENGTH_BITS (JSSTRING_LENGTH_BITS / 2) +#define JSSTRDEP_LENGTH_MASK JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS) + +#define JSSTRDEP(str) ((JSDependentString *)(str)) +#define JSSTRDEP_START(str) (JSSTRING_IS_PREFIX(str) ? 0 \ + : ((JSSTRDEP(str)->length \ + >> JSSTRDEP_START_SHIFT) \ + & JSSTRDEP_START_MASK)) +#define JSSTRDEP_LENGTH(str) (JSSTRDEP(str)->length \ + & (JSSTRING_IS_PREFIX(str) \ + ? JSSTRING_LENGTH_MASK \ + : JSSTRDEP_LENGTH_MASK)) + +#define JSSTRDEP_SET_START_AND_LENGTH(str,off,len) \ + (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT \ + | ((off) << JSSTRDEP_START_SHIFT) \ + | (len)) +#define JSPREFIX_SET_LENGTH(str,len) \ + (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len)) + +#define JSSTRDEP_BASE(str) (JSSTRDEP(str)->base) +#define JSSTRDEP_SET_BASE(str,bstr) (JSSTRDEP(str)->base = (bstr)) +#define JSPREFIX_BASE(str) JSSTRDEP_BASE(str) +#define JSPREFIX_SET_BASE(str,bstr) JSSTRDEP_SET_BASE(str,bstr) + +#define JSSTRDEP_CHARS(str) \ + (JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \ + ? js_GetDependentStringChars(str) \ + : JSSTRDEP_BASE(str)->chars + JSSTRDEP_START(str)) + +extern size_t +js_MinimizeDependentStrings(JSString *str, int level, JSString **basep); + +extern jschar * +js_GetDependentStringChars(JSString *str); + +extern jschar * +js_GetStringChars(JSString *str); + +extern JSString * +js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); + +extern const jschar * +js_UndependString(JSContext *cx, JSString *str); + +struct JSSubString { + size_t length; + const jschar *chars; +}; + +extern jschar js_empty_ucstr[]; +extern JSSubString js_EmptySubString; + +/* Unicode character attribute lookup tables. */ +extern const uint8 js_X[]; +extern const uint8 js_Y[]; +extern const uint32 js_A[]; + +/* Enumerated Unicode general category types. */ +typedef enum JSCharType { + JSCT_UNASSIGNED = 0, + JSCT_UPPERCASE_LETTER = 1, + JSCT_LOWERCASE_LETTER = 2, + JSCT_TITLECASE_LETTER = 3, + JSCT_MODIFIER_LETTER = 4, + JSCT_OTHER_LETTER = 5, + JSCT_NON_SPACING_MARK = 6, + JSCT_ENCLOSING_MARK = 7, + JSCT_COMBINING_SPACING_MARK = 8, + JSCT_DECIMAL_DIGIT_NUMBER = 9, + JSCT_LETTER_NUMBER = 10, + JSCT_OTHER_NUMBER = 11, + JSCT_SPACE_SEPARATOR = 12, + JSCT_LINE_SEPARATOR = 13, + JSCT_PARAGRAPH_SEPARATOR = 14, + JSCT_CONTROL = 15, + JSCT_FORMAT = 16, + JSCT_PRIVATE_USE = 18, + JSCT_SURROGATE = 19, + JSCT_DASH_PUNCTUATION = 20, + JSCT_START_PUNCTUATION = 21, + JSCT_END_PUNCTUATION = 22, + JSCT_CONNECTOR_PUNCTUATION = 23, + JSCT_OTHER_PUNCTUATION = 24, + JSCT_MATH_SYMBOL = 25, + JSCT_CURRENCY_SYMBOL = 26, + JSCT_MODIFIER_SYMBOL = 27, + JSCT_OTHER_SYMBOL = 28 +} JSCharType; + +/* Character classifying and mapping macros, based on java.lang.Character. */ +#define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) +#define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) + +#define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER)) \ + >> JS_CTYPE(c)) & 1) + +#define JS_ISALNUM(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_DECIMAL_DIGIT_NUMBER)) \ + >> JS_CTYPE(c)) & 1) + +/* A unicode letter, suitable for use in an identifier. */ +#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER)) \ + >> JS_CTYPE(c)) & 1) + +/* + * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or + * digit or connector punctuation. + */ +#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER) | \ + (1 << JSCT_NON_SPACING_MARK) | \ + (1 << JSCT_COMBINING_SPACING_MARK) | \ + (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ + (1 << JSCT_CONNECTOR_PUNCTUATION)) \ + >> JS_CTYPE(c)) & 1) + +/* Unicode control-format characters, ignored in input */ +#define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) + +/* + * Per ECMA-262 15.10.2.6, these characters are the only ones that make up a + * "word", as far as a RegExp is concerned. If we want a Unicode-friendlier + * definition of "word", we should rename this macro to something regexp-y. + */ +#define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) + +#define JS_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') +#define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') + +#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ + (c) == '\n') +#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') +#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ + (c) == '-' || (c) == '_') +#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') +#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') + +#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) + +/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ +/* XXXbe fs, etc. ? */ +#define JS_ISSPACE(c) ((JS_CCODE(c) & 0x00070000) == 0x00040000) +#define JS_ISPRINT(c) ((c) < 128 && isprint(c)) + +#define JS_ISUPPER(c) (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER) +#define JS_ISLOWER(c) (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER) + +#define JS_TOUPPER(c) ((jschar) ((JS_CCODE(c) & 0x00100000) \ + ? (c) - ((int32)JS_CCODE(c) >> 22) \ + : (c))) +#define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ + ? (c) + ((int32)JS_CCODE(c) >> 22) \ + : (c))) + +/* + * Shorthands for ASCII (7-bit) decimal and hex conversion. + * Manually inline isdigit for performance; MSVC doesn't do this for us. + */ +#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) +#define JS7_UNDEC(c) ((c) - '0') +#define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) +#define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') +#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) + +/* Initialize per-runtime string state for the first context in the runtime. */ +extern JSBool +js_InitRuntimeStringState(JSContext *cx); + +extern void +js_FinishRuntimeStringState(JSContext *cx); + +extern void +js_FinishDeflatedStringCache(JSRuntime *rt); + +/* Initialize the String class, returning its prototype object. */ +extern JSClass js_StringClass; + +extern JSObject * +js_InitStringClass(JSContext *cx, JSObject *obj); + +extern const char js_escape_str[]; +extern const char js_unescape_str[]; +extern const char js_uneval_str[]; +extern const char js_decodeURI_str[]; +extern const char js_encodeURI_str[]; +extern const char js_decodeURIComponent_str[]; +extern const char js_encodeURIComponent_str[]; + +/* GC-allocate a string descriptor for the given malloc-allocated chars. */ +extern JSString * +js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag); + +extern JSString * +js_NewDependentString(JSContext *cx, JSString *base, size_t start, + size_t length, uintN gcflag); + +/* Copy a counted string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag); + +/* Copy a C string and GC-allocate a descriptor for it. */ +extern JSString * +js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag); + +/* Free the chars held by str when it is finalized by the GC. */ +extern void +js_FinalizeString(JSContext *cx, JSString *str); + +extern void +js_FinalizeStringRT(JSRuntime *rt, JSString *str); + +/* Wrap a string value in a String object. */ +extern JSObject * +js_StringToObject(JSContext *cx, JSString *str); + +/* + * Convert a value to a printable C string. + */ +typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v); + +extern JS_FRIEND_API(const char *) +js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun); + +#define js_ValueToPrintableString(cx,v) \ + js_ValueToPrintable(cx, v, js_ValueToString) + +#define js_ValueToPrintableSource(cx,v) \ + js_ValueToPrintable(cx, v, js_ValueToSource) + +/* + * Convert a value to a string, returning null after reporting an error, + * otherwise returning a new string reference. + */ +extern JS_FRIEND_API(JSString *) +js_ValueToString(JSContext *cx, jsval v); + +/* + * Convert a value to its source expression, returning null after reporting + * an error, otherwise returning a new string reference. + */ +extern JS_FRIEND_API(JSString *) +js_ValueToSource(JSContext *cx, jsval v); + +#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ +/* + * Compute a hash function from str. + */ +extern JSHashNumber +js_HashString(JSString *str); +#endif + +/* + * Return less than, equal to, or greater than zero depending on whether + * str1 is less than, equal to, or greater than str2. + */ +extern intN +js_CompareStrings(JSString *str1, JSString *str2); + +/* + * Test if strings are equal. + */ +extern JSBool +js_EqualStrings(JSString *str1, JSString *str2); + +/* + * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. + * The patlen argument must be positive and no greater than BMH_PATLEN_MAX. + * The start argument tells where in text to begin the search. + * + * Return the index of pat in text, or -1 if not found. + */ +#define BMH_CHARSET_SIZE 256 /* ISO-Latin-1 */ +#define BMH_PATLEN_MAX 255 /* skip table element is uint8 */ + +#define BMH_BAD_PATTERN (-2) /* return value if pat is not ISO-Latin-1 */ + +extern jsint +js_BoyerMooreHorspool(const jschar *text, jsint textlen, + const jschar *pat, jsint patlen, + jsint start); + +extern size_t +js_strlen(const jschar *s); + +extern jschar * +js_strchr(const jschar *s, jschar c); + +extern jschar * +js_strchr_limit(const jschar *s, jschar c, const jschar *limit); + +#define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) + +/* + * Return s advanced past any Unicode white space characters. + */ +extern const jschar * +js_SkipWhiteSpace(const jschar *s); + +/* + * Inflate bytes to JS chars and vice versa. Report out of memory via cx + * and return null on error, otherwise return the jschar or byte vector that + * was JS_malloc'ed. length is updated with the length of the new string in jschars. + */ +extern jschar * +js_InflateString(JSContext *cx, const char *bytes, size_t *length); + +extern char * +js_DeflateString(JSContext *cx, const jschar *chars, size_t length); + +/* + * Inflate bytes to JS chars into a buffer. + * 'chars' must be large enough for 'length' jschars. + * The buffer is NOT null-terminated. + * cx may be NULL, which means no errors are thrown. + * The destination length needs to be initialized with the buffer size, takes + * the number of chars moved. + */ +extern JSBool +js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, + jschar *chars, size_t* charsLength); + +/* + * Deflate JS chars to bytes into a buffer. + * 'bytes' must be large enough for 'length chars. + * The buffer is NOT null-terminated. + * cx may be NULL, which means no errors are thrown. + * The destination length needs to be initialized with the buffer size, takes + * the number of bytes moved. + */ +extern JSBool +js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, + size_t charsLength, char *bytes, size_t* length); + +/* + * Associate bytes with str in the deflated string cache, returning true on + * successful association, false on out of memory. + */ +extern JSBool +js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length); + +/* + * Find or create a deflated string cache entry for str that contains its + * characters chopped from Unicode code points into bytes. + */ +extern char * +js_GetStringBytes(JSRuntime *rt, JSString *str); + +/* Remove a deflated string cache entry associated with str if any. */ +extern void +js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); + +JSBool +js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +/* + * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at + * least 6 bytes long. Return the number of UTF-8 bytes of data written. + */ +extern int +js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); + +JS_END_EXTERN_C + +#endif /* jsstr_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jstypes.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jstypes.h new file mode 100644 index 0000000..8aca929 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jstypes.h @@ -0,0 +1,464 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* +** File: jstypes.h +** Description: Definitions of NSPR's basic types +** +** Prototypes and macros used to make up for deficiencies in ANSI environments +** that we have found. +** +** Since we do not wrap and all the other standard headers, authors +** of portable code will not know in general that they need these definitions. +** Instead of requiring these authors to find the dependent uses in their code +** and take the following steps only in those C files, we take steps once here +** for all C files. +**/ + +#ifndef jstypes_h___ +#define jstypes_h___ + +#include + +/*********************************************************************** +** MACROS: JS_EXTERN_API +** JS_EXPORT_API +** DESCRIPTION: +** These are only for externally visible routines and globals. For +** internal routines, just use "extern" for type checking and that +** will not export internal cross-file or forward-declared symbols. +** Define a macro for declaring procedures return types. We use this to +** deal with windoze specific type hackery for DLL definitions. Use +** JS_EXTERN_API when the prototype for the method is declared. Use +** JS_EXPORT_API for the implementation of the method. +** +** Example: +** in dowhim.h +** JS_EXTERN_API( void ) DoWhatIMean( void ); +** in dowhim.c +** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; } +** +** +***********************************************************************/ +#ifdef WIN32 +/* These also work for __MWERKS__ */ +#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_API(__type) __declspec(dllexport) __type +#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#elif defined(XP_OS2) && defined(__declspec) + +#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_API(__type) __declspec(dllexport) __type +#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#elif defined(WIN16) + +#ifdef _WINDLL +#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds +#define JS_EXPORT_API(__type) __type _cdecl _export _loadds +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK + +#else /* this must be .EXE */ +#define JS_EXTERN_API(__type) extern __type _cdecl _export +#define JS_EXPORT_API(__type) __type _cdecl _export +#define JS_EXTERN_DATA(__type) extern __type _export +#define JS_EXPORT_DATA(__type) __type _export + +#define JS_DLL_CALLBACK __cdecl __loadds +#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK +#endif /* _WINDLL */ + +#else /* Unix */ + +#ifdef HAVE_VISIBILITY_ATTRIBUTE +#define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) +#else +#define JS_EXTERNAL_VIS +#endif + +#define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type +#define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type +#define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type + +#define JS_DLL_CALLBACK +#define JS_STATIC_DLL_CALLBACK(__x) static __x + +#endif + +#ifdef _WIN32 +# if defined(__MWERKS__) || defined(__GNUC__) +# define JS_IMPORT_API(__x) __x +# else +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +# endif +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_API(__x) __declspec(dllimport) __x +#else +# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) +#endif + +#if defined(_WIN32) && !defined(__MWERKS__) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#elif defined(XP_OS2) && defined(__declspec) +# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x +#else +# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) +#endif + +/* + * The linkage of JS API functions differs depending on whether the file is + * used within the JS library or not. Any source file within the JS + * interpreter should define EXPORT_JS_API whereas any client of the library + * should not. + */ +#ifdef EXPORT_JS_API +#define JS_PUBLIC_API(t) JS_EXPORT_API(t) +#define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) +#else +#define JS_PUBLIC_API(t) JS_IMPORT_API(t) +#define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) +#endif + +#define JS_FRIEND_API(t) JS_PUBLIC_API(t) +#define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) + +#ifdef _WIN32 +# define JS_INLINE __inline +#elif defined(__GNUC__) +# define JS_INLINE +#else +# define JS_INLINE +#endif + +/*********************************************************************** +** MACROS: JS_BEGIN_MACRO +** JS_END_MACRO +** DESCRIPTION: +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +***********************************************************************/ +#define JS_BEGIN_MACRO do { +#define JS_END_MACRO } while (0) + +/*********************************************************************** +** MACROS: JS_BEGIN_EXTERN_C +** JS_END_EXTERN_C +** DESCRIPTION: +** Macro shorthands for conditional C++ extern block delimiters. +***********************************************************************/ +#ifdef __cplusplus +#define JS_BEGIN_EXTERN_C extern "C" { +#define JS_END_EXTERN_C } +#else +#define JS_BEGIN_EXTERN_C +#define JS_END_EXTERN_C +#endif + +/*********************************************************************** +** MACROS: JS_BIT +** JS_BITMASK +** DESCRIPTION: +** Bit masking macros. XXX n must be <= 31 to be portable +***********************************************************************/ +#define JS_BIT(n) ((JSUint32)1 << (n)) +#define JS_BITMASK(n) (JS_BIT(n) - 1) + +/*********************************************************************** +** MACROS: JS_PTR_TO_INT32 +** JS_PTR_TO_UINT32 +** JS_INT32_TO_PTR +** JS_UINT32_TO_PTR +** DESCRIPTION: +** Integer to pointer and pointer to integer conversion macros. +***********************************************************************/ +#define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) +#define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) +#define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) +#define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) + +/*********************************************************************** +** MACROS: JS_HOWMANY +** JS_ROUNDUP +** JS_MIN +** JS_MAX +** DESCRIPTION: +** Commonly used macros for operations on compatible types. +***********************************************************************/ +#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y)) +#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y)) +#define JS_MIN(x,y) ((x)<(y)?(x):(y)) +#define JS_MAX(x,y) ((x)>(y)?(x):(y)) + +#if (defined(XP_WIN) && !defined(CROSS_COMPILE)) || defined (WINCE) +# include "jscpucfg.h" /* Use standard Mac or Windows configuration */ +#elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE) +# include "jsautocfg.h" /* Use auto-detected configuration */ +# include "jsosdep.h" /* ...and platform-specific flags */ +#else +# error "Must define one of XP_BEOS, XP_OS2, XP_WIN or XP_UNIX" +#endif + +JS_BEGIN_EXTERN_C + +/************************************************************************ +** TYPES: JSUint8 +** JSInt8 +** DESCRIPTION: +** The int8 types are known to be 8 bits each. There is no type that +** is equivalent to a plain "char". +************************************************************************/ +#if JS_BYTES_PER_BYTE == 1 +typedef unsigned char JSUint8; +typedef signed char JSInt8; +#else +#error No suitable type for JSInt8/JSUint8 +#endif + +/************************************************************************ +** TYPES: JSUint16 +** JSInt16 +** DESCRIPTION: +** The int16 types are known to be 16 bits each. +************************************************************************/ +#if JS_BYTES_PER_SHORT == 2 +typedef unsigned short JSUint16; +typedef short JSInt16; +#else +#error No suitable type for JSInt16/JSUint16 +#endif + +/************************************************************************ +** TYPES: JSUint32 +** JSInt32 +** DESCRIPTION: +** The int32 types are known to be 32 bits each. +************************************************************************/ +#if JS_BYTES_PER_INT == 4 +typedef unsigned int JSUint32; +typedef int JSInt32; +#define JS_INT32(x) x +#define JS_UINT32(x) x ## U +#elif JS_BYTES_PER_LONG == 4 +typedef unsigned long JSUint32; +typedef long JSInt32; +#define JS_INT32(x) x ## L +#define JS_UINT32(x) x ## UL +#else +#error No suitable type for JSInt32/JSUint32 +#endif + +/************************************************************************ +** TYPES: JSUint64 +** JSInt64 +** DESCRIPTION: +** The int64 types are known to be 64 bits each. Care must be used when +** declaring variables of type JSUint64 or JSInt64. Different hardware +** architectures and even different compilers have varying support for +** 64 bit values. The only guaranteed portability requires the use of +** the JSLL_ macros (see jslong.h). +************************************************************************/ +#ifdef JS_HAVE_LONG_LONG +#if JS_BYTES_PER_LONG == 8 +typedef long JSInt64; +typedef unsigned long JSUint64; +#elif defined(WIN16) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#elif defined(WIN32) && !defined(__GNUC__) +typedef __int64 JSInt64; +typedef unsigned __int64 JSUint64; +#else +typedef long long JSInt64; +typedef unsigned long long JSUint64; +#endif /* JS_BYTES_PER_LONG == 8 */ +#else /* !JS_HAVE_LONG_LONG */ +typedef struct { +#ifdef IS_LITTLE_ENDIAN + JSUint32 lo, hi; +#else + JSUint32 hi, lo; +#endif +} JSInt64; +typedef JSInt64 JSUint64; +#endif /* !JS_HAVE_LONG_LONG */ + +/************************************************************************ +** TYPES: JSUintn +** JSIntn +** DESCRIPTION: +** The JSIntn types are most appropriate for automatic variables. They are +** guaranteed to be at least 16 bits, though various architectures may +** define them to be wider (e.g., 32 or even 64 bits). These types are +** never valid for fields of a structure. +************************************************************************/ +#if JS_BYTES_PER_INT >= 2 +typedef int JSIntn; +typedef unsigned int JSUintn; +#else +#error 'sizeof(int)' not sufficient for platform use +#endif + +/************************************************************************ +** TYPES: JSFloat64 +** DESCRIPTION: +** NSPR's floating point type is always 64 bits. +************************************************************************/ +typedef double JSFloat64; + +/************************************************************************ +** TYPES: JSSize +** DESCRIPTION: +** A type for representing the size of objects. +************************************************************************/ +typedef size_t JSSize; + +/************************************************************************ +** TYPES: JSPtrDiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +typedef ptrdiff_t JSPtrdiff; + +/************************************************************************ +** TYPES: JSUptrdiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 +typedef JSUint64 JSUptrdiff; +#else +typedef unsigned long JSUptrdiff; +#endif + +/************************************************************************ +** TYPES: JSBool +** DESCRIPTION: +** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE +** for clarity of target type in assignments and actual arguments. Use +** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans +** just as you would C int-valued conditions. +************************************************************************/ +typedef JSIntn JSBool; +#define JS_TRUE (JSIntn)1 +#define JS_FALSE (JSIntn)0 + +/************************************************************************ +** TYPES: JSPackedBool +** DESCRIPTION: +** Use JSPackedBool within structs where bitfields are not desireable +** but minimum and consistent overhead matters. +************************************************************************/ +typedef JSUint8 JSPackedBool; + +/* +** A JSWord is an integer that is the same size as a void* +*/ +#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 +typedef JSInt64 JSWord; +typedef JSUint64 JSUword; +#else +typedef long JSWord; +typedef unsigned long JSUword; +#endif + +#include "jsotypes.h" + +/*********************************************************************** +** MACROS: JS_LIKELY +** JS_UNLIKELY +** DESCRIPTION: +** These macros allow you to give a hint to the compiler about branch +** probability so that it can better optimize. Use them like this: +** +** if (JS_LIKELY(v == 1)) { +** ... expected code path ... +** } +** +** if (JS_UNLIKELY(v == 0)) { +** ... non-expected code path ... +** } +** +***********************************************************************/ +#if defined(__GNUC__) && (__GNUC__ > 2) +#define JS_LIKELY(x) (__builtin_expect((x), 1)) +#define JS_UNLIKELY(x) (__builtin_expect((x), 0)) +#else +#define JS_LIKELY(x) (x) +#define JS_UNLIKELY(x) (x) +#endif + +/*********************************************************************** +** MACROS: JS_ARRAY_LENGTH +** JS_ARRAY_END +** DESCRIPTION: +** Macros to get the number of elements and the pointer to one past the +** last element of a C array. Use them like this: +** +** jschar buf[10], *s; +** JSString *str; +** ... +** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; +** ... +** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); +** ... +** +***********************************************************************/ + +#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) +#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) + +JS_END_EXTERN_C + +#endif /* jstypes_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsutil.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsutil.c new file mode 100644 index 0000000..1bb9f93 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsutil.c @@ -0,0 +1,198 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ +#include "jsstddef.h" +#include +#include +#include "jstypes.h" +#include "jsutil.h" + +#ifdef WIN32 +# include +#endif + +JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) +{ + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); +#if defined(WIN32) + DebugBreak(); + exit(3); +#elif defined(XP_OS2) || (defined(__GNUC__) && defined(__i386)) + asm("int $3"); +#endif + abort(); +} + +#if defined DEBUG_notme && defined XP_UNIX + +#define __USE_GNU 1 +#include +#include +#include "jshash.h" +#include "jsprf.h" + +JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; + +static JSCallsite * +CallTree(void **bp) +{ + void **bpup, **bpdown, *pc; + JSCallsite *parent, *site, **csp; + Dl_info info; + int ok, offset; + const char *symbol; + char *method; + + /* Reverse the stack frame list to avoid recursion. */ + bpup = NULL; + for (;;) { + bpdown = (void**) bp[0]; + bp[0] = (void*) bpup; + if ((void**) bpdown[0] < bpdown) + break; + bpup = bp; + bp = bpdown; + } + + /* Reverse the stack again, finding and building a path in the tree. */ + parent = &js_calltree_root; + do { + bpup = (void**) bp[0]; + bp[0] = (void*) bpdown; + pc = bp[1]; + + csp = &parent->kids; + while ((site = *csp) != NULL) { + if (site->pc == pc) { + /* Put the most recently used site at the front of siblings. */ + *csp = site->siblings; + site->siblings = parent->kids; + parent->kids = site; + + /* Site already built -- go up the stack. */ + goto upward; + } + csp = &site->siblings; + } + + /* Check for recursion: see if pc is on our ancestor line. */ + for (site = parent; site; site = site->parent) { + if (site->pc == pc) + goto upward; + } + + /* + * Not in tree at all: let's find our symbolic callsite info. + * XXX static syms are masked by nearest lower global + */ + info.dli_fname = info.dli_sname = NULL; + ok = dladdr(pc, &info); + if (ok < 0) { + fprintf(stderr, "dladdr failed!\n"); + return NULL; + } + +/* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ + symbol = info.dli_sname; + offset = (char*)pc - (char*)info.dli_fbase; + method = symbol + ? strdup(symbol) + : JS_smprintf("%s+%X", + info.dli_fname ? info.dli_fname : "main", + offset); + if (!method) + return NULL; + + /* Create a new callsite record. */ + site = (JSCallsite *) malloc(sizeof(JSCallsite)); + if (!site) + return NULL; + + /* Insert the new site into the tree. */ + site->pc = pc; + site->name = method; + site->library = info.dli_fname; + site->offset = offset; + site->parent = parent; + site->siblings = parent->kids; + parent->kids = site; + site->kids = NULL; + + upward: + parent = site; + bpdown = bp; + bp = bpup; + } while (bp); + + return site; +} + +JSCallsite * +JS_Backtrace(int skip) +{ + void **bp, **bpdown; + + /* Stack walking code adapted from Kipp's "leaky". */ +#if defined(__i386) + __asm__( "movl %%ebp, %0" : "=g"(bp)); +#elif defined(__x86_64__) + __asm__( "movq %%rbp, %0" : "=g"(bp)); +#else + /* + * It would be nice if this worked uniformly, but at least on i386 and + * x86_64, it stopped working with gcc 4.1, because it points to the + * end of the saved registers instead of the start. + */ + bp = (void**) __builtin_frame_address(0); +#endif + while (--skip >= 0) { + bpdown = (void**) *bp++; + if (bpdown < bp) + break; + bp = bpdown; + } + + return CallTree(bp); +} + +#endif /* DEBUG_notme && XP_UNIX */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsutil.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsutil.h new file mode 100644 index 0000000..efcb614 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsutil.h @@ -0,0 +1,106 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR assertion checker. + */ + +#ifndef jsutil_h___ +#define jsutil_h___ + +JS_BEGIN_EXTERN_C + +#ifdef DEBUG + +extern JS_PUBLIC_API(void) +JS_Assert(const char *s, const char *file, JSIntn ln); +#define JS_ASSERT(_expr) \ + ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__)) + +#define JS_NOT_REACHED(_reasonStr) \ + JS_Assert(_reasonStr,__FILE__,__LINE__) + +#else + +#define JS_ASSERT(expr) ((void) 0) +#define JS_NOT_REACHED(reasonStr) + +#endif /* defined(DEBUG) */ + +/* + * Compile-time assert. "condition" must be a constant expression. + * The macro should be used only once per source line in places where + * a "typedef" declaration is allowed. + */ +#define JS_STATIC_ASSERT(condition) \ + JS_STATIC_ASSERT_IMPL(condition, __LINE__) +#define JS_STATIC_ASSERT_IMPL(condition, line) \ + JS_STATIC_ASSERT_IMPL2(condition, line) +#define JS_STATIC_ASSERT_IMPL2(condition, line) \ + typedef int js_static_assert_line_##line[(condition) ? 1 : -1] + +/* +** Abort the process in a non-graceful manner. This will cause a core file, +** call to the debugger or other moral equivalent as well as causing the +** entire process to stop. +*/ +extern JS_PUBLIC_API(void) JS_Abort(void); + +#ifdef XP_UNIX + +typedef struct JSCallsite JSCallsite; + +struct JSCallsite { + uint32 pc; + char *name; + const char *library; + int offset; + JSCallsite *parent; + JSCallsite *siblings; + JSCallsite *kids; + void *handy; +}; + +extern JSCallsite *JS_Backtrace(int skip); + +#endif + +JS_END_EXTERN_C + +#endif /* jsutil_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxdrapi.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxdrapi.c new file mode 100644 index 0000000..2855c60 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxdrapi.c @@ -0,0 +1,835 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#include "jsstddef.h" +#include "jsconfig.h" + +#if JS_HAS_XDR + +#include +#include "jstypes.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jsdhash.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jscntxt.h" +#include "jsnum.h" +#include "jsobj.h" /* js_XDRObject */ +#include "jsscript.h" /* js_XDRScript */ +#include "jsstr.h" +#include "jsxdrapi.h" + +#ifdef DEBUG +#define DBG(x) x +#else +#define DBG(x) ((void)0) +#endif + +typedef struct JSXDRMemState { + JSXDRState state; + char *base; + uint32 count; + uint32 limit; +} JSXDRMemState; + +#define MEM_BLOCK 8192 +#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) + +#define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) +#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) +#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) + +#define MEM_LEFT(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_DECODE && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ + JSMSG_END_OF_DATA); \ + return 0; \ + } \ + JS_END_MACRO + +#define MEM_NEED(xdr, bytes) \ + JS_BEGIN_MACRO \ + if ((xdr)->mode == JSXDR_ENCODE) { \ + if (MEM_LIMIT(xdr) && \ + MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ + uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ + void *data_ = JS_realloc((xdr)->cx, MEM_BASE(xdr), limit_); \ + if (!data_) \ + return 0; \ + MEM_BASE(xdr) = data_; \ + MEM_LIMIT(xdr) = limit_; \ + } \ + } else { \ + MEM_LEFT(xdr, bytes); \ + } \ + JS_END_MACRO + +#define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr))) +#define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) + +static JSBool +mem_get32(JSXDRState *xdr, uint32 *lp) +{ + MEM_LEFT(xdr, 4); + *lp = *(uint32 *)MEM_DATA(xdr); + MEM_INCR(xdr, 4); + return JS_TRUE; +} + +static JSBool +mem_set32(JSXDRState *xdr, uint32 *lp) +{ + MEM_NEED(xdr, 4); + *(uint32 *)MEM_DATA(xdr) = *lp; + MEM_INCR(xdr, 4); + return JS_TRUE; +} + +static JSBool +mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + MEM_LEFT(xdr, len); + memcpy(bytes, MEM_DATA(xdr), len); + MEM_INCR(xdr, len); + return JS_TRUE; +} + +static JSBool +mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + MEM_NEED(xdr, len); + memcpy(MEM_DATA(xdr), bytes, len); + MEM_INCR(xdr, len); + return JS_TRUE; +} + +static void * +mem_raw(JSXDRState *xdr, uint32 len) +{ + void *data; + if (xdr->mode == JSXDR_ENCODE) { + MEM_NEED(xdr, len); + } else if (xdr->mode == JSXDR_DECODE) { + MEM_LEFT(xdr, len); + } + data = MEM_DATA(xdr); + MEM_INCR(xdr, len); + return data; +} + +static JSBool +mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) +{ + switch (whence) { + case JSXDR_SEEK_CUR: + if ((int32)MEM_COUNT(xdr) + offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_START); + return JS_FALSE; + } + if (offset > 0) + MEM_NEED(xdr, offset); + MEM_COUNT(xdr) += offset; + return JS_TRUE; + case JSXDR_SEEK_SET: + if (offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_START); + return JS_FALSE; + } + if (xdr->mode == JSXDR_ENCODE) { + if ((uint32)offset > MEM_COUNT(xdr)) + MEM_NEED(xdr, offset - MEM_COUNT(xdr)); + MEM_COUNT(xdr) = offset; + } else { + if ((uint32)offset > MEM_LIMIT(xdr)) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_SEEK_BEYOND_END); + return JS_FALSE; + } + MEM_COUNT(xdr) = offset; + } + return JS_TRUE; + case JSXDR_SEEK_END: + if (offset >= 0 || + xdr->mode == JSXDR_ENCODE || + (int32)MEM_LIMIT(xdr) + offset < 0) { + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_END_SEEK); + return JS_FALSE; + } + MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset; + return JS_TRUE; + default: { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", whence); + JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, + JSMSG_WHITHER_WHENCE, numBuf); + return JS_FALSE; + } + } +} + +static uint32 +mem_tell(JSXDRState *xdr) +{ + return MEM_COUNT(xdr); +} + +static void +mem_finalize(JSXDRState *xdr) +{ + JS_free(xdr->cx, MEM_BASE(xdr)); +} + +static JSXDROps xdrmem_ops = { + mem_get32, mem_set32, mem_getbytes, mem_setbytes, + mem_raw, mem_seek, mem_tell, mem_finalize +}; + +JS_PUBLIC_API(void) +JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) +{ + xdr->mode = mode; + xdr->cx = cx; + xdr->registry = NULL; + xdr->numclasses = xdr->maxclasses = 0; + xdr->reghash = NULL; + xdr->userdata = NULL; + xdr->script = NULL; +} + +JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode) +{ + JSXDRState *xdr = (JSXDRState *) JS_malloc(cx, sizeof(JSXDRMemState)); + if (!xdr) + return NULL; + JS_XDRInitBase(xdr, mode, cx); + if (mode == JSXDR_ENCODE) { + if (!(MEM_BASE(xdr) = JS_malloc(cx, MEM_BLOCK))) { + JS_free(cx, xdr); + return NULL; + } + } else { + /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */ + MEM_BASE(xdr) = NULL; + } + xdr->ops = &xdrmem_ops; + MEM_COUNT(xdr) = 0; + MEM_LIMIT(xdr) = MEM_BLOCK; + return xdr; +} + +JS_PUBLIC_API(void *) +JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) +{ + if (xdr->ops != &xdrmem_ops) + return NULL; + *lp = MEM_COUNT(xdr); + return MEM_BASE(xdr); +} + +JS_PUBLIC_API(void) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) +{ + if (xdr->ops != &xdrmem_ops) + return; + MEM_LIMIT(xdr) = len; + MEM_BASE(xdr) = data; + MEM_COUNT(xdr) = 0; +} + +JS_PUBLIC_API(uint32) +JS_XDRMemDataLeft(JSXDRState *xdr) +{ + if (xdr->ops != &xdrmem_ops) + return 0; + return MEM_LIMIT(xdr) - MEM_COUNT(xdr); +} + +JS_PUBLIC_API(void) +JS_XDRMemResetData(JSXDRState *xdr) +{ + if (xdr->ops != &xdrmem_ops) + return; + MEM_COUNT(xdr) = 0; +} + +JS_PUBLIC_API(void) +JS_XDRDestroy(JSXDRState *xdr) +{ + JSContext *cx = xdr->cx; + xdr->ops->finalize(xdr); + if (xdr->registry) { + JS_free(cx, xdr->registry); + if (xdr->reghash) + JS_DHashTableDestroy(xdr->reghash); + } + JS_free(cx, xdr); +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint8(JSXDRState *xdr, uint8 *b) +{ + uint32 l = *b; + if (!JS_XDRUint32(xdr, &l)) + return JS_FALSE; + *b = (uint8) l; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint16(JSXDRState *xdr, uint16 *s) +{ + uint32 l = *s; + if (!JS_XDRUint32(xdr, &l)) + return JS_FALSE; + *s = (uint16) l; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRUint32(JSXDRState *xdr, uint32 *lp) +{ + JSBool ok = JS_TRUE; + if (xdr->mode == JSXDR_ENCODE) { + uint32 xl = JSXDR_SWAB32(*lp); + ok = xdr->ops->set32(xdr, &xl); + } else if (xdr->mode == JSXDR_DECODE) { + ok = xdr->ops->get32(xdr, lp); + *lp = JSXDR_SWAB32(*lp); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) +{ + uint32 padlen; + static char padbuf[JSXDR_ALIGN-1]; + + if (xdr->mode == JSXDR_ENCODE) { + if (!xdr->ops->setbytes(xdr, bytes, len)) + return JS_FALSE; + } else { + if (!xdr->ops->getbytes(xdr, bytes, len)) + return JS_FALSE; + } + len = xdr->ops->tell(xdr); + if (len % JSXDR_ALIGN) { + padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN); + if (xdr->mode == JSXDR_ENCODE) { + if (!xdr->ops->setbytes(xdr, padbuf, padlen)) + return JS_FALSE; + } else { + if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +/** + * Convert between a C string and the XDR representation: + * leading 32-bit count, then counted vector of chars, + * then possibly \0 padding to multiple of 4. + */ +JS_PUBLIC_API(JSBool) +JS_XDRCString(JSXDRState *xdr, char **sp) +{ + uint32 len; + + if (xdr->mode == JSXDR_ENCODE) + len = strlen(*sp); + JS_XDRUint32(xdr, &len); + if (xdr->mode == JSXDR_DECODE) { + if (!(*sp = (char *) JS_malloc(xdr->cx, len + 1))) + return JS_FALSE; + } + if (!JS_XDRBytes(xdr, *sp, len)) { + if (xdr->mode == JSXDR_DECODE) + JS_free(xdr->cx, *sp); + return JS_FALSE; + } + if (xdr->mode == JSXDR_DECODE) { + (*sp)[len] = '\0'; + } else if (xdr->mode == JSXDR_FREE) { + JS_free(xdr->cx, *sp); + *sp = NULL; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) +{ + uint32 null = (*sp == NULL); + if (!JS_XDRUint32(xdr, &null)) + return JS_FALSE; + if (null) { + *sp = NULL; + return JS_TRUE; + } + return JS_XDRCString(xdr, sp); +} + +static JSBool +XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars) +{ + uint32 i, padlen, nbytes; + jschar *raw; + + nbytes = nchars * sizeof(jschar); + padlen = nbytes % JSXDR_ALIGN; + if (padlen) { + padlen = JSXDR_ALIGN - padlen; + nbytes += padlen; + } + if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) + return JS_FALSE; + if (xdr->mode == JSXDR_ENCODE) { + for (i = 0; i != nchars; i++) + raw[i] = JSXDR_SWAB16(chars[i]); + if (padlen) + memset((char *)raw + nbytes - padlen, 0, padlen); + } else if (xdr->mode == JSXDR_DECODE) { + for (i = 0; i != nchars; i++) + chars[i] = JSXDR_SWAB16(raw[i]); + } + return JS_TRUE; +} + +/* + * Convert between a JS (Unicode) string and the XDR representation. + */ +JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp) +{ + uint32 nchars; + jschar *chars; + + if (xdr->mode == JSXDR_ENCODE) + nchars = JSSTRING_LENGTH(*strp); + if (!JS_XDRUint32(xdr, &nchars)) + return JS_FALSE; + + if (xdr->mode == JSXDR_DECODE) { + chars = (jschar *) JS_malloc(xdr->cx, (nchars + 1) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + } else { + chars = JSSTRING_CHARS(*strp); + } + + if (!XDRChars(xdr, chars, nchars)) + goto bad; + if (xdr->mode == JSXDR_DECODE) { + chars[nchars] = 0; + *strp = JS_NewUCString(xdr->cx, chars, nchars); + if (!*strp) + goto bad; + } + return JS_TRUE; + +bad: + if (xdr->mode == JSXDR_DECODE) + JS_free(xdr->cx, chars); + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) +{ + uint32 null = (*strp == NULL); + if (!JS_XDRUint32(xdr, &null)) + return JS_FALSE; + if (null) { + *strp = NULL; + return JS_TRUE; + } + return JS_XDRString(xdr, strp); +} + +static JSBool +XDRDoubleValue(JSXDRState *xdr, jsdouble *dp) +{ + jsdpun u; + + if (xdr->mode == JSXDR_ENCODE) + u.d = *dp; + if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *dp = u.d; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) +{ + jsdouble d; + + if (xdr->mode == JSXDR_ENCODE) + d = **dpp; + if (!XDRDoubleValue(xdr, &d)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) { + *dpp = JS_NewDouble(xdr->cx, d); + if (!*dpp) + return JS_FALSE; + } + return JS_TRUE; +} + +/* These are magic pseudo-tags: see jsapi.h, near the top, for real tags. */ +#define JSVAL_XDRNULL 0x8 +#define JSVAL_XDRVOID 0xA + +static JSBool +XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) +{ + switch (type) { + case JSVAL_XDRNULL: + *vp = JSVAL_NULL; + break; + case JSVAL_XDRVOID: + *vp = JSVAL_VOID; + break; + case JSVAL_STRING: { + JSString *str; + if (xdr->mode == JSXDR_ENCODE) + str = JSVAL_TO_STRING(*vp); + if (!JS_XDRString(xdr, &str)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = STRING_TO_JSVAL(str); + break; + } + case JSVAL_DOUBLE: { + jsdouble *dp; + if (xdr->mode == JSXDR_ENCODE) + dp = JSVAL_TO_DOUBLE(*vp); + if (!JS_XDRDouble(xdr, &dp)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = DOUBLE_TO_JSVAL(dp); + break; + } + case JSVAL_OBJECT: { + JSObject *obj; + if (xdr->mode == JSXDR_ENCODE) + obj = JSVAL_TO_OBJECT(*vp); + if (!js_XDRObject(xdr, &obj)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = OBJECT_TO_JSVAL(obj); + break; + } + case JSVAL_BOOLEAN: { + uint32 b; + if (xdr->mode == JSXDR_ENCODE) + b = (uint32) JSVAL_TO_BOOLEAN(*vp); + if (!JS_XDRUint32(xdr, &b)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = BOOLEAN_TO_JSVAL((JSBool) b); + break; + } + default: { + uint32 i; + + JS_ASSERT(type & JSVAL_INT); + if (xdr->mode == JSXDR_ENCODE) + i = (uint32) JSVAL_TO_INT(*vp); + if (!JS_XDRUint32(xdr, &i)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + *vp = INT_TO_JSVAL((int32) i); + break; + } + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp) +{ + uint32 type; + + if (xdr->mode == JSXDR_ENCODE) { + if (JSVAL_IS_NULL(*vp)) + type = JSVAL_XDRNULL; + else if (JSVAL_IS_VOID(*vp)) + type = JSVAL_XDRVOID; + else + type = JSVAL_TAG(*vp); + } + return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); +} + +JSBool +js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) +{ + jsval v; + uint32 type; + jsdouble d; + JSAtom *atom; + + if (xdr->mode == JSXDR_ENCODE) { + v = ATOM_KEY(*atomp); + return JS_XDRValue(xdr, &v); + } + + /* + * Inline JS_XDRValue when decoding to avoid ceation of GC things when + * then corresponding atom already exists. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &type)) + return JS_FALSE; + if (type == JSVAL_STRING) + return js_XDRStringAtom(xdr, atomp); + + if (type == JSVAL_DOUBLE) { + if (!XDRDoubleValue(xdr, &d)) + return JS_FALSE; + atom = js_AtomizeDouble(xdr->cx, d, 0); + } else { + if (!XDRValueBody(xdr, type, &v)) + return JS_FALSE; + atom = js_AtomizeValue(xdr->cx, v, 0); + } + + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; +} + +extern JSBool +js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp) +{ + JSString *str; + uint32 nchars; + JSAtom *atom; + JSContext *cx; + void *mark; + jschar *chars; + + if (xdr->mode == JSXDR_ENCODE) { + JS_ASSERT(ATOM_IS_STRING(*atomp)); + str = ATOM_TO_STRING(*atomp); + return JS_XDRString(xdr, &str); + } + + /* + * Inline JS_XDRString when decoding to avoid JSString allocation + * for already existing atoms. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &nchars)) + return JS_FALSE; + atom = NULL; + cx = xdr->cx; + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(chars, jschar *, &cx->tempPool, + nchars * sizeof(jschar)); + if (!chars) + JS_ReportOutOfMemory(cx); + else if (XDRChars(xdr, chars, nchars)) + atom = js_AtomizeChars(cx, chars, nchars, 0); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; +} + +/* + * FIXME: This performs lossy conversion and we need to switch to + * js_XDRStringAtom while allowing to read older XDR files. See bug 325202. + */ +JSBool +js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp) +{ + char *bytes; + uint32 nbytes; + JSAtom *atom; + JSContext *cx; + void *mark; + + if (xdr->mode == JSXDR_ENCODE) { + JS_ASSERT(ATOM_IS_STRING(*atomp)); + bytes = JS_GetStringBytes(ATOM_TO_STRING(*atomp)); + return JS_XDRCString(xdr, &bytes); + } + + /* + * Inline JS_XDRCString when decoding not to malloc temporary buffer + * just to free it after atomization. See bug 321985. + */ + if (!JS_XDRUint32(xdr, &nbytes)) + return JS_FALSE; + atom = NULL; + cx = xdr->cx; + mark = JS_ARENA_MARK(&cx->tempPool); + JS_ARENA_ALLOCATE_CAST(bytes, char *, &cx->tempPool, + nbytes * sizeof *bytes); + if (!bytes) + JS_ReportOutOfMemory(cx); + else if (JS_XDRBytes(xdr, bytes, nbytes)) + atom = js_Atomize(cx, bytes, nbytes, 0); + JS_ARENA_RELEASE(&cx->tempPool, mark); + if (!atom) + return JS_FALSE; + *atomp = atom; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) +{ + if (!js_XDRScript(xdr, scriptp, NULL)) + return JS_FALSE; + if (xdr->mode == JSXDR_DECODE) + js_CallNewScriptHook(xdr->cx, *scriptp, NULL); + return JS_TRUE; +} + +#define CLASS_REGISTRY_MIN 8 +#define CLASS_INDEX_TO_ID(i) ((i)+1) +#define CLASS_ID_TO_INDEX(id) ((id)-1) + +typedef struct JSRegHashEntry { + JSDHashEntryHdr hdr; + const char *name; + uint32 index; +} JSRegHashEntry; + +JS_PUBLIC_API(JSBool) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) +{ + uintN numclasses, maxclasses; + JSClass **registry; + + numclasses = xdr->numclasses; + maxclasses = xdr->maxclasses; + if (numclasses == maxclasses) { + maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1; + registry = (JSClass **) + JS_realloc(xdr->cx, xdr->registry, maxclasses * sizeof(JSClass *)); + if (!registry) + return JS_FALSE; + xdr->registry = registry; + xdr->maxclasses = maxclasses; + } else { + JS_ASSERT(numclasses && numclasses < maxclasses); + registry = xdr->registry; + } + + registry[numclasses] = clasp; + if (xdr->reghash) { + JSRegHashEntry *entry = (JSRegHashEntry *) + JS_DHashTableOperate(xdr->reghash, clasp->name, JS_DHASH_ADD); + if (!entry) { + JS_ReportOutOfMemory(xdr->cx); + return JS_FALSE; + } + entry->name = clasp->name; + entry->index = numclasses; + } + *idp = CLASS_INDEX_TO_ID(numclasses); + xdr->numclasses = ++numclasses; + return JS_TRUE; +} + +JS_PUBLIC_API(uint32) +JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) +{ + uintN i, numclasses; + + numclasses = xdr->numclasses; + if (numclasses >= 10) { + JSRegHashEntry *entry; + + /* Bootstrap reghash from registry on first overpopulated Find. */ + if (!xdr->reghash) { + xdr->reghash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, + sizeof(JSRegHashEntry), + numclasses); + if (xdr->reghash) { + for (i = 0; i < numclasses; i++) { + JSClass *clasp = xdr->registry[i]; + entry = (JSRegHashEntry *) + JS_DHashTableOperate(xdr->reghash, clasp->name, + JS_DHASH_ADD); + entry->name = clasp->name; + entry->index = i; + } + } + } + + /* If we managed to create reghash, use it for O(1) Find. */ + if (xdr->reghash) { + entry = (JSRegHashEntry *) + JS_DHashTableOperate(xdr->reghash, name, JS_DHASH_LOOKUP); + if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) + return CLASS_INDEX_TO_ID(entry->index); + } + } + + /* Only a few classes, or we couldn't malloc reghash: use linear search. */ + for (i = 0; i < numclasses; i++) { + if (!strcmp(name, xdr->registry[i]->name)) + return CLASS_INDEX_TO_ID(i); + } + return 0; +} + +JS_PUBLIC_API(JSClass *) +JS_XDRFindClassById(JSXDRState *xdr, uint32 id) +{ + uintN i = CLASS_ID_TO_INDEX(id); + + if (i >= xdr->numclasses) + return NULL; + return xdr->registry[i]; +} + +#endif /* JS_HAS_XDR */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxdrapi.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxdrapi.h new file mode 100644 index 0000000..35d9918 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxdrapi.h @@ -0,0 +1,223 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxdrapi_h___ +#define jsxdrapi_h___ + +/* + * JS external data representation interface API. + * + * The XDR system is comprised of three major parts: + * + * - the state serialization/deserialization APIs, which allow consumers + * of the API to serialize JS runtime state (script bytecodes, atom maps, + * object graphs, etc.) for later restoration. These portions + * are implemented in various appropriate files, such as jsscript.c + * for the script portions and jsobj.c for object state. + * - the callback APIs through which the runtime requests an opaque + * representation of a native object, and through which the runtime + * constructs a live native object from an opaque representation. These + * portions are the responsibility of the native object implementor. + * - utility functions for en/decoding of primitive types, such as + * JSStrings. This portion is implemented in jsxdrapi.c. + * + * Spiritually guided by Sun's XDR, where appropriate. + */ + +#include "jspubtd.h" +#include "jsprvtd.h" + +JS_BEGIN_EXTERN_C + +/* We use little-endian byteorder for all encoded data */ + +#if defined IS_LITTLE_ENDIAN +#define JSXDR_SWAB32(x) x +#define JSXDR_SWAB16(x) x +#elif defined IS_BIG_ENDIAN +#define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ + (((uint32)(x) >> 8) & 0xff00) | \ + (((uint32)(x) << 8) & 0xff0000) | \ + ((uint32)(x) << 24)) +#define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) +#else +#error "unknown byte order" +#endif + +#define JSXDR_ALIGN 4 + +typedef enum JSXDRMode { + JSXDR_ENCODE, + JSXDR_DECODE, + JSXDR_FREE +} JSXDRMode; + +typedef enum JSXDRWhence { + JSXDR_SEEK_SET, + JSXDR_SEEK_CUR, + JSXDR_SEEK_END +} JSXDRWhence; + +typedef struct JSXDROps { + JSBool (*get32)(JSXDRState *, uint32 *); + JSBool (*set32)(JSXDRState *, uint32 *); + JSBool (*getbytes)(JSXDRState *, char *, uint32); + JSBool (*setbytes)(JSXDRState *, char *, uint32); + void * (*raw)(JSXDRState *, uint32); + JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); + uint32 (*tell)(JSXDRState *); + void (*finalize)(JSXDRState *); +} JSXDROps; + +struct JSXDRState { + JSXDRMode mode; + JSXDROps *ops; + JSContext *cx; + JSClass **registry; + uintN numclasses; + uintN maxclasses; + void *reghash; + void *userdata; + JSScript *script; +}; + +extern JS_PUBLIC_API(void) +JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); + +extern JS_PUBLIC_API(JSXDRState *) +JS_XDRNewMem(JSContext *cx, JSXDRMode mode); + +extern JS_PUBLIC_API(void *) +JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(void) +JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); + +extern JS_PUBLIC_API(uint32) +JS_XDRMemDataLeft(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRMemResetData(JSXDRState *xdr); + +extern JS_PUBLIC_API(void) +JS_XDRDestroy(JSXDRState *xdr); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint8(JSXDRState *xdr, uint8 *b); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint16(JSXDRState *xdr, uint16 *s); + +extern JS_PUBLIC_API(JSBool) +JS_XDRUint32(JSXDRState *xdr, uint32 *lp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCString(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRCStringOrNull(JSXDRState *xdr, char **sp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRString(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRDouble(JSXDRState *xdr, jsdouble **dp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRValue(JSXDRState *xdr, jsval *vp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); + +extern JS_PUBLIC_API(JSBool) +JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); + +extern JS_PUBLIC_API(uint32) +JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); + +extern JS_PUBLIC_API(JSClass *) +JS_XDRFindClassById(JSXDRState *xdr, uint32 id); + +/* + * Magic numbers. + */ +#define JSXDR_MAGIC_SCRIPT_1 0xdead0001 +#define JSXDR_MAGIC_SCRIPT_2 0xdead0002 +#define JSXDR_MAGIC_SCRIPT_3 0xdead0003 +#define JSXDR_MAGIC_SCRIPT_4 0xdead0004 +#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 +#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 + +/* + * Bytecode version number. Decrement the second term whenever JS bytecode + * changes incompatibly. + * + * This version number should be XDR'ed once near the front of any file or + * larger storage unit containing XDR'ed bytecode and other data, and checked + * before deserialization of bytecode. If the saved version does not match + * the current version, abort deserialization and invalidate the file. + */ +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) + +/* + * Library-private functions. + */ +extern JSBool +js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); + +extern JSBool +js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); + +/* + * FIXME: This is non-unicode version of js_XDRStringAtom that performs lossy + * conversion. Do not use it in the new code! See bug 325202. + */ +extern JSBool +js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp); + +JS_END_EXTERN_C + +#endif /* ! jsxdrapi_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxml.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxml.c new file mode 100644 index 0000000..1266255 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxml.c @@ -0,0 +1,8357 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=78: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsstddef.h" +#include "jsconfig.h" + +#if JS_HAS_XML_SUPPORT + +#include +#include +#include +#include "jstypes.h" +#include "jsbit.h" +#include "jsprf.h" +#include "jsutil.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsxml.h" + +#ifdef DEBUG +#include /* for #ifdef DEBUG memset calls */ +#endif + +/* + * NOTES + * - in the js shell, you must use the -x command line option, or call + * options('xml') before compiling anything that uses XML literals + * + * TODO + * - XXXbe patrol + * - Fuse objects and their JSXML* private data into single GC-things + * - fix function::foo vs. x.(foo == 42) collision using proper namespacing + * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants + * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! + * - JS_TypeOfValue sure could use a cleaner interface to "types" + */ + +#ifdef DEBUG_brendan +#define METERING 1 +#endif + +#ifdef METERING +static struct { + jsrefcount qname; + jsrefcount qnameobj; + jsrefcount liveqname; + jsrefcount liveqnameobj; + jsrefcount namespace; + jsrefcount namespaceobj; + jsrefcount livenamespace; + jsrefcount livenamespaceobj; + jsrefcount xml; + jsrefcount xmlobj; + jsrefcount livexml; + jsrefcount livexmlobj; +} xml_stats; + +#define METER(x) JS_ATOMIC_INCREMENT(&(x)) +#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) +#else +#define METER(x) /* nothing */ +#define UNMETER(x) /* nothing */ +#endif + +/* + * Random utilities and global functions. + */ +const char js_isXMLName_str[] = "isXMLName"; +const char js_XMLList_str[] = "XMLList"; +const char js_localName_str[] = "localName"; +const char js_xml_parent_str[] = "parent"; +const char js_prefix_str[] = "prefix"; +const char js_toXMLString_str[] = "toXMLString"; +const char js_uri_str[] = "uri"; + +const char js_amp_entity_str[] = "&"; +const char js_gt_entity_str[] = ">"; +const char js_lt_entity_str[] = "<"; +const char js_quot_entity_str[] = """; + +#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) +#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') + +static JSBool +xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); + return JS_TRUE; +} + +/* + * Namespace class and library functions. + */ +enum namespace_tinyid { + NAMESPACE_PREFIX = -1, + NAMESPACE_URI = -2 +}; + +static JSBool +namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLNamespace *ns; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL); + if (!ns) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case NAMESPACE_PREFIX: + *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; + break; + case NAMESPACE_URI: + *vp = STRING_TO_JSVAL(ns->uri); + break; + } + return JS_TRUE; +} + +static void +namespace_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLNamespace *ns; + JSRuntime *rt; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + if (!ns) + return; + JS_ASSERT(ns->object == obj); + ns->object = NULL; + UNMETER(xml_stats.livenamespaceobj); + + rt = cx->runtime; + if (rt->functionNamespaceObject == obj) + rt->functionNamespaceObject = NULL; +} + +static void +namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len) +{ + uint32 i; + JSXMLNamespace *ns; + + for (i = 0; i < len; i++) { + ns = vec[i]; + { +#ifdef GC_MARK_DEBUG + char buf[100]; + + JS_snprintf(buf, sizeof buf, "%s=%s", + ns->prefix ? JS_GetStringBytes(ns->prefix) : "", + JS_GetStringBytes(ns->uri)); +#endif + GC_MARK(cx, ns, buf); + } + } +} + +static uint32 +namespace_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + GC_MARK(cx, ns, "private"); + return 0; +} + +static JSBool +namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXMLNamespace *ns, *ns2; + JSObject *obj2; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) { + *bp = JS_FALSE; + } else { + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); + *bp = js_EqualStrings(ns->uri, ns2->uri); + } + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { + { "Namespace", + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), + JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, namespace_mark, NULL }, + namespace_equality,NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +#define NAMESPACE_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec namespace_props[] = { + {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, + {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv); + if (!ns) + return JS_FALSE; + + *rval = STRING_TO_JSVAL(ns->uri); + return JS_TRUE; +} + +static JSFunctionSpec namespace_methods[] = { + {js_toString_str, namespace_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) + js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace)); + if (!ns) + return NULL; + ns->object = NULL; + ns->prefix = prefix; + ns->uri = uri; + ns->declared = declared; + METER(xml_stats.namespace); + METER(xml_stats.livenamespace); + return ns; +} + +void +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns) +{ + GC_MARK(cx, ns->object, "object"); + GC_MARK(cx, ns->prefix, "prefix"); + GC_MARK(cx, ns->uri, "uri"); +} + +void +js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns) +{ + UNMETER(xml_stats.livenamespace); +} + +JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared) +{ + JSXMLNamespace *ns; + + ns = js_NewXMLNamespace(cx, prefix, uri, declared); + if (!ns) + return NULL; + return js_GetXMLNamespaceObject(cx, ns); +} + +JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) +{ + JSObject *obj; + + obj = ns->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == ns); + return obj; + } + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, ns)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + ns->object = obj; + METER(xml_stats.namespaceobj); + METER(xml_stats.livenamespaceobj); + return obj; +} + +/* + * QName class and library functions. + */ +enum qname_tinyid { + QNAME_URI = -1, + QNAME_LOCALNAME = -2 +}; + +static JSBool +qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLQName *qn; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + qn = (JSXMLQName *) + JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL); + if (!qn) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case QNAME_URI: + *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; + break; + case QNAME_LOCALNAME: + *vp = STRING_TO_JSVAL(qn->localName); + break; + } + return JS_TRUE; +} + +static void +qname_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + if (!qn) + return; + JS_ASSERT(qn->object == obj); + qn->object = NULL; + UNMETER(xml_stats.liveqnameobj); +} + +static void +anyname_finalize(JSContext* cx, JSObject* obj) +{ + JSRuntime *rt; + + /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ + rt = cx->runtime; + if (rt->anynameObject == obj) + rt->anynameObject = NULL; + + qname_finalize(cx, obj); +} + +static uint32 +qname_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + GC_MARK(cx, qn, "private"); + return 0; +} + +static JSBool +qname_identity(JSXMLQName *qna, JSXMLQName *qnb) +{ + if (!qna->uri ^ !qnb->uri) + return JS_FALSE; + if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri)) + return JS_FALSE; + return js_EqualStrings(qna->localName, qnb->localName); +} + +static JSBool +qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXMLQName *qn, *qn2; + JSObject *obj2; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + JS_ASSERT(JSVAL_IS_OBJECT(v)); + obj2 = JSVAL_TO_OBJECT(v); + if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) { + *bp = JS_FALSE; + } else { + qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2); + *bp = qname_identity(qn, qn2); + } + return JS_TRUE; +} + +JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { + { "QName", + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_CACHED_PROTO(JSProto_QName), + JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL }, + qname_equality, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +/* + * Classes for the ECMA-357-internal types AttributeName and AnyName, which + * are like QName, except that they have no property getters. They share the + * qname_toString method, and therefore are exposed as constructable objects + * in this implementation. + */ +JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { + js_AttributeName_str, + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | + JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +JS_FRIEND_DATA(JSClass) js_AnyNameClass = { + js_AnyName_str, + JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | + JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +#define QNAME_ATTRS \ + (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec qname_props[] = { + {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, + {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSClass *clasp; + JSXMLQName *qn; + JSString *str, *qualstr; + size_t length; + jschar *chars; + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) { + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + } else { + qn = (JSXMLQName *) + JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv); + if (!qn) + return JS_FALSE; + } + + if (!qn->uri) { + /* No uri means wildcard qualifier. */ + str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); + } else if (IS_EMPTY(qn->uri)) { + /* Empty string for uri means localName is in no namespace. */ + str = cx->runtime->emptyString; + } else { + qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); + str = js_ConcatStrings(cx, qn->uri, qualstr); + if (!str) + return JS_FALSE; + } + str = js_ConcatStrings(cx, str, qn->localName); + if (!str) + return JS_FALSE; + + if (str && clasp == &js_AttributeNameClass) { + length = JSSTRING_LENGTH(str); + chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar)); + if (!chars) + return JS_FALSE; + *chars = '@'; + js_strncpy(chars + 1, JSSTRING_CHARS(str), length); + chars[++length] = 0; + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + return JS_FALSE; + } + } + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSFunctionSpec qname_methods[] = { + {js_toString_str, qname_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName)); + if (!qn) + return NULL; + qn->object = NULL; + qn->uri = uri; + qn->prefix = prefix; + qn->localName = localName; + METER(xml_stats.qname); + METER(xml_stats.liveqname); + return qn; +} + +void +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn) +{ + GC_MARK(cx, qn->object, "object"); + GC_MARK(cx, qn->uri, "uri"); + GC_MARK(cx, qn->prefix, "prefix"); + GC_MARK(cx, qn->localName, "localName"); +} + +void +js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn) +{ + UNMETER(xml_stats.liveqname); +} + +JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + + qn = js_NewXMLQName(cx, uri, prefix, localName); + if (!qn) + return NULL; + return js_GetXMLQNameObject(cx, qn); +} + +JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) +{ + JSObject *obj; + + obj = qn->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == qn); + return obj; + } + obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + return obj; +} + +JSObject * +js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) +{ + JSObject *obj; + + obj = qn->object; + if (obj) { + if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass) + return obj; + qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); + if (!qn) + return NULL; + } + + obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + return obj; +} + +JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) +{ + jsval argv[2]; + + /* + * ECMA-357 11.1.2, + * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ + * production, step 2. + */ + if (!JSVAL_IS_PRIMITIVE(nsval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { + nsval = JSVAL_NULL; + } + + argv[0] = nsval; + argv[1] = lnval; + return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); +} + +static JSBool +IsXMLName(const jschar *cp, size_t n) +{ + JSBool rv; + jschar c; + + rv = JS_FALSE; + if (n != 0 && JS_ISXMLNSSTART(*cp)) { + while (--n != 0) { + c = *++cp; + if (!JS_ISXMLNS(c)) + return rv; + } + rv = JS_TRUE; + } + return rv; +} + +JSBool +js_IsXMLName(JSContext *cx, jsval v) +{ + JSClass *clasp; + JSXMLQName *qn; + JSString *name; + JSErrorReporter older; + + /* + * Inline specialization of the QName constructor called with v passed as + * the only argument, to compute the localName for the constructed qname, + * without actually allocating the object or computing its uri and prefix. + * See ECMA-357 13.1.2.1 step 1 and 13.3.2. + */ + if (!JSVAL_IS_PRIMITIVE(v) && + (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)), + clasp == &js_QNameClass.base || + clasp == &js_AttributeNameClass || + clasp == &js_AnyNameClass)) { + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + name = qn->localName; + } else { + older = JS_SetErrorReporter(cx, NULL); + name = js_ValueToString(cx, v); + JS_SetErrorReporter(cx, older); + if (!name) { + JS_ClearPendingException(cx); + return JS_FALSE; + } + } + + return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); +} + +static JSBool +Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval urival, prefixval; + JSObject *uriobj; + JSBool isNamespace, isQName; + JSClass *clasp; + JSString *empty, *prefix; + JSXMLNamespace *ns, *ns2; + JSXMLQName *qn; + + urival = argv[argc > 1]; + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(urival)) { + uriobj = JSVAL_TO_OBJECT(urival); + clasp = OBJ_GET_CLASS(cx, uriobj); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else uriobj = NULL; +#endif + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* Namespace called as function. */ + if (argc == 1 && isNamespace) { + /* Namespace called with one Namespace argument is identity. */ + *rval = urival; + return JS_TRUE; + } + + /* Create and return a new QName object exactly as if constructed. */ + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.namespaceobj); + METER(xml_stats.livenamespaceobj); + + /* + * Create and connect private data to rooted obj early, so we don't have + * to worry about rooting string newborns hanging off of the private data + * further below. + */ + empty = cx->runtime->emptyString; + ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE); + if (!ns) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, ns)) + return JS_FALSE; + ns->object = obj; + + if (argc == 1) { + if (isNamespace) { + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); + ns->uri = ns2->uri; + ns->prefix = ns2->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + ns->prefix = qn->prefix; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + if (!IS_EMPTY(ns->uri)) + ns->prefix = NULL; + } + } else if (argc == 2) { + if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + } + + prefixval = argv[0]; + if (IS_EMPTY(ns->uri)) { + if (!JSVAL_IS_VOID(prefixval)) { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + if (!IS_EMPTY(prefix)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix))); + return JS_FALSE; + } + } + } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { + /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ + ns->prefix = NULL; + } else { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + ns->prefix = prefix; + } + } + + return JS_TRUE; +} + +static JSBool +QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval nameval, nsval; + JSBool isQName, isNamespace; + JSXMLQName *qn; + JSString *uri, *prefix, *name; + JSObject *nsobj; + JSClass *clasp; + JSXMLNamespace *ns; + + nameval = argv[argc > 1]; + isQName = + !JSVAL_IS_PRIMITIVE(nameval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* QName called as function. */ + if (argc == 1 && isQName) { + /* QName called with one QName argument is identity. */ + *rval = nameval; + return JS_TRUE; + } + + /* + * Create and return a new QName object exactly as if constructed. + * Use the constructor's clasp so we can be shared by AttributeName + * (see below after this function). + */ + obj = js_NewObject(cx, + JS_ValueToFunction(cx, argv[-2])->clasp, + NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + + if (isQName) { + /* If namespace is not specified and name is a QName, clone it. */ + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); + if (argc == 1) { + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + goto out; + } + + /* Namespace and qname were passed -- use the qname's localName. */ + nameval = STRING_TO_JSVAL(qn->localName); + } + + if (argc == 0) { + name = cx->runtime->emptyString; + } else { + name = js_ValueToString(cx, nameval); + if (!name) + return JS_FALSE; + + /* Use argv[1] as a local root for name, even if it was not passed. */ + argv[1] = STRING_TO_JSVAL(name); + } + + nsval = argv[0]; + if (argc == 1 || JSVAL_IS_VOID(nsval)) { + if (IS_STAR(name)) { + nsval = JSVAL_NULL; + } else { + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return JS_FALSE; + } + } + + if (JSVAL_IS_NULL(nsval)) { + /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ + uri = prefix = NULL; + } else { + /* + * Inline specialization of the Namespace constructor called with + * nsval passed as the only argument, to compute the uri and prefix + * for the constructed namespace, without actually allocating the + * object or computing other members. See ECMA-357 13.3.2 6(a) and + * 13.2.2. + */ + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(nsval)) { + nsobj = JSVAL_TO_OBJECT(nsval); + clasp = OBJ_GET_CLASS(cx, nsobj); + isNamespace = (clasp == &js_NamespaceClass.base); + isQName = (clasp == &js_QNameClass.base); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else nsobj = NULL; +#endif + + if (isNamespace) { + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + uri = ns->uri; + prefix = ns->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { + uri = qn->uri; + prefix = qn->prefix; + } else { + uri = js_ValueToString(cx, nsval); + if (!uri) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(uri); /* local root */ + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; + } + } + +out: + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, qn)) + return JS_FALSE; + qn->object = obj; + return JS_TRUE; +} + +static JSBool +AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + /* + * Since js_AttributeNameClass was initialized, obj will have that as its + * class, not js_QNameClass. + */ + return QName(cx, obj, argc, argv, rval); +} + +/* + * XMLArray library functions. + */ +static JSBool +namespace_identity(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsa->prefix && nsb->prefix) { + if (!js_EqualStrings(nsa->prefix, nsb->prefix)) + return JS_FALSE; + } else { + if (nsa->prefix || nsb->prefix) + return JS_FALSE; + } + return js_EqualStrings(nsa->uri, nsb->uri); +} + +static JSBool +attr_identity(const void *a, const void *b) +{ + const JSXML *xmla = (const JSXML *) a; + const JSXML *xmlb = (const JSXML *) b; + + return qname_identity(xmla->name, xmlb->name); +} + +static void +XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array) +{ + JSXMLArrayCursor *next; + + cursor->array = array; + cursor->index = 0; + next = cursor->next = array->cursors; + if (next) + next->prevp = &cursor->next; + cursor->prevp = &array->cursors; + array->cursors = cursor; + cursor->root = NULL; +} + +static void +XMLArrayCursorFinish(JSXMLArrayCursor *cursor) +{ + JSXMLArrayCursor *next; + + if (!cursor->array) + return; + next = cursor->next; + if (next) + next->prevp = cursor->prevp; + *cursor->prevp = next; + cursor->array = NULL; +} + +static void * +XMLArrayCursorNext(JSXMLArrayCursor *cursor) +{ + JSXMLArray *array; + + array = cursor->array; + if (!array || cursor->index >= array->length) + return NULL; + return cursor->root = array->vector[cursor->index++]; +} + +static void * +XMLArrayCursorItem(JSXMLArrayCursor *cursor) +{ + JSXMLArray *array; + + array = cursor->array; + if (!array || cursor->index >= array->length) + return NULL; + return cursor->root = array->vector[cursor->index]; +} + +static void +XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) +{ + while (cursor) { + GC_MARK(cx, cursor->root, "cursor->root"); + cursor = cursor->next; + } +} + +/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */ +static JSBool +XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + void **vector; + + if (capacity == 0) { + /* We could let realloc(p, 0) free this, but purify gets confused. */ + if (array->vector) + free(array->vector); + vector = NULL; + } else { + if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || + !(vector = (void **) + realloc(array->vector, capacity * sizeof(void *)))) { + if (cx) + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + array->capacity = JSXML_PRESET_CAPACITY | capacity; + array->vector = vector; + return JS_TRUE; +} + +static void +XMLArrayTrim(JSXMLArray *array) +{ + if (array->capacity & JSXML_PRESET_CAPACITY) + return; + if (array->length < array->capacity) + XMLArraySetCapacity(NULL, array, array->length); +} + +static JSBool +XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) +{ + array->length = array->capacity = 0; + array->vector = NULL; + array->cursors = NULL; + return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); +} + +static void +XMLArrayFinish(JSContext *cx, JSXMLArray *array) +{ + JSXMLArrayCursor *cursor; + + JS_free(cx, array->vector); + + while ((cursor = array->cursors) != NULL) + XMLArrayCursorFinish(cursor); + +#ifdef DEBUG + memset(array, 0xd5, sizeof *array); +#endif +} + +#define XML_NOT_FOUND ((uint32) -1) + +static uint32 +XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) +{ + void **vector; + uint32 i, n; + + /* The identity op must not reallocate array->vector. */ + vector = array->vector; + if (identity) { + for (i = 0, n = array->length; i < n; i++) { + if (identity(vector[i], elt)) + return i; + } + } else { + for (i = 0, n = array->length; i < n; i++) { + if (vector[i] == elt) + return i; + } + } + return XML_NOT_FOUND; +} + +/* + * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after + * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold + * should be greater than increment. + */ +#define LINEAR_THRESHOLD 256 +#define LINEAR_INCREMENT 32 + +static JSBool +XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) +{ + uint32 capacity, i; + int log2; + void **vector; + + if (index >= array->length) { + if (index >= JSXML_CAPACITY(array)) { + /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ + capacity = index + 1; + if (index >= LINEAR_THRESHOLD) { + capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); + } else { + JS_CEILING_LOG2(log2, capacity); + capacity = JS_BIT(log2); + } + if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || + !(vector = (void **) + realloc(array->vector, capacity * sizeof(void *)))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + array->capacity = capacity; + array->vector = vector; + for (i = array->length; i < index; i++) + vector[i] = NULL; + } + array->length = index + 1; + } + + array->vector[index] = elt; + return JS_TRUE; +} + +static JSBool +XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) +{ + uint32 j; + JSXMLArrayCursor *cursor; + + j = array->length; + JS_ASSERT(i <= j); + if (!XMLArraySetCapacity(cx, array, j + n)) + return JS_FALSE; + + array->length = j + n; + JS_ASSERT(n != (uint32)-1); + while (j != i) { + --j; + array->vector[j + n] = array->vector[j]; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > i) + cursor->index += n; + } + return JS_TRUE; +} + +static void * +XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) +{ + uint32 length; + void **vector, *elt; + JSXMLArrayCursor *cursor; + + length = array->length; + if (index >= length) + return NULL; + + vector = array->vector; + elt = vector[index]; + if (compress) { + while (++index < length) + vector[index-1] = vector[index]; + array->length = length - 1; + array->capacity = JSXML_CAPACITY(array); + } else { + vector[index] = NULL; + } + + for (cursor = array->cursors; cursor; cursor = cursor->next) { + if (cursor->index > index) + --cursor->index; + } + return elt; +} + +static void +XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) +{ + void **vector; + + JS_ASSERT(!array->cursors); + if (length >= array->length) + return; + + if (length == 0) { + if (array->vector) + free(array->vector); + vector = NULL; + } else { + vector = realloc(array->vector, length * sizeof(void *)); + if (!vector) + return; + } + + if (array->length > length) + array->length = length; + array->capacity = length; + array->vector = vector; +} + +#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) +#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ + XML_NOT_FOUND) +#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ + ? (t *) (a)->vector[i] \ + : NULL) +#define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ + if ((a)->length <= (i)) \ + (a)->length = (i) + 1; \ + ((a)->vector[i] = (void *)(e)); \ + JS_END_MACRO +#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) +#define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) +#define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) +#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) +#define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) + +/* + * Define XML setting property strings and constants early, so everyone can + * use the same names and their magic numbers (tinyids, flags). + */ +static const char js_ignoreComments_str[] = "ignoreComments"; +static const char js_ignoreProcessingInstructions_str[] + = "ignoreProcessingInstructions"; +static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; +static const char js_prettyPrinting_str[] = "prettyPrinting"; +static const char js_prettyIndent_str[] = "prettyIndent"; + +/* + * NB: These XML static property tinyids must + * (a) not collide with the generic negative tinyids at the top of jsfun.c; + * (b) index their corresponding xml_static_props array elements. + * Don't change 'em! + */ +enum xml_static_tinyid { + XML_IGNORE_COMMENTS, + XML_IGNORE_PROCESSING_INSTRUCTIONS, + XML_IGNORE_WHITESPACE, + XML_PRETTY_PRINTING, + XML_PRETTY_INDENT +}; + +static JSBool +xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +static JSBool +xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool b; + uint8 flag; + + JS_ASSERT(JSVAL_IS_INT(id)); + if (!js_ValueToBoolean(cx, *vp, &b)) + return JS_FALSE; + + flag = JS_BIT(JSVAL_TO_INT(id)); + if (b) + cx->xmlSettingFlags |= flag; + else + cx->xmlSettingFlags &= ~flag; + return JS_TRUE; +} + +static JSPropertySpec xml_static_props[] = { + {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreProcessingInstructions_str, + XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, + xml_setting_getter, xml_setting_setter}, + {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, + xml_setting_getter, NULL}, + {0,0,0,0,0} +}; + +/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ +#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) +#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ + JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) +#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) +#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) +#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) + +/* + * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. + * This flag means a couple of things: + * + * - The top JSXML created for a parse tree must have an object owning it. + * + * - That the default namespace normally inherited from the temporary + * tag that wraps a runtime-concatenated XML source + * string must, in the case of a precompiled XML object tree, inherit via + * ad-hoc code in ParseNodeToXML. + * + * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. + */ +#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) + +/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ +#define IS_XML(str) \ + (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str))) + +#define IS_XMLNS(str) \ + (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str))) + +#define IS_XML_CHARS(chars) \ + (JS_TOLOWER((chars)[0]) == 'x' && \ + JS_TOLOWER((chars)[1]) == 'm' && \ + JS_TOLOWER((chars)[2]) == 'l') + +#define HAS_NS_AFTER_XML(chars) \ + (JS_TOLOWER((chars)[3]) == 'n' && \ + JS_TOLOWER((chars)[4]) == 's') + +#define IS_XMLNS_CHARS(chars) \ + (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) + +#define STARTS_WITH_XML(chars,length) \ + (length >= 3 && IS_XML_CHARS(chars)) + +static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; +static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; + +static JSXMLQName * +ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, + JSBool isAttributeName) +{ + JSString *str, *uri, *prefix, *localName; + size_t length, offset; + const jschar *start, *limit, *colon; + uint32 n; + JSXMLNamespace *ns; + + JS_ASSERT(pn->pn_arity == PN_NULLARY); + str = ATOM_TO_STRING(pn->pn_atom); + length = JSSTRING_LENGTH(str); + start = JSSTRING_CHARS(str); + JS_ASSERT(length != 0 && *start != '@'); + JS_ASSERT(length != 1 || *start != '*'); + + uri = cx->runtime->emptyString; + limit = start + length; + colon = js_strchr_limit(start, ':', limit); + if (colon) { + offset = PTRDIFF(colon, start, jschar); + prefix = js_NewDependentString(cx, str, 0, offset, 0); + if (!prefix) + return NULL; + + if (STARTS_WITH_XML(start, offset)) { + if (offset == 3) { + uri = JS_InternString(cx, xml_namespace_str); + if (!uri) + return NULL; + } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { + uri = JS_InternString(cx, xmlns_namespace_str); + if (!uri) + return NULL; + } else { + uri = NULL; + } + } else { + uri = NULL; + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); + if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) { + uri = ns->uri; + break; + } + } + } + + if (!uri) { + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_XML_NAMESPACE, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(prefix))); + return NULL; + } + + localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); + if (!localName) + return NULL; + } else { + if (isAttributeName) { + /* + * An unprefixed attribute is not in any namespace, so set prefix + * as well as uri to the empty string. + */ + prefix = uri; + } else { + /* + * Loop from back to front looking for the closest declared default + * namespace. + */ + n = inScopeNSes->length; + while (n != 0) { + --n; + ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); + if (!ns->prefix || IS_EMPTY(ns->prefix)) { + uri = ns->uri; + break; + } + } + prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; + } + localName = str; + } + + return js_NewXMLQName(cx, uri, prefix, localName); +} + +static JSString * +ChompXMLWhitespace(JSContext *cx, JSString *str) +{ + size_t length, newlength, offset; + const jschar *cp, *start, *end; + jschar c; + + length = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (!JS_ISXMLSPACE(c)) + break; + } + while (end > cp) { + c = end[-1]; + if (!JS_ISXMLSPACE(c)) + break; + --end; + } + newlength = PTRDIFF(end, cp, jschar); + if (newlength == length) + return str; + offset = PTRDIFF(cp, start, jschar); + return js_NewDependentString(cx, str, offset, newlength, 0); +} + +static JSXML * +ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, + uintN flags) +{ + JSXML *xml, *kid, *attr, *attrj; + JSString *str; + uint32 length, n, i, j; + JSParseNode *pn2, *pn3, *head, **pnp; + JSXMLNamespace *ns; + JSXMLQName *qn, *attrjqn; + JSXMLClass xml_class; + int stackDummy; + + if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { + js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, + JSMSG_OVER_RECURSED); + return NULL; + } + +#define PN2X_SKIP_CHILD ((JSXML *) 1) + + /* + * Cases return early to avoid common code that gets an outermost xml's + * object, which protects GC-things owned by xml and its descendants from + * garbage collection. + */ + xml = NULL; + if (!js_EnterLocalRootScope(cx)) + return NULL; + switch (pn->pn_type) { + case TOK_XMLELEM: + length = inScopeNSes->length; + pn2 = pn->pn_head; + xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (!xml) + goto fail; + + flags &= ~XSF_PRECOMPILED_ROOT; + n = pn->pn_count; + JS_ASSERT(n >= 2); + n -= 2; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + while ((pn2 = pn2->pn_next) != NULL) { + if (!pn2->pn_next) { + /* Don't append the end tag! */ + JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); + break; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + /* Store kid in xml right away, to protect it from GC. */ + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + kid->parent = xml; + ++i; + + /* XXX where is this documented in an XML spec, or in E4X? */ + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid->xml_value); + if (!str) + goto fail; + kid->xml_value = str; + } + } + + JS_ASSERT(i == n); + if (n < pn->pn_count - 2) + XMLArrayTrim(&xml->xml_kids); + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLLIST: + xml = js_NewXML(cx, JSXML_CLASS_LIST); + if (!xml) + goto fail; + + n = pn->pn_count; + if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) + goto fail; + + i = 0; + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + /* + * Always ignore insignificant whitespace in lists -- we shouldn't + * condition this on an XML.ignoreWhitespace setting when the list + * constructor is XMLList (note XML/XMLList unification hazard). + */ + if (pn2->pn_type == TOK_XMLSPACE) { + --n; + continue; + } + + kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); + if (kid == PN2X_SKIP_CHILD) { + --n; + continue; + } + + if (!kid) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); + ++i; + } + + if (n < pn->pn_count) + XMLArrayTrim(&xml->xml_kids); + break; + + case TOK_XMLSTAGO: + case TOK_XMLPTAGC: + length = inScopeNSes->length; + pn2 = pn->pn_head; + JS_ASSERT(pn2->pn_type == TOK_XMLNAME); + if (pn2->pn_arity == PN_LIST) + goto syntax; + + xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); + if (!xml) + goto fail; + + /* First pass: check syntax and process namespace declarations. */ + JS_ASSERT(pn->pn_count >= 1); + n = pn->pn_count - 1; + pnp = &pn2->pn_next; + head = *pnp; + while ((pn2 = *pnp) != NULL) { + size_t length; + const jschar *chars; + + if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) + goto syntax; + + /* Enforce "Well-formedness constraint: Unique Att Spec". */ + for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { + if (pn3->pn_atom == pn2->pn_atom) { + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + str = ATOM_TO_STRING(pn2->pn_atom); + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + if (pn2->pn_type != TOK_XMLATTR) + goto syntax; + + length = JSSTRING_LENGTH(str); + chars = JSSTRING_CHARS(str); + if (length >= 5 && + IS_XMLNS_CHARS(chars) && + (length == 5 || chars[5] == ':')) { + JSString *uri, *prefix; + + uri = ATOM_TO_STRING(pn2->pn_atom); + if (length == 5) { + /* 10.3.2.1. Step 6(h)(i)(1)(a). */ + prefix = cx->runtime->emptyString; + } else { + prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); + if (!prefix) + goto fail; + } + + /* + * Once the new ns is appended to xml->xml_namespaces, it is + * protected from GC by the object that owns xml -- which is + * either xml->object if outermost, or the object owning xml's + * oldest ancestor if !outermost. + */ + ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); + if (!ns) + goto fail; + + /* + * Don't add a namespace that's already in scope. If someone + * extracts a child property from its parent via [[Get]], then + * we enforce the invariant, noted many times in ECMA-357, that + * the child's namespaces form a possibly-improper superset of + * its ancestors' namespaces. + */ + if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || + !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { + goto fail; + } + } + + JS_ASSERT(n >= 2); + n -= 2; + *pnp = pn2->pn_next; + /* XXXbe recycle pn2 */ + continue; + } + + pnp = &pn2->pn_next; + } + + /* + * If called from js_ParseNodeToXMLObject, emulate the effect of the + * ... wrapping done by "ToXML Applied to + * the String Type" (ECMA-357 10.3.1). + */ + if (flags & XSF_PRECOMPILED_ROOT) { + JS_ASSERT(length >= 1); + ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, + namespace_identity)); + ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); + if (!ns) + goto fail; + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + goto fail; + } + XMLArrayTrim(&xml->xml_namespaces); + + /* Second pass: process tag name and attributes, using namespaces. */ + pn2 = pn->pn_head; + qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + xml->name = qn; + + JS_ASSERT((n & 1) == 0); + n >>= 1; + if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) + goto fail; + + for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { + qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); + if (!qn) { + xml->xml_attrs.length = i; + goto fail; + } + + /* + * Enforce "Well-formedness constraint: Unique Att Spec", part 2: + * this time checking local name and namespace URI. + */ + for (j = 0; j < i; j++) { + attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); + attrjqn = attrj->name; + if (js_EqualStrings(attrjqn->uri, qn->uri) && + js_EqualStrings(attrjqn->localName, qn->localName)) { + js_ReportCompileErrorNumber(cx, pn2, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_DUPLICATE_XML_ATTR, + js_ValueToPrintableString(cx, + ATOM_KEY(pn2->pn_atom))); + goto fail; + } + } + + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + JS_ASSERT(pn2->pn_type == TOK_XMLATTR); + + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto fail; + + XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); + attr->parent = xml; + attr->name = qn; + attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); + } + + /* Point tag closes its own namespace scope. */ + if (pn->pn_type == TOK_XMLPTAGC) + XMLARRAY_TRUNCATE(cx, inScopeNSes, length); + break; + + case TOK_XMLSPACE: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: + str = ATOM_TO_STRING(pn->pn_atom); + qn = NULL; + if (pn->pn_type == TOK_XMLCOMMENT) { + if (flags & XSF_IGNORE_COMMENTS) + goto skip_child; + xml_class = JSXML_CLASS_COMMENT; + } else if (pn->pn_type == TOK_XMLPI) { + if (IS_XML(str)) { + js_ReportCompileErrorNumber(cx, pn, + JSREPORT_PN | JSREPORT_ERROR, + JSMSG_RESERVED_ID, + js_ValueToPrintableString(cx, + STRING_TO_JSVAL(str))); + goto fail; + } + + if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) + goto skip_child; + + qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE); + if (!qn) + goto fail; + + str = pn->pn_atom2 + ? ATOM_TO_STRING(pn->pn_atom2) + : cx->runtime->emptyString; + xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; + } else { + /* CDATA section content, or element text. */ + xml_class = JSXML_CLASS_TEXT; + } + + xml = js_NewXML(cx, xml_class); + if (!xml) + goto fail; + xml->name = qn; + if (pn->pn_type == TOK_XMLSPACE) + xml->xml_flags |= XMLF_WHITESPACE_TEXT; + xml->xml_value = str; + break; + + default: + goto syntax; + } + + js_LeaveLocalRootScopeWithResult(cx, (jsval) xml); + if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) + return NULL; + return xml; + +skip_child: + js_LeaveLocalRootScope(cx); + return PN2X_SKIP_CHILD; + +#undef PN2X_SKIP_CHILD + +syntax: + js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); +fail: + js_LeaveLocalRootScope(cx); + return NULL; +} + +/* + * XML helper, object-ops, and library functions. We start with the helpers, + * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. + */ +static JSBool +GetXMLSetting(JSContext *cx, const char *name, jsval *vp) +{ + jsval v; + + if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v)) + return JS_FALSE; + if (!VALUE_IS_FUNCTION(cx, v)) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); +} + +static JSBool +FillSettingsCache(JSContext *cx) +{ + int i; + const char *name; + jsval v; + JSBool isSet; + + /* Note: XML_PRETTY_INDENT is not a boolean setting. */ + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet)) + return JS_FALSE; + if (isSet) + cx->xmlSettingFlags |= JS_BIT(i); + else + cx->xmlSettingFlags &= ~JS_BIT(i); + } + + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return JS_TRUE; +} + +static JSBool +GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) +{ + int i; + + if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) + return JS_FALSE; + + for (i = 0; xml_static_props[i].name; i++) { + if (!strcmp(xml_static_props[i].name, name)) { + *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; + return JS_TRUE; + } + } + *bp = JS_FALSE; + return JS_TRUE; +} + +static JSBool +GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) +{ + jsval v; + + return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip); +} + +static JSBool +GetXMLSettingFlags(JSContext *cx, uintN *flagsp) +{ + JSBool flag; + + /* Just get the first flag to validate the setting flags cache. */ + if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) + return JS_FALSE; + *flagsp = cx->xmlSettingFlags; + return JS_TRUE; +} + +static JSXML * +ParseXMLSource(JSContext *cx, JSString *src) +{ + jsval nsval; + JSXMLNamespace *ns; + size_t urilen, srclen, length, offset, dstlen; + jschar *chars; + const jschar *srcp, *endp; + void *mark; + JSTokenStream *ts; + uintN lineno; + JSStackFrame *fp; + JSOp op; + JSParseNode *pn; + JSXML *xml; + JSXMLArray nsarray; + uintN flags; + + static const char prefix[] = ""; + static const char suffix[] = ""; + +#define constrlen(constr) (sizeof(constr) - 1) + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + + urilen = JSSTRING_LENGTH(ns->uri); + srclen = JSSTRING_LENGTH(src); + length = constrlen(prefix) + urilen + constrlen(middle) + srclen + + constrlen(suffix); + + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return NULL; + + dstlen = length; + js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); + offset = dstlen; + js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); + offset += urilen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, + &dstlen); + offset += dstlen; + srcp = JSSTRING_CHARS(src); + js_strncpy(chars + offset, srcp, srclen); + offset += srclen; + dstlen = length - offset + 1; + js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, + &dstlen); + chars [offset + dstlen] = 0; + + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewBufferTokenStream(cx, chars, length); + if (!ts) + return NULL; + for (fp = cx->fp; fp && !fp->pc; fp = fp->down) + continue; + if (fp) { + op = (JSOp) *fp->pc; + if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { + ts->filename = fp->script->filename; + lineno = js_PCToLineNumber(cx, fp->script, fp->pc); + for (endp = srcp + srclen; srcp < endp; srcp++) + if (*srcp == '\n') + --lineno; + ts->lineno = lineno; + } + } + + JS_KEEP_ATOMS(cx->runtime); + pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); + xml = NULL; + if (pn && XMLArrayInit(cx, &nsarray, 1)) { + if (GetXMLSettingFlags(cx, &flags)) + xml = ParseNodeToXML(cx, pn, &nsarray, flags); + + XMLArrayFinish(cx, &nsarray); + } + JS_UNKEEP_ATOMS(cx->runtime); + + JS_ARENA_RELEASE(&cx->tempPool, mark); + JS_free(cx, chars); + return xml; + +#undef constrlen +} + +/* + * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). + * + * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce + * the constraint: + * + * for all x belonging to XML: + * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] + * + * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here + * (in new sub-step 6(a), renumbering the others to (b) and (c)). + * + * Same goes for 10.4.1 Step 7(a). + * + * In order for XML.prototype.namespaceDeclarations() to work correctly, the + * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be + * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such + * undeclared namespaces associated with x not belonging to ancestorNS. + */ +static JSXML * +OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) +{ + JSXMLNamespace *ns; + + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace); + xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!ns || !xml) + return xml; + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return NULL; + ns->declared = JS_FALSE; + } + xml->parent = NULL; + return xml; +} + +static JSObject * +ToXML(JSContext *cx, jsval v) +{ + JSObject *obj; + JSXML *xml; + JSClass *clasp; + JSString *str; + uint32 length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length != 1) + goto bad; + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + return js_GetXMLObject(cx, xml); + } + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + length = 0; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + xml = NULL; +#endif + } else { + xml = ParseXMLSource(cx, str); + if (!xml) + return NULL; + length = JSXML_LENGTH(xml); + } + + if (length == 0) { + obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); + if (!obj) + return NULL; + } else if (length == 1) { + xml = OrphanXMLChild(cx, xml, 0); + if (!xml) + return NULL; + obj = js_GetXMLObject(cx, xml); + if (!obj) + return NULL; + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); + return NULL; + } + return obj; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *kid); + +static JSObject * +ToXMLList(JSContext *cx, jsval v) +{ + JSObject *obj, *listobj; + JSXML *xml, *list, *kid; + JSClass *clasp; + JSString *str; + uint32 i, length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, obj)) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return NULL; + return listobj; + } + return obj; + } + + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + xml = NULL; + length = 0; + } else { + if (!js_EnterLocalRootScope(cx)) + return NULL; + xml = ParseXMLSource(cx, str); + if (!xml) { + js_LeaveLocalRootScope(cx); + return NULL; + } + length = JSXML_LENGTH(xml); + } + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + list = (JSXML *) JS_GetPrivate(cx, listobj); + for (i = 0; i < length; i++) { + kid = OrphanXMLChild(cx, xml, i); + if (!kid || !Append(cx, list, kid)) { + listobj = NULL; + break; + } + } + } + + if (xml) + js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); + return listobj; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +/* + * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString + * and their library-public js_* counterparts. The guts of MakeXMLCDataString, + * MakeXMLCommentString, and MakeXMLPIString are further factored into a common + * MakeXMLSpecialString subroutine. + * + * These functions take ownership of sb->base, if sb is non-null, in all cases + * of success or failure. + */ +static JSString * +MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb, + JSString *str, JSString *str2, + const jschar *prefix, size_t prefixlength, + const jschar *suffix, size_t suffixlength) +{ + JSStringBuffer localSB; + size_t length, length2, newlength; + jschar *bp, *base; + + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + + length = JSSTRING_LENGTH(str); + length2 = str2 ? JSSTRING_LENGTH(str2) : 0; + newlength = STRING_BUFFER_OFFSET(sb) + + prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) + + suffixlength; + bp = base = (jschar *) + JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar)); + if (!bp) { + js_FinishStringBuffer(sb); + return NULL; + } + + bp += STRING_BUFFER_OFFSET(sb); + js_strncpy(bp, prefix, prefixlength); + bp += prefixlength; + js_strncpy(bp, JSSTRING_CHARS(str), length); + bp += length; + if (length2 != 0) { + *bp++ = (jschar) ' '; + js_strncpy(bp, JSSTRING_CHARS(str2), length2); + bp += length2; + } + js_strncpy(bp, suffix, suffixlength); + bp[suffixlength] = 0; + + str = js_NewString(cx, base, newlength, 0); + if (!str) + free(base); + return str; +} + +static JSString * +MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', + 'C', 'D', 'A', 'T', 'A', + '['}; + static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; + + return MakeXMLSpecialString(cx, sb, str, NULL, + cdata_prefix_ucNstr, 9, + cdata_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; + static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; + + return MakeXMLSpecialString(cx, sb, str, NULL, + comment_prefix_ucNstr, 4, + comment_suffix_ucNstr, 3); +} + +static JSString * +MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name, + JSString *value) +{ + static const jschar pi_prefix_ucNstr[] = {'<', '?'}; + static const jschar pi_suffix_ucNstr[] = {'?', '>'}; + + return MakeXMLSpecialString(cx, sb, name, value, + pi_prefix_ucNstr, 2, + pi_suffix_ucNstr, 2); +} + +/* + * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends + * equals, a double quote, an attribute value, and a closing double quote. + */ +static void +AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr) +{ + js_AppendCString(sb, "=\""); + valstr = js_EscapeAttributeValue(cx, valstr); + if (!valstr) { + free(sb->base); + sb->base = STRING_BUFFER_ERROR_BASE; + return; + } + js_AppendJSString(sb, valstr); + js_AppendChar(sb, '"'); +} + +/* + * ECMA-357 10.2.1.1 EscapeElementValue helper method. + * + * This function takes ownership of sb->base, if sb is non-null, in all cases + * of success or failure. + */ +static JSString * +EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '<' || c == '>') + newlength += 3; + else if (c == '&') + newlength += 4; + + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '>') + js_AppendCString(sb, js_gt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +/* + * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. + * This function takes ownership of sb->base, if sb is non-null, in all cases. + */ +static JSString * +EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '"') + newlength += 5; + else if (c == '<') + newlength += 3; + else if (c == '&' || c == '\n' || c == '\r' || c == '\t') + newlength += 4; + + if (newlength < length) { + JS_ReportOutOfMemory(cx); + return NULL; + } + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '"') + js_AppendCString(sb, js_quot_entity_str); + else if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else if (c == '\n') + js_AppendCString(sb, " "); + else if (c == '\r') + js_AppendCString(sb, " "); + else if (c == '\t') + js_AppendCString(sb, " "); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ +static JSXMLNamespace * +GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) +{ + JSXMLNamespace *match, *ns; + uint32 i, n; + jsval argv[2]; + JSObject *nsobj; + + JS_ASSERT(qn->uri); + if (!qn->uri) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE, + qn->prefix + ? js_ValueToPrintableString(cx, + STRING_TO_JSVAL(qn->prefix)) + : js_type_strs[JSTYPE_VOID]); + return NULL; + } + + /* Look for a matching namespace in inScopeNSes, if provided. */ + match = NULL; + if (inScopeNSes) { + for (i = 0, n = inScopeNSes->length; i < n; i++) { + ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); + if (!ns) + continue; + + /* + * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: + * If we preserve prefixes, we must match null qn->prefix against + * an empty ns->prefix, in order to avoid generating redundant + * prefixed and default namespaces for cases such as: + * + * x = + * print(x.toXMLString()); + * + * Per 10.3.2.1, the namespace attribute in t has an empty string + * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): + * + * 1. If the [local name] property of a is "xmlns" + * a. Map ns.prefix to the empty string + * + * But t's name has a null prefix in this implementation, meaning + * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to + * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without + * saying how "no value" maps to an ECMA-357 value -- but it must + * map to the *undefined* prefix value). + * + * Since "" != undefined (or null, in the current implementation) + * the ECMA-357 spec will fail to match in [[GetNamespace]] called + * on t with argument {} U {(prefix="", uri="http://foo.com")}. + * This spec bug leads to ToXMLString results that duplicate the + * declared namespace. + */ + if (js_EqualStrings(ns->uri, qn->uri) && + (ns->prefix == qn->prefix || + ((ns->prefix && qn->prefix) + ? js_EqualStrings(ns->prefix, qn->prefix) + : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { + match = ns; + break; + } + } + } + + /* If we didn't match, make a new namespace from qn. */ + if (!match) { + argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID; + argv[1] = STRING_TO_JSVAL(qn->uri); + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, + 2, argv); + if (!nsobj) + return NULL; + match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + } + return match; +} + +static JSString * +GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) +{ + const jschar *cp, *start, *end; + size_t length, newlength, offset; + uint32 i, n, m, serial; + jschar *bp, *dp; + JSBool done; + JSXMLNamespace *ns; + JSString *prefix; + + JS_ASSERT(!IS_EMPTY(uri)); + + /* + * If there are no *declared* namespaces, skip all collision detection and + * return a short prefix quickly; an example of such a situation: + * + * var x = ; + * var n = new Namespace("http://example.com/"); + * x.@n::att = "val"; + * x.toXMLString(); + * + * This is necessary for various log10 uses below to be valid. + */ + if (decls->length == 0) + return JS_NewStringCopyZ(cx, "a"); + + /* + * Try peeling off the last filename suffix or pathname component till + * we have a valid XML name. This heuristic will prefer "xul" given + * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any + * likely URI of the form ".../xbl2/2005". + */ + start = JSSTRING_CHARS(uri); + cp = end = start + JSSTRING_LENGTH(uri); + while (--cp > start) { + if (*cp == '.' || *cp == '/' || *cp == ':') { + ++cp; + length = PTRDIFF(end, cp, jschar); + if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length)) + break; + end = --cp; + } + } + length = PTRDIFF(end, cp, jschar); + + /* + * If the namespace consisted only of non-XML names or names that begin + * case-insensitively with "xml", arbitrarily create a prefix consisting + * of 'a's of size length (allowing dp-calculating code to work with or + * without this branch executing) plus the space for storing a hyphen and + * the serial number (avoiding reallocation if a collision happens). + */ + bp = (jschar *) cp; + newlength = length; + if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) { + newlength = length + 2 + (size_t) log10(decls->length); + bp = (jschar *) + JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + + bp[newlength] = 0; + for (i = 0; i < newlength; i++) + bp[i] = 'a'; + } + + /* + * Now search through decls looking for a collision. If we collide with + * an existing prefix, start tacking on a hyphen and a serial number. + */ + serial = 0; + do { + done = JS_TRUE; + for (i = 0, n = decls->length; i < n; i++) { + ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); + if (ns && ns->prefix && + JSSTRING_LENGTH(ns->prefix) == newlength && + !memcmp(JSSTRING_CHARS(ns->prefix), bp, + newlength * sizeof(jschar))) { + if (bp == cp) { + newlength = length + 2 + (size_t) log10(n); + bp = (jschar *) + JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + js_strncpy(bp, cp, length); + } + + ++serial; + JS_ASSERT(serial <= n); + dp = bp + length + 2 + (size_t) log10(serial); + *dp = 0; + for (m = serial; m != 0; m /= 10) + *--dp = (jschar)('0' + m % 10); + *--dp = '-'; + JS_ASSERT(dp == bp + length); + + done = JS_FALSE; + break; + } + } + } while (!done); + + if (bp == cp) { + offset = PTRDIFF(cp, start, jschar); + prefix = js_NewDependentString(cx, uri, offset, length, 0); + } else { + prefix = js_NewString(cx, bp, newlength, 0); + if (!prefix) + JS_free(cx, bp); + } + return prefix; +} + +static JSBool +namespace_match(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsb->prefix) + return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix); + return js_EqualStrings(nsa->uri, nsb->uri); +} + +/* ECMA-357 10.2.1 and 10.2.2 */ +static JSString * +XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, + uintN indentLevel) +{ + JSBool pretty, indentKids; + JSStringBuffer sb; + JSString *str, *prefix, *kidstr; + JSXMLArrayCursor cursor; + uint32 i, n; + JSXMLArray empty, decls, ancdecls; + JSXMLNamespace *ns, *ns2; + uintN nextIndentLevel; + JSXML *attr, *kid; + + if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) + return NULL; + + js_InitStringBuffer(&sb); + if (pretty) + js_RepeatChar(&sb, ' ', indentLevel); + str = NULL; + + switch (xml->xml_class) { + case JSXML_CLASS_TEXT: + /* Step 4. */ + if (pretty) { + str = ChompXMLWhitespace(cx, xml->xml_value); + if (!str) + return NULL; + } else { + str = xml->xml_value; + } + return EscapeElementValue(cx, &sb, str); + + case JSXML_CLASS_ATTRIBUTE: + /* Step 5. */ + return EscapeAttributeValue(cx, &sb, xml->xml_value); + + case JSXML_CLASS_COMMENT: + /* Step 6. */ + return MakeXMLCommentString(cx, &sb, xml->xml_value); + + case JSXML_CLASS_PROCESSING_INSTRUCTION: + /* Step 7. */ + return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value); + + case JSXML_CLASS_LIST: + /* ECMA-357 10.2.2. */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + i = 0; + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (pretty && i != 0) + js_AppendChar(&sb, '\n'); + + kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); + if (!kidstr) + break; + + js_AppendJSString(&sb, kidstr); + ++i; + } + XMLArrayCursorFinish(&cursor); + if (kid) + goto list_out; + + if (!sb.base) { + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + return cx->runtime->emptyString; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); + list_out: + if (!str) + js_FinishStringBuffer(&sb); + return str; + + default:; + } + + /* After this point, control must flow through label out: to exit. */ + if (!js_EnterLocalRootScope(cx)) + return NULL; + + /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ + if (!ancestorNSes) { + XMLArrayInit(cx, &empty, 0); + ancestorNSes = ∅ + } + XMLArrayInit(cx, &decls, 0); + ancdecls.capacity = 0; + + /* Clone in-scope namespaces not in ancestorNSes into decls. */ + XMLArrayCursorInit(&cursor, &xml->xml_namespaces); + while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { + if (!ns->declared) + continue; + if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { + /* NOTE: may want to exclude unused namespaces here. */ + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE); + if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) + break; + } + } + XMLArrayCursorFinish(&cursor); + if (ns) + goto out; + + /* + * Union ancestorNSes and decls into ancdecls. Note that ancdecls does + * not own its member references. In the spec, ancdecls has no name, but + * is always written out as (AncestorNamespaces U namespaceDeclarations). + */ + if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) + goto out; + for (i = 0, n = ancestorNSes->length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + for (i = 0, n = decls.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); + if (!ns2) + continue; + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + + /* Step 11, except we don't clone ns unless its prefix is undefined. */ + ns = GetNamespace(cx, xml->name, &ancdecls); + if (!ns) + goto out; + + /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ + if (!ns->prefix) { + /* + * Create a namespace prefix that isn't used by any member of decls. + * Assign the new prefix to a copy of ns. Flag this namespace as if + * it were declared, for assertion-testing's sake later below. + * + * Erratum: if ns->prefix and xml->name are both null (*undefined* in + * ECMA-357), we know that xml was named using the default namespace + * (proof: see GetNamespace and the Namespace constructor called with + * two arguments). So we ought not generate a new prefix here, when + * we can declare ns as the default namespace for xml. + * + * This helps descendants inherit the namespace instead of redundantly + * redeclaring it with generated prefixes in each descendant. + */ + if (!xml->name->prefix) { + prefix = cx->runtime->emptyString; + } else { + prefix = GeneratePrefix(cx, ns->uri, &ancdecls); + if (!prefix) + goto out; + } + ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE); + if (!ns) + goto out; + + /* + * If the xml->name was unprefixed, we must remove any declared default + * namespace from decls before appending ns. How can you get a default + * namespace in decls that doesn't match the one from name? Apparently + * by calling x.setNamespace(ns) where ns has no prefix. The other way + * to fix this is to update x's in-scope namespaces when setNamespace + * is called, but that's not specified by ECMA-357. + * + * Likely Erratum here, depending on whether the lack of update to x's + * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an + * erratum or not. Note that changing setNamespace to update the list + * of in-scope namespaces will change x.namespaceDeclarations(). + */ + if (IS_EMPTY(prefix)) { + i = XMLArrayFindMember(&decls, ns, namespace_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &decls, i, JS_TRUE); + } + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || + !XMLARRAY_APPEND(cx, &decls, ns)) { + goto out; + } + } + + /* Format the element or point-tag into sb. */ + js_AppendChar(&sb, '<'); + + if (ns->prefix && !IS_EMPTY(ns->prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + js_AppendJSString(&sb, xml->name->localName); + + /* + * Step 16 makes a union to avoid writing two loops in step 17, to share + * common attribute value appending spec-code. We prefer two loops for + * faster code and less data overhead. + */ + + /* Step 17(b): append attributes. */ + XMLArrayCursorInit(&cursor, &xml->xml_attrs); + while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + js_AppendChar(&sb, ' '); + ns2 = GetNamespace(cx, attr->name, &ancdecls); + if (!ns2) + break; + + /* 17(b)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + break; + + /* Again, we avoid copying ns2 until we know it's prefix-less. */ + ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE); + if (!ns2) + break; + + /* + * In the spec, ancdecls has no name, but is always written out as + * (AncestorNamespaces U namespaceDeclarations). Since we compute + * that union in ancdecls, any time we append a namespace strong + * ref to decls, we must also append a weak ref to ancdecls. Order + * matters here: code at label out: releases strong refs in decls. + */ + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || + !XMLARRAY_APPEND(cx, &decls, ns2)) { + break; + } + } + + /* 17(b)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendJSString(&sb, ns2->prefix); + js_AppendChar(&sb, ':'); + } + + /* 17(b)(iv). */ + js_AppendJSString(&sb, attr->name->localName); + + /* 17(d-g). */ + AppendAttributeValue(cx, &sb, attr->xml_value); + } + XMLArrayCursorFinish(&cursor); + if (attr) + goto out; + + /* Step 17(c): append XML namespace declarations. */ + XMLArrayCursorInit(&cursor, &decls); + while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { + JS_ASSERT(ns2->declared); + + js_AppendCString(&sb, " xmlns"); + + /* 17(c)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + break; + ns2->prefix = prefix; + } + + /* 17(c)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendChar(&sb, ':'); + js_AppendJSString(&sb, ns2->prefix); + } + + /* 17(d-g). */ + AppendAttributeValue(cx, &sb, ns2->uri); + } + XMLArrayCursorFinish(&cursor); + if (ns2) + goto out; + + /* Step 18: handle point tags. */ + n = xml->xml_kids.length; + if (n == 0) { + js_AppendCString(&sb, "/>"); + } else { + /* Steps 19 through 25: handle element content, and open the end-tag. */ + js_AppendChar(&sb, '>'); + indentKids = n > 1 || + (n == 1 && + (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && + kid->xml_class != JSXML_CLASS_TEXT); + + if (pretty && indentKids) { + if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) + goto out; + nextIndentLevel = indentLevel + i; + } else { + nextIndentLevel = 0; + } + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (pretty && indentKids) + js_AppendChar(&sb, '\n'); + + kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); + if (!kidstr) + break; + + js_AppendJSString(&sb, kidstr); + } + XMLArrayCursorFinish(&cursor); + if (kid) + goto out; + + if (pretty && indentKids) { + js_AppendChar(&sb, '\n'); + js_RepeatChar(&sb, ' ', indentLevel); + } + js_AppendCString(&sb, "prefix && !IS_EMPTY(ns->prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + + /* Step 27. */ + js_AppendJSString(&sb, xml->name->localName); + js_AppendChar(&sb, '>'); + } + + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + goto out; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); +out: + js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + if (!str && STRING_BUFFER_OK(&sb)) + js_FinishStringBuffer(&sb); + XMLArrayFinish(cx, &decls); + if (ancdecls.capacity != 0) + XMLArrayFinish(cx, &ancdecls); + return str; +} + +/* ECMA-357 10.2 */ +static JSString * +ToXMLString(JSContext *cx, jsval v) +{ + JSObject *obj; + JSString *str; + JSXML *xml; + + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + js_type_strs[JSVAL_IS_NULL(v) + ? JSTYPE_NULL + : JSTYPE_VOID]); + return NULL; + } + + if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) + return js_ValueToString(cx, v); + + if (JSVAL_IS_STRING(v)) + return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); + + obj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, obj)) { + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) + return NULL; + str = js_ValueToString(cx, v); + if (!str) + return NULL; + return EscapeElementValue(cx, NULL, str); + } + + /* Handle non-element cases in this switch, returning from each case. */ + xml = (JSXML *) JS_GetPrivate(cx, obj); + return XMLToXMLString(cx, xml, NULL, 0); +} + +static JSXMLQName * +ToAttributeName(JSContext *cx, jsval v) +{ + JSString *name, *uri, *prefix; + JSObject *obj; + JSClass *clasp; + JSXMLQName *qn; + JSTempValueRooter tvr; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + uri = prefix = cx->runtime->emptyString; + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_ATTR_NAME, + JS_GetStringBytes(name)); + } + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass) + return (JSXMLQName *) JS_GetPrivate(cx, obj); + + if (clasp == &js_QNameClass.base) { + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + } else { + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + } else { + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + uri = prefix = cx->runtime->emptyString; + } + } + + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return NULL; + + JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr); + obj = js_GetAttributeNameObject(cx, qn); + JS_POP_TEMP_ROOT(cx, &tvr); + if (!obj) + return NULL; + return qn; +} + +static JSXMLQName * +ToXMLName(JSContext *cx, jsval v, jsid *funidp) +{ + JSString *name; + JSObject *obj; + JSClass *clasp; + uint32 index; + JSXMLQName *qn; + JSAtom *atom; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + } else { + if (JSVAL_IS_PRIMITIVE(v)) { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) + goto bad; + return NULL; + } + + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) + goto out; + if (clasp == &js_AnyNameClass) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + goto construct; + } + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + + /* + * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: + * + * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception + * + * First, _P_ should be _s_, to refer to the given string. + * + * Second, why does ToXMLName applied to the string type throw TypeError + * only for numeric literals without any leading or trailing whitespace? + * + * If the idea is to reject uint32 property names, then the check needs to + * be stricter, to exclude hexadecimal and floating point literals. + */ + if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) + goto bad; + + if (*JSSTRING_CHARS(name) == '@') { + name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); + if (!name) + return NULL; + *funidp = 0; + return ToAttributeName(cx, STRING_TO_JSVAL(name)); + } + +construct: + v = STRING_TO_JSVAL(name); + obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); + if (!obj) + return NULL; + +out: + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; + if (qn->uri && atom && + (qn->uri == ATOM_TO_STRING(atom) || + js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) { + if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp)) + return NULL; + } else { + *funidp = 0; + } + return qn; + +bad: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); + return NULL; +} + +/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ +static JSBool +AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) +{ + JSXMLNamespace *match, *ns2; + uint32 i, n, m; + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ + if (!ns->prefix) { + match = NULL; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) { + match = ns2; + break; + } + } + if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) + return JS_FALSE; + } else { + if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) + return JS_TRUE; + match = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + m = XML_NOT_FOUND; +#endif + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns2 && ns2->prefix && + js_EqualStrings(ns2->prefix, ns->prefix)) { + match = ns2; + m = i; + break; + } + } + if (match && !js_EqualStrings(match->uri, ns->uri)) { + ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, + JSXMLNamespace); + JS_ASSERT(ns2 == match); + match->prefix = NULL; + if (!AddInScopeNamespace(cx, xml, match)) + return JS_FALSE; + } + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return JS_FALSE; + } + + /* OPTION: enforce that descendants have superset namespaces. */ + return JS_TRUE; +} + +/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *xml) +{ + uint32 i, j, k, n; + JSXML *kid; + + JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); + i = list->xml_kids.length; + n = 1; + if (xml->xml_class == JSXML_CLASS_LIST) { + list->xml_target = xml->xml_target; + list->xml_targetprop = xml->xml_targetprop; + n = JSXML_LENGTH(xml); + k = i + n; + if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) + return JS_FALSE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); + if (kid) + XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); + } + return JS_TRUE; + } + + list->xml_target = xml->parent; + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + list->xml_targetprop = NULL; + else + list->xml_targetprop = xml->name; + if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) + return JS_FALSE; + return JS_TRUE; +} + +/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); + +static JSXML * +DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) +{ + JSXML *copy; + JSBool ok; + + /* Our caller may not be protecting newborns with a local root scope. */ + if (!js_EnterLocalRootScope(cx)) + return NULL; + copy = DeepCopyInLRS(cx, xml, flags); + if (copy) { + if (obj) { + /* Caller provided the object for this copy, hook 'em up. */ + ok = JS_SetPrivate(cx, obj, copy); + if (ok) + copy->object = obj; + } else { + ok = js_GetXMLObject(cx, copy) != NULL; + } + if (!ok) + copy = NULL; + } + js_LeaveLocalRootScopeWithResult(cx, (jsval) copy); + return copy; +} + +/* + * (i) We must be in a local root scope (InLRS). + * (ii) parent must have a rooted object. + * (iii) from's owning object must be locked if not thread-local. + */ +static JSBool +DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, + uintN flags) +{ + uint32 j, n; + JSXMLArrayCursor cursor; + JSBool ok; + JSXML *kid, *kid2; + JSString *str; + + JS_ASSERT(cx->localRootStack); + + n = from->length; + if (!XMLArraySetCapacity(cx, to, n)) + return JS_FALSE; + + XMLArrayCursorInit(&cursor, from); + j = 0; + ok = JS_TRUE; + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if ((flags & XSF_IGNORE_COMMENTS) && + kid->xml_class == JSXML_CLASS_COMMENT) { + continue; + } + if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && + kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { + continue; + } + if ((flags & XSF_IGNORE_WHITESPACE) && + (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { + continue; + } + kid2 = DeepCopyInLRS(cx, kid, flags); + if (!kid2) { + to->length = j; + ok = JS_FALSE; + break; + } + + if ((flags & XSF_IGNORE_WHITESPACE) && + n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { + str = ChompXMLWhitespace(cx, kid2->xml_value); + if (!str) { + to->length = j; + ok = JS_FALSE; + break; + } + kid2->xml_value = str; + } + + XMLARRAY_SET_MEMBER(to, j, kid2); + ++j; + if (parent->xml_class != JSXML_CLASS_LIST) + kid2->parent = parent; + } + XMLArrayCursorFinish(&cursor); + if (!ok) + return JS_FALSE; + + if (j < n) + XMLArrayTrim(to); + return JS_TRUE; +} + +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) +{ + JSXML *copy; + JSXMLQName *qn; + JSBool ok; + uint32 i, n; + JSXMLNamespace *ns, *ns2; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + copy = js_NewXML(cx, xml->xml_class); + if (!copy) + return NULL; + qn = xml->name; + if (qn) { + qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); + if (!qn) { + ok = JS_FALSE; + goto out; + } + } + copy->name = qn; + copy->xml_flags = xml->xml_flags; + + if (JSXML_HAS_VALUE(xml)) { + copy->xml_value = xml->xml_value; + ok = JS_TRUE; + } else { + ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); + if (!ok) + goto out; + + if (xml->xml_class == JSXML_CLASS_LIST) { + copy->xml_target = xml->xml_target; + copy->xml_targetprop = xml->xml_targetprop; + } else { + n = xml->xml_namespaces.length; + ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); + if (!ok) + goto out; + for (i = 0; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared); + if (!ns2) { + copy->xml_namespaces.length = i; + ok = JS_FALSE; + goto out; + } + XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); + } + + ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, + 0); + if (!ok) + goto out; + } + } + +out: + if (!ok) + return NULL; + return copy; +} + +static void +ReportBadXMLName(JSContext *cx, jsval id) +{ + JSString *name; + + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + JS_GetStringBytes(name)); + } +} + +/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ +static JSBool +DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) +{ + uint32 index; + JSXML *kid; + + if (!js_IdIsIndex(id, &index)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid) + kid->parent = NULL; + XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); + } + + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml); + +static JSBool +MatchAttrName(JSXMLQName *nameqn, JSXML *attr) +{ + JSXMLQName *attrqn = attr->name; + + return (IS_STAR(nameqn->localName) || + js_EqualStrings(attrqn->localName, nameqn->localName)) && + (!nameqn->uri || + js_EqualStrings(attrqn->uri, nameqn->uri)); +} + +static JSBool +MatchElemName(JSXMLQName *nameqn, JSXML *elem) +{ + return (IS_STAR(nameqn->localName) || + (elem->xml_class == JSXML_CLASS_ELEMENT && + js_EqualStrings(elem->name->localName, nameqn->localName))) && + (!nameqn->uri || + (elem->xml_class == JSXML_CLASS_ELEMENT && + js_EqualStrings(elem->name->uri, nameqn->uri))); +} + +/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ +static JSBool +DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) +{ + uint32 i, n; + JSXML *attr, *kid; + + if (xml->xml_class == JSXML_CLASS_ELEMENT && + OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) { + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (attr && MatchAttrName(nameqn, attr)) { + if (!Append(cx, list, attr)) + return JS_FALSE; + } + } + } + + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass && + MatchElemName(nameqn, kid)) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + if (!DescendantsHelper(cx, kid, nameqn, list)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSXML * +Descendants(JSContext *cx, JSXML *xml, jsval id) +{ + jsid funid; + JSXMLQName *nameqn; + JSObject *listobj; + JSXML *list, *kid; + uint32 i, n; + JSBool ok; + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return NULL; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (funid) + return list; + + /* + * Protect nameqn's object and strings from GC by linking list to it + * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, + * which protects list. Any other object allocations occuring beneath + * DescendantsHelper use local roots. + */ + list->name = nameqn; + if (!js_EnterLocalRootScope(cx)) + return NULL; + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = DescendantsHelper(cx, kid, nameqn, list); + if (!ok) + break; + } + } + } else { + ok = DescendantsHelper(cx, xml, nameqn, list); + } + js_LeaveLocalRootScopeWithResult(cx, (jsval) list); + if (!ok) + return NULL; + list->name = NULL; + return list; +} + +static JSBool +xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +/* Recursive (JSXML *) parameterized version of Equals. */ +static JSBool +XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) +{ + JSXMLQName *qn, *vqn; + uint32 i, j, n; + JSXMLArrayCursor cursor, vcursor; + JSXML *kid, *vkid, *attr, *vattr; + JSBool ok; + JSObject *xobj, *vobj; + +retry: + if (xml->xml_class != vxml->xml_class) { + if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) + goto retry; + } + if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); + if (vxml) + goto retry; + } + *bp = JS_FALSE; + return JS_TRUE; + } + + qn = xml->name; + vqn = vxml->name; + if (qn) { + *bp = vqn && + js_EqualStrings(qn->localName, vqn->localName) && + js_EqualStrings(qn->uri, vqn->uri); + } else { + *bp = vqn == NULL; + } + if (!*bp) + return JS_TRUE; + + if (JSXML_HAS_VALUE(xml)) { + *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); + } else if (xml->xml_kids.length != vxml->xml_kids.length) { + *bp = JS_FALSE; + } else { + XMLArrayCursorInit(&cursor, &xml->xml_kids); + XMLArrayCursorInit(&vcursor, &vxml->xml_kids); + for (;;) { + kid = (JSXML *) XMLArrayCursorNext(&cursor); + vkid = (JSXML *) XMLArrayCursorNext(&vcursor); + if (!kid || !vkid) { + *bp = !kid && !vkid; + ok = JS_TRUE; + break; + } + xobj = js_GetXMLObject(cx, kid); + vobj = js_GetXMLObject(cx, vkid); + ok = xobj && vobj && + xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp); + if (!ok || !*bp) + break; + } + XMLArrayCursorFinish(&vcursor); + XMLArrayCursorFinish(&cursor); + if (!ok) + return JS_FALSE; + + if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { + n = xml->xml_attrs.length; + if (n != vxml->xml_attrs.length) + *bp = JS_FALSE; + for (i = 0; *bp && i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); + if (j == XML_NOT_FOUND) { + *bp = JS_FALSE; + break; + } + vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); + if (!vattr) + continue; + *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); + } + } + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ +static JSBool +Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) +{ + JSObject *vobj; + JSXML *vxml; + + if (JSVAL_IS_PRIMITIVE(v)) { + *bp = JS_FALSE; + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.length == 1) { + vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!vxml) + return JS_TRUE; + vobj = js_GetXMLObject(cx, vxml); + if (!vobj) + return JS_FALSE; + return js_XMLObjectOps.equality(cx, vobj, v, bp); + } + if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) + *bp = JS_TRUE; + } + } else { + vobj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, vobj)) { + *bp = JS_FALSE; + } else { + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + if (!XMLEquals(cx, xml, vxml, bp)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) +{ + JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); + + do { + if (xml == kid) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CYCLIC_VALUE, js_XML_str); + return JS_FALSE; + } + } while ((xml = xml->parent) != NULL); + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.11 XML [[Insert]]. */ +static JSBool +Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) +{ + uint32 j, n; + JSXML *vxml, *kid; + JSObject *vobj; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + n = 1; + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + if (vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) + return JS_TRUE; + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + if (!CheckCycle(cx, xml, kid)) + return JS_FALSE; + } + } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + } + } + } + if (!vxml) { + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + } + + if (i > xml->xml_kids.length) + i = xml->xml_kids.length; + + if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) + return JS_FALSE; + + if (vxml->xml_class == JSXML_CLASS_LIST) { + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!kid) + continue; + kid->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); + + /* OPTION: enforce that descendants have superset namespaces. */ + } + } else { + vxml->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + return JS_TRUE; +} + +static JSBool +IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) +{ + JSString *str; + + if (index <= JSVAL_INT_MAX) { + *idvp = INT_TO_JSVAL(index); + } else { + str = js_NumberToString(cx, (jsdouble) index); + if (!str) + return JS_FALSE; + *idvp = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +/* ECMA-357 9.1.1.12 XML [[Replace]]. */ +static JSBool +Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) +{ + uint32 i, n; + JSXML *vxml, *kid; + JSObject *vobj; + jsval junk; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (!js_IdIsIndex(id, &i)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + /* + * 9.1.1.12 + * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. + * It should therefore constrain callers to pass in _i <= x.[[Length]]_. + */ + n = xml->xml_kids.length; + if (i >= n) { + if (!IndexToIdVal(cx, n, &id)) + return JS_FALSE; + i = n; + } + + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { + case JSXML_CLASS_ELEMENT: + /* OPTION: enforce that descendants have superset namespaces. */ + if (!CheckCycle(cx, xml, vxml)) + return JS_FALSE; + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + goto do_replace; + + case JSXML_CLASS_LIST: + if (i < n && !DeleteByIndex(cx, xml, id, &junk)) + return JS_FALSE; + if (!Insert(cx, xml, i, v)) + return JS_FALSE; + break; + + default: + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->xml_value = str; + + do_replace: + vxml->parent = xml; + if (i < n) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid) + kid->parent = NULL; + } + if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) + return JS_FALSE; + break; + } + + return JS_TRUE; +} + +/* Forward declared -- its implementation uses other statics that call it. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result); + +/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ +static JSBool +DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *kid, *parent; + JSBool isIndex; + JSXMLArray *array; + uint32 length, index, kidIndex, deleteCount; + JSXMLQName *nameqn; + jsid funid; + JSObject *nameobj, *kidobj; + JSXMLNameMatcher matcher; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + isIndex = js_IdIsIndex(id, &index); + if (JSXML_HAS_KIDS(xml)) { + array = &xml->xml_kids; + length = array->length; + } else { + array = NULL; + length = 0; + } + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.3. */ + if (isIndex && index < length) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (!kid) + goto out; + parent = kid->parent; + if (parent) { + JS_ASSERT(parent != xml); + JS_ASSERT(JSXML_HAS_KIDS(parent)); + + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameqn = kid->name; + nameobj = js_GetAttributeNameObject(cx, nameqn); + if (!nameobj || !js_GetXMLObject(cx, parent)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(nameobj); + if (!DeleteProperty(cx, parent->object, id, vp)) + return JS_FALSE; + } else { + kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, + NULL); + JS_ASSERT(kidIndex != XML_NOT_FOUND); + if (!IndexToIdVal(cx, kidIndex, &id)) + return JS_FALSE; + if (!DeleteByIndex(cx, parent, id, vp)) + return JS_FALSE; + } + } + + XMLArrayDelete(cx, array, index, JS_TRUE); + } else { + for (index = 0; index < length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !DeleteProperty(cx, kidobj, id, vp)) + return JS_FALSE; + } + } + } + } else { + /* ECMA-357 9.1.1.3. */ + if (isIndex) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + goto out; + nameobj = nameqn->object; + + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + if (xml->xml_class != JSXML_CLASS_ELEMENT) + goto out; + array = &xml->xml_attrs; + length = array->length; + matcher = MatchAttrName; + } else { + matcher = MatchElemName; + } + if (length != 0) { + deleteCount = 0; + for (index = 0; index < length; index++) { + kid = XMLARRAY_MEMBER(array, index, JSXML); + if (kid && matcher(nameqn, kid)) { + kid->parent = NULL; + XMLArrayDelete(cx, array, index, JS_FALSE); + ++deleteCount; + } else if (deleteCount != 0) { + XMLARRAY_SET_MEMBER(array, + index - deleteCount, + array->vector[index]); + } + } + array->length -= deleteCount; + } + } + +out: + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +static JSBool +SyncInScopeNamespaces(JSContext *cx, JSXML *xml) +{ + JSXMLArray *nsarray; + uint32 i, n; + JSXMLNamespace *ns; + + nsarray = &xml->xml_namespaces; + while ((xml = xml->parent) != NULL) { + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +static JSBool +GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, + JSBool attributes, JSXML *list) +{ + JSXMLArray *array; + JSXMLNameMatcher matcher; + JSXMLArrayCursor cursor; + JSXML *kid; + JSBool ok; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (attributes) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + + XMLArrayCursorInit(&cursor, array); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (matcher(nameqn, kid)) { + if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = SyncInScopeNamespaces(cx, kid); + if (!ok) + goto out; + } + ok = Append(cx, list, kid); + if (!ok) + goto out; + } + } + ok = JS_TRUE; + + out: + XMLArrayCursorFinish(&cursor); + return ok; +} + +/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ +static JSBool +GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list, *kid; + uint32 index; + JSObject *kidobj, *listobj; + JSXMLQName *nameqn; + jsid funid; + jsval roots[2]; + JSTempValueRooter tvr; + JSBool attributes; + JSXMLArrayCursor cursor; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + + if (js_IdIsIndex(id, &index)) { + if (xml->xml_class != JSXML_CLASS_LIST) { + *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; + } else { + /* + * ECMA-357 9.2.1.1 starts here. + * + * Erratum: 9.2 is not completely clear that indexed properties + * correspond to kids, but that's what it seems to say, and it's + * what any sane user would want. + */ + if (index < xml->xml_kids.length) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + + *vp = OBJECT_TO_JSVAL(kidobj); + } else { + *vp = JSVAL_VOID; + } + } + return JS_TRUE; + } + + /* + * ECMA-357 9.2.1.1/9.1.1.1 qname case. + */ + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return js_GetXMLFunction(cx, obj, funid, vp); + + roots[0] = OBJECT_TO_JSVAL(nameqn->object); + JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + roots[1] = OBJECT_TO_JSVAL(listobj); + tvr.count++; + + list = (JSXML *) JS_GetPrivate(cx, listobj); + attributes = (OBJ_GET_CLASS(cx, nameqn->object) == + &js_AttributeNameClass); + + if (xml->xml_class == JSXML_CLASS_LIST) { + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT && + !GetNamedProperty(cx, kid, nameqn, attributes, list)) { + listobj = NULL; + break; + } + } + XMLArrayCursorFinish(&cursor); + } else { + if (!GetNamedProperty(cx, xml, nameqn, attributes, list)) + listobj = NULL; + } + + /* + * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given + * list's [[TargetProperty]] to the property that is being appended. + * This means that any use of the internal [[Get]] property returns + * a list which, when used by e.g. [[Insert]] duplicates the last + * element matched by id. + * See bug 336921. + */ + list->xml_target = xml; + list->xml_targetprop = nameqn; + *vp = OBJECT_TO_JSVAL(listobj); + } + + JS_POP_TEMP_ROOT(cx, &tvr); + return listobj != NULL; +} + +static JSXML * +CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) +{ + JS_ASSERT(xml->object != obj); + + xml = DeepCopy(cx, xml, obj, 0); + if (!xml) + return NULL; + + JS_ASSERT(xml->object == obj); + return xml; +} + +#define CHECK_COPY_ON_WRITE(cx,xml,obj) \ + (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) + +static JSString * +KidToString(JSContext *cx, JSXML *xml, uint32 index) +{ + JSXML *kid; + JSObject *kidobj; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) + return cx->runtime->emptyString; + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return NULL; + return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); +} + +/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ +static JSBool +PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool ok, primitiveAssign; + enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; + jsval roots[3]; + JSTempValueRooter tvr; + JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; + JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; + JSXMLQName *targetprop, *nameqn, *attrqn; + uint32 index, i, j, k, n, q; + jsval attrval, nsval, junk; + jsid funid; + JSString *left, *right, *space; + JSXMLNamespace *ns; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(*vp)) { + vobj = JSVAL_TO_OBJECT(*vp); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + /* Control flow after here must exit via label out. */ + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); + roots[ID_ROOT] = id; + roots[VAL_ROOT] = *vp; + JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.2. */ + if (js_IdIsIndex(id, &index)) { + /* Step 1 sets i to the property index. */ + i = index; + + /* 2(a-b). */ + if (xml->xml_target) { + ok = ResolveValue(cx, xml->xml_target, &rxml); + if (!ok) + goto out; + if (!rxml) + goto out; + JS_ASSERT(rxml->object); + } else { + rxml = NULL; + } + + /* 2(c). */ + if (index >= xml->xml_kids.length) { + /* 2(c)(i). */ + if (rxml) { + if (rxml->xml_class == JSXML_CLASS_LIST) { + if (rxml->xml_kids.length != 1) + goto out; + rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); + if (!rxml) + goto out; + ok = js_GetXMLObject(cx, rxml) != NULL; + if (!ok) + goto out; + } + + /* + * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets + * _y.[[Parent]] = r_ where _r_ is the result of + * [[ResolveValue]] called on _x.[[TargetObject]] in + * 2(a)(i). This can result in text parenting text: + * + * var MYXML = new XML(); + * MYXML.appendChild(new XML("Giants")); + * + * (testcase from Werner Sharp ). + * + * To match insertChildAfter, insertChildBefore, + * prependChild, and setChildren, we should silently + * do nothing in this case. + */ + if (!JSXML_HAS_KIDS(rxml)) + goto out; + } + + /* 2(c)(ii) is distributed below as several js_NewXML calls. */ + targetprop = xml->xml_targetprop; + if (!targetprop || IS_STAR(targetprop->localName)) { + /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ + kid = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!kid) + goto bad; + } else { + nameobj = js_GetXMLQNameObject(cx, targetprop); + if (!nameobj) + goto bad; + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* + * 2(c)(iii)(1-3). + * Note that rxml can't be null here, because target + * and targetprop are non-null. + */ + ok = GetProperty(cx, rxml->object, id, &attrval); + if (!ok) + goto out; + if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */ + goto out; + attrobj = JSVAL_TO_OBJECT(attrval); + attr = (JSXML *) JS_GetPrivate(cx, attrobj); + if (JSXML_LENGTH(attr) != 0) + goto out; + + kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + } else { + /* 2(c)(v). */ + kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); + } + if (!kid) + goto bad; + + /* An important bit of 2(c)(ii). */ + kid->name = targetprop; + } + + /* Final important bit of 2(c)(ii). */ + kid->parent = rxml; + + /* 2(c)(vi-vii). */ + i = xml->xml_kids.length; + if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { + /* + * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. + * y.[[Parent]] is here called kid->parent, which we know + * from 2(c)(ii) is _r_, here called rxml. So let's just + * test that! Erratum, the spec should be simpler here. + */ + if (rxml) { + JS_ASSERT(JSXML_HAS_KIDS(rxml)); + n = rxml->xml_kids.length; + j = n - 1; + if (n != 0 && i != 0) { + for (n = j, j = 0; j < n; j++) { + if (rxml->xml_kids.vector[j] == + xml->xml_kids.vector[i-1]) { + break; + } + } + } + + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); + if (!ok) + goto out; + } + + /* + * 2(c)(vii)(2-3). + * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a + * typo for [[TargetProperty]]. + */ + if (vxml) { + kid->name = (vxml->xml_class == JSXML_CLASS_LIST) + ? vxml->xml_targetprop + : vxml->name; + } + } + + /* 2(c)(viii). */ + ok = Append(cx, xml, kid); + if (!ok) + goto out; + } + + /* 2(d). */ + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 2(e). */ + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + goto out; + parent = kid->parent; + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameobj = js_GetAttributeNameObject(cx, kid->name); + if (!nameobj) + goto bad; + id = OBJECT_TO_JSVAL(nameobj); + + if (parent) { + /* 2(e)(i). */ + parentobj = js_GetXMLObject(cx, parent); + if (!parentobj) + goto bad; + ok = PutProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + + /* 2(e)(ii). */ + ok = GetProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); + + /* 2(e)(iii). */ + xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; + } + } + + /* 2(f). */ + else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + /* 2(f)(i) Create a shallow copy _c_ of _V_. */ + copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!copyobj) + goto bad; + copy = (JSXML *) JS_GetPrivate(cx, copyobj); + n = vxml->xml_kids.length; + ok = XMLArraySetCapacity(cx, ©->xml_kids, n); + if (!ok) + goto out; + for (k = 0; k < n; k++) { + kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); + XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2); + } + + JS_ASSERT(parent != xml); + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); + if (!ok) + goto out; + +#ifdef DEBUG + /* Erratum: this loop in the spec is useless. */ + for (j = 0, n = copy->xml_kids.length; j < n; j++) { + kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); + JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) + == kid2); + } +#endif + } + + /* + * 2(f)(iv-vi). + * Erratum: notice the unhandled zero-length V basis case and + * the off-by-one errors for the n != 0 cases in the spec. + */ + if (n == 0) { + XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); + } else { + ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); + if (!ok) + goto out; + + for (j = 0; j < n; j++) + xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; + } + } + + /* 2(g). */ + else if (vxml || JSXML_HAS_VALUE(kid)) { + if (parent) { + q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != XML_NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, *vp); + if (!ok) + goto out; + + vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); + if (!vxml) + goto out; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); + } + + /* + * 2(g)(iii). + * Erratum: _V_ may not be of type XML, but all index-named + * properties _x[i]_ in an XMLList _x_ must be of type XML, + * according to 9.2.1.1 Overview and other places in the spec. + * + * Thanks to 2(d), we know _V_ (*vp here) is either a string + * or an XML/XMLList object. If *vp is a string, call ToXML + * on it to satisfy the constraint. + */ + if (!vxml) { + JS_ASSERT(JSVAL_IS_STRING(*vp)); + vobj = ToXML(cx, *vp); + if (!vobj) + goto bad; + roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + + /* 2(h). */ + else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + id = ATOM_KEY(cx->runtime->atomState.starAtom); + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* + * 3. + * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null + * or an r with r.[[Length]] != 1, throw TypeError. + */ + n = JSXML_LENGTH(xml); + if (n > 1) + goto type_error; + if (n == 0) { + ok = ResolveValue(cx, xml, &rxml); + if (!ok) + goto out; + if (!rxml || JSXML_LENGTH(rxml) != 1) + goto type_error; + ok = Append(cx, xml, rxml); + if (!ok) + goto out; + } + JS_ASSERT(JSXML_LENGTH(xml) == 1); + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!kid) + goto out; + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* + * ECMA-357 9.1.1.2. + * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted + * effort in ToString or [[DeepCopy]]. + */ + if (js_IdIsIndex(id, &index)) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + goto bad; + } + + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + goto bad; + if (funid) { + ok = js_SetProperty(cx, obj, funid, vp); + goto out; + } + nameobj = nameqn->object; + + if (JSXML_HAS_VALUE(xml)) + goto out; + + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + } else { + rxml = DeepCopyInLRS(cx, vxml, 0); + if (!rxml || !js_GetXMLObject(cx, rxml)) + goto bad; + vxml = rxml; + *vp = OBJECT_TO_JSVAL(vxml->object); + } + roots[VAL_ROOT] = *vp; + + /* + * 6. + * Erratum: why is this done here, so early? use is way later.... + */ + ok = js_GetDefaultXMLNamespace(cx, &nsval); + if (!ok) + goto out; + + if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { + /* 7(a). */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) + goto out; + + /* 7(b-c). */ + if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.length; + if (n == 0) { + *vp = STRING_TO_JSVAL(cx->runtime->emptyString); + } else { + left = KidToString(cx, vxml, 0); + if (!left) + goto bad; + + space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); + for (i = 1; i < n; i++) { + left = js_ConcatStrings(cx, left, space); + if (!left) + goto bad; + right = KidToString(cx, vxml, i); + if (!right) + goto bad; + left = js_ConcatStrings(cx, left, right); + if (!left) + goto bad; + } + + roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); + } + } else { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + roots[VAL_ROOT] = *vp; + } + + /* 7(d-e). */ + match = NULL; + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrqn = attr->name; + if (js_EqualStrings(attrqn->localName, nameqn->localName) && + (!nameqn->uri || + js_EqualStrings(attrqn->uri, nameqn->uri))) { + if (!match) { + match = attr; + } else { + nameobj = js_GetAttributeNameObject(cx, attrqn); + if (!nameobj) + goto bad; + + id = OBJECT_TO_JSVAL(nameobj); + ok = DeleteProperty(cx, obj, id, &junk); + if (!ok) + goto out; + --i; + } + } + } + + /* 7(f). */ + attr = match; + if (!attr) { + /* 7(f)(i-ii). */ + if (!nameqn->uri) { + left = right = cx->runtime->emptyString; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 7(f)(iii). */ + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto bad; + attr->parent = xml; + attr->name = nameqn; + + /* 7(f)(iv). */ + ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); + if (!ok) + goto out; + + /* 7(f)(v-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = AddInScopeNamespace(cx, xml, ns); + if (!ok) + goto out; + } + + /* 7(g). */ + attr->xml_value = JSVAL_TO_STRING(*vp); + goto out; + } + + /* 8-9. */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && + !IS_STAR(nameqn->localName)) { + goto out; + } + + /* 10-11. */ + id = JSVAL_VOID; + primitiveAssign = !vxml && !IS_STAR(nameqn->localName); + + /* 12. */ + k = n = xml->xml_kids.length; + kid2 = NULL; + while (k != 0) { + --k; + kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (!JSVAL_IS_VOID(id)) { + ok = DeleteByIndex(cx, xml, id, &junk); + if (!ok) + goto out; + } + ok = IndexToIdVal(cx, k, &id); + if (!ok) + goto out; + kid2 = kid; + } + } + + /* + * Erratum: ECMA-357 specified child insertion inconsistently: + * insertChildBefore and insertChildAfter insert an arbitrary XML + * instance, and therefore can create cycles, but appendChild as + * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on + * its argument. But the "Semantics" in 13.4.4.3 do not include + * any [[DeepCopy]] call. + * + * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) + * required adding cycle detection, and allowing duplicate kids to + * be created (see comment 6 in the bug). Allowing duplicate kid + * references means the loop above will delete all but the lowest + * indexed reference, and each [[DeleteByIndex]] nulls the kid's + * parent. Thus the need to restore parent here. This is covered + * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. + */ + if (kid2) { + JS_ASSERT(kid2->parent == xml || !kid2->parent); + if (!kid2->parent) + kid2->parent = xml; + } + + /* 13. */ + if (JSVAL_IS_VOID(id)) { + /* 13(a). */ + ok = IndexToIdVal(cx, n, &id); + if (!ok) + goto out; + + /* 13(b). */ + if (primitiveAssign) { + if (!nameqn->uri) { + ns = (JSXMLNamespace *) + JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + left = ns->uri; + right = ns->prefix; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 13(b)(iii). */ + vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); + if (!vobj) + goto bad; + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + vxml->parent = xml; + vxml->name = nameqn; + + /* 13(b)(iv-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); + if (!ok) + goto out; + ok = AddInScopeNamespace(cx, vxml, ns); + if (!ok) + goto out; + } + } + + /* 14. */ + if (primitiveAssign) { + JSXMLArrayCursor cursor; + + js_IdIsIndex(id, &index); + XMLArrayCursorInit(&cursor, &xml->xml_kids); + cursor.index = index; + kid = (JSXML *) XMLArrayCursorItem(&cursor); + if (JSXML_HAS_KIDS(kid)) { + XMLArrayFinish(cx, &kid->xml_kids); + ok = XMLArrayInit(cx, &kid->xml_kids, 1); + } + + /* 14(b-c). */ + /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ + if (ok) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) { + roots[VAL_ROOT] = *vp; + if ((JSXML *) XMLArrayCursorItem(&cursor) == kid) + ok = Replace(cx, kid, JSVAL_ZERO, *vp); + } + } + XMLArrayCursorFinish(&cursor); + } else { + /* 15(a). */ + ok = Replace(cx, xml, id, *vp); + } + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + js_LeaveLocalRootScope(cx); + return ok; + +type_error: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_PUT, + js_ValueToPrintableString(cx, id)); +bad: + ok = JS_FALSE; + goto out; +} + +/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result) +{ + JSXML *target, *base; + JSXMLQName *targetprop; + JSObject *targetpropobj; + jsval id, tv; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { + if (!js_GetXMLObject(cx, list)) + return JS_FALSE; + *result = list; + return JS_TRUE; + } + + target = list->xml_target; + targetprop = list->xml_targetprop; + if (!target || !targetprop || IS_STAR(targetprop->localName)) { + *result = NULL; + return JS_TRUE; + } + + targetpropobj = js_GetXMLQNameObject(cx, targetprop); + if (!targetpropobj) + return JS_FALSE; + if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) { + *result = NULL; + return JS_TRUE; + } + + if (!ResolveValue(cx, target, &base)) + return JS_FALSE; + if (!base) { + *result = NULL; + return JS_TRUE; + } + if (!js_GetXMLObject(cx, base)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(targetpropobj); + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + + if (JSXML_LENGTH(target) == 0) { + if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { + *result = NULL; + return JS_TRUE; + } + tv = STRING_TO_JSVAL(cx->runtime->emptyString); + if (!PutProperty(cx, base->object, id, &tv)) + return JS_FALSE; + if (!GetProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + } + + *result = target; + return JS_TRUE; +} + +/* + * HasProperty must be able to return a found JSProperty and the object in + * which it was found, if id is of the form function::name. For other ids, + * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp + * and null in *objp. + * + * DROP_PROPERTY helps HasProperty callers drop function properties without + * trying to drop the magic FOUND_XML_PROPERTY cookie. + */ +#define FOUND_XML_PROPERTY ((JSProperty *) 1) +#define DROP_PROPERTY(cx,pobj,prop) (((prop) != FOUND_XML_PROPERTY) \ + ? OBJ_DROP_PROPERTY(cx, pobj, prop) \ + : (void) 0) + +/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ +static JSBool +HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, + JSProperty **propp) +{ + JSXML *xml, *kid; + JSXMLArrayCursor cursor; + JSObject *kidobj; + JSXMLQName *qn; + jsid funid; + JSXMLArray *array; + JSXMLNameMatcher matcher; + uint32 i, n; + + *objp = NULL; + *propp = NULL; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + n = JSXML_LENGTH(xml); + if (js_IdIsIndex(id, &i)) { + if (i < n) + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp)) + break; + if (*propp) + break; + } + } + XMLArrayCursorFinish(&cursor); + if (kid) + return *propp != NULL; + } else { + if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) { + if (i == 0) + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + + qn = ToXMLName(cx, id, &funid); + if (!qn) + return JS_FALSE; + if (funid) + return js_LookupProperty(cx, obj, funid, objp, propp); + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + for (i = 0, n = array->length; i < n; i++) { + kid = XMLARRAY_MEMBER(array, i, JSXML); + if (kid && matcher(qn, kid)) { + *propp = FOUND_XML_PROPERTY; + return JS_TRUE; + } + } + } + + return JS_TRUE; +} + +static void +xml_finalize(JSContext *cx, JSObject *obj) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (!xml) + return; + if (xml->object == obj) + xml->object = NULL; + UNMETER(xml_stats.livexmlobj); +} + +static void +xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) +{ + uint32 i; + JSXML *elt; + + for (i = 0; i < len; i++) { + elt = vec[i]; + { +#ifdef GC_MARK_DEBUG + char buf[120]; + + if (elt->xml_class == JSXML_CLASS_LIST) { + strcpy(buf, js_XMLList_str); + } else if (JSXML_HAS_NAME(elt)) { + JSXMLQName *qn = elt->name; + + JS_snprintf(buf, sizeof buf, "%s::%s", + qn->uri ? JS_GetStringBytes(qn->uri) : "*", + JS_GetStringBytes(qn->localName)); + } else { + JSString *str = elt->xml_value; + size_t srclen = JSSTRING_LENGTH(str); + size_t dstlen = sizeof buf; + + if (srclen >= sizeof buf / 6) + srclen = sizeof buf / 6 - 1; + js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, + buf, &dstlen); + } +#endif + GC_MARK(cx, elt, buf); + } + } +} + +/* + * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to + * be native. Therefore, xml_lookupProperty must return a valid JSProperty + * pointer parameter via *propp to signify "property found". Since the only + * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from + * js_FindXMLProperty (in this file), js_FindProperty (in jsobj.c, called from + * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a + * JSScopeProperty here is when an unqualified name or XML name is being + * accessed or when "name in xml" is called. + * + * This scope property keeps the JSOP_NAME code in js_Interpret happy by + * giving it an sprop with (getter, setter) == (GetProperty, PutProperty). + * + * NB: xml_deleteProperty must take care to remove any property added here. + * + * FIXME This clashes with the function namespace implementation which also + * uses native properties. Effectively after xml_lookupProperty any property + * stored previously using assignments to xml.function::name will be removed. + * We partially workaround the problem in js_GetXMLFunction. There we take + * advantage of the fact that typically function:: is used to access the + * functions from XML.prototype. So when js_GetProperty returns a non-function + * property, we assume that it represents the result of GetProperty setter + * hiding the function and use an extra prototype chain lookup to recover it. + * For a proper solution see bug 355257. + */ +static JSBool +xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + JSScopeProperty *sprop; + + if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp)) + return JS_FALSE; + + if (*propp == FOUND_XML_PROPERTY) { + sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, + SPROP_INVALID_SLOT, JSPROP_ENUMERATE, + 0, 0); + if (!sprop) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + *objp = obj; + *propp = (JSProperty *) sprop; + } + return JS_TRUE; +} + +static JSBool +xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) +{ + if (VALUE_IS_FUNCTION(cx, value) || getter || setter || + (attrs & JSPROP_ENUMERATE) == 0 || + (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { + return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, + propp); + } + + if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value)) + return JS_FALSE; + if (propp) + *propp = NULL; + return JS_TRUE; +} + +static JSBool +xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + if (id == JS_DEFAULT_XML_NAMESPACE_ID) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + return GetProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return PutProperty(cx, obj, ID_TO_VALUE(id), vp); +} + +static JSBool +FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + JSBool *foundp) +{ + JSObject *pobj; + + if (prop) { + *foundp = JS_TRUE; + } else { + if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop)) + return JS_FALSE; + if (prop) + DROP_PROPERTY(cx, pobj, prop); + *foundp = (prop != NULL); + } + return JS_TRUE; +} + +static JSBool +xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + *attrsp = found ? JSPROP_ENUMERATE : 0; + return JS_TRUE; +} + +static JSBool +xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + if (found) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SET_XML_ATTRS); + } + return !found; +} + +static JSBool +xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + /* + * If this object has its own (mutable) scope, and if id isn't an index, + * then we may have added a property to the scope in xml_lookupProperty + * for it to return to mean "found" and to provide a handle for access + * operations to call the property's getter or setter. The property also + * helps speed up unqualified accesses via the property cache, avoiding + * what amount to two HasProperty searches. + * + * But now it's time to remove any such property, to purge the property + * cache and remove the scope entry. + */ + if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) { + if (!js_DeleteProperty(cx, obj, id, rval)) + return JS_FALSE; + } + + return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval); +} + +static JSBool +xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + JSXML *xml; + + if (hint == JSTYPE_OBJECT) { + /* Called from for..in code in js_Interpret: return an XMLList. */ + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); + if (!obj) + return JS_FALSE; + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); +} + +static JSBool +xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + JSXML *xml; + uint32 length, index; + JSXMLArrayCursor *cursor; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + length = JSXML_LENGTH(xml); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); + if (!cursor) + return JS_FALSE; + XMLArrayCursorInit(cursor, &xml->xml_kids); + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + break; + + case JSENUMERATE_NEXT: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + *idp = INT_TO_JSID(index); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor) { + XMLArrayCursorFinish(cursor); + JS_free(cx, cursor); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return JS_TRUE; +} + +static uint32 +xml_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + GC_MARK(cx, xml, "private"); + return js_Mark(cx, obj, NULL); +} + +static void +xml_clear(JSContext *cx, JSObject *obj) +{ +} + +static JSBool +HasSimpleContent(JSXML *xml) +{ + JSXML *kid; + JSBool simple; + uint32 i, n; + +again: + switch (xml->xml_class) { + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + return JS_FALSE; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) + return JS_TRUE; + if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + xml = kid; + goto again; + } + } + /* FALL THROUGH */ + default: + simple = JS_TRUE; + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + simple = JS_FALSE; + break; + } + } + return simple; + } +} + +/* + * 11.2.2.1 Step 3(d) onward. + */ +static JSObject * +xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSTempValueRooter tvr; + + JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); + + /* + * As our callers have a bad habit of passing a pointer to an unrooted + * local value as vp, we use a proper root here. + */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); + if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value)) + obj = NULL; + *vp = tvr.u.value; + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; +} + +static JSBool +xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + return js_SetProperty(cx, obj, id, vp); +} + +static JSBool +xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp) +{ + JSXML *xml, *kid; + uint32 length, index; + JSXMLArrayCursor *cursor; + JSObject *kidobj; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + length = JSXML_LENGTH(xml); + JS_ASSERT(INT_FITS_IN_JSVAL(length)); + + switch (enum_op) { + case JSENUMERATE_INIT: + if (length == 0) { + cursor = NULL; + } else { + cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); + if (!cursor) + return JS_FALSE; + XMLArrayCursorInit(cursor, &xml->xml_kids); + } + *statep = PRIVATE_TO_JSVAL(cursor); + if (idp) + *idp = INT_TO_JSID(length); + if (vp) + *vp = JSVAL_VOID; + break; + + case JSENUMERATE_NEXT: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor && cursor->array && (index = cursor->index) < length) { + while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { + if (++index == length) + goto destroy; + } + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + JS_ASSERT(INT_FITS_IN_JSVAL(index)); + *idp = INT_TO_JSID(index); + *vp = OBJECT_TO_JSVAL(kidobj); + cursor->index = index + 1; + break; + } + /* FALL THROUGH */ + + case JSENUMERATE_DESTROY: + cursor = JSVAL_TO_PRIVATE(*statep); + if (cursor) { + destroy: + XMLArrayCursorFinish(cursor); + JS_free(cx, cursor); + } + *statep = JSVAL_NULL; + break; + } + return JS_TRUE; +} + +static JSBool +xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + JSXML *xml, *vxml; + JSObject *vobj; + JSBool ok; + JSString *str, *vstr; + jsdouble d, d2; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, xml, v, bp); + } else if (vxml) { + if (vxml->xml_class == JSXML_CLASS_LIST) { + ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); + } else { + if (((xml->xml_class == JSXML_CLASS_TEXT || + xml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(vxml)) || + ((vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && + HasSimpleContent(xml))) { + ok = js_EnterLocalRootScope(cx); + if (ok) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = js_EqualStrings(str, vstr); + js_LeaveLocalRootScope(cx); + } + } else { + ok = XMLEquals(cx, xml, vxml, bp); + } + } + } else { + ok = js_EnterLocalRootScope(cx); + if (ok) { + if (HasSimpleContent(xml)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + vstr = js_ValueToString(cx, v); + ok = str && vstr; + if (ok) + *bp = js_EqualStrings(str, vstr); + } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) { + ok = JS_FALSE; + } else if (JSVAL_IS_STRING(v)) { + *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); + } else { + ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); + if (ok) { + d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) + : *JSVAL_TO_DOUBLE(v); + *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); + } + } + } else { + *bp = JS_FALSE; + } + js_LeaveLocalRootScope(cx); + } + } + return ok; +} + +static JSBool +xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) +{ + JSBool ok; + JSObject *listobj, *robj; + JSXML *list, *lxml, *rxml; + + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) { + ok = JS_FALSE; + goto out; + } + + list = (JSXML *) JS_GetPrivate(cx, listobj); + lxml = (JSXML *) JS_GetPrivate(cx, obj); + ok = Append(cx, list, lxml); + if (!ok) + goto out; + + if (VALUE_IS_XML(cx, v)) { + rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + } else { + robj = ToXML(cx, v); + if (!robj) { + ok = JS_FALSE; + goto out; + } + rxml = (JSXML *) JS_GetPrivate(cx, robj); + } + ok = Append(cx, list, rxml); + if (!ok) + goto out; + + *vp = OBJECT_TO_JSVAL(listobj); +out: + js_LeaveLocalRootScopeWithResult(cx, *vp); + return ok; +} + +/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ +JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = { + { js_NewObjectMap, js_DestroyObjectMap, + xml_lookupProperty, xml_defineProperty, + xml_getProperty, xml_setProperty, + xml_getAttributes, xml_setAttributes, + xml_deleteProperty, xml_defaultValue, + xml_enumerate, js_CheckAccess, + NULL, NULL, + NULL, NULL, + NULL, xml_hasInstance, + js_SetProtoOrParent, js_SetProtoOrParent, + xml_mark, xml_clear, + NULL, NULL }, + xml_getMethod, xml_setMethod, + xml_enumerateValues, xml_equality, + xml_concatenate +}; + +static JSObjectOps * +xml_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_XMLObjectOps.base; +} + +JS_FRIEND_DATA(JSClass) js_XMLClass = { + js_XML_str, + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, + xml_getObjectOps, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +static JSObject * +CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, + uintN argc, jsval *argv) +{ + JSObject *tmp; + jsval rval; + + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); + return JSVAL_TO_OBJECT(rval); +} + +static JSXML * +StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv) +{ + JSXML *xml; + JSFunction *fun; + + JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2])); + + xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv); + if (!xml || xml->xml_class != JSXML_CLASS_LIST) + return xml; + + if (xml->xml_kids.length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (xml) { + *objp = js_GetXMLObject(cx, xml); + if (!*objp) + return NULL; + argv[-1] = OBJECT_TO_JSVAL(*objp); + return xml; + } + } + + fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2])); + if (fun) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NON_LIST_XML_METHOD, + JS_GetFunctionName(fun), numBuf); + } + return NULL; +} + +#define XML_METHOD_PROLOG \ + JS_BEGIN_MACRO \ + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ + if (!xml) \ + return JS_FALSE; \ + JS_END_MACRO + +#define NON_LIST_XML_METHOD_PROLOG \ + JS_BEGIN_MACRO \ + xml = StartNonListXMLMethod(cx, &obj, argv); \ + if (!xml) \ + return JS_FALSE; \ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); \ + JS_END_MACRO + +static JSBool +xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSObject *nsobj; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); + if (!nsobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nsobj); + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + if (!AddInScopeNamespace(cx, xml, ns)) + return JS_FALSE; + ns->declared = JS_TRUE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *vxml; + jsval name, v; + JSObject *vobj; + + NON_LIST_XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + if (!js_GetAnyName(cx, &name)) + return JS_FALSE; + + if (!GetProperty(cx, obj, name, &v)) + return JS_FALSE; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vobj = JSVAL_TO_OBJECT(v); + JS_ASSERT(OBJECT_IS_XML(cx, vobj)); + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); + + if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) + return JS_FALSE; + if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0])) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLQName *qn; + + qn = ToAttributeName(cx, argv[0]); + if (!qn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */ + return GetProperty(cx, obj, argv[0], rval); +} + +/* XML and XMLList */ +static JSBool +xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSXMLQName *qn; + JSTempValueRooter tvr; + JSBool ok; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + qn = ToAttributeName(cx, name); + if (!qn) + return JS_FALSE; + name = OBJECT_TO_JSVAL(qn->object); + JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr); + ok = GetProperty(cx, obj, name, rval); + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} + +static JSXML * +xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval) +{ + JSObject *listobj; + JSXML *list; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + return list; +} + +static JSBool +xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, + jsval *rval) +{ + uint32 index; + JSXML *kid; + JSObject *kidobj; + + /* ECMA-357 13.4.4.6 */ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + + if (js_IdIsIndex(name, &index)) { + if (index >= JSXML_LENGTH(xml)) { + *rval = JSVAL_VOID; + } else { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (!kid) { + *rval = JSVAL_VOID; + } else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(kidobj); + } + } + return JS_TRUE; + } + + return GetProperty(cx, obj, name, rval); +} + +/* XML and XMLList */ +static JSBool +xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSXMLArrayCursor cursor; + jsval name, v; + JSObject *kidobj; + + XML_METHOD_PROLOG; + name = argv[0]; + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 13.5.4.4 */ + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + break; + if (!xml_child_helper(cx, kidobj, kid, name, &v)) + break; + if (JSVAL_IS_VOID(v)) { + /* The property didn't exist in this kid. */ + continue; + } + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && + !Append(cx, list, vxml)) { + break; + } + } + XMLArrayCursorFinish(&cursor); + return !kid; + } + + /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */ + if (!xml_child_helper(cx, obj, xml, name, rval)) + return JS_FALSE; + if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *parent; + uint32 i, n; + + NON_LIST_XML_METHOD_PROLOG; + parent = xml->parent; + if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { + if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) + break; + } + JS_ASSERT(i < n); + return js_NewNumberValue(cx, i, rval); +} + +/* XML and XMLList */ +static JSBool +xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + return GetProperty(cx, obj, name, rval); +} + +/* XML and XMLList */ +static JSBool +xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSBool ok; + uint32 i, n; + JSObject *kidobj; + jsval v; + + XML_METHOD_PROLOG; + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_comments(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + /* 13.4.4.9 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval value; + JSBool eq; + JSXMLArrayCursor cursor; + JSObject *kidobj; + + XML_METHOD_PROLOG; + value = argv[0]; + if (xml->xml_class == JSXML_CLASS_LIST) { + eq = JS_FALSE; + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !xml_equality(cx, kidobj, value, &eq)) + break; + if (eq) + break; + } + XMLArrayCursorFinish(&cursor); + if (kid && !eq) + return JS_FALSE; + } else { + if (!xml_equality(cx, obj, value, &eq)) + return JS_FALSE; + } + *rval = BOOLEAN_TO_JSVAL(eq); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *copy; + + XML_METHOD_PROLOG; + copy = DeepCopy(cx, xml, NULL, 0); + if (!copy) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(copy->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list; + jsval name; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + list = Descendants(cx, xml, name); + if (!list) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + JSXMLQName *nameqn; + jsid funid; + JSBool ok; + JSXMLArrayCursor cursor; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameqn->object); + + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + if (funid) + return JS_TRUE; + + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_elements(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + XMLArrayCursorFinish(&cursor); + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && + MatchElemName(nameqn, kid)) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSObject *pobj; + JSProperty *prop; + + if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) + return JS_FALSE; + + name = argv[0]; + if (!HasProperty(cx, obj, name, &pobj, &prop)) + return JS_FALSE; + if (!prop) { + return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv, + rval); + } + DROP_PROPERTY(cx, pobj, prop); + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; +again: + switch (xml->xml_class) { + case JSXML_CLASS_ATTRIBUTE: + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + *rval = JSVAL_FALSE; + break; + case JSXML_CLASS_LIST: + if (xml->xml_kids.length == 0) { + *rval = JSVAL_TRUE; + } else if (xml->xml_kids.length == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (kid) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + obj = kidobj; + xml = (JSXML *) JS_GetPrivate(cx, obj); + goto again; + } + } + /* FALL THROUGH */ + default: + *rval = JSVAL_FALSE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + *rval = JSVAL_TRUE; + break; + } + } + break; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); + return JS_TRUE; +} + +typedef struct JSTempRootedNSArray { + JSTempValueRooter tvr; + JSXMLArray array; + jsval value; /* extra root for temporaries */ +} JSTempRootedNSArray; + +JS_STATIC_DLL_CALLBACK(void) +mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) +{ + JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; + + namespace_mark_vector(cx, + (JSXMLNamespace **)tmp->array.vector, + tmp->array.length); + XMLArrayCursorMark(cx, tmp->array.cursors); + if (JSVAL_IS_GCTHING(tmp->value)) + GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value"); +} + +static void +InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + XMLArrayInit(cx, &tmp->array, 0); + tmp->value = JSVAL_NULL; + JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr); +} + +static void +FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) +{ + JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array); + JS_POP_TEMP_ROOT(cx, &tmp->tvr); + XMLArrayFinish(cx, &tmp->array); +} + +/* + * Populate a new JS array with elements of JSTempRootedNSArray.array and + * place the result into rval. rval must point to a rooted location. + */ +static JSBool +TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) +{ + JSObject *arrayobj; + uint32 i, n; + JSXMLNamespace *ns; + JSObject *nsobj; + + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + for (i = 0, n = tmp->array.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace); + if (!ns) + continue; + nsobj = js_GetXMLNamespaceObject(cx, ns); + if (!nsobj) + return JS_FALSE; + tmp->value = OBJECT_TO_JSVAL(nsobj); + if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) +{ + uint32 length, i, j, n; + JSXMLNamespace *ns, *ns2; + + length = nsarray->length; + do { + if (xml->xml_class != JSXML_CLASS_ELEMENT) + continue; + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + + for (j = 0; j < length; j++) { + ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); + if (ns2 && + ((ns2->prefix && ns->prefix) + ? js_EqualStrings(ns2->prefix, ns->prefix) + : js_EqualStrings(ns2->uri, ns->uri))) { + break; + } + } + + if (j == length) { + if (!XMLARRAY_APPEND(cx, nsarray, ns)) + return JS_FALSE; + ++length; + } + } + } while ((xml = xml->parent) != NULL); + JS_ASSERT(length == nsarray->length); + + return JS_TRUE; +} + +static JSBool +xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSTempRootedNSArray namespaces; + JSBool ok; + + NON_LIST_XML_METHOD_PROLOG; + + InitTempNSArray(cx, &namespaces); + ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && + TempNSArrayToJSArray(cx, &namespaces, rval); + FinishTempNSArray(cx, &namespaces); + return ok; +} + +static JSBool +xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval arg; + uint32 i; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + arg = argv[0]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = 0; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + ++i; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + if (!Insert(cx, xml, i, argv[1])) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + jsval arg; + uint32 i; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + arg = argv[0]; + if (JSVAL_IS_NULL(arg)) { + kid = NULL; + i = xml->xml_kids.length; + } else { + if (!VALUE_IS_XML(cx, arg)) + return JS_TRUE; + kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); + i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); + if (i == XML_NOT_FOUND) + return JS_TRUE; + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + if (!Insert(cx, xml, i, argv[1])) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + + XML_METHOD_PROLOG; + if (xml->xml_class != JSXML_CLASS_LIST) { + *rval = JSVAL_ONE; + } else { + if (!js_NewNumberValue(cx, xml->xml_kids.length, rval)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + NON_LIST_XML_METHOD_PROLOG; + *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; + return JS_TRUE; +} + +static JSBool +xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + JSObject *nameobj; + + NON_LIST_XML_METHOD_PROLOG; + if (!xml->name) { + *rval = JSVAL_NULL; + } else { + nameobj = js_GetXMLQNameObject(cx, xml->name); + if (!nameobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(nameobj); + } + return JS_TRUE; +} + +static JSBool +xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *prefix; + JSTempRootedNSArray inScopeNSes; + JSBool ok; + jsuint i, length; + JSXMLNamespace *ns; + JSObject *nsobj; + + NON_LIST_XML_METHOD_PROLOG; + if (argc == 0 && !JSXML_HAS_NAME(xml)) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + if (argc == 0) { + prefix = NULL; + } else { + prefix = js_ValueToString(cx, argv[0]); + if (!prefix) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(prefix); /* local root */ + } + + /* After this point the control must flow through label out. */ + InitTempNSArray(cx, &inScopeNSes); + ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); + if (!ok) + goto out; + + if (!prefix) { + ns = GetNamespace(cx, xml->name, &inScopeNSes.array); + if (!ns) { + ok = JS_FALSE; + goto out; + } + } else { + ns = NULL; + for (i = 0, length = inScopeNSes.array.length; i < length; i++) { + ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); + if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix)) + break; + ns = NULL; + } + } + + if (!ns) { + *rval = JSVAL_VOID; + } else { + nsobj = js_GetXMLNamespaceObject(cx, ns); + if (!nsobj) { + ok = JS_FALSE; + goto out; + } + *rval = OBJECT_TO_JSVAL(nsobj); + } + + out: + FinishTempNSArray(cx, &inScopeNSes); + return JS_TRUE; +} + +static JSBool +xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *yml; + JSBool ok; + JSTempRootedNSArray ancestors, declared; + uint32 i, n; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (JSXML_HAS_VALUE(xml)) + return JS_TRUE; + + /* From here, control flow must goto out to finish these arrays. */ + ok = JS_TRUE; + InitTempNSArray(cx, &ancestors); + InitTempNSArray(cx, &declared); + yml = xml; + + while ((yml = yml->parent) != NULL) { + JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); + for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace); + if (ns && + !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); + if (!ok) + goto out; + } + } + } + + for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!ns) + continue; + if (!ns->declared) + continue; + if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { + ok = XMLARRAY_APPEND(cx, &declared.array, ns); + if (!ok) + goto out; + } + } + + ok = TempNSArrayToJSArray(cx, &declared, rval); + +out: + /* Finishing must be in reverse order of initialization to follow LIFO. */ + FinishTempNSArray(cx, &declared); + FinishTempNSArray(cx, &ancestors); + return ok; +} + +static const char js_attribute_str[] = "attribute"; +static const char js_text_str[] = "text"; + +/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */ +const char *js_xml_class_str[] = { + "list", + "element", + js_attribute_str, + "processing-instruction", + js_text_str, + "comment" +}; + +static JSBool +xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *str; + + NON_LIST_XML_METHOD_PROLOG; + str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id) +{ + jsval junk; + + if (xml->xml_class == JSXML_CLASS_LIST) + return DeleteProperty(cx, obj, id, &junk); + return DeleteByIndex(cx, xml, id, &junk); +} + +/* + * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace + * text between tags to be removed by normalize. + */ +static JSBool +IsXMLSpace(JSString *str) +{ + const jschar *cp, *end; + + cp = JSSTRING_CHARS(str); + end = cp + JSSTRING_LENGTH(str); + while (cp < end) { + if (!JS_ISXMLSPACE(*cp)) + return JS_FALSE; + ++cp; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid, *kid2; + uint32 i, n; + JSObject *kidobj; + JSString *str; + jsval junk; + + XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (!kid) + continue; + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk)) + return JS_FALSE; + } else if (kid->xml_class == JSXML_CLASS_TEXT) { + while (i + 1 < n && + (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && + kid2->xml_class == JSXML_CLASS_TEXT) { + str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); + if (!str) + return JS_FALSE; + if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1))) + return JS_FALSE; + n = xml->xml_kids.length; + kid->xml_value = str; + } + if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) { + if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i))) + return JS_FALSE; + n = xml->xml_kids.length; + --i; + } + } + } + + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *parent, *kid; + uint32 i, n; + JSObject *parentobj; + + XML_METHOD_PROLOG; + parent = xml->parent; + if (xml->xml_class == JSXML_CLASS_LIST) { + *rval = JSVAL_VOID; + n = xml->xml_kids.length; + if (n == 0) + return JS_TRUE; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!kid) + return JS_TRUE; + parent = kid->parent; + for (i = 1; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->parent != parent) + return JS_TRUE; + } + } + + if (!parent) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + + parentobj = js_GetXMLObject(cx, parent); + if (!parentobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(parentobj); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + JSXMLQName *nameqn; + jsid funid; + JSBool ok; + JSXMLArrayCursor cursor; + JSObject *kidobj; + uint32 i, n; + + XML_METHOD_PROLOG; + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + nameqn = ToXMLName(cx, name, &funid); + if (!nameqn) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameqn->object); + + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + if (funid) + return JS_TRUE; + + list->xml_targetprop = nameqn; + ok = JS_TRUE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_processingInstructions(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + XMLArrayCursorFinish(&cursor); + } else { + /* 13.4.4.28 Step 4. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && + (IS_STAR(nameqn->localName) || + js_EqualStrings(nameqn->localName, kid->name->localName))) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + return ok; +} + +static JSBool +xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + NON_LIST_XML_METHOD_PROLOG; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + return Insert(cx, xml, 0, argv[0]); +} + +/* XML and XMLList */ +static JSBool +xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + jsval name; + uint32 index; + + XML_METHOD_PROLOG; + name = argv[0]; + *rval = JSVAL_FALSE; + if (js_IdIsIndex(name, &index)) { + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.18. */ + *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); + } else { + /* 13.4.4.30. */ + *rval = BOOLEAN_TO_JSVAL(index == 0); + } + } + return JS_TRUE; +} + +static JSBool +namespace_full_match(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsa->prefix && nsb->prefix && + !js_EqualStrings(nsa->prefix, nsb->prefix)) { + return JS_FALSE; + } + return js_EqualStrings(nsa->uri, nsb->uri); +} + +static JSBool +xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) +{ + JSXMLNamespace *thisns, *attrns; + uint32 i, n; + JSXML *attr, *kid; + + thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); + JS_ASSERT(thisns); + if (thisns == ns) + return JS_TRUE; + + for (i = 0, n = xml->xml_attrs.length; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (!attr) + continue; + attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); + JS_ASSERT(attrns); + if (attrns == ns) + return JS_TRUE; + } + + i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); + if (i != XML_NOT_FOUND) + XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); + + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + if (!xml_removeNamespace_helper(cx, kid, ns)) + return JS_FALSE; + } + } + return JS_TRUE; +} + +static JSBool +xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSObject *nsobj; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); + if (!nsobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nsobj); + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + + /* NOTE: remove ns from each ancestor if not used by that ancestor. */ + return xml_removeNamespace_helper(cx, xml, ns); +} + +static JSBool +xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *vxml, *kid; + jsval name, value, id, junk; + uint32 index; + JSObject *nameobj; + JSXMLQName *nameqn; + + NON_LIST_XML_METHOD_PROLOG; + *rval = OBJECT_TO_JSVAL(obj); + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + value = argv[1]; + vxml = VALUE_IS_XML(cx, value) + ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value)) + : NULL; + if (!vxml) { + if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1])) + return JS_FALSE; + value = argv[1]; + } else { + vxml = DeepCopy(cx, vxml, NULL, 0); + if (!vxml) + return JS_FALSE; + value = argv[1] = OBJECT_TO_JSVAL(vxml->object); + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + + name = argv[0]; + if (js_IdIsIndex(name, &index)) + return Replace(cx, xml, name, value); + + /* Call function QName per spec, not ToXMLName, to avoid attribute names. */ + nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name); + if (!nameobj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(nameobj); + nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); + + id = JSVAL_VOID; + index = xml->xml_kids.length; + while (index != 0) { + --index; + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (kid && MatchElemName(nameqn, kid)) { + if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk)) + return JS_FALSE; + if (!IndexToIdVal(cx, index, &id)) + return JS_FALSE; + } + } + if (JSVAL_IS_VOID(id)) + return JS_TRUE; + return Replace(cx, xml, id, value); +} + +static JSBool +xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + if (!StartNonListXMLMethod(cx, &obj, argv)) + return JS_FALSE; + + if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), + &argv[0])) { + return JS_FALSE; + } + + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + jsval name; + JSXMLQName *nameqn; + JSString *namestr; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + name = argv[0]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { + nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)); + namestr = nameqn->localName; + } else { + if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0])) + return JS_FALSE; + name = argv[0]; + namestr = JSVAL_TO_STRING(name); + } + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name->localName = namestr; + return JS_TRUE; +} + +static JSBool +xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *nsowner; + jsval name; + JSXMLQName *nameqn; + JSObject *nameobj; + JSXMLArray *nsarray; + uint32 i, n; + JSXMLNamespace *ns; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + name = argv[0]; + if (!JSVAL_IS_PRIMITIVE(name) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && + !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name))) + ->uri) { + name = argv[0] = STRING_TO_JSVAL(nameqn->localName); + } + + nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); + if (!nameobj) + return JS_FALSE; + nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); + + /* ECMA-357 13.4.4.35 Step 4. */ + if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + nameqn->uri = cx->runtime->emptyString; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml) + return JS_FALSE; + xml->name = nameqn; + + /* + * Erratum: nothing in 13.4.4.35 talks about making the name match the + * in-scope namespaces, either by finding an in-scope namespace with a + * matching uri and setting the new name's prefix to that namespace's + * prefix, or by extending the in-scope namespaces for xml (which are in + * xml->parent if xml is an attribute or a PI). + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + + if (nameqn->prefix) { + /* + * The name being set has a prefix, which originally came from some + * namespace object (which may be the null namespace, where both the + * prefix and uri are the empty string). We must go through a full + * GetNamespace in case that namespace is in-scope in nsowner. + * + * If we find such an in-scope namespace, we return true right away, + * in this block. Otherwise, we fall through to the final return of + * AddInScopeNamespace(cx, nsowner, ns). + */ + ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); + if (!ns) + return JS_FALSE; + + /* XXXbe have to test membership to see whether GetNamespace added */ + if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) + return JS_TRUE; + } else { + /* + * At this point, we know nameqn->prefix is null, so nameqn->uri can't + * be the empty string (the null namespace always uses the empty string + * for both prefix and uri). + * + * This means we must inline GetNamespace and specialize it to match + * uri only, never prefix. If we find a namespace with nameqn's uri + * already in nsowner->xml_namespaces, then all that we need do is set + * nameqn->prefix to that namespace's prefix. + * + * If no such namespace exists, we can create one without going through + * the constructor, because we know nameqn->uri is non-empty (so prefix + * does not need to be converted from null to empty by QName). + */ + JS_ASSERT(!IS_EMPTY(nameqn->uri)); + + nsarray = &nsowner->xml_namespaces; + for (i = 0, n = nsarray->length; i < n; i++) { + ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); + if (ns && js_EqualStrings(ns->uri, nameqn->uri)) { + nameqn->prefix = ns->prefix; + return JS_TRUE; + } + } + + ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE); + if (!ns) + return JS_FALSE; + } + + return AddInScopeNamespace(cx, nsowner, ns); +} + +static JSBool +xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *nsowner; + JSObject *nsobj, *qnobj; + JSXMLNamespace *ns; + jsval qnargv[2]; + + NON_LIST_XML_METHOD_PROLOG; + if (!JSXML_HAS_NAME(xml)) + return JS_TRUE; + + xml = CHECK_COPY_ON_WRITE(cx, xml, obj); + if (!xml || !js_GetXMLQNameObject(cx, xml->name)) + return JS_FALSE; + + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv); + if (!nsobj) + return JS_FALSE; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + ns->declared = JS_TRUE; + + qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj); + qnargv[1] = OBJECT_TO_JSVAL(xml->name->object); + qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); + if (!qnobj) + return JS_FALSE; + + xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj); + + /* + * Erratum: the spec fails to update the governing in-scope namespaces. + * See the erratum noted in xml_setName, above. + */ + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + nsowner = xml; + } else { + if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + nsowner = xml->parent; + } + return AddInScopeNamespace(cx, nsowner, ns); +} + +/* XML and XMLList */ +static JSBool +xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + uint32 i, n; + JSBool ok; + JSObject *kidobj; + jsval v; + + XML_METHOD_PROLOG; + list = xml_list_helper(cx, xml, rval); + if (!list) + return JS_FALSE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + ok = JS_TRUE; + for (i = 0, n = xml->xml_kids.length; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = js_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + if (kidobj) { + ok = xml_text(cx, kidobj, argc, argv, &v); + } else { + ok = JS_FALSE; + v = JSVAL_NULL; + } + js_LeaveLocalRootScopeWithResult(cx, v); + if (!ok) + return JS_FALSE; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) + return JS_FALSE; + } + } + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid && kid->xml_class == JSXML_CLASS_TEXT) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + } + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = ToXMLString(cx, OBJECT_TO_JSVAL(obj)); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSString * +xml_toString_helper(JSContext *cx, JSXML *xml) +{ + JSString *str, *kidstr; + JSXML *kid; + JSXMLArrayCursor cursor; + + if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || + xml->xml_class == JSXML_CLASS_TEXT) { + return xml->xml_value; + } + + if (!HasSimpleContent(xml)) + return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); + + str = cx->runtime->emptyString; + js_EnterLocalRootScope(cx); + XMLArrayCursorInit(&cursor, &xml->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (kid->xml_class != JSXML_CLASS_COMMENT && + kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { + kidstr = xml_toString_helper(cx, kid); + if (!kidstr) { + str = NULL; + break; + } + str = js_ConcatStrings(cx, str, kidstr); + if (!str) + break; + } + } + XMLArrayCursorFinish(&cursor); + js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); + return str; +} + +static JSBool +xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSString *str; + + XML_METHOD_PROLOG; + str = xml_toString_helper(cx, xml); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec xml_methods[] = { + {"addNamespace", xml_addNamespace, 1,0,0}, + {"appendChild", xml_appendChild, 1,0,0}, + {js_attribute_str, xml_attribute, 1,0,0}, + {"attributes", xml_attributes, 0,0,0}, + {"child", xml_child, 1,0,0}, + {"childIndex", xml_childIndex, 0,0,0}, + {"children", xml_children, 0,0,0}, + {"comments", xml_comments, 0,0,0}, + {"contains", xml_contains, 1,0,0}, + {"copy", xml_copy, 0,0,0}, + {"descendants", xml_descendants, 1,0,0}, + {"elements", xml_elements, 1,0,0}, + {"hasOwnProperty", xml_hasOwnProperty, 1,0,0}, + {"hasComplexContent", xml_hasComplexContent, 1,0,0}, + {"hasSimpleContent", xml_hasSimpleContent, 1,0,0}, + {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,0}, + {"insertChildAfter", xml_insertChildAfter, 2,0,0}, + {"insertChildBefore", xml_insertChildBefore, 2,0,0}, + {js_length_str, xml_length, 0,0,0}, + {js_localName_str, xml_localName, 0,0,0}, + {js_name_str, xml_name, 0,0,0}, + {js_namespace_str, xml_namespace, 1,0,0}, + {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0}, + {"nodeKind", xml_nodeKind, 0,0,0}, + {"normalize", xml_normalize, 0,0,0}, + {js_xml_parent_str, xml_parent, 0,0,0}, + {"processingInstructions",xml_processingInstructions,1,0,0}, + {"prependChild", xml_prependChild, 1,0,0}, + {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,0}, + {"removeNamespace", xml_removeNamespace, 1,0,0}, + {"replace", xml_replace, 2,0,0}, + {"setChildren", xml_setChildren, 1,0,0}, + {"setLocalName", xml_setLocalName, 1,0,0}, + {"setName", xml_setName, 1,0,0}, + {"setNamespace", xml_setNamespace, 1,0,0}, + {js_text_str, xml_text, 0,0,0}, + {js_toString_str, xml_toString, 0,0,0}, + {js_toXMLString_str, xml_toXMLString, 0,0,0}, + {js_toSource_str, xml_toXMLString, 0,0,0}, + {js_valueOf_str, xml_valueOf, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) +{ + int i; + const char *name; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + } + + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +SetDefaultXMLSettings(JSContext *cx, JSObject *obj) +{ + int i; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + v = JSVAL_TRUE; + if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) + return JS_FALSE; + } + v = INT_TO_JSVAL(2); + return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); +} + +static JSBool +xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return CopyXMLSettings(cx, obj, settings); +} + +static JSBool +xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval v; + JSBool ok; + JSObject *settings; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + cx->xmlSettingFlags = 0; + ok = SetDefaultXMLSettings(cx, obj); + } else { + if (JSVAL_IS_PRIMITIVE(v)) + return JS_TRUE; + settings = JSVAL_TO_OBJECT(v); + cx->xmlSettingFlags = 0; + ok = CopyXMLSettings(cx, settings, obj); + } + if (ok) + cx->xmlSettingFlags |= XSF_CACHE_VALID; + return ok; +} + +static JSBool +xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return SetDefaultXMLSettings(cx, settings); +} + +static JSFunctionSpec xml_static_methods[] = { + {"settings", xml_settings, 0,0,0}, + {"setSettings", xml_setSettings, 1,0,0}, + {"defaultSettings", xml_defaultSettings, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSXML *xml, *copy; + JSObject *xobj, *vobj; + JSClass *clasp; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + xobj = ToXML(cx, v); + if (!xobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(xobj); + xml = (JSXML *) JS_GetPrivate(cx, xobj); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, vobj); + if (clasp == &js_XMLClass || + (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { + /* No need to lock obj, it's newly constructed and thread local. */ + copy = DeepCopy(cx, xml, obj, 0); + if (!copy) + return JS_FALSE; + JS_ASSERT(copy->object == obj); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool +XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSObject *vobj, *listobj; + JSXML *xml, *list; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJECT_IS_XML(cx, vobj)) { + xml = (JSXML *) JS_GetPrivate(cx, vobj); + if (xml->xml_class == JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return JS_FALSE; + return JS_TRUE; + } + } + } + + /* Toggle on XML support since the script has explicitly requested it. */ + listobj = ToXMLList(cx, v); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + return JS_TRUE; +} + +#define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar)) +#define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLVar)) +#define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) + +static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { + JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ + JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */ + JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */ + JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */ +}; + +#ifdef DEBUG_notme +JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); +uint32 xml_serial; +#endif + +JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + + xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]); + if (!xml) + return NULL; + + xml->object = NULL; + xml->domnode = NULL; + xml->parent = NULL; + xml->name = NULL; + xml->xml_class = xml_class; + xml->xml_flags = 0; + if (JSXML_CLASS_HAS_VALUE(xml_class)) { + xml->xml_value = cx->runtime->emptyString; + } else { + XMLArrayInit(cx, &xml->xml_kids, 0); + if (xml_class == JSXML_CLASS_LIST) { + xml->xml_target = NULL; + xml->xml_targetprop = NULL; + } else { + XMLArrayInit(cx, &xml->xml_namespaces, 0); + XMLArrayInit(cx, &xml->xml_attrs, 0); + } + } + +#ifdef DEBUG_notme + JS_APPEND_LINK(&xml->links, &xml_leaks); + xml->serial = xml_serial++; +#endif + METER(xml_stats.xml); + METER(xml_stats.livexml); + return xml; +} + +void +js_MarkXML(JSContext *cx, JSXML *xml) +{ + GC_MARK(cx, xml->object, "object"); + GC_MARK(cx, xml->name, "name"); + GC_MARK(cx, xml->parent, "xml_parent"); + + if (JSXML_HAS_VALUE(xml)) { + GC_MARK(cx, xml->xml_value, "value"); + return; + } + + xml_mark_vector(cx, + (JSXML **) xml->xml_kids.vector, + xml->xml_kids.length); + XMLArrayCursorMark(cx, xml->xml_kids.cursors); + XMLArrayTrim(&xml->xml_kids); + + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_target) + GC_MARK(cx, xml->xml_target, "target"); + if (xml->xml_targetprop) + GC_MARK(cx, xml->xml_targetprop, "targetprop"); + } else { + namespace_mark_vector(cx, + (JSXMLNamespace **) xml->xml_namespaces.vector, + xml->xml_namespaces.length); + XMLArrayCursorMark(cx, xml->xml_namespaces.cursors); + XMLArrayTrim(&xml->xml_namespaces); + + xml_mark_vector(cx, + (JSXML **) xml->xml_attrs.vector, + xml->xml_attrs.length); + XMLArrayCursorMark(cx, xml->xml_attrs.cursors); + XMLArrayTrim(&xml->xml_attrs); + } +} + +void +js_FinalizeXML(JSContext *cx, JSXML *xml) +{ + if (JSXML_HAS_KIDS(xml)) { + XMLArrayFinish(cx, &xml->xml_kids); + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + XMLArrayFinish(cx, &xml->xml_namespaces); + XMLArrayFinish(cx, &xml->xml_attrs); + } + } + +#ifdef DEBUG_notme + JS_REMOVE_LINK(&xml->links); +#endif + + UNMETER(xml_stats.livexml); +} + +JSObject * +js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn) +{ + jsval nsval; + JSXMLNamespace *ns; + JSXMLArray nsarray; + JSXML *xml; + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + + if (!XMLArrayInit(cx, &nsarray, 1)) + return NULL; + + XMLARRAY_APPEND(cx, &nsarray, ns); + xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT); + XMLArrayFinish(cx, &nsarray); + if (!xml) + return NULL; + + return xml->object; +} + +JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + JSObject *obj; + JSTempValueRooter tvr; + + xml = js_NewXML(cx, xml_class); + if (!xml) + return NULL; + JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr); + obj = js_GetXMLObject(cx, xml); + JS_POP_TEMP_ROOT(cx, &tvr); + return obj; +} + +static JSObject * +NewXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, xml)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + return NULL; + } + METER(xml_stats.xmlobj); + METER(xml_stats.livexmlobj); + return obj; +} + +JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = xml->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == xml); + return obj; + } + + /* + * A JSXML cannot be shared among threads unless it has an object. + * A JSXML cannot be given an object unless: + * (a) it has no parent; or + * (b) its parent has no object (therefore is thread-private); or + * (c) its parent's object is locked. + * + * Once given an object, a JSXML is immutable. + */ + JS_ASSERT(!xml->parent || + !xml->parent->object || + JS_IS_OBJ_LOCKED(cx, xml->parent->object)); + + obj = NewXMLObject(cx, xml); + if (!obj) + return NULL; + xml->object = obj; + return obj; +} + +JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, + namespace_props, namespace_methods, NULL, NULL); +} + +JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj) +{ + jsval v; + + if (!js_GetAnyName(cx, &v)) + return NULL; + return JSVAL_TO_OBJECT(v); +} + +JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *pobj, *ctor; + JSFunction *fun; + JSXML *xml; + JSProperty *prop; + JSScopeProperty *sprop; + jsval cval, argv[1], junk; + + /* Define the isXMLName function. */ + if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) + return NULL; + + /* Define the XML class constructor and prototype. */ + proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, + NULL, xml_methods, + xml_static_props, xml_static_methods); + if (!proto) + return NULL; + + xml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!xml || !JS_SetPrivate(cx, proto, xml)) + return NULL; + xml->object = proto; + METER(xml_stats.xmlobj); + METER(xml_stats.livexmlobj); + + /* + * Prepare to set default settings on the XML constructor we just made. + * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, + * which is xml_getProperty, which creates a new XMLList every time! We + * must instead call js_LookupProperty directly. + */ + if (!js_LookupProperty(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), + &pobj, &prop)) { + return NULL; + } + JS_ASSERT(prop); + sprop = (JSScopeProperty *) prop; + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); + OBJ_DROP_PROPERTY(cx, pobj, prop); + JS_ASSERT(VALUE_IS_FUNCTION(cx, cval)); + + /* Set default settings. */ + ctor = JSVAL_TO_OBJECT(cval); + argv[0] = JSVAL_VOID; + if (!xml_setSettings(cx, ctor, 1, argv, &junk)) + return NULL; + + /* Define the XMLList function and give it the same prototype as XML. */ + fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); + if (!fun) + return NULL; + if (!js_SetClassPrototype(cx, fun->object, proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + return NULL; + } + return proto; +} + +JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj) +{ + if (!js_InitNamespaceClass(cx, obj)) + return NULL; + if (!js_InitQNameClass(cx, obj)) + return NULL; + if (!js_InitAttributeNameClass(cx, obj)) + return NULL; + if (!js_InitAnyNameClass(cx, obj)) + return NULL; + return js_InitXMLClass(cx, obj); +} + +JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSAtom *atom; + JSString *prefix, *uri; + + /* An invalid URI, for internal use only, guaranteed not to collide. */ + static const char anti_uri[] = "@mozilla.org/js/function"; + + /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ + rt = cx->runtime; + obj = rt->functionNamespaceObject; + if (!obj) { + JS_LOCK_GC(rt); + obj = rt->functionNamespaceObject; + if (!obj) { + JS_UNLOCK_GC(rt); + atom = js_Atomize(cx, js_function_str, 8, 0); + JS_ASSERT(atom); + prefix = ATOM_TO_STRING(atom); + + /* + * Note that any race to atomize anti_uri here is resolved by + * the atom table code, such that at most one atom for anti_uri + * is created. We store in rt->atomState.lazy unconditionally, + * since we are guaranteed to overwrite either null or the same + * atom pointer. + */ + atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); + if (!atom) + return JS_FALSE; + rt->atomState.lazy.functionNamespaceURIAtom = atom; + + uri = ATOM_TO_STRING(atom); + obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE); + if (!obj) + return JS_FALSE; + + /* + * Avoid entraining any in-scope Object.prototype. The loss of + * Namespace.prototype is not detectable, as there is no way to + * refer to this instance in scripts. When used to qualify method + * names, its prefix and uri references are copied to the QName. + */ + OBJ_SET_PROTO(cx, obj, NULL); + OBJ_SET_PARENT(cx, obj, NULL); + + JS_LOCK_GC(rt); + if (!rt->functionNamespaceObject) + rt->functionNamespaceObject = obj; + else + obj = rt->functionNamespaceObject; + } + JS_UNLOCK_GC(rt); + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +/* + * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- + * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, + * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a + * lightweight function activation). There's no requirement that fp->varobj + * lie directly on fp->scopeChain, although it should be reachable using the + * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). + * + * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it + * creates a default namespace via 'new Namespace()'. In contrast, Set uses + * its v argument as the uri of a new Namespace, with "" as the prefix. See + * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, + * the default XML namespace will be set to ("", n.uri). So the uri string + * is really the only usefully stored value of the default namespace. + */ +JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) +{ + JSStackFrame *fp; + JSObject *nsobj, *obj, *tmp; + jsval v; + + fp = cx->fp; + nsobj = fp->xmlNamespace; + if (nsobj) { + *vp = OBJECT_TO_JSVAL(nsobj); + return JS_TRUE; + } + + obj = NULL; + for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { + obj = tmp; + if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + *vp = v; + return JS_TRUE; + } + } + + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + if (obj && + !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v, + JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } + fp->xmlNamespace = nsobj; + *vp = v; + return JS_TRUE; +} + +JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v) +{ + jsval argv[2]; + JSObject *nsobj, *varobj; + JSStackFrame *fp; + + argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); + argv[1] = v; + nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, + 2, argv); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + + fp = cx->fp; + varobj = fp->varobj; + if (varobj) { + if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v, + JS_PropertyStub, JS_PropertyStub, + JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } + } else { + JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags)); + } + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + return JS_TRUE; +} + +JSBool +js_ToAttributeName(JSContext *cx, jsval *vp) +{ + JSXMLQName *qn; + + qn = ToAttributeName(cx, *vp); + if (!qn) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(qn->object); + return JS_TRUE; +} + +JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str) +{ + return EscapeAttributeValue(cx, NULL, str); +} + +JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) +{ + size_t len, len2, newlen; + jschar *chars; + + if (JSSTRING_IS_DEPENDENT(str) || + !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) { + str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), + 0); + if (!str) + return NULL; + } + + len = str->length; + len2 = JSSTRING_LENGTH(str2); + newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; + chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar)); + if (!chars) + return NULL; + + /* + * Reallocating str (because we know it has no other references) requires + * purging any deflated string cached for it. + */ + js_PurgeDeflatedStringCache(cx->runtime, str); + + str->chars = chars; + str->length = newlen; + chars += len; + if (isName) { + *chars++ = ' '; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + } else { + *chars++ = '='; + *chars++ = '"'; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + *chars++ = '"'; + } + *chars = 0; + return str; +} + +JSString * +js_EscapeElementValue(JSContext *cx, JSString *str) +{ + return EscapeElementValue(cx, NULL, str); +} + +JSString * +js_ValueToXMLString(JSContext *cx, jsval v) +{ + return ToXMLString(cx, v); +} + +static JSBool +anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = ATOM_KEY(cx->runtime->atomState.starAtom); + return JS_TRUE; +} + +JSBool +js_GetAnyName(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSObject *obj; + JSXMLQName *qn; + JSBool ok; + + /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ + rt = cx->runtime; + obj = rt->anynameObject; + if (!obj) { + JS_LOCK_GC(rt); + obj = rt->anynameObject; + if (!obj) { + JS_UNLOCK_GC(rt); + + /* + * Protect multiple newborns created below, in the do-while(0) + * loop used to ensure that we leave this local root scope. + */ + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + do { + qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, + ATOM_TO_STRING(rt->atomState.starAtom)); + if (!qn) { + ok = JS_FALSE; + break; + } + + obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->weakRoots.newborn[GCX_OBJECT] = NULL; + ok = JS_FALSE; + break; + } + qn->object = obj; + METER(xml_stats.qnameobj); + METER(xml_stats.liveqnameobj); + + /* + * Avoid entraining any Object.prototype found via cx's scope + * chain or global object. This loses the default toString, + * but no big deal: we want to customize toString anyway for + * clearer diagnostics. + */ + if (!JS_DefineFunction(cx, obj, js_toString_str, + anyname_toString, 0, 0)) { + ok = JS_FALSE; + break; + } + OBJ_SET_PROTO(cx, obj, NULL); + JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); + } while (0); + + js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj)); + if (!ok) + return JS_FALSE; + + JS_LOCK_GC(rt); + if (!rt->anynameObject) + rt->anynameObject = obj; + else + obj = rt->anynameObject; + } + JS_UNLOCK_GC(rt); + } + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) +{ + JSXMLQName *qn; + jsid funid, id; + JSObject *obj, *pobj, *lastobj; + JSProperty *prop; + const char *printable; + + qn = ToXMLName(cx, name, &funid); + if (!qn) + return JS_FALSE; + id = OBJECT_TO_JSID(qn->object); + + obj = cx->fp->scopeChain; + do { + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + + /* + * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML + * object to carry scope chain linkage in js_FilterXMLList. + */ + pobj = OBJ_THIS_OBJECT(cx, obj); + if (OBJECT_IS_XML(cx, pobj)) { + *objp = pobj; + *namep = ID_TO_VALUE(id); + return JS_TRUE; + } + } + + lastobj = obj; + } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); + + printable = js_ValueToPrintableString(cx, name); + if (printable) { + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UNDEFINED_XML_NAME, printable); + } + return JS_FALSE; +} + +JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + return GetProperty(cx, obj, name, vp); +} + +JSBool +js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSObject *target; + JSXML *xml; + JSTempValueRooter tvr; + JSBool ok; + + JS_ASSERT(OBJECT_IS_XML(cx, obj)); + + /* After this point, control must flow through label out: to exit. */ + JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); + + /* + * See comments before xml_lookupProperty about the need for the proto + * chain lookup. + */ + target = obj; + for (;;) { + ok = js_GetProperty(cx, target, id, vp); + if (!ok) + goto out; + if (VALUE_IS_FUNCTION(cx, *vp)) { + ok = JS_TRUE; + goto out; + } + target = OBJ_GET_PROTO(cx, target); + if (target == NULL) + break; + tvr.u.object = target; + } + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (HasSimpleContent(xml)) { + /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ + ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), + &tvr.u.object); + if (!ok) + goto out; + JS_ASSERT(tvr.u.object); + ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp); + } + + out: + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; +} + +JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + return PutProperty(cx, obj, name, vp); +} + +static JSXML * +GetPrivate(JSContext *cx, JSObject *obj, const char *method) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_METHOD, + js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); + } + return xml; +} + +JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list; + + xml = GetPrivate(cx, obj, "descendants internal method"); + if (!xml) + return JS_FALSE; + + list = Descendants(cx, xml, id); + if (!list) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) +{ + JSXML *list; + uint32 n; + jsval junk; + + list = (JSXML *) JS_GetPrivate(cx, listobj); + for (n = list->xml_kids.length; n != 0; --n) { + if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk)) + return JS_FALSE; + } + return JS_TRUE; +} + +JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) +{ + JSBool ok, match; + JSStackFrame *fp; + uint32 flags; + JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; + JSXML *xml, *list, *result, *kid; + JSXMLArrayCursor cursor; + + ok = js_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + /* All control flow after this point must exit via label out or bad. */ + *vp = JSVAL_NULL; + fp = cx->fp; + flags = fp->flags; + fp->flags = flags | JSFRAME_FILTERING; + scobj = js_GetScopeChain(cx, fp); + withobj = NULL; + if (!scobj) + goto bad; + xml = GetPrivate(cx, obj, "filtering predicate operator"); + if (!xml) + goto bad; + + if (xml->xml_class == JSXML_CLASS_LIST) { + list = xml; + } else { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + goto bad; + list = (JSXML *) JS_GetPrivate(cx, listobj); + ok = Append(cx, list, xml); + if (!ok) + goto out; + } + + resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!resobj) + goto bad; + result = (JSXML *) JS_GetPrivate(cx, resobj); + + /* Hoist the scope chain update out of the loop over kids. */ + withobj = js_NewWithObject(cx, NULL, scobj, -1); + if (!withobj) + goto bad; + fp->scopeChain = withobj; + + XMLArrayCursorInit(&cursor, &list->xml_kids); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + break; + OBJ_SET_PROTO(cx, withobj, kidobj); + ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match); + if (ok && match) + ok = Append(cx, result, kid); + if (!ok) + break; + } + XMLArrayCursorFinish(&cursor); + if (!ok) + goto out; + if (kid) + goto bad; + + *vp = OBJECT_TO_JSVAL(resobj); + +out: + fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS); + if (withobj) { + fp->scopeChain = scobj; + JS_SetPrivate(cx, withobj, NULL); + } + js_LeaveLocalRootScopeWithResult(cx, *vp); + return ok; +bad: + ok = JS_FALSE; + goto out; +} + +JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v) +{ + return ToXML(cx, v); +} + +JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v) +{ + return ToXMLList(cx, v); +} + +JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj) +{ + uintN flags; + JSXML *xml; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (flags & (XSF_IGNORE_COMMENTS | + XSF_IGNORE_PROCESSING_INSTRUCTIONS | + XSF_IGNORE_WHITESPACE)) { + xml = DeepCopy(cx, xml, NULL, flags); + if (!xml) + return NULL; + return xml->object; + } + return NewXMLObject(cx, xml); +} + +JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value) +{ + uintN flags; + JSObject *obj; + JSXML *xml; + JSXMLQName *qn; + + if (!GetXMLSettingFlags(cx, &flags)) + return NULL; + + if ((xml_class == JSXML_CLASS_COMMENT && + (flags & XSF_IGNORE_COMMENTS)) || + (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && + (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { + return js_NewXMLObject(cx, JSXML_CLASS_TEXT); + } + + obj = js_NewXMLObject(cx, xml_class); + if (!obj) + return NULL; + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (name) { + qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name); + if (!qn) + return NULL; + xml->name = qn; + } + xml->xml_value = value; + return obj; +} + +JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str) +{ + return MakeXMLCDATAString(cx, NULL, str); +} + +JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str) +{ + return MakeXMLCommentString(cx, NULL, str); +} + +JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) +{ + return MakeXMLPIString(cx, NULL, name, str); +} + +#endif /* JS_HAS_XML_SUPPORT */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxml.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxml.h new file mode 100644 index 0000000..71e591a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/jsxml.h @@ -0,0 +1,332 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxml_h___ +#define jsxml_h___ + +#include "jsstddef.h" +#include "jspubtd.h" + +extern const char js_AnyName_str[]; +extern const char js_AttributeName_str[]; +extern const char js_isXMLName_str[]; +extern const char js_XMLList_str[]; + +extern const char js_amp_entity_str[]; +extern const char js_gt_entity_str[]; +extern const char js_lt_entity_str[]; +extern const char js_quot_entity_str[]; + +struct JSXMLNamespace { + JSObject *object; + JSString *prefix; + JSString *uri; + JSBool declared; /* true if declared in its XML tag */ +}; + +extern JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared); + +extern void +js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns); + +extern void +js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns); + +extern JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, + JSBool declared); + +extern JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns); + +struct JSXMLQName { + JSObject *object; + JSString *uri; + JSString *prefix; + JSString *localName; +}; + +extern JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern void +js_MarkXMLQName(JSContext *cx, JSXMLQName *qn); + +extern void +js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval); + +typedef JSBool +(* JS_DLL_CALLBACK JSIdentityOp)(const void *a, const void *b); + +struct JSXMLArray { + uint32 length; + uint32 capacity; + void **vector; + JSXMLArrayCursor *cursors; +}; + +#define JSXML_PRESET_CAPACITY JS_BIT(31) +#define JSXML_CAPACITY_MASK JS_BITMASK(31) +#define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) + +struct JSXMLArrayCursor { + JSXMLArray *array; + uint32 index; + JSXMLArrayCursor *next; + JSXMLArrayCursor **prevp; + void *root; +}; + +/* + * NB: don't reorder this enum without changing all array initializers that + * depend on it in jsxml.c. + */ +typedef enum JSXMLClass { + JSXML_CLASS_LIST, + JSXML_CLASS_ELEMENT, + JSXML_CLASS_ATTRIBUTE, + JSXML_CLASS_PROCESSING_INSTRUCTION, + JSXML_CLASS_TEXT, + JSXML_CLASS_COMMENT, + JSXML_CLASS_LIMIT +} JSXMLClass; + +#define JSXML_CLASS_HAS_KIDS(class_) ((class_) < JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_VALUE(class_) ((class_) >= JSXML_CLASS_ATTRIBUTE) +#define JSXML_CLASS_HAS_NAME(class_) \ + ((uintN)((class_) - JSXML_CLASS_ELEMENT) <= \ + (uintN)(JSXML_CLASS_PROCESSING_INSTRUCTION - JSXML_CLASS_ELEMENT)) + +#ifdef DEBUG_notme +#include "jsclist.h" +#endif + +struct JSXML { +#ifdef DEBUG_notme + JSCList links; + uint32 serial; +#endif + JSObject *object; + void *domnode; /* DOM node if mapped info item */ + JSXML *parent; + JSXMLQName *name; + uint16 xml_class; /* discriminates u, below */ + uint16 xml_flags; /* flags, see below */ + union { + struct JSXMLListVar { + JSXMLArray kids; /* NB: must come first */ + JSXML *target; + JSXMLQName *targetprop; + } list; + struct JSXMLVar { + JSXMLArray kids; /* NB: must come first */ + JSXMLArray namespaces; + JSXMLArray attrs; + } elem; + JSString *value; + } u; + + /* Don't add anything after u -- see js_NewXML for why. */ +}; + +/* union member shorthands */ +#define xml_kids u.list.kids +#define xml_target u.list.target +#define xml_targetprop u.list.targetprop +#define xml_namespaces u.elem.namespaces +#define xml_attrs u.elem.attrs +#define xml_value u.value + +/* xml_flags values */ +#define XMLF_WHITESPACE_TEXT 0x1 + +/* xml_class-testing macros */ +#define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) +#define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) +#define JSXML_HAS_NAME(xml) JSXML_CLASS_HAS_NAME((xml)->xml_class) +#define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ + ? (xml)->xml_kids.length \ + : 0) + +extern JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class); + +extern void +js_MarkXML(JSContext *cx, JSXML *xml); + +extern void +js_FinalizeXML(JSContext *cx, JSXML *xml); + +extern JSObject * +js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn); + +extern JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); + +extern JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml); + +extern JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps; +extern JS_FRIEND_DATA(JSClass) js_XMLClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass; +extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass; +extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass; +extern JS_FRIEND_DATA(JSClass) js_AnyNameClass; + +/* + * Macros to test whether an object or a value is of type "xml" (per typeof). + * NB: jsapi.h must be included before any call to VALUE_IS_XML. + */ +#define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps.base) +#define VALUE_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ + OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) + +extern JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAttributeNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitAnyNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj); + +extern JSBool +js_GetFunctionNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v); + +/* + * Return true if v is a XML QName object, or if it converts to a string that + * contains a valid XML qualified name (one containing no :), false otherwise. + * NB: This function is an infallible predicate, it hides exceptions. + */ +extern JSBool +js_IsXMLName(JSContext *cx, jsval v); + +extern JSBool +js_ToAttributeName(JSContext *cx, jsval *vp); + +extern JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str); + +extern JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, + JSString *str2); + +extern JSString * +js_EscapeElementValue(JSContext *cx, JSString *str); + +extern JSString * +js_ValueToXMLString(JSContext *cx, jsval v); + +extern JSBool +js_GetAnyName(JSContext *cx, jsval *vp); + +extern JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep); + +extern JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); + +extern JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); + +extern JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp); + +extern JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v); + +extern JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v); + +extern JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj); + +extern JSObject * +js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, + JSString *value); + +extern JSString * +js_MakeXMLCDATAString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLCommentString(JSContext *cx, JSString *str); + +extern JSString * +js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str); + +#endif /* jsxml_h___ */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/perfect.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/perfect.js new file mode 100644 index 0000000..aeca121 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/perfect.js @@ -0,0 +1,39 @@ +// Some simple testing of new, eval and some string stuff. + +// constructor -- expression array initialization +function ExprArray(n,v) +{ + // Initializes n values to v coerced to a string. + for (var i = 0; i < n; i++) { + this[i] = "" + v; + } +} + + +// Print the perfect numbers up to n and the sum expression for n's divisors. +function perfect(n) +{ + print("The perfect numbers up to " + n + " are:"); + + // We build sumOfDivisors[i] to hold a string expression for + // the sum of the divisors of i, excluding i itself. + var sumOfDivisors = new ExprArray(n+1,1); + for (var divisor = 2; divisor <= n; divisor++) { + for (var j = divisor + divisor; j <= n; j += divisor) { + sumOfDivisors[j] += " + " + divisor; + } + // At this point everything up to 'divisor' has its sumOfDivisors + // expression calculated, so we can determine whether it's perfect + // already by evaluating. + if (eval(sumOfDivisors[divisor]) == divisor) { + print("" + divisor + " = " + sumOfDivisors[divisor]); + } + } + print("That's all."); +} + + +print("\nA number is 'perfect' if it is equal to the sum of its") +print("divisors (excluding itself).\n"); +perfect(500); + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/plify_jsdhash.sed b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/plify_jsdhash.sed new file mode 100644 index 0000000..eff4901 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/plify_jsdhash.sed @@ -0,0 +1,33 @@ +/ * Double hashing implementation./a\ + * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! +/ * Double hashing, a la Knuth 6./a\ + * GENERATED BY js/src/plify_jsdhash.sed -- DO NOT EDIT!!! +s/jsdhash_h___/pldhash_h___/ +s/jsdhash\.bigdump/pldhash.bigdump/ +s/jstypes\.h/nscore.h/ +s/jsbit\.h/prbit.h/ +s/jsdhash\.h/pldhash.h/ +s/jsdhash\.c/pldhash.c/ +s/jsdhash:/pldhash:/ +s/jsutil\.h/nsDebug.h/ +s/JS_DHASH/PL_DHASH/g +s/JS_DHash/PL_DHash/g +s/JSDHash/PLDHash/g +s/JSHash/PLHash/g +s/uint32 /PRUint32/g +s/\([^U]\)int32 /\1PRInt32/g +s/uint16 /PRUint16/g +s/\([^U]\)int16 /\1PRInt16/g +s/uint32/PRUint32/g +s/\([^U]\)int32/\1PRInt32/g +s/uint16/PRUint16/g +s/\([^U]\)int16/\1PRInt16/g +s/JSBool/PRBool/g +s/extern JS_PUBLIC_API(\([^()]*\))/NS_COM_GLUE \1/ +s/JS_PUBLIC_API(\([^()]*\))/\1/ +s/JS_DLL_CALLBACK/PR_CALLBACK/ +s/JS_STATIC_DLL_CALLBACK/PR_STATIC_CALLBACK/ +s/JS_NewDHashTable/PL_NewDHashTable/ +s/JS_ASSERT(0)/NS_NOTREACHED("0")/ +s/\( *\)JS_ASSERT(\(.*\));/\1NS_ASSERTION(\2,\n\1 "\2");/ +s/JS_/PR_/g diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/prmjtime.c b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/prmjtime.c new file mode 100644 index 0000000..6e08423 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/prmjtime.c @@ -0,0 +1,440 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * PR time code. + */ +#include "jsstddef.h" +#ifdef SOLARIS +#define _REENTRANT 1 +#endif +#include +#include +#include "jstypes.h" +#include "jsutil.h" + +#include "jsprf.h" +#include "prmjtime.h" + +#define PRMJ_DO_MILLISECONDS 1 + +#ifdef XP_OS2 +#include +#endif +#ifdef XP_WIN +#include +#include +#endif + +#if defined(XP_UNIX) || defined(XP_BEOS) + +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ +extern int gettimeofday(struct timeval *tv); +#endif + +#include + +#endif /* XP_UNIX */ + +#define IS_LEAP(year) \ + (year != 0 && ((((year & 0x3) == 0) && \ + ((year - ((year/100) * 100)) != 0)) || \ + (year - ((year/400) * 400)) == 0)) + +#define PRMJ_HOUR_SECONDS 3600L +#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS) +#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L) +#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */ +/* function prototypes */ +static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm); +/* + * get the difference in seconds between this time zone and UTC (GMT) + */ +JSInt32 +PRMJ_LocalGMTDifference() +{ +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) + struct tm ltime; + + /* get the difference between this time zone and GMT */ + memset((char *)<ime,0,sizeof(ltime)); + ltime.tm_mday = 2; + ltime.tm_year = 70; +#ifdef SUNOS4 + ltime.tm_zone = 0; + ltime.tm_gmtoff = 0; + return timelocal(<ime) - (24 * 3600); +#else + return mktime(<ime) - (24L * 3600L); +#endif +#endif +} + +/* Constants for GMT offset from 1970 */ +#define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */ +#define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */ + +#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */ +#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */ + +/* Convert from base time to extended time */ +static JSInt64 +PRMJ_ToExtendedTime(JSInt32 base_time) +{ + JSInt64 exttime; + JSInt64 g1970GMTMicroSeconds; + JSInt64 low; + JSInt32 diff; + JSInt64 tmp; + JSInt64 tmp1; + + diff = PRMJ_LocalGMTDifference(); + JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC); + JSLL_I2L(tmp1,diff); + JSLL_MUL(tmp,tmp,tmp1); + + JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI); + JSLL_UI2L(low,G1970GMTMICROLOW); +#ifndef JS_HAVE_LONG_LONG + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); +#else + JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32); +#endif + JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low); + + JSLL_I2L(exttime,base_time); + JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds); + JSLL_SUB(exttime,exttime,tmp); + return exttime; +} + +JSInt64 +PRMJ_Now(void) +{ +#ifdef XP_OS2 + JSInt64 s, us, ms2us, s2us; + struct timeb b; +#endif +#ifdef XP_WIN + JSInt64 s, us, + win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000), + ten = JSLL_INIT(0, 10); + FILETIME time, midnight; +#endif +#if defined(XP_UNIX) || defined(XP_BEOS) + struct timeval tv; + JSInt64 s, us, s2us; +#endif /* XP_UNIX */ + +#ifdef XP_OS2 + ftime(&b); + JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC); + JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); + JSLL_UI2L(s, b.time); + JSLL_UI2L(us, b.millitm); + JSLL_MUL(us, us, ms2us); + JSLL_MUL(s, s, s2us); + JSLL_ADD(s, s, us); + return s; +#endif +#ifdef XP_WIN + /* The windows epoch is around 1600. The unix epoch is around 1970. + win2un is the difference (in windows time units which are 10 times + more precise than the JS time unit) */ + GetSystemTimeAsFileTime(&time); + /* Win9x gets confused at midnight + http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423 + So if the low part (precision <8mins) is 0 then we get the time + again. */ + if (!time.dwLowDateTime) { + GetSystemTimeAsFileTime(&midnight); + time.dwHighDateTime = midnight.dwHighDateTime; + } + JSLL_UI2L(s, time.dwHighDateTime); + JSLL_UI2L(us, time.dwLowDateTime); + JSLL_SHL(s, s, 32); + JSLL_ADD(s, s, us); + JSLL_SUB(s, s, win2un); + JSLL_DIV(s, s, ten); + return s; +#endif + +#if defined(XP_UNIX) || defined(XP_BEOS) +#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ + gettimeofday(&tv); +#else + gettimeofday(&tv, 0); +#endif /* _SVID_GETTOD */ + JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); + JSLL_UI2L(s, tv.tv_sec); + JSLL_UI2L(us, tv.tv_usec); + JSLL_MUL(s, s, s2us); + JSLL_ADD(s, s, us); + return s; +#endif /* XP_UNIX */ +} + +/* Get the DST timezone offset for the time passed in */ +JSInt64 +PRMJ_DSTOffset(JSInt64 local_time) +{ + JSInt64 us2s; + time_t local; + JSInt32 diff; + JSInt64 maxtimet; + struct tm tm; + PRMJTime prtm; +#ifndef HAVE_LOCALTIME_R + struct tm *ptm; +#endif + + + JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); + JSLL_DIV(local_time, local_time, us2s); + + /* get the maximum of time_t value */ + JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET); + + if(JSLL_CMP(local_time,>,maxtimet)){ + JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET); + } else if(!JSLL_GE_ZERO(local_time)){ + /*go ahead a day to make localtime work (does not work with 0) */ + JSLL_UI2L(local_time,PRMJ_DAY_SECONDS); + } + JSLL_L2UI(local,local_time); + PRMJ_basetime(local_time,&prtm); +#ifndef HAVE_LOCALTIME_R + ptm = localtime(&local); + if(!ptm){ + return JSLL_ZERO; + } + tm = *ptm; +#else + localtime_r(&local,&tm); /* get dst information */ +#endif + + diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) + + ((tm.tm_min - prtm.tm_min) * 60); + + if(diff < 0){ + diff += PRMJ_DAY_SECONDS; + } + + JSLL_UI2L(local_time,diff); + + JSLL_MUL(local_time,local_time,us2s); + + return(local_time); +} + +/* Format a time value into a buffer. Same semantics as strftime() */ +size_t +PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) +{ +#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) + struct tm a; + + /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int + * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets + * confused and dumps core. NSPR20 prtime.c attempts to fill these in by + * calling mktime on the partially filled struct, but this doesn't seem to + * work as well; the result string has "can't get timezone" for ECMA-valid + * years. Might still make sense to use this, but find the range of years + * for which valid tz information exists, and map (per ECMA hint) from the + * given year into that range. + + * N.B. This hasn't been tested with anything that actually _uses_ + * tm_gmtoff; zero might be the wrong thing to set it to if you really need + * to format a time. This fix is for jsdate.c, which only uses + * JS_FormatTime to get a string representing the time zone. */ + memset(&a, 0, sizeof(struct tm)); + + a.tm_sec = prtm->tm_sec; + a.tm_min = prtm->tm_min; + a.tm_hour = prtm->tm_hour; + a.tm_mday = prtm->tm_mday; + a.tm_mon = prtm->tm_mon; + a.tm_wday = prtm->tm_wday; + a.tm_year = prtm->tm_year - 1900; + a.tm_yday = prtm->tm_yday; + a.tm_isdst = prtm->tm_isdst; + + /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff + * are null. This doesn't quite work, though - the timezone is off by + * tzoff + dst. (And mktime seems to return -1 for the exact dst + * changeover time.) + + */ + +#if defined(SUNOS4) + if (mktime(&a) == -1) { + /* Seems to fail whenever the requested date is outside of the 32-bit + * UNIX epoch. We could proceed at this point (setting a.tm_zone to + * "") but then strftime returns a string with a 2-digit field of + * garbage for the year. So we return 0 and hope jsdate.c + * will fall back on toString. + */ + return 0; + } +#endif + + return strftime(buf, buflen, fmt, &a); +#endif +} + +/* table for number of days in a month */ +static int mtab[] = { + /* jan, feb,mar,apr,may,jun */ + 31,28,31,30,31,30, + /* july,aug,sep,oct,nov,dec */ + 31,31,30,31,30,31 +}; + +/* + * basic time calculation functionality for localtime and gmtime + * setups up prtm argument with correct values based upon input number + * of seconds. + */ +static void +PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm) +{ + /* convert tsecs back to year,month,day,hour,secs */ + JSInt32 year = 0; + JSInt32 month = 0; + JSInt32 yday = 0; + JSInt32 mday = 0; + JSInt32 wday = 6; /* start on a Sunday */ + JSInt32 days = 0; + JSInt32 seconds = 0; + JSInt32 minutes = 0; + JSInt32 hours = 0; + JSInt32 isleap = 0; + JSInt64 result; + JSInt64 result1; + JSInt64 result2; + JSInt64 base; + + JSLL_UI2L(result,0); + JSLL_UI2L(result1,0); + JSLL_UI2L(result2,0); + + /* get the base time via UTC */ + base = PRMJ_ToExtendedTime(0); + JSLL_UI2L(result, PRMJ_USEC_PER_SEC); + JSLL_DIV(base,base,result); + JSLL_ADD(tsecs,tsecs,base); + + JSLL_UI2L(result, PRMJ_YEAR_SECONDS); + JSLL_UI2L(result1,PRMJ_DAY_SECONDS); + JSLL_ADD(result2,result,result1); + + /* get the year */ + while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) { + /* subtract a year from tsecs */ + JSLL_SUB(tsecs,tsecs,result); + days += 365; + /* is it a leap year ? */ + if(IS_LEAP(year)){ + JSLL_SUB(tsecs,tsecs,result1); + days++; + } + year++; + isleap = IS_LEAP(year); + } + + JSLL_UI2L(result1,PRMJ_DAY_SECONDS); + + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(mday,result); + + /* let's find the month */ + while(((month == 1 && isleap) ? + (mday >= mtab[month] + 1) : + (mday >= mtab[month]))){ + yday += mtab[month]; + days += mtab[month]; + + mday -= mtab[month]; + + /* it's a Feb, check if this is a leap year */ + if(month == 1 && isleap != 0){ + yday++; + days++; + mday--; + } + month++; + } + + /* now adjust tsecs */ + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + mday++; /* day of month always start with 1 */ + days += mday; + wday = (days + wday) % 7; + + yday += mday; + + /* get the hours */ + JSLL_UI2L(result1,PRMJ_HOUR_SECONDS); + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(hours,result); + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + /* get minutes */ + JSLL_UI2L(result1,60); + JSLL_DIV(result,tsecs,result1); + JSLL_L2I(minutes,result); + JSLL_MUL(result,result,result1); + JSLL_SUB(tsecs,tsecs,result); + + JSLL_L2I(seconds,tsecs); + + prtm->tm_usec = 0L; + prtm->tm_sec = (JSInt8)seconds; + prtm->tm_min = (JSInt8)minutes; + prtm->tm_hour = (JSInt8)hours; + prtm->tm_mday = (JSInt8)mday; + prtm->tm_mon = (JSInt8)month; + prtm->tm_wday = (JSInt8)wday; + prtm->tm_year = (JSInt16)year; + prtm->tm_yday = (JSInt16)yday; +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/prmjtime.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/prmjtime.h new file mode 100644 index 0000000..b74fe84 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/prmjtime.h @@ -0,0 +1,95 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef prmjtime_h___ +#define prmjtime_h___ +/* + * PR date stuff for mocha and java. Placed here temporarily not to break + * Navigator and localize changes to mocha. + */ +#include +#include "jslong.h" +#ifdef MOZILLA_CLIENT +#include "jscompat.h" +#endif + +JS_BEGIN_EXTERN_C + +typedef struct PRMJTime PRMJTime; + +/* + * Broken down form of 64 bit time value. + */ +struct PRMJTime { + JSInt32 tm_usec; /* microseconds of second (0-999999) */ + JSInt8 tm_sec; /* seconds of minute (0-59) */ + JSInt8 tm_min; /* minutes of hour (0-59) */ + JSInt8 tm_hour; /* hour of day (0-23) */ + JSInt8 tm_mday; /* day of month (1-31) */ + JSInt8 tm_mon; /* month of year (0-11) */ + JSInt8 tm_wday; /* 0=sunday, 1=monday, ... */ + JSInt16 tm_year; /* absolute year, AD */ + JSInt16 tm_yday; /* day of year (0 to 365) */ + JSInt8 tm_isdst; /* non-zero if DST in effect */ +}; + +/* Some handy constants */ +#define PRMJ_USEC_PER_SEC 1000000L +#define PRMJ_USEC_PER_MSEC 1000L + +/* Return the current local time in micro-seconds */ +extern JSInt64 +PRMJ_Now(void); + +/* get the difference between this time zone and gmt timezone in seconds */ +extern JSInt32 +PRMJ_LocalGMTDifference(void); + +/* Format a time value into a buffer. Same semantics as strftime() */ +extern size_t +PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *tm); + +/* Get the DST offset for the local time passed in */ +extern JSInt64 +PRMJ_DSTOffset(JSInt64 local_time); + +JS_END_EXTERN_C + +#endif /* prmjtime_h___ */ + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/resource.h b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/resource.h new file mode 100644 index 0000000..9301810 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by js3240.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/win32.order b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/win32.order new file mode 100644 index 0000000..cf4e8c4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/javascriptlint/spidermonkey/src/win32.order @@ -0,0 +1,391 @@ +js_MarkGCThing ; 5893956 +JS_GetPrivate ; 2090130 +JS_HashTableRawLookup ; 1709984 +js_Mark ; 1547496 +js_GetToken ; 1406677 +js_UngetToken ; 1154416 +js_MarkAtom ; 992874 +js_MatchToken ; 980277 +js_CompareStrings ; 662772 +js_Lock ; 628184 +js_Unlock ; 628184 +js_AtomizeString ; 611102 +js_HashString ; 611102 +js_DropScopeProperty ; 546476 +JS_malloc ; 484350 +js_Atomize ; 464433 +js_InflateStringToBuffer ; 460739 +js_HoldScopeProperty ; 442612 +JS_free ; 382991 +js_MarkScript ; 376942 +js_HashId ; 365238 +JS_CompareValues ; 352366 +js_IdToValue ; 337594 +JS_GetClass ; 325296 +js_LookupProperty ; 324680 +js_GetAtom ; 244669 +js_DropProperty ; 223217 +JS_GetParent ; 209680 +js_LiveContext ; 205767 +js_PeekToken ; 200646 +js_GetSlotThreadSafe ; 198839 +JS_GetStringChars ; 190862 +JS_HashTableRawAdd ; 179156 +js_FoldConstants ; 162626 +js_EmitTree ; 145634 +JS_EnumerateStub ; 140640 +js_NewSrcNote ; 136983 +js_GetProperty ; 135639 +js_NewScopeProperty ; 135057 +js_MutateScope ; 135057 +js_GetMutableScope ; 135057 +js_AllocSlot ; 132401 +JS_GetRuntime ; 127316 +JS_FrameIterator ; 121963 +JS_GetFrameFunctionObject ; 120567 +js_AllocGCThing ; 119828 +js_DestroyScopeProperty ; 115989 +js_Emit3 ; 109135 +js_AtomizeChars ; 108038 +JS_HashTableLookup ; 107154 +JS_InstanceOf ; 103905 +js_DefineProperty ; 99514 +js_strncpy ; 88276 +js_PeekTokenSameLine ; 87197 +js_HoldObjectMap ; 79084 +js_DropObjectMap ; 77824 +js_NewObject ; 72421 +js_ValueToString ; 72143 +js_GetClassPrototype ; 66235 +js_UnlockRuntime ; 64699 +js_LockRuntime ; 64699 +js_ContextIterator ; 64586 +JS_ClearWatchPointsForObject ; 64155 +js_FinalizeObject ; 63925 +js_IndexAtom ; 63789 +JS_SetPrivate ; 63702 +JS_GetGlobalObject ; 63546 +js_Emit1 ; 63012 +JS_ContextIterator ; 57847 +JS_GetInstancePrivate ; 57817 +JS_HashTableRawRemove ; 57057 +js_AllocRawStack ; 54181 +js_Invoke ; 53568 +js_FindProperty ; 53150 +JS_GetFrameScript ; 51395 +js_LinkFunctionObject ; 50651 +js_SetSrcNoteOffset ; 47735 +js_InWithStatement ; 47346 +js_NewFunction ; 47074 +js_NewSrcNote2 ; 46165 +JS_HashTableAdd ; 45503 +JS_HashTableRemove ; 45213 +js_InCatchBlock ; 42198 +js_AddRootRT ; 40587 +js_AddRoot ; 40587 +js_SetProperty ; 40558 +JS_AddNamedRoot ; 40462 +js_RemoveRoot ; 40384 +JS_RemoveRootRT ; 38129 +js_NewString ; 37471 +js_DefineFunction ; 36629 +JS_GetContextThread ; 36498 +JS_LookupProperty ; 35137 +JS_ValueToString ; 34072 +JS_realloc ; 33776 +JS_DefineFunction ; 33268 +JS_SetErrorReporter ; 32851 +js_FinalizeString ; 30311 +js_FinalizeStringRT ; 30311 +JS_ArenaAllocate ; 30099 +JS_BeginRequest ; 29323 +JS_EndRequest ; 29323 +JS_GetContextPrivate ; 29189 +JS_CompactArenaPool ; 28874 +js_ValueToStringAtom ; 27934 +JS_ValueToId ; 26517 +js_ValueToBoolean ; 25908 +JS_InternString ; 25467 +js_PopStatement ; 24364 +js_PushStatement ; 24364 +js_NewStringCopyN ; 23911 +js_FlushPropertyCacheByProp ; 23883 +js_GetStringBytes ; 23421 +JS_ArenaRelease ; 23267 +JS_GetStringBytes ; 23106 +js_FreeStack ; 22399 +js_AllocStack ; 22399 +JS_SetProperty ; 21240 +js_InitObjectMap ; 19991 +js_NewScope ; 19991 +js_strlen ; 19070 +JS_GetScriptPrincipals ; 18063 +js_SrcNoteLength ; 17369 +js_DestroyObjectMap ; 17198 +js_DestroyScope ; 17198 +JS_GetStringLength ; 16306 +js_PopStatementCG ; 15418 +JS_GetFrameAnnotation ; 14949 +js_FreeRawStack ; 14032 +js_Interpret ; 14032 +js_TransferScopeLock ; 13899 +JS_ResolveStandardClass ; 13645 +JS_ResumeRequest ; 12837 +JS_SuspendRequest ; 12837 +JS_GetProperty ; 12488 +JS_NewObject ; 11660 +js_AllocTryNotes ; 11418 +js_NewNumberValue ; 10859 +js_InternalInvoke ; 10051 +js_NewDouble ; 9936 +js_SetJumpOffset ; 9886 +js_SkipWhiteSpace ; 9299 +js_NewDoubleValue ; 7474 +JS_GetPendingException ; 7404 +js_NewObjectMap ; 7236 +JS_ClearPendingException ; 7092 +JS_strtod ; 7053 +js_strtod ; 7053 +js_InflateString ; 7004 +JS_GetFunctionName ; 6808 +JS_NewHashTable ; 6794 +JS_NewFunction ; 6575 +js_FreeSlot ; 6476 +js_LockScope ; 6332 +JS_HashTableEnumerateEntries ; 6285 +js_GetLengthProperty ; 6162 +js_LockObj ; 6149 +JS_NewUCStringCopyN ; 5994 +JS_NewNumberValue ; 5904 +js_NewStringCopyZ ; 5809 +JS_NewUCStringCopyZ ; 5809 +js_DeflateString ; 5612 +js_ValueToNumber ; 5456 +JS_SetOptions ; 5322 +js_NewScript ; 4941 +js_InitCodeGenerator ; 4810 +js_FinishTakingSrcNotes ; 4810 +js_NewScriptFromParams ; 4810 +js_InitAtomMap ; 4810 +js_FinishTakingTryNotes ; 4810 +js_NewScriptFromCG ; 4810 +js_FinishCodeGenerator ; 4810 +JS_strdup ; 4534 +JS_HashTableDestroy ; 4119 +js_CheckRedeclaration ; 3965 +JS_DefineFunctions ; 3808 +js_EmitFunctionBody ; 3739 +js_TryMethod ; 3685 +js_DefaultValue ; 3610 +js_CloneFunctionObject ; 3577 +JS_InitClass ; 3546 +js_SetClassPrototype ; 3377 +JS_GetPrototype ; 3268 +JS_DefineProperties ; 3115 +js_FindVariable ; 3093 +js_DestroyScript ; 3041 +JS_ClearScriptTraps ; 3041 +js_FreeAtomMap ; 3041 +JS_NewStringCopyZ ; 2953 +js_AtomizeObject ; 2709 +JS_ValueToBoolean ; 2643 +js_SetLengthProperty ; 2637 +JS_GetOptions ; 2593 +js_ValueToObject ; 2522 +js_ValueToNonNullObject ; 2510 +js_StringToObject ; 2482 +JS_SetElement ; 2448 +js_NumberToString ; 2407 +JS_TypeOfValue ; 2275 +js_NewBufferTokenStream ; 2253 +js_NewTokenStream ; 2253 +js_CloseTokenStream ; 2253 +JS_RemoveRoot ; 2148 +JS_NewDouble ; 2129 +JS_vsnprintf ; 1937 +JS_snprintf ; 1937 +JS_CallFunctionValue ; 1844 +JS_DHashVoidPtrKeyStub ; 1840 +JS_DHashTableOperate ; 1840 +js_SetProtoOrParent ; 1758 +js_DoubleToInteger ; 1729 +JS_SetVersion ; 1531 +js_ValueToFunction ; 1476 +JS_SetPrototype ; 1408 +JS_CeilingLog2 ; 1317 +js_Execute ; 1199 +js_CompileFunctionBody ; 1182 +JS_CompileUCFunctionForPrincipals ; 1182 +js_GetSrcNoteOffset ; 1139 +JS_DHashMatchEntryStub ; 1094 +JS_VersionToString ; 1090 +JS_CompileUCScriptForPrincipals ; 1071 +js_CompileTokenStream ; 1071 +js_CurrentThreadId ; 1058 +JS_IdToValue ; 1046 +js_ConstructObject ; 974 +JS_DestroyScript ; 967 +js_PCToLineNumber ; 967 +JS_DefineProperty ; 930 +JS_GetScriptFilename ; 924 +JS_GetFramePC ; 899 +JS_EvaluateUCScriptForPrincipals ; 892 +JS_PCToLineNumber ; 848 +JS_StringToVersion ; 761 +js_ExecuteRegExp ; 755 +JS_MaybeGC ; 717 +JS_ValueToNumber ; 698 +JS_GetVersion ; 698 +JS_AliasProperty ; 693 +js_AtomizeValue ; 664 +js_BooleanToString ; 664 +js_SetSlotThreadSafe ; 596 +JS_DHashClearEntryStub ; 584 +JS_DHashTableRawRemove ; 584 +JS_DefineObject ; 557 +js_PutCallObject ; 516 +js_GetCallObject ; 516 +js_strchr ; 511 +JS_DefineUCProperty ; 480 +JS_dtostr ; 475 +JS_ValueToInt32 ; 464 +js_ValueToInt32 ; 464 +JS_FinishArenaPool ; 453 +js_NewTryNote ; 441 +js_strtointeger ; 437 +JS_vsmprintf ; 428 +JS_DHashTableInit ; 423 +JS_DHashAllocTable ; 423 +JS_DHashGetStubOps ; 423 +JS_NewDHashTable ; 423 +JS_DHashTableDestroy ; 423 +JS_DHashFreeTable ; 423 +JS_DHashTableFinish ; 423 +js_EmitBreak ; 412 +js_GetAttributes ; 412 +JS_DefineConstDoubles ; 407 +JS_ArenaGrow ; 374 +js_AtomizeInt ; 372 +JS_SetParent ; 345 +JS_CloneFunctionObject ; 343 +JS_IsNativeFrame ; 343 +JS_ReportErrorNumber ; 340 +js_ErrorToException ; 340 +js_ReportErrorNumberVA ; 340 +js_GetErrorMessage ; 340 +js_ExpandErrorArguments ; 340 +js_ReportUncaughtException ; 315 +JS_IsExceptionPending ; 315 +js_ReportErrorAgain ; 315 +js_ErrorFromException ; 315 +JS_LookupUCProperty ; 307 +JS_InitArenaPool ; 293 +PRMJ_Now ; 262 +DllMain@12 ; 235 +JS_ExecuteScript ; 232 +JS_GetFrameFunction ; 226 +PRMJ_LocalGMTDifference ; 175 +JS_GetConstructor ; 175 +JS_SetGlobalObject ; 164 +js_LockGCThing ; 155 +js_NewRegExpObject ; 152 +js_NewRegExp ; 152 +js_InitObjectClass ; 131 +js_InitFunctionClass ; 131 +js_EmitN ; 128 +JS_ArenaFinish ; 124 +js_GC ; 124 +js_SweepAtomState ; 124 +js_MarkAtomState ; 124 +JS_ArenaRealloc ; 124 +js_ForceGC ; 124 +js_FlushPropertyCache ; 122 +js_InitNumberClass ; 114 +JS_smprintf ; 112 +js_DoubleToECMAInt32 ; 112 +js_ValueToECMAInt32 ; 111 +JS_ValueToECMAInt32 ; 111 +JS_SetContextPrivate ; 109 +PRMJ_DSTOffset ; 108 +js_Clear ; 105 +JS_ClearScope ; 105 +JS_NewScriptObject ; 104 +JS_smprintf_free ; 104 +JS_ConvertValue ; 99 +js_GetSrcNote ; 98 +JS_ValueToECMAUint32 ; 93 +js_ValueToECMAUint32 ; 93 +js_printf ; 93 +js_DoubleToECMAUint32 ; 93 +js_DestroyRegExp ; 89 +js_UnlockGCThing ; 89 +js_TryValueOf ; 87 +js_NewSrcNote3 ; 86 +JS_ConvertStub ; 81 +JS_SetPendingException ; 80 +js_InitStringClass ; 79 +JS_GC ; 78 +js_InitArrayClass ; 74 +js_InitDateClass ; 67 +JS_NewContext ; 64 +JS_AddArgumentFormatter ; 64 +js_InitContextForLocking ; 64 +js_NewContext ; 64 +JS_SetBranchCallback ; 64 +JS_ClearRegExpStatics ; 64 +js_InitRegExpStatics ; 64 +js_InitCallClass ; 63 +js_InitRegExpClass ; 61 +js_Enumerate ; 58 +JS_DestroyContext ; 46 +js_DestroyContext ; 46 +js_FreeRegExpStatics ; 46 +js_InitScanner ; 39 +js_NewPrinter ; 36 +js_DestroyPrinter ; 36 +js_GetPrinterOutput ; 36 +JS_FreeArenaPool ; 36 +js_DecompileCode ; 34 +js_EmitContinue ; 33 +js_CheckAccess ; 30 +js_DecompileValueGenerator ; 28 +js_InitMathClass ; 27 +js_InitExceptionClasses ; 25 +js_NewArrayObject ; 24 +js_InitArgumentsClass ; 21 +js_puts ; 20 +js_InitBooleanClass ; 19 +JS_InitStandardClasses ; 19 +js_InitScriptClass ; 19 +js_obj_toString ; 15 +js_GetArgsValue ; 14 +js_GetArgsObject ; 14 +js_AtomizeDouble ; 12 +JS_DestroyIdArray ; 11 +js_NewIdArray ; 11 +JS_GetElement ; 11 +JS_EvaluateScript ; 9 +JS_EvaluateUCScript ; 9 +JS_DecompileFunction ; 8 +js_DecompileFunction ; 8 +JS_NewString ; 8 +js_SetStringBytes ; 8 +JS_GetArrayLength ; 7 +JS_NewArrayObject ; 7 +JS_IsArrayObject ; 7 +JS_ValueToObject ; 7 +JS_DefineElement ; 6 +js_DecompileScript ; 6 +JS_PushArguments ; 4 +JS_PopArguments ; 4 +JS_PushArgumentsVA ; 4 +js_PutArgsObject ; 2 +JS_SetGCCallbackRT ; 2 +JS_Init ; 1 +js_SetupLocks ; 1 +js_InitRuntimeNumberState ; 1 +js_InitRuntimeStringState ; 1 +js_InitLock ; 1 +js_InitGC ; 1 +js_InitAtomState ; 1 +js_InitStringGlobals ; 1 diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/.npmignore new file mode 100644 index 0000000..ceeb05b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/.npmignore @@ -0,0 +1 @@ +/tmp diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/README.md new file mode 100644 index 0000000..e231399 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/README.md @@ -0,0 +1,99 @@ +# jsstyle + +## Overview + +`jsstyle` is a style checker for JavaScript coding style. This tool is derived +from the cstyle tool used to check for the style used in the Solaris kernel, +sometimes known as "Bill Joy Normal Form". This tool is a *little bit* +configurable. However it strives to enforces a single coding style based on +that cstyle. See "Configuration Options" below. + +The original cstyle tool can be found here: + + +The document describing C Style is available here: + + +Examples of conditions checked by this tool include: + +* Strings must be quoted with single quotes. +* Blocks must be indented with tabs, not spaces. +* Continuation lines must be indented with 4 spaces. +* Keywords (for, if, function, etc.) must be followed with a space. +* One line cannot contain multiple keywords. +* Relational operators must be surrounded with spaces. +* There must be no spaces between tabs, nor tabs between spaces. +* Lines must not end with whitespace. +* Multi-line block comments must start and end with a blank line. +* Return expressions must be parenthesized. + + +## Status + +No known bugs. No new features planned. + + +## Usage + + jsstyle [OPTIONS] file1.js [file2.js ...] + + +## Configuration Options + +Configuration options may be specified in a file (one option per line) +with the "-f PATH" switch, or on the command line with the "-o +OPTION1,OPTION2" switch. + +As stated about, `jsstyle` is opinionated and intends to stay that way. +That said, this author was arm twisted under duress to allow the following +configurability. + + doxygen Allow doxygen-style block comments `/** /*!`. + splint Allow splint-style lint comments `/*@ ... @*/`. + This is legacy. Does anyone use this? + indent= An integer number of spaces for indentation, or + 'tab' for tab indentation (the default). + strict-indent Boolean option, set to 1 to force indents of spaces + to be a multiple of indent parameter. + literal-string-quote 'single' (the default) or 'double'. Specifies + the preferred quote character for literal strings. + unparenthesized-return Boolean option, set to 0 to disable the + "unparenthesized return expression" check. + blank-after-start-comment + Boolean option, set to 0 to disable the + "missing blank after start comment" check. + continuation-at-front Boolean option, set to 1 to force continations + to be at the beginning rather than end of line. + leading-right-paren-ok Boolean option, set to 1 to allow ) to start a + line. + + whitespace-after-left-paren-ok + Boolean option, allow whitespace after a ( + character. + + leading-comma-ok Boolean option to allow lines to begin with commas + (preceded by whitespace). + + uncuddled-else-ok Boolean option to allow for an else block to begin + on a new line. + +## "JSSTYLED"-comments + +When you want `jsstyle` to ignore a line, you can use this: + + /* JSSTYLED */ + ignore = this + line; + +Or for a block: + + /* BEGIN JSSTYLED */ + var here + , be + , some = funky + , style + /* END JSSTYLED */ + + +## License + +CDDL diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/jsstyle b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/jsstyle new file mode 100755 index 0000000..9df33c9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/deps/jsstyle/jsstyle @@ -0,0 +1,953 @@ +#!/usr/bin/env perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# +# jsstyle - check for some common stylistic errors. +# +# jsstyle is a sort of "lint" for Javascript coding style. This tool is +# derived from the cstyle tool, used to check for the style used in the +# Solaris kernel, sometimes known as "Bill Joy Normal Form". +# +# There's a lot this can't check for, like proper indentation of code +# blocks. There's also a lot more this could check for. +# +# A note to the non perl literate: +# +# perl regular expressions are pretty much like egrep +# regular expressions, with the following special symbols +# +# \s any space character +# \S any non-space character +# \w any "word" character [a-zA-Z0-9_] +# \W any non-word character +# \d a digit [0-9] +# \D a non-digit +# \b word boundary (between \w and \W) +# \B non-word boundary +# + +require 5.0; +use IO::File; +use Getopt::Std; +use strict; + +my $usage = +"Usage: jsstyle [-h?vcC] [-t ] [-f ] [-o ] file ... + +Check your JavaScript file for style. +See for details on config options. +Report bugs to . + +Options: + -h print this help and exit + -v verbose + + -c check continuation indentation inside functions + -t specify tab width for line length calculation + -C don't check anything in header block comments + + -f PATH + path to a jsstyle config file + -o OPTION1,OPTION2 + set config options, e.g. '-o doxygen,indent=2' + +"; + +my %opts; + +if (!getopts("ch?o:t:f:vC", \%opts)) { + print $usage; + exit 2; +} + +if (defined($opts{'h'}) || defined($opts{'?'})) { + print $usage; + exit; +} + +my $check_continuation = $opts{'c'}; +my $verbose = $opts{'v'}; +my $ignore_hdr_comment = $opts{'C'}; +my $tab_width = $opts{'t'}; + +# By default, tabs are 8 characters wide +if (! defined($opts{'t'})) { + $tab_width = 8; +} + + +# Load config +my %config = ( + indent => "tab", + doxygen => 0, # doxygen comments: /** ... */ + splint => 0, # splint comments. Needed? + "unparenthesized-return" => 1, + "literal-string-quote" => "single", # 'single' or 'double' + "blank-after-start-comment" => 1, + "continuation-at-front" => 0, + "leading-right-paren-ok" => 0, + "strict-indent" => 0 +); +sub add_config_var ($$) { + my ($scope, $str) = @_; + + if ($str !~ /^([\w-]+)(?:\s*=\s*(.*?))?$/) { + die "$scope: invalid option: '$str'"; + } + my $name = $1; + my $value = ($2 eq '' ? 1 : $2); + #print "scope: '$scope', str: '$str', name: '$name', value: '$value'\n"; + + # Validate config var. + if ($name eq "indent") { + # A number of spaces or "tab". + if ($value !~ /^\d+$/ && $value ne "tab") { + die "$scope: invalid '$name': must be a number (of ". + "spaces) or 'tab'"; + } + } elsif ($name eq "doxygen" || # boolean vars + $name eq "splint" || + $name eq "unparenthesized-return" || + $name eq "continuation-at-front" || + $name eq "leading-right-paren-ok" || + $name eq "leading-comma-ok" || + $name eq "uncuddled-else-ok" || + $name eq "whitespace-after-left-paren-ok" || + $name eq "strict-indent" || + $name eq "blank-after-start-comment") { + + if ($value != 1 && $value != 0) { + die "$scope: invalid '$name': don't give a value"; + } + } elsif ($name eq "literal-string-quote") { + if ($value !~ /single|double/) { + die "$scope: invalid '$name': must be 'single' ". + "or 'double'"; + } + } else { + die "$scope: unknown config var: $name"; + } + $config{$name} = $value; +} + +if (defined($opts{'f'})) { + my $path = $opts{'f'}; + my $fh = new IO::File $path, "r"; + if (!defined($fh)) { + die "cannot open config path '$path'"; + } + my $line = 0; + while (<$fh>) { + $line++; + s/^\s*//; # drop leading space + s/\s*$//; # drop trailing space + next if ! $_; # skip empty line + next if /^#/; # skip comments + add_config_var "$path:$line", $_; + } +} + +if (defined($opts{'o'})) { + for my $x (split /,/, $opts{'o'}) { + add_config_var "'-o' option", $x; + } +} + + +my ($filename, $line, $prev); # shared globals + +my $fmt; +my $hdr_comment_start; + +if ($verbose) { + $fmt = "%s: %d: %s\n%s\n"; +} else { + $fmt = "%s: %d: %s\n"; +} + +if ($config{"doxygen"}) { + # doxygen comments look like "/*!" or "/**"; allow them. + $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/; +} else { + $hdr_comment_start = qr/^\s*\/\*$/; +} + +# Note, following must be in single quotes so that \s and \w work right. +my $lint_re = qr/\/\*(?: + jsl:\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*| + CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY| + FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*| + PROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*? + )\*\//x; + +my $splint_re = qr/\/\*@.*?@\*\//x; + +my $err_stat = 0; # exit status + +if ($#ARGV >= 0) { + foreach my $arg (@ARGV) { + my $fh = new IO::File $arg, "r"; + if (!defined($fh)) { + printf "%s: cannot open\n", $arg; + } else { + &jsstyle($arg, $fh); + close $fh; + } + } +} else { + &jsstyle("", *STDIN); +} +exit $err_stat; + +my $no_errs = 0; # set for JSSTYLED-protected lines + +sub err($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $line; + $err_stat = 1; + } +} + +sub err_prefix($$) { + my ($prevline, $error) = @_; + my $out = $prevline."\n".$line; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $out; + $err_stat = 1; + } +} + +sub err_prev($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $. - 1, $error, $prev; + $err_stat = 1; + } +} + +sub jsstyle($$) { + +my ($fn, $filehandle) = @_; +$filename = $fn; # share it globally + +my $in_cpp = 0; +my $next_in_cpp = 0; + +my $in_comment = 0; +my $in_header_comment = 0; +my $comment_done = 0; +my $in_function = 0; +my $in_function_header = 0; +my $in_declaration = 0; +my $note_level = 0; +my $nextok = 0; +my $nocheck = 0; + +my $in_string = 0; + +my ($okmsg, $comment_prefix); + +$line = ''; +$prev = ''; +reset_indent(); + +line: while (<$filehandle>) { + s/\r?\n$//; # strip return and newline + + # save the original line, then remove all text from within + # double or single quotes, we do not want to check such text. + + $line = $_; + + # + # C allows strings to be continued with a backslash at the end of + # the line. We translate that into a quoted string on the previous + # line followed by an initial quote on the next line. + # + # (we assume that no-one will use backslash-continuation with character + # constants) + # + $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); + + # + # normal strings and characters + # + s/'([^\\']|\\.)*'/\'\'/g; + s/"([^\\"]|\\.)*"/\"\"/g; + + # + # detect string continuation + # + if ($nocheck || $in_comment) { + $in_string = 0; + } else { + # + # Now that all full strings are replaced with "", we check + # for unfinished strings continuing onto the next line. + # + $in_string = + (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || + s/^("")*"([^\\"]|\\.)*\\$/""/); + } + + # + # figure out if we are in a cpp directive + # + $in_cpp = $next_in_cpp || /^\s*#/; # continued or started + $next_in_cpp = $in_cpp && /\\$/; # only if continued + + # strip off trailing backslashes, which appear in long macros + s/\s*\\$//; + + # an /* END JSSTYLED */ comment ends a no-check block. + if ($nocheck) { + if (/\/\* *END *JSSTYLED *\*\//) { + $nocheck = 0; + } else { + reset_indent(); + next line; + } + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if ($nextok) { + if ($okmsg) { + err($okmsg); + } + $nextok = 0; + $okmsg = 0; + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + $no_errs = 1; + } elsif ($no_errs) { + $no_errs = 0; + } + + # check length of line. + # first, a quick check to see if there is any chance of being too long. + if ((($line =~ tr/\t/\t/) * ($tab_width - 1)) + length($line) > 80) { + # yes, there is a chance. + # replace tabs with spaces and check again. + my $eline = $line; + 1 while $eline =~ + s/\t+/' ' x + (length($&) * $tab_width - length($`) % $tab_width)/e; + if (length($eline) > 80) { + err("line > 80 characters"); + } + } + + # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). + if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE + s/[^()]//g; # eliminate all non-parens + $note_level += s/\(//g - length; # update paren nest level + next; + } + + # a /* BEGIN JSSTYLED */ comment starts a no-check block. + if (/\/\* *BEGIN *JSSTYLED *\*\//) { + $nocheck = 1; + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + if (/\/\/ *JSSTYLED/) { + /^.*\/\/ *JSSTYLED *(.*)$/; + $okmsg = $1; + $nextok = 1; + } + + # universal checks; apply to everything + if (/\t +\t/) { + err("spaces between tabs"); + } + if (/ \t+ /) { + err("tabs between spaces"); + } + if (/\s$/) { + err("space or tab at end of line"); + } + if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { + err("comment preceded by non-blank"); + } + + # is this the beginning or ending of a function? + # (not if "struct foo\n{\n") + if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { + $in_function = 1; + $in_declaration = 1; + $in_function_header = 0; + $prev = $line; + next line; + } + if (/^}\s*(\/\*.*\*\/\s*)*$/) { + if ($prev =~ /^\s*return\s*;/) { + err_prev("unneeded return at end of function"); + } + $in_function = 0; + reset_indent(); # we don't check between functions + $prev = $line; + next line; + } + if (/^\w*\($/) { + $in_function_header = 1; + } + + # a blank line terminates the declarations within a function. + # XXX - but still a problem in sub-blocks. + if ($in_declaration && /^$/) { + $in_declaration = 0; + } + + if ($comment_done) { + $in_comment = 0; + $in_header_comment = 0; + $comment_done = 0; + } + # does this looks like the start of a block comment? + if (/$hdr_comment_start/) { + if ($config{"indent"} eq "tab") { + if (!/^\t*\/\*/) { + err("block comment not indented by tabs"); + } + } elsif (!/^ *\/\*/) { + err("block comment not indented by spaces"); + } + $in_comment = 1; + /^(\s*)\//; + $comment_prefix = $1; + if ($comment_prefix eq "") { + $in_header_comment = 1; + } + $prev = $line; + next line; + } + # are we still in the block comment? + if ($in_comment) { + if (/^$comment_prefix \*\/$/) { + $comment_done = 1; + } elsif (/\*\//) { + $comment_done = 1; + err("improper block comment close") + unless ($ignore_hdr_comment && $in_header_comment); + } elsif (!/^$comment_prefix \*[ \t]/ && + !/^$comment_prefix \*$/) { + err("improper block comment") + unless ($ignore_hdr_comment && $in_header_comment); + } + } + + if ($in_header_comment && $ignore_hdr_comment) { + $prev = $line; + next line; + } + + # check for errors that might occur in comments and in code. + + # allow spaces to be used to draw pictures in header comments. + #if (/[^ ] / && !/".* .*"/ && !$in_header_comment) { + # err("spaces instead of tabs"); + #} + #if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && + # (!/^ \w/ || $in_function != 0)) { + # err("indent by spaces instead of tabs"); + #} + if ($config{"indent"} eq "tab") { + if (/^ {2,}/ && !/^ [^ ]/) { + err("indent by spaces instead of tabs"); + } + } elsif (/^\t/) { + err("indent by tabs instead of spaces") + } elsif (/^( +)/ && !$in_comment) { + my $indent = $1; + if (length($indent) < $config{"indent"}) { + err("indent of " . length($indent) . + " space(s) instead of " . $config{"indent"}); + } elsif ($config{"strict-indent"} && + length($indent) % $config{"indent"} != 0) { + err("indent is " . length($indent) . + " not a multiple of " . $config{'indent'} . " spaces"); + } + } + if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { + err("continuation line not indented by 4 spaces"); + } + + # A multi-line block comment must not have content on the first line. + if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) { + err("improper first line of block comment"); + } + + if ($in_comment) { # still in comment, don't do further checks + $prev = $line; + next line; + } + + if ((/[^(]\/\*\S/ || /^\/\*\S/) && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank after open comment"); + } + if (/\S\*\/[^)]|\S\*\/$/ && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank before close comment"); + } + if ($config{"blank-after-start-comment"} && /(?\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\s,=]/ || + (/[^->]>[^,=>\s]/ && !/[^->]>$/) || + (/[^<]<[^,=<\s]/ && !/[^<]<$/) || + /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { + err("missing space around relational operator"); + } + if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || + (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || + (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { + # XXX - should only check this for C++ code + # XXX - there are probably other forms that should be allowed + if (!/\soperator=/) { + err("missing space around assignment operator"); + } + } + if (/[,;]\S/ && !/\bfor \(;;\)/ && + # Allow a comma in a regex quantifier. + !/\/.*?\{\d+,?\d*\}.*?\//) { + err("comma or semicolon followed by non-blank"); + } + # check for commas preceded by blanks + if ((!$config{"leading-comma-ok"} && /^\s*,/) || (!/^\s*,/ && /\s,/)) { + err("comma preceded by blank"); + } + # check for semicolons preceded by blanks + # allow "for" statements to have empty "while" clauses + if (/\s;/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { + err("semicolon preceded by blank"); + } + if (!$config{"continuation-at-front"} && /^\s*(&&|\|\|)/) { + err("improper boolean continuation"); + } elsif ($config{"continuation-at-front"} && /(&&|\|\||\+)$/) { + err("improper continuation"); + } + if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { + err("more than one space around boolean operator"); + } + # We allow methods which look like obj.delete() but not keywords without + # spaces ala: delete(obj) + if (/(?= MAX_INT) + SEQUENCE = 1; + + return (SEQUENCE); +} + + +function enqueue(list, obj) { + return (list.unshift(obj)); +} + + +function dequeue(list) { + return (list.pop()); +} + + +function createPoolObject(client) { + var now = Date.now(); + + var obj = { + id: nextSequence(), + alive: true, + client: client, + atime: now, + ctime: now + }; + + return (obj); +} + + +function getPoolObject(list, client, ignoreHealth) { + var obj = null; + + for (var i = 0; i < list.length; i++) { + if ((list[i].client === client) && + (list[i].alive || ignoreHealth)) { + obj = list[i]; + break; + } + } + + if (obj) + obj.atime = Date.now(); + + return (obj); +} + + +function removePoolObject(list, obj) { + var i; + + for (i = 0; i < list.length; i++) { + if (list[i] === obj) + break; + } + + if (i !== list.length) { + list.splice(i, 1); + obj.alive = false; + obj.dtime = Date.now(); + } + + return (obj); +} + + + + +///--- API + +function Pool(options) { + assert.object(options, 'options'); + assert.optionalFunc(options.assert, 'options.assert'); + assert.func(options.check, 'options.check'); + assert.number(options.checkInterval, 'options.checkInterval'); + assert.func(options.create, 'options.create'); + assert.object(options.log, 'options.log'); + assert.number(options.max, 'options.max'); + assert.number(options.maxIdleTime, 'options.maxIdleTime'); + assert.string(options.name, 'options.name'); + + EventEmitter.call(this, options); + + this.available = []; + this.assert = options.assert || function () { return (true); }; + this.create = options.create; + this.check = options.check; + this.checkInterval = options.checkInterval; + this.destroy = options.destroy || false; + this.events = (options.events || DEFAULT_EVENTS).slice(); + this.log = options.log.child({pool: options.name}, true); + this.max = Math.max(options.max, 1); + this.maxIdleTime = options.maxIdleTime; + this.name = options.name; + this.pendingResources = 0; + this.queue = []; + this.resources = []; + this.stopped = false; + this.stopping = false; + this.timer = false; + + this._scheduleReaper(); +} +util.inherits(Pool, EventEmitter); +module.exports = Pool; + + +Pool.prototype.acquire = function acquire(callback) { + assert.func(callback, 'callback'); + + var log = this.log; + var obj; + var self = this; + + if (log.trace()) + log.trace({state: self._state()}, 'acquire entered'); + + if (this.stopping) + return (callback(new Error('pool is shutting down'))); + + if (this.stopped) + return (callback(new Error('pool has been closed'))); + + if ((obj = dequeue(this.available))) { + obj.atime = Date.now(); + if (this._ensure(obj)) { + callback(null, obj.client); + return (true); + } else { + return (acquire(callback)); + } + } + + if (this.resources.length + this.pendingResources >= this.max) { + enqueue(this.queue, callback); + return (false); + } + + this.pendingResources++; + this.create(function createCallback(err, client) { + self.pendingResources = Math.max(self.pendingResources - 1, 0); + if (err) + return (callback(err)); + + // Handle shutdown being called while a + // new client was being made + if (self.stopped || self.stopping) { + self.destroy(client); + return (callback(new Error('pool is shutting down'))); + } + + obj = createPoolObject(client); + self._watch(client); + enqueue(self.resources, obj); + + return (callback(null, client)); + }); + return (true); +}; + + +Pool.prototype.remove = function remove(client) { + assert.object(client, 'client'); + + var log = this.log; + var obj; + var self = this; + + if (log.trace()) { + log.trace({ + state: self._state(), + client: util.inspect(client) + }, 'destroy entered'); + } + + if (!(obj = getPoolObject(this.resources, client))) + return (false); + + this._kill(obj); + process.nextTick(function emitDrain() { + if (self.available.length === self.resources.length) + self.emit('drain'); + }); + return (true); +}; + + +Pool.prototype.release = function release(client) { + assert.object(client, 'client'); + + var log = this.log; + var obj; + var self = this; + var waiter; + + if (log.trace()) { + log.trace({ + state: self._state(), + client: util.inspect(client) + }, 'release entered'); + } + + if (!(obj = getPoolObject(this.resources, client))) + return (false); + + if (!this._ensure(obj)) + return (true); + + + function emitDrain() { + if (self.available.length === self.resources.length) + self.emit('drain'); + } + + if (this.stopping) { + this._kill(obj); + process.nextTick(emitDrain); + } else if (this.stopped) { + // Done + this._kill(obj); + } else { + enqueue(this.available, obj); + if ((waiter = dequeue(this.queue))) { + process.nextTick(function runNextWaiter() { + self.acquire(waiter); + }); + } else { + process.nextTick(emitDrain); + } + } + + return (true); +}; + + +Pool.prototype.shutdown = function shutdown(callback) { + var log = this.log; + var self = this; + + function end() { + self.resources.forEach(function (o) { + self._kill(o); + }); + + if (!self.stopped) { + log.trace('emitting end'); + self.emit('end'); + } + + self.stopped = true; + self.stopping = false; + return (typeof (callback) === 'function' ? callback() : false); + } + + if (log.trace()) + log.trace({state: self._state()}, 'shutdown entered'); + + if (this.stopped) + return (end()); + + this.stopping = true; + if (this.timer) + clearTimeout(this.timer); + + if (this.available.length === this.resources.length) { + process.nextTick(function () { + return (end()); + }); + } else { + this.once('drain', end); + } + + return (undefined); +}; + + +///--- Private methods + +Pool.prototype._ensure = function _ensure(obj) { + var ok = false; + var log = this.log; + + try { + ok = this.assert(obj.client); + if (ok === undefined) + ok = true; + } catch (e) { + if (log.trace()) { + log.trace({ + err: e, + client: util.inspect(obj.client) + }, '_ensure: assert failed'); + } + ok = false; + } + + if (!ok) { + this._kill(obj); + return (false); + } + + return (true); +}; + + +Pool.prototype._reap = function _reap() { + var toCheck = []; + var self = this; + + if (this.stopped || this.stopping) + return (false); + + // Kind of ghetto - we temporarily purge the available list + // and then run health checks over those that need it. + this.available = this.available.filter(function (obj) { + var skip = ((Date.now() - obj.atime) < self.maxIdleTime); + if (!skip) + toCheck.push(obj); + + return (skip); + }); + + if (toCheck.length === 0) { + return (self._scheduleReaper()); + } + + // Run over all the "locked" items in || + var opts = { + func: function iterator(obj, cb) { + var done = false; + function _cb(err) { + if (!done) { + done = true; + cb(err, obj); + } + } + + if (!self._ensure(obj)) + return (_cb(new Error('dead client'))); + + try { + self.check(obj.client, _cb); + } catch (e) { + _cb(e); + } + + return (undefined); + }, + inputs: toCheck + }; + vasync.forEachParallel(opts, function (err, results) { + results.operations.forEach(function (op) { + if (op.status === 'ok') { + self.available.push(op.result); + } else { + self._kill(op.result); + } + }); + + self._scheduleReaper(); + }); + + return (true); +}; + + +Pool.prototype._kill = function _kill(obj) { + var log = this.log; + var self = this; + + if (log.trace()) + log.trace({state: self._state()}, 'killing object: %d', obj.id); + + removePoolObject(this.available, obj); + removePoolObject(this.resources, obj); + + // Deregister + if (obj.client instanceof EventEmitter) { + self.events.forEach(function (e) { + (obj.client.listeners(e) || []).forEach(function (l) { + if (l.name === '__poolingWatch') + obj.client.removeListener(e, l); + }); + }); + } + + if (typeof (this.destroy) === 'function') + this.destroy(obj.client); + + process.nextTick(function () { + self.emit('death', obj.client); + }); + +}; + + +Pool.prototype._scheduleReaper = function _scheduleReaper() { + var self = this; + + if (this.stopped || this.stopping) + return (false); + + this.timer = setTimeout(function reaper() { + return (self._reap()); + }, this.checkInterval); + + return (true); +}; + + +Pool.prototype._state = function _state() { + var self = this; + + return { + available: self.available.length, + resources: self.resources.length, + queue: self.queue.length, + interval: self.interval, + max: self.max, + min: self.min, + stopped: self.stopped, + stopping: self.stopping, + timer: self.timer ? util.inspect(self.timer) : null, + timeout: self.timeout + }; +}; + + +Pool.prototype._watch = function _watch(client) { + var log = this.log; + var obj; + var self = this; + + if (!(client instanceof EventEmitter)) + return (false); + + this.events.forEach(function (e) { + client.once(e, function __poolingWatch() { + if (log.trace()) { + var a = Array.prototype.slice.call(arguments); + log.trace({ + args: a.join(', '), + event: e, + state: self._state() + }, 'client event triggering closure'); + } + + // Now purge from ourself + if ((obj = getPoolObject(self.resources, client, true))) + self._kill(obj); + }); + }); + + return (true); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/README.md new file mode 100644 index 0000000..c0c3a53 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/README.md @@ -0,0 +1,126 @@ +# node-assert-plus + +This library is a super small wrapper over node's assert module that has two +things: (1) the ability to disable assertions with the environment variable +NODE_NDEBUG, and (2) some API wrappers for argument testing. Like +`assert.string(myArg, 'myArg')`. As a simple example, most of my code looks +like this: + + var assert = require('assert-plus'); + + function fooAccount(options, callback) { + assert.object(options, 'options'); + assert.number(options.id, 'options.id); + assert.bool(options.isManager, 'options.isManager'); + assert.string(options.name, 'options.name'); + assert.arrayOfString(options.email, 'options.email'); + assert.func(callback, 'callback'); + + // Do stuff + callback(null, {}); + } + +# API + +All methods that *aren't* part of node's core assert API are simply assumed to +take an argument, and then a string 'name' that's not a message; `AssertionError` +will be thrown if the assertion fails with a message like: + + AssertionError: foo (string) is required + at test (/home/mark/work/foo/foo.js:3:9) + at Object. (/home/mark/work/foo/foo.js:15:1) + at Module._compile (module.js:446:26) + at Object..js (module.js:464:10) + at Module.load (module.js:353:31) + at Function._load (module.js:311:12) + at Array.0 (module.js:484:10) + at EventEmitter._tickCallback (node.js:190:38) + +from: + + function test(foo) { + assert.string(foo, 'foo'); + } + +There you go. You can check that arrays are of a homogenous type with `Arrayof$Type`: + + function test(foo) { + assert.arrayOfString(foo, 'foo'); + } + +You can assert IFF an argument is not `undefined` (i.e., an optional arg): + + assert.optionalString(foo, 'foo'); + +Lastly, you can opt-out of assertion checking altogether by setting the +environment variable `NODE_NDEBUG=1`. This is pseudo-useful if you have +lots of assertions, and don't want to pay `typeof ()` taxes to v8 in +production. + +The complete list of APIs is: + +* assert.bool +* assert.buffer +* assert.func +* assert.number +* assert.object +* assert.string +* assert.arrayOfBool +* assert.arrayOfFunc +* assert.arrayOfNumber +* assert.arrayOfObject +* assert.arrayOfString +* assert.optionalBool +* assert.optionalBuffer +* assert.optionalFunc +* assert.optionalNumber +* assert.optionalObject +* assert.optionalString +* assert.optionalArrayOfBool +* assert.optionalArrayOfFunc +* assert.optionalArrayOfNumber +* assert.optionalArrayOfObject +* assert.optionalArrayOfString +* assert.AssertionError +* assert.fail +* assert.ok +* assert.equal +* assert.notEqual +* assert.deepEqual +* assert.notDeepEqual +* assert.strictEqual +* assert.notStrictEqual +* assert.throws +* assert.doesNotThrow +* assert.ifError + +# Installation + + npm install assert-plus + +## License + +The MIT License (MIT) +Copyright (c) 2012 Mark Cavage + +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. + +## Bugs + +See . diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/assert.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/assert.js new file mode 100644 index 0000000..70583f1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/assert.js @@ -0,0 +1,196 @@ +// Copyright (c) 2012, Mark Cavage. All rights reserved. + +var assert = require('assert'); +var Stream = require('stream').Stream; +var util = require('util'); + + + +///--- Globals + +var NDEBUG = process.env.NODE_NDEBUG || false; + + + +///--- Messages + +var ARRAY_TYPE_REQUIRED = '%s ([%s]) required'; +var TYPE_REQUIRED = '%s (%s) is required'; + + + +///--- Internal + +function capitalize(str) { + return (str.charAt(0).toUpperCase() + str.slice(1)); +} + +function uncapitalize(str) { + return (str.charAt(0).toLowerCase() + str.slice(1)); +} + +function _() { + return (util.format.apply(util, arguments)); +} + + +function _assert(arg, type, name, stackFunc) { + if (!NDEBUG) { + name = name || type; + stackFunc = stackFunc || _assert.caller; + var t = typeof (arg); + + if (t !== type) { + throw new assert.AssertionError({ + message: _(TYPE_REQUIRED, name, type), + actual: t, + expected: type, + operator: '===', + stackStartFunction: stackFunc + }); + } + } +} + + + +///--- API + +function array(arr, type, name) { + if (!NDEBUG) { + name = name || type; + + if (!Array.isArray(arr)) { + throw new assert.AssertionError({ + message: _(ARRAY_TYPE_REQUIRED, name, type), + actual: typeof (arr), + expected: 'array', + operator: 'Array.isArray', + stackStartFunction: array.caller + }); + } + + for (var i = 0; i < arr.length; i++) { + _assert(arr[i], type, name, array); + } + } +} + + +function bool(arg, name) { + _assert(arg, 'boolean', name, bool); +} + + +function buffer(arg, name) { + if (!Buffer.isBuffer(arg)) { + throw new assert.AssertionError({ + message: _(TYPE_REQUIRED, name, type), + actual: typeof (arg), + expected: 'buffer', + operator: 'Buffer.isBuffer', + stackStartFunction: buffer + }); + } +} + + +function func(arg, name) { + _assert(arg, 'function', name); +} + + +function number(arg, name) { + _assert(arg, 'number', name); +} + + +function object(arg, name) { + _assert(arg, 'object', name); +} + + +function stream(arg, name) { + if (!(arg instanceof Stream)) { + throw new assert.AssertionError({ + message: _(TYPE_REQUIRED, name, type), + actual: typeof (arg), + expected: 'Stream', + operator: 'instanceof', + stackStartFunction: buffer + }); + } +} + + +function string(arg, name) { + _assert(arg, 'string', name); +} + + + +///--- Exports + +module.exports = { + bool: bool, + buffer: buffer, + func: func, + number: number, + object: object, + stream: stream, + string: string +}; + + +Object.keys(module.exports).forEach(function (k) { + if (k === 'buffer') + return; + + var name = 'arrayOf' + capitalize(k); + + if (k === 'bool') + k = 'boolean'; + if (k === 'func') + k = 'function'; + module.exports[name] = function (arg, name) { + array(arg, k, name); + }; +}); + +Object.keys(module.exports).forEach(function (k) { + var _name = 'optional' + capitalize(k); + var s = uncapitalize(k.replace('arrayOf', '')); + if (s === 'bool') + s = 'boolean'; + if (s === 'func') + s = 'function'; + + if (k.indexOf('arrayOf') !== -1) { + module.exports[_name] = function (arg, name) { + if (!NDEBUG && arg !== undefined) { + array(arg, s, name); + } + }; + } else { + module.exports[_name] = function (arg, name) { + if (!NDEBUG && arg !== undefined) { + _assert(arg, s, name); + } + }; + } +}); + + +// Reexport built-in assertions +Object.keys(assert).forEach(function (k) { + if (k === 'AssertionError') { + module.exports[k] = assert[k]; + return; + } + + module.exports[k] = function () { + if (!NDEBUG) { + assert[k].apply(assert[k], arguments); + } + }; +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/package.json new file mode 100644 index 0000000..0e3e480 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/assert-plus/package.json @@ -0,0 +1,13 @@ +{ + "author": "Mark Cavage ", + "name": "assert-plus", + "description": "Extra assertions on top of node's assert module", + "version": "0.1.2", + "main": "./assert.js", + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "engines": { + "node": ">=0.6" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/.npmignore new file mode 100644 index 0000000..3de30f0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/.npmignore @@ -0,0 +1,4 @@ +/tmp +/node_modules +*.log +!/test/corpus/*.log diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/34.patch b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/34.patch new file mode 100644 index 0000000..56cf025 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/34.patch @@ -0,0 +1,71 @@ +From cdab7fc428cac630f65d1eae5b0dbccd4ea68298 Mon Sep 17 00:00:00 2001 +From: Rob Gulewich +Date: Mon, 20 Aug 2012 15:43:14 -0700 +Subject: [PATCH] Log req/res body if present (as per the restify audit + logger) + +--- + bin/bunyan | 20 ++++++++++++++++---- + 1 file changed, 16 insertions(+), 4 deletions(-) + +diff --git a/bin/bunyan b/bin/bunyan +index db15e06..61ff203 100755 +--- a/bin/bunyan ++++ b/bin/bunyan +@@ -614,30 +614,39 @@ function emitRecord(rec, line, opts, stylize) { + + if (rec.req) { + var headers = rec.req.headers; +- details.push(indent(format("%s %s HTTP/1.1%s", rec.req.method, ++ var s = format("%s %s HTTP/1.1%s", rec.req.method, + rec.req.url, + (headers + ? '\n' + Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n') + : '') +- ))); ++ ); ++ if (rec.req.body) { ++ s += '\n\n' + rec.req.body ++ } ++ details.push(indent(s)); + } + delete rec.req; + + if (rec.client_req) { + var headers = rec.client_req.headers; + var hostHeaderLine = ''; ++ var s = ''; + if (rec.client_req.address) { + hostHeaderLine = 'Host: ' + rec.client_req.address; + if (rec.client_req.port) + hostHeaderLine += ':' + rec.client_req.port; + hostHeaderLine += '\n'; + } +- details.push(indent(format("%s %s HTTP/1.1\n%s%s", rec.client_req.method, ++ s += format("%s %s HTTP/1.1\n%s%s", rec.client_req.method, + rec.client_req.url, + hostHeaderLine, + Object.keys(headers).map( +- function (h) { return h + ': ' + headers[h]; }).join('\n')))); ++ function (h) { return h + ': ' + headers[h]; }).join('\n')); ++ if (rec.client_req.body) { ++ s += '\n\n' + rec.client_req.body ++ } ++ details.push(indent(s)); + } + delete rec.client_req; + +@@ -654,6 +663,9 @@ function emitRecord(rec, line, opts, stylize) { + s += Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n'); + } ++ if (rec.res.body) { ++ s += '\n\n' + rec.res.body ++ } + if (s) { + details.push(indent(s)); + } +-- +1.7.10 + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/AUTHORS b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/AUTHORS new file mode 100644 index 0000000..ba2edfe --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/AUTHORS @@ -0,0 +1,6 @@ +Trent Mick (http://trentm.com) +Mark Cavage (https://github.com/mcavage) +Dave Pacheco (https://github.com/davepacheco) +Michael Hart (https://github.com/mhart) +Isaac Schlueter (https://github.com/isaacs) +Rob Gulewich (https://github.com/rgulewich) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/CHANGES.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/CHANGES.md new file mode 100644 index 0000000..a51e635 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/CHANGES.md @@ -0,0 +1,382 @@ +# bunyan Changelog + +## bunyan 0.14.0 + +- [pull #41] Safe `JSON.stringify`ing of emitted log records to avoid blowing + up on circular objects (by Isaac Schlueter). + + +## bunyan 0.13.5 + +- [issue #39] Fix a bug with `client_req` handling in the default output + of the `bunyan` CLI. + + +## bunyan 0.13.4 + +- [issue #38] Fix the default `bunyan` CLI output of a `req.body` that is an + object instead of a string. + + +## bunyan 0.13.3 + +- Export `bunyan.resolveLevel(NAME-OR-NUM)` to resolve a level name or number + to its log level number value: + + > bunyan.resolveLevel('INFO') + 30 + > bunyan.resolveLevel('debug') + 20 + + A side-effect of this change is that the uppercase level name is now allowed + in the logger constructor. + + +## bunyan 0.13.2 + +- [issue #35] Ensure that an accidental `log.info(BUFFER)`, where BUFFER is + a node.js Buffer object, doesn't blow up. + + +## bunyan 0.13.1 + +- [issue #34] Ensure `req.body`, `res.body` and other request/response fields + are emitted by the `bunyan` CLI (mostly by Rob Gulewich). + + + +## bunyan 0.13.0 + +- [issue #31] Re-instate defines for the (uppercase) log level names (TRACE, + DEBUG, etc.) in `bunyan -c "..."` filtering condition code. E.g.: + + $ ... | bunyan -c 'level >= ERROR' + + +## bunyan 0.12.0 + +- [pull #32] `bunyan -o short` for more concise output (by Dave Pacheco). E.g.: + + 22:56:52.856Z INFO myservice: My message + + instead of: + + [2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: My message + + +## bunyan 0.11.3 + +- Add '--strict' option to `bunyan` CLI to suppress all but legal Bunyan JSON + log lines. By default non-JSON, and non-Bunyan lines are passed through. + + +## bunyan 0.11.2 + +- [issue #30] Robust handling of 'req' field without a 'headers' subfield + in `bunyan` CLI. +- [issue #31] Pull the TRACE, DEBUG, et al defines from `bunyan -c "..."` + filtering code. This was added in v0.11.1, but has a significant adverse + affect. + + +## bunyan 0.11.1 + +- **Bad release. The TRACE et al names are bleeding into the log records + when using '-c'.** +- Add defines for the (uppercase) log level names (TRACE, DEBUG, etc.) in + `bunyan -c "..."` filtering condition code. E.g.: + + $ ... | bunyan -c 'level >= ERROR' + + +## bunyan 0.11.0 + +- [pull #29] Add -l/--level for level filtering, and -c/--condition for + arbitrary conditional filtering (by github.com/isaacs): + + $ ... | bunyan -l error # filter out log records below error + $ ... | bunyan -l 50 # numeric value works too + $ ... | bunyan -c 'level===50' # equiv with -c filtering + $ ... | bunyan -c 'pid===123' # filter on any field + $ ... | bunyan -c 'pid===123' -c '_audit' # multiple filters + + +## bunyan 0.10.0 + +- [pull #24] Support for gzip'ed log files in the bunyan CLI (by + github.com/mhart): + + $ bunyan foo.log.gz + ... + + +## bunyan 0.9.0 + +- [pull #16] Bullet proof the `bunyan.stdSerializers` (by github.com/rlidwka). + +- [pull #15] The `bunyan` CLI will now chronologically merge multiple log + streams when it is given multiple file arguments. (by github.com/davepacheco) + + $ bunyan foo.log bar.log + ... merged log records ... + +- [pull #15] A new `bunyan.RingBuffer` stream class that is useful for + keeping the last N log messages in memory. This can be a fast way to keep + recent, and thus hopefully relevant, log messages. (by @dapsays, + github.com/davepacheco) + + Potential uses: Live debugging if a running process could inspect those + messages. One could dump recent log messages at a finer log level than is + typically logged on + [`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception). + + var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); + var log = new bunyan({ + name: 'foo', + streams: [{ + type: 'raw', + stream: ringbuffer, + level: 'debug' + }] + }); + + log.info('hello world'); + console.log(ringbuffer.records); + +- Add support for "raw" streams. This is a logging stream that is given + raw log record objects instead of a JSON-stringified string. + + function Collector() { + this.records = []; + } + Collector.prototype.write = function (rec) { + this.records.push(rec); + } + var log = new Logger({ + name: 'mylog', + streams: [{ + type: 'raw', + stream: new Collector() + }] + }); + + See "examples/raw-stream.js". I expect raw streams to be useful for + piping Bunyan logging to separate services (e.g. , + ) or to separate in-process handling. + +- Add test/corpus/*.log files (accidentally excluded) so the test suite + actually works(!). + + +## bunyan 0.8.0 + +- [pull #21] Bunyan loggers now re-emit `fs.createWriteStream` error events. + By github.com/EvanOxfeld. See "examples/handle-fs-error.js" and + "test/error-event.js" for details. + + var log = new Logger({name: 'mylog', streams: [{path: FILENAME}]}); + log.on('error', function (err, stream) { + // Handle error writing to or creating FILENAME. + }); + +- jsstyle'ing (via `make check`) + + +## bunyan 0.7.0 + +- [issue #12] Add `bunyan.createLogger(OPTIONS)` form, as is more typical in + node.js APIs. This'll eventually become the preferred form. + + +## bunyan 0.6.9 + +- Change `bunyan` CLI default output to color "src" info red. Before the "src" + information was uncolored. The "src" info is the filename, line number and + function name resulting from using `src: true` in `Logger` creation. I.e., + the `(/Users/trentm/tm/node-bunyan/examples/hi.js:10)` in: + + [2012-04-10T22:28:58.237Z] INFO: myapp/39339 on banana.local (/Users/trentm/tm/node-bunyan/examples/hi.js:10): hi + +- Tweak `bunyan` CLI default output to still show an "err" field if it doesn't + have a "stack" attribute. + + +## bunyan 0.6.8 + +- Fix bad bug in `log.child({...}, true);` where the added child fields **would + be added to the parent's fields**. This bug only existed for the "fast child" + path (that second `true` argument). A side-effect of fixing this is that + the "fast child" path is only 5 times as fast as the regular `log.child`, + instead of 10 times faster. + + +## bunyan 0.6.7 + +- [issue #6] Fix bleeding 'type' var to global namespace. (Thanks Mike!) + + +## bunyan 0.6.6 + +- Add support to the `bunyan` CLI taking log file path args, `bunyan foo.log`, + in addition to the usual `cat foo.log | bunyan`. +- Improve reliability of the default output formatting of the `bunyan` CLI. + Before it could blow up processing log records missing some expected + fields. + + +## bunyan 0.6.5 + +- ANSI coloring output from `bunyan` CLI tool (for the default output mode/style). + Also add the '--color' option to force coloring if the output stream is not + a TTY, e.g. `cat my.log | bunyan --color | less -R`. Use `--no-color` to + disable coloring, e.g. if your terminal doesn't support ANSI codes. +- Add 'level' field to log record before custom fields for that record. This + just means that the raw record JSON will show the 'level' field earlier, + which is a bit nicer for raw reading. + + +## bunyan 0.6.4 + +- [issue #5] Fix `log.info() -> boolean` to work properly. Previous all were + returning false. Ditto all trace/debug/.../fatal methods. + + +## bunyan 0.6.3 + +- Allow an optional `msg` and arguments to the `log.info( err)` logging + form. For example, before: + + log.debug(my_error_instance) // good + log.debug(my_error_instance, "boom!") // wasn't allowed + + Now the latter is allowed if you want to expliciting set the log msg. Of course + this applies to all the `log.{trace|debug|info...}()` methods. + +- `bunyan` cli output: clarify extra fields with quoting if empty or have + spaces. E.g. 'cmd' and 'stderr' in the following: + + [2012-02-12T00:30:43.736Z] INFO: mo-docs/43194 on banana.local: buildDocs results (req_id=185edca2-2886-43dc-911c-fe41c09ec0f5, route=PutDocset, error=null, stderr="", cmd="make docs") + + +## bunyan 0.6.2 + +- Fix/guard against unintended inclusion of some files in npm published package + due to + + +## bunyan 0.6.1 + +- Internal: starting jsstyle usage. +- Internal: add .npmignore. Previous packages had reams of bunyan crud in them. + + +## bunyan 0.6.0 + +- Add 'pid' automatic log record field. + + +## bunyan 0.5.3 + +- Add 'client_req' (HTTP client request) standard formatting in `bunyan` CLI + default output. +- Improve `bunyan` CLI default output to include *all* log record keys. Unknown keys + are either included in the first line parenthetical (if short) or in the indented + subsequent block (if long or multiline). + + +## bunyan 0.5.2 + +- [issue #3] More type checking of `new Logger(...)` and `log.child(...)` + options. +- Start a test suite. + + +## bunyan 0.5.1 + +- [issue #2] Add guard on `JSON.stringify`ing of log records before emission. + This will prevent `log.info` et al throwing on record fields that cannot be + represented as JSON. An error will be printed on stderr and a clipped log + record emitted with a 'bunyanMsg' key including error details. E.g.: + + bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON + { + "name": "foo", + "hostname": "banana.local", + "bunyanMsg": "bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON", + ... + + Some timing shows this does effect log speed: + + $ node tools/timeguard.js # before + Time try/catch-guard on JSON.stringify: + - log.info: 0.07365ms per iteration + $ node tools/timeguard.js # after + Time try/catch-guard on JSON.stringify: + - log.info: 0.07368ms per iteration + + +## bunyan 0.5.0 + +- Use 10/20/... instead of 1/2/... for level constant values. Ostensibly this + allows for intermediary levels from the defined "trace/debug/..." set. + However, that is discouraged. I'd need a strong user argument to add + support for easily using alternative levels. Consider using a separate + JSON field instead. +- s/service/name/ for Logger name field. "service" is unnecessarily tied + to usage for a service. No need to differ from log4j Logger "name". +- Add `log.level(...)` and `log.levels(...)` API for changing logger stream + levels. +- Add `TRACE|DEBUG|INFO|WARN|ERROR|FATAL` level constants to exports. +- Add `log.info(err)` special case for logging an `Error` instance. For + example `log.info(new TypeError("boom")` will produce: + + ... + "err": { + "message": "boom", + "name": "TypeError", + "stack": "TypeError: boom\n at Object. ..." + }, + "msg": "boom", + ... + + +## bunyan 0.4.0 + +- Add `new Logger({src: true})` config option to have a 'src' attribute be + automatically added to log records with the log call source info. Example: + + "src": { + "file": "/Users/trentm/tm/node-bunyan/examples/src.js", + "line": 20, + "func": "Wuzzle.woos" + }, + + +## bunyan 0.3.0 + +- `log.child(options[, simple])` Added `simple` boolean arg. Set `true` to + assert that options only add fields (no config changes). Results in a 10x + speed increase in child creation. See "tools/timechild.js". On my Mac, + "fast child" creation takes about 0.001ms. IOW, if your app is dishing + 10,000 req/s, then creating a log child for each request will take + about 1% of the request time. +- `log.clone` -> `log.child` to better reflect the relationship: streams and + serializers are inherited. Streams can't be removed as part of the child + creation. The child doesn't own the parent's streams (so can't close them). +- Clean up Logger creation. The goal here was to ensure `log.child` usage + is fast. TODO: measure that. +- Add `Logger.stdSerializers.err` serializer which is necessary to get good + Error object logging with node 0.6 (where core Error object properties + are non-enumerable). + + +## bunyan 0.2.0 + +- Spec'ing core/recommended log record fields. +- Add `LOG_VERSION` to exports. +- Improvements to request/response serializations. + + +## bunyan 0.1.0 + +First release. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/Makefile new file mode 100644 index 0000000..b74f643 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/Makefile @@ -0,0 +1,67 @@ + +#---- Tools + +TAP := ./node_modules/.bin/tap + + +#---- Files + +JSSTYLE_FILES := $(shell find lib test tools examples -name "*.js") bin/bunyan + + + +#---- Targets + +all: + +# Ensure all version-carrying files have the same version. +.PHONY: versioncheck +versioncheck: + [[ `cat package.json | json version` == `grep '^## ' CHANGES.md | head -1 | awk '{print $$3}'` ]] + [[ `cat package.json | json version` == `grep '^var VERSION' bin/bunyan | awk -F'"' '{print $$2}'` ]] + [[ `cat package.json | json version` == `grep '^var VERSION' lib/bunyan.js | awk -F"'" '{print $$2}'` ]] + @echo Version check ok. + +.PHONY: cutarelease +cutarelease: versioncheck + ./tools/cutarelease.py -p bunyan -f package.json -f lib/bunyan.js -f bin/bunyan + + +#---- test + +.PHONY: test +test: $(TAP) + TAP=1 $(TAP) test/*.test.js + +# Test will all node supported versions (presumes install locations I use on my machine). +.PHONY: testall +testall: test08 test06 test09 + +.PHONY: test09 +test09: + @echo "# Test node 0.9.x (with node `$(HOME)/opt/node-0.9/bin/node --version`)" + PATH="$(HOME)/opt/node-0.9/bin:$(PATH)" TAP=1 $(TAP) test/*.test.js +.PHONY: test08 +test08: + @echo "# Test node 0.8.x (with node `$(HOME)/opt/node-0.8/bin/node --version`)" + PATH="$(HOME)/opt/node-0.8/bin:$(PATH)" TAP=1 $(TAP) test/*.test.js +.PHONY: test06 +test06: + @echo "# Test node 0.6.x (with node `$(HOME)/opt/node-0.6/bin/node --version`)" + PATH="$(HOME)/opt/node-0.6/bin:$(PATH)" TAP=1 $(TAP) test/*.test.js + + + +#---- check + +.PHONY: check-jsstyle +check-jsstyle: $(JSSTYLE_FILES) + ./tools/jsstyle -o indent=2,doxygen,unparenthesized-return=0,blank-after-start-comment=0,leading-right-paren-ok $(JSSTYLE_FILES) + +.PHONY: check +check: check-jsstyle + @echo "Check ok." + +.PHONY: prepush +prepush: check testall + @echo "Okay to push." diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/README.md new file mode 100644 index 0000000..7c8c3d7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/README.md @@ -0,0 +1,534 @@ +Bunyan is **a simple and fast a JSON logging library** for node.js services and +**a `bunyan` CLI tool** for nicely viewing those logs). + +![bunyan CLI screenshot](https://raw.github.com/trentm/node-bunyan/master/tools/screenshot1.png) + +Server logs should be structured. JSON's a good format. Let's do that: a log +record is one line of `JSON.stringify`'d output. Let's also specify some common +names for the requisite and common fields for a log record (see below). + +Also: log4j is way more than you need. + + +# Current Status + +Solid core functionality is there. Joyent is using this for a number of +production services. Bunyan supports node 0.6 and greater. + +Follow @trentmick +for updates to Bunyan. + +See also: [Bunyan for Bash](https://github.com/trevoro/bash-bunyan). + + +# Installation + + npm install bunyan + + +# Usage + +**The usual.** All loggers must provide a "name". This is somewhat akin +to log4j logger "name", but Bunyan doesn't do hierarchical logger names. + + $ cat hi.js + var Logger = require('bunyan'); + var log = new Logger({name: "myapp"}); + log.info("hi"); + +Alternatively, bunyan 0.7.0 and up supports a more node.js-land typical +style (which might become the preferred form over time): + + var bunyan = require('bunyan'); + var log = bunyan.createLogger({name: "myapp"}); + +**Log records are JSON.** "hostname", "time" and "v" (the Bunyan log +format version) are added for you. + + $ node hi.js + {"name":"myapp","hostname":"banana.local","pid":123,"level":2,"msg":"hi","time":"2012-01-31T00:07:44.216Z","v":0} + +The full `log.{trace|debug|...|fatal}(...)` API is: + + log.info(); // Returns a boolean: is the "info" level enabled? + + log.info('hi'); // Log a simple string message. + log.info('hi %s', bob, anotherVar); // Uses `util.format` for msg formatting. + + log.info({foo: 'bar'}, 'hi'); // Adds "foo" field to log record. + + log.info(err); // Special case to log an `Error` instance, adds "err" + // key with exception details (including the stack) and + // sets "msg" to the exception message. + log.info(err, 'more on this: %s', more); + // ... or you can specify the "msg". + +Note that this implies **you cannot pass any object as the first argument +to log it**. IOW, `log.info(myobject)` isn't going to work the way you +expect. Adding support for this API would necessitate (a) JSON-ifying safely +that given object (sometimes hard, and probably slow) and (b) putting all its +attribs under a top-level 'x' field name or something to avoid field name +collisions. + + +## bunyan tool + +A `bunyan` tool is provided **for pretty-printing bunyan logs** and, eventually, +for filtering (e.g. `| bunyan -c 'level>3'`). This shows the default output +(which is fluid right now) and indented-JSON output. More output formats will +be added, including support for custom formats. + + $ node hi.js | ./bin/bunyan # CLI tool to filter/pretty-print JSON logs. + [2012-01-31T00:08:11.387Z] INFO: myapp on banana.local/123: hi + + $ node hi.js | ./bin/bunyan -o json + { + "name": "myapp", + "hostname": "banana.local", + "pid": 123, + "level": 2, + "msg": "hi", + "time": "2012-01-31T00:10:00.676Z", + "v": 0 + } + + +## streams + +By default, log output is to stdout (**stream**) and at the "info" level. +Explicitly that looks like: + + var log = new Logger({name: "myapp", stream: process.stdout, + level: "info"}); + +That is an abbreviated form for a single stream. **You can define multiple +streams at different levels**. + + var log = new Logger({ + name: "amon", + streams: [ + { + level: "info", + stream: process.stdout, // log INFO and above to stdout + }, + { + level: "error", + path: "tmp/error.log" // log ERROR and above to a file + } + ] + }); + +More on streams in the "Streams" section below. + + +## log.child + +A `log.child(...)` is provided to **specialize a logger for a sub-component**. +The following will have log records from "Wuzzle" instances use exactly the +same config as its parent, plus include the "component" field. + + var log = new Logger(...); + + ... + + function Wuzzle(options) { + this.log = options.log; + this.log.info("creating a wuzzle") + } + Wuzzle.prototype.woos = function () { + this.log.warn("This wuzzle is woosey.") + } + + var wuzzle = new Wuzzle({log: log.child({component: "wuzzle"})}); + wuzzle.woos(); + log.info("done with the wuzzle") + +The [node-restify](https://github.com/mcavage/node-restify) +framework integrates bunyan. One feature of its integration, is that each +restify request handler includes a `req.log` logger that is: + + log.child({req_id: }, true) + +Apps using restify can then use `req.log` and have all such log records +include the unique request id (as "req_id"). Handy. + + +## serializers + +Bunyan has a concept of **"serializers" to produce a JSON-able object from a +JavaScript object**, so you can easily do the following: + + log.info({req: }, "something about handling this request"); + +Association is by log record field name, "req" in this example, so this +requires a registered serializer something like this: + + function reqSerializer(req) { + return { + method: req.method, + url: req.url, + headers: req.headers + } + } + var log = new Logger({ + ... + serializers: { + req: reqSerializer + } + }); + +Or this: + + var log = new Logger({ + ... + serializers: {req: Logger.stdSerializers.req} + }); + +because Buyan includes a small set of standard serializers. To use all the +standard serializers you can use: + + var log = new Logger({ + ... + serializers: Logger.stdSerializers + }); + +*Note: Your own serializers should never throw, otherwise you'll get an +ugly message on stderr from Bunyan (along with the traceback) and the field +in your log record will be replaced with a short error message.* + +## src + +The **source file, line and function of the log call site** can be added to +log records by using the `src: true` config option: + + var log = new Logger({src: true, ...}); + +This adds the call source info with the 'src' field, like this: + + { + "name": "src-example", + "hostname": "banana.local", + "pid": 123, + "component": "wuzzle", + "level": 4, + "msg": "This wuzzle is woosey.", + "time": "2012-02-06T04:19:35.605Z", + "src": { + "file": "/Users/trentm/tm/node-bunyan/examples/src.js", + "line": 20, + "func": "Wuzzle.woos" + }, + "v": 0 + } + +**WARNING: Determining the call source info is slow. Never use this option +in production.** + + +# Levels + +The log levels in bunyan are: + +- "fatal" (60): the service/app is going to stop or become unusable now +- "error" (50): fatal for a particular request, but the service/app continues servicing other requests +- "warn" (40): a note on something that should probably be looked at by an operator +- "info" (30): detail on regular operation +- "debug" (20): anything else, i.e. too verbose to be included in "info" level. +- "trace" (10): logging from external libraries used by your app + +General level usage suggestions: "debug" should be used sparingly. +Information that will be useful to debug errors *post mortem* should usually +be included in "info" messages if it's generally relevant or else with the +corresponding "error" event. Don't rely on spewing mostly irrelevant debug +messages all the time and sifting through them when an error occurs. + +Integers are used for the actual level values (10 for "trace", ..., 60 for +"fatal") and constants are defined for the (Logger.TRACE ... Logger.DEBUG). +The lowercase level names are aliases supported in the API. + +Here is the API for changing levels in an existing logger: + + log.level() -> INFO // gets current level (lowest level of all streams) + + log.level(INFO) // set all streams to level INFO + log.level("info") // set all streams to level INFO + + log.levels() -> [DEBUG, INFO] // get array of levels of all streams + log.levels(0) -> DEBUG // get level of stream at index 0 + log.levels("foo") // get level of stream with name "foo" + + log.levels(0, INFO) // set level of stream 0 to INFO + log.levels(0, "info") // can use "info" et al aliases + log.levels("foo", WARN) // set stream named "foo" to WARN + + + +# Log Record Fields + +This section will describe *rules* for the Bunyan log format: field names, +field meanings, required fields, etc. However, a Bunyan library doesn't +strictly enforce all these rules while records are being emitted. For example, +Bunyan will add a `time` field with the correct format to your log records, +but you can specify your own. It is the caller's responsibility to specify +the appropriate format. + +The reason for the above leniency is because IMO logging a message should +never break your app. This leads to this rule of logging: **a thrown +exception from `log.info(...)` or equivalent (other than for calling with the +incorrect signature) is always a bug in Bunyan.** + + +A typical Bunyan log record looks like this: + + {"name":"myserver","hostname":"banana.local","pid":123,"req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","connection":"close"}},"level":3,"msg":"start request","time":"2012-02-03T19:02:46.178Z","v":0} + +Pretty-printed: + + { + "name": "myserver", + "hostname": "banana.local", + "pid": 123, + "req": { + "method": "GET", + "url": "/path?q=1#anchor", + "headers": { + "x-hi": "Mom", + "connection": "close" + }, + "remoteAddress": "120.0.0.1", + "remotePort": 51244 + }, + "level": 3, + "msg": "start request", + "time": "2012-02-03T19:02:57.534Z", + "v": 0 + } + + +Core fields: + +- `v`: Required. Integer. Added by Bunion. Cannot be overriden. + This is the Bunyan log format version (`require('bunyan').LOG_VERSION`). + The log version is a single integer. `0` is until I release a version + "1.0.0" of node-bunyan. Thereafter, starting with `1`, this will be + incremented if there is any backward incompatible change to the log record + format. Details will be in "CHANGES.md" (the change log). +- `level`: Required. Integer. Added by Bunion. Cannot be overriden. + See the "Levels" section. +- `name`: Required. String. Provided at Logger creation. + You must specify a name for your logger when creating it. Typically this + is the name of the service/app using Bunyan for logging. +- `hostname`: Required. String. Provided or determined at Logger creation. + You can specify your hostname at Logger creation or it will be retrieved + vi `os.hostname()`. +- `pid`: Required. Integer. Filled in automatically at Logger creation. +- `time`: Required. String. Added by Bunion. Can be overriden. + The date and time of the event in [ISO 8601 + Extended Format](http://en.wikipedia.org/wiki/ISO_8601) format and in UTC, + as from + [`Date.toISOString()`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString). +- `msg`: Required. String. + Every `log.debug(...)` et al call must provide a log message. +- `src`: Optional. Object giving log call source info. This is added + automatically by Bunyan if the "src: true" config option is given to the + Logger. Never use in production as this is really slow. + + +Go ahead and add more fields, and nest ones are fine (and recommended) as +well. This is why we're using JSON. Some suggestions and best practices +follow (feedback from actual users welcome). + + +Recommended/Best Practice Fields: + +- `err`: Object. A caught JS exception. Log that thing with `log.info(err)` + to get: + + ... + "err": { + "message": "boom", + "name": "TypeError", + "stack": "TypeError: boom\n at Object. ..." + }, + "msg": "boom", + ... + + Or use the `Logger.stdSerializers.err` serializer in your Logger and + do this `log.error({err: err}, "oops")`. See "examples/err.js". + +- `req_id`: String. A request identifier. Including this field in all logging + tied to handling a particular request to your server is strongly suggested. + This allows post analysis of logs to easily collate all related logging + for a request. This really shines when you have a SOA with multiple services + and you carry a single request ID from the top API down through all APIs + (as [node-restify](https://github.com/mcavage/node-restify) facilitates + with its 'X-Request-Id' header). + +- `req`: An HTTP server request. Bunyan provides `Logger.stdSerializers.req` + to serialize a request with a suggested set of keys. Example: + + { + "method": "GET", + "url": "/path?q=1#anchor", + "headers": { + "x-hi": "Mom", + "connection": "close" + }, + "remoteAddress": "120.0.0.1", + "remotePort": 51244 + } + +- `res`: An HTTP server response. Bunyan provides `Logger.stdSerializers.res` + to serialize a response with a suggested set of keys. Example: + + { + "statusCode": 200, + "header": "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n" + } + + +Other fields to consider: + +- `req.username`: Authenticated user (or for a 401, the user attempting to + auth). +- Some mechanism to calculate response latency. "restify" users will have + a "X-Response-Time" header. A `latency` custom field would be fine. +- `req.body`: If you know that request bodies are small (common in APIs, + for example), then logging the request body is good. + + +# Streams + +A "stream" is Bunyan's name for an output for log messages (the equivalent +to a log4j Appender). Ultimately Bunyan uses a +[Writable Stream](http://nodejs.org/docs/latest/api/all.html#writable_Stream) +interface, but there are some additional attributes used to create and +manage the stream. A Bunyan Logger instance has one or more streams. +In general streams are specified with the "streams" option: + + var Logger = require('bunyan'); + var log = new Logger({ + name: "foo", + streams: [ + { + stream: process.stderr, + level: "debug" + }, + ... + ] + }) + +For convenience, if there is only one stream, it can specified with the +"stream" and "level" options (internal converted to a `Logger.streams`): + + var log = new Logger({ + name: "foo", + stream: process.stderr, + level: "debug" + }) + +If none are specified, the default is a stream on `process.stdout` at the +"info" level. + +`Logger.streams` is an array of stream objects with the following attributes: + +- `type`: One of "stream", "file" or "raw". See below. Often this is + implied from the other arguments. +- `path`: A file path for a file stream. If `path` is given and `type` is + not specified, then `type` will be set to "file". +- `stream`: This is the "Writable Stream", e.g. a std handle or an open + file write stream. If `stream` is given and `type` is not specified, then + `type` will be set to "stream". +- `level`: The level at which logging to this stream is enabled. If not + specified it defaults to INFO. + +Supported stream types are: + +- `stream`: A plain ol' node.js [Writable + Stream](http://nodejs.org/docs/latest/api/all.html#writable_Stream). + A "stream" (the writeable stream) value is required. + +- `file`: A "path" argument is given. Bunyan will open this file for + appending. E.g.: + + { + "path": "/var/log/foo.log", + "level": "warn" + } + + Bunyan re-emits error events from the created `WriteStream`. So you can + do this: + + var log = new Logger({name: 'mylog', streams: [{path: LOG_PATH}]}); + log.on('error', function (err, stream) { + // Handle stream write or create error here. + }); + +- `raw`: Similar to a "stream" writeable stream, except that the write method + is given raw log record *Object*s instead of a JSON-stringified string. + This can be useful for hooking on further processing to all Bunyan logging: + pushing to an external service, a RingBuffer (see below), etc. + + +## RingBuffer Stream + +Bunyan comes with a special stream called a RingBuffer which keeps the last N +records in memory and does *not* write the data anywhere else. One common +strategy is to log 'info' and higher to a normal log file but log all records +(including 'trace') to a ringbuffer that you can access via a debugger, or your +own HTTP interface, or a post-mortem facility like MDB or node-panic. + +To use a RingBuffer: + + /* Create a ring buffer that stores the last 100 records. */ + var bunyan = require('bunyan'); + var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); + var log = new bunyan({ + name: 'foo', + streams: [ + { + level: 'info', + stream: process.stdout + }, + { + level: 'trace', + type: 'raw', // use 'raw' to get raw log record objects + stream: ringbuffer + } + ] + }); + + log.info('hello world'); + console.log(ringbuffer.records); + +This example emits: + + [ { name: 'foo', + hostname: '912d2b29', + pid: 50346, + level: 30, + msg: 'hello world', + time: '2012-06-19T21:34:19.906Z', + v: 0 } ] + + +# Versioning + +The scheme I follow is most succintly described by the bootstrap guys +[here](https://github.com/twitter/bootstrap#versioning). + +tl;dr: All versions are `..` which will be incremented for +breaking backward compat and major reworks, new features without breaking +change, and bug fixes, respectively. + + + +# License + +MIT. + + + +# Future + +See "TODO.md". diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/TODO.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/TODO.md new file mode 100644 index 0000000..33c7c73 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/TODO.md @@ -0,0 +1,81 @@ +- "all" or "off" levels? log4j? logging.py? + logging.py has NOTSET === 0. I think that is only needed/used for + multi-level hierarchical effective level. +- move custom keys out to 'x' ? What about req, res? Compat issues there? + Bunyan CLI would have to deal with both for a while. Just a change in + record.v from 0 to 1. +- buffered writes to increase speed: + - I'd start with a tools/timeoutput.js for some numbers to compare + before/after. Sustained high output to a file. + - perhaps this would be a "buffered: true" option on the stream object + - then wrap the "stream" with a local class that handles the buffering + - to finish this, need the 'log.close' and `process.on('exit', ...)` + work that Trent has started. +- "canWrite" handling for full streams. Need to buffer a la log4js +- test file log with logadm rotation: does it handle that? +- test suite: + - test for a cloned logger double-`stream.end()` causing problems. + Perhaps the "closeOnExit" for existing streams should be false for + clones. + - test that a `log.clone(...)` adding a new field matching a serializer + works *and* that an existing field in the parent is not *re-serialized*. +- a "rolling-file" stream: but specifically by time, e.g. hourly. (MarkC + requested) +- split out `bunyan` cli to a "bunyan" or "bunyan-reader" or "node-bunyan-reader" + as the basis for tools to consume bunyan logs. It can grow indep of node-bunyan + for generating the logs. + It would take a Bunyan log record object and be expected to emit it. + + node-bunyan-reader + .createReadStream(path, [options]) ? + +- document "well-known" keys from bunyan CLI p.o.v.. Add "client_req". +- bunyan tool: built in less usage (a la git?) so that I don't have to + go through this: `bunyan --color master.log | less -R` +- want `bunyan -f foo.log` a la `tail -f` + + +# someday/maybe + +- More `bunyan` output formats and filtering features. +- Think about a bunyan dashboard that supports organizing and viewing logs + from multiple hosts and services. +- Syslog support. +- A vim plugin (a la http://vim.cybermirror.org/runtime/autoload/zip.vim ?) to + allow browsing (read-only) a bunyan log in rendered form. +- Some speed comparisons with others to get a feel for Bunyan's speed. +- remove "rm -rf tmp" when this fixed: +- what about promoting 'latency' field and making that easier? +- `log.close` to close streams and shutdown and `this.closed` + process.on('exit', log.close) +- bunyan cli: -c COND args a la json +- bunyan cli: more layouts (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/EnhancedPatternLayout.html) + Custom log formats (in config file? in '-f' arg) using printf or hogan.js + or whatever. Dap wants field width control for lining up. Hogan.js is + probably overkill for this. +- syslog: Josh uses https://github.com/chrisdew/node-syslog + streams: [ + ... + { + level: "warn", + type: "syslog", + syslog_facility: "LOG_LOCAL1", // one of the syslog facility defines + syslog_pid: true, // syslog logopt "LOG_PID" + syslog_cons: false // syslog logopt "LOG_CONS" + } +- bunyan "compact" or "light", '-l'? Something like. Or pehaps this (with + color) could be the default, with '-l' for long output. + 13:51.340 [src.js:20#Wuzzle.woos] WARN: This wuzzle is woosey. +- get Mark to show me dtrace provider stuff and consider adding for + logging, if helpful. +- add option to "streams" to take the raw object, not serialized. + It would be a good hook for people with custom needs that Bunyan doesn't + care about (e.g. http://loggly.com/ or hook.io or whatever). +- serializer `req_id` that pulls it from req? `log.info({req_id: req}, "hi")` +- serializer support: + - restify-server.js example -> restifyReq ? or have `req` detect that. + That is nicer for the "use all standard ones". *Does* restify req + have anything special? + - differential HTTP *client* req/res with *server* req/res. +- statsd stream? http://codeascraft.etsy.com/2011/02/15/measure-anything-measure-everything/ + Think about it. diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/bar.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/bar.js new file mode 100644 index 0000000..d09b054 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/bar.js @@ -0,0 +1,2 @@ +s = '[2012-02-23T03:12:15.498Z] \u001b[36m INFO\u001b[39m: amon-relay/2909 on headnode: \u001b[36mGetting master URL from MAPI.\u001b[39m\u001b[90m\u001b[39m\n\u001b[90m\u001b[39m' +console.log(s) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/bin/bunyan b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/bin/bunyan new file mode 100755 index 0000000..f20a6f8 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/bin/bunyan @@ -0,0 +1,1024 @@ +#!/usr/bin/env node +// +// bunyan -- filter and pretty-print JSON logs, like Bunyan logs. +// +// See . +// + +var VERSION = "0.14.0"; + +var util = require('util'); +var pathlib = require('path'); +var vm = require('vm'); +var http = require('http'); +var fs = require('fs'); +var warn = console.warn; + + + +//---- globals and constants + +// Output modes. +var OM_PAUL = 1; +var OM_JSON = 2; +var OM_INSPECT = 3; +var OM_SIMPLE = 4; +var OM_SHORT = 5; +var OM_FROM_NAME = { + "paul": OM_PAUL, + "json": OM_JSON, + "inspect": OM_INSPECT, + "simple": OM_SIMPLE, + "short": OM_SHORT +}; + + +// Levels +var TRACE = 10; +var DEBUG = 20; +var INFO = 30; +var WARN = 40; +var ERROR = 50; +var FATAL = 60; + +var levelFromName = { + 'trace': TRACE, + 'debug': DEBUG, + 'info': INFO, + 'warn': WARN, + 'error': ERROR, + 'fatal': FATAL +}; +var nameFromLevel = {}; +var upperNameFromLevel = {}; +var upperPaddedNameFromLevel = {}; +Object.keys(levelFromName).forEach(function (name) { + var lvl = levelFromName[name]; + nameFromLevel[lvl] = name; + upperNameFromLevel[lvl] = name.toUpperCase(); + upperPaddedNameFromLevel[lvl] = ( + name.length === 4 ? ' ' : '') + name.toUpperCase(); +}); + + + +//---- support functions + +function getVersion() { + return VERSION; +} + + +var format = util.format; +if (!format) { + // If not node 0.6, then use its `util.format`: + // : + var inspect = util.inspect; + var formatRegExp = /%[sdj%]/g; + format = function format(f) { + if (typeof f !== 'string') { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': return JSON.stringify(args[i++]); + case '%%': return '%'; + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (x === null || typeof x !== 'object') { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; + }; +} + +function indent(s) { + return ' ' + s.split(/\r?\n/).join('\n '); +} + +function objCopy(obj) { + if (obj === null) { + return null; + } else if (Array.isArray(obj)) { + return obj.slice(); + } else { + var copy = {}; + Object.keys(obj).forEach(function (k) { + copy[k] = obj[k]; + }); + return copy; + } +} + +function printHelp() { + console.log("Usage:"); + console.log(" bunyan [OPTIONS] [FILE ...]"); + console.log(""); + console.log("Filter and pretty-print Bunyan log file content."); + console.log(""); + console.log("General options:"); + console.log(" -h, --help print this help info and exit"); + console.log(" --version print version of this command and exit"); + console.log(""); + console.log("Filtering options:"); + console.log(" -l, --level LEVEL"); + console.log(" Only show messages at or above the specified level."); + console.log(" You can specify level *names* or numeric values."); + console.log(" (See 'Log Levels' below.)"); + console.log(" -c, --condition CONDITION"); + console.log(" Run each log message through the condition"); + console.log(" and only show those that resolve to a truish value."); + console.log(" E.g. `-c 'pid == 123'`, `-c 'level == 50'`. You must"); + console.log(" use the numeric values for filtering by level."); + console.log(" --strict Suppress all but legal Bunyan JSON log lines. By default"); + console.log(" non-JSON, and non-Bunyan lines are passed through."); + console.log(""); + console.log("Output options:"); + console.log(" --color Colorize output. Defaults to try if output"); + console.log(" stream is a TTY."); + console.log(" --no-color Force no coloring (e.g. terminal doesn't support it)"); + console.log(" -o, --output MODE"); + console.log(" Specify an output mode/format. One of"); + console.log(" paul: (the default) pretty"); + console.log(" json: JSON output, 2-space indent"); + console.log(" json-N: JSON output, N-space indent, e.g. 'json-4'"); + console.log(" inspect: node.js `util.inspect` output"); + console.log(" short: like paul, but more concise"); + console.log(" -j shortcut for `-o json`"); + console.log(""); + console.log("Log Levels:"); + console.log(" Either numeric values or their associated strings are valid for the"); + console.log(" -l|--level argument. However, -c|--condition scripts will see a numeric"); + console.log(" 'level' value, not a string."); + console.log(""); + Object.keys(levelFromName).forEach(function(name) { + var n = name; + while (n.length < 6) + n += " "; + console.log(" %s %d", n, levelFromName[name]); + }); + console.log(""); + console.log("See for more complete docs."); + console.log("Please report bugs to ."); +} + +/* + * If the user specifies multiple input sources, we want to print out records + * from all sources in a single, chronologically ordered stream. To do this + * efficiently, we first assume that all records within each source are ordered + * already, so we need only keep track of the next record in each source and + * the time of the last record emitted. To avoid excess memory usage, we + * pause() streams that are ahead of others. + * + * "streams" is an object indexed by source name (file name) which specifies: + * + * stream Actual stream object, so that we can pause and resume it. + * + * records Array of log records we've read, but not yet emitted. Each + * record includes "line" (the raw line), "rec" (the JSON + * record), and "time" (the parsed time value). + * + * done Whether the stream has any more records to emit. + */ +var streams = {}; + +function gotRecord(file, line, rec, opts, stylize) +{ + var time = new Date(rec.time); + + streams[file]['records'].push({ line: line, rec: rec, time: time }); + emitNextRecord(opts, stylize); +} + +function filterRecord(rec, opts) +{ + if (opts.level && rec.level < opts.level) { + return false; + } + + if (opts.conditions) { + for (var i = 0; i < opts.conditions.length; i++) { + var pass = opts.conditions[i].runInNewContext(rec); + if (!pass) + return false; + } + } + + return true; +} + +function emitNextRecord(opts, stylize) +{ + var ofile, ready, minfile, rec; + + for (;;) { + /* + * Take a first pass through the input streams to see if we have a record + * from all of them. If not, we'll pause any streams for which we do + * already have a record (to avoid consuming excess memory) and then wait + * until we have records from the others before emitting the next record. + * + * As part of the same pass, we look for the earliest record we have not yet + * emitted. + */ + minfile = undefined; + ready = true; + for (ofile in streams) { + + if (streams[ofile].stream === null || + (!streams[ofile].done && streams[ofile].records.length === 0)) { + ready = false; + break; + } + + if (streams[ofile].records.length > 0 && (minfile === undefined || + streams[minfile].records[0].time > streams[ofile].records[0].time)) { + minfile = ofile; + } + } + + if (!ready || minfile === undefined) { + for (ofile in streams) { + if (!streams[ofile].stream || streams[ofile].done) + continue; + + if (streams[ofile].records.length > 0) { + if (!streams[ofile].paused) { + streams[ofile].paused = true; + streams[ofile].stream.pause(); + } + } else if (streams[ofile].paused) { + streams[ofile].paused = false; + streams[ofile].stream.resume(); + } + } + + return; + } + + /* + * Emit the next record for "minfile", and invoke ourselves again to make + * sure we emit as many records as we can right now. + */ + rec = streams[minfile].records.shift(); + emitRecord(rec.rec, rec.line, opts, stylize); + } +} + +/** + * Parse the command-line options and arguments into an object. + * + * { + * 'args': [...] // arguments + * 'help': true, // true if '-h' option given + * // etc. + * } + * + * @return {Object} The parsed options. `.args` is the argument list. + * @throws {Error} If there is an error parsing argv. + */ +function parseArgv(argv) { + var parsed = { + args: [], + help: false, + color: process.stdout.isTTY, + outputMode: OM_PAUL, + jsonIndent: 2, + level: null, + conditions: null, + strict: false + }; + + // Turn '-iH' into '-i -H', except for argument-accepting options. + var args = argv.slice(2); // drop ['node', 'scriptname'] + var newArgs = []; + var optTakesArg = {'d': true, 'o': true, 'c': true, 'l': true}; + for (var i = 0; i < args.length; i++) { + if (args[i].charAt(0) === "-" && args[i].charAt(1) !== '-' && args[i].length > 2) { + var splitOpts = args[i].slice(1).split(""); + for (var j = 0; j < splitOpts.length; j++) { + newArgs.push('-' + splitOpts[j]); + if (optTakesArg[splitOpts[j]]) { + var optArg = splitOpts.slice(j+1).join(""); + if (optArg.length) { + newArgs.push(optArg); + } + break; + } + } + } else { + newArgs.push(args[i]); + } + } + args = newArgs; + + var condDefines = []; + Object.keys(upperNameFromLevel).forEach(function (lvl) { + condDefines.push( + format("Object.prototype.%s = %s;", upperNameFromLevel[lvl], lvl)); + }); + condDefines = condDefines.join('\n') + '\n'; + + var endOfOptions = false; + while (args.length > 0) { + var arg = args.shift(); + switch(arg) { + case "--": + endOfOptions = true; + break; + case "-h": // display help and exit + case "--help": + parsed.help = true; + break; + case "--version": + parsed.version = true; + break; + case "--strict": + parsed.strict = true; + break; + case "--color": + parsed.color = true; + break; + case "--no-color": + parsed.color = false; + break; + case "-o": + case "--output": + var name = args.shift(); + var idx = name.lastIndexOf('-'); + if (idx !== -1) { + var indent = Number(name.slice(idx+1)); + if (! isNaN(indent)) { + parsed.jsonIndent = indent; + name = name.slice(0, idx); + } + } + parsed.outputMode = OM_FROM_NAME[name]; + if (parsed.outputMode === undefined) { + throw new Error("unknown output mode: '"+name+"'"); + } + break; + case "-j": // output with JSON.stringify + parsed.outputMode = OM_JSON; + break; + case "-l": + case "--level": + var levelArg = args.shift(); + var level = +(levelArg); + if (isNaN(level)) { + level = +levelFromName[levelArg.toLowerCase()]; + } + if (isNaN(level)) { + throw new Error("unknown level value: '"+levelArg+"'"); + } + parsed.level = level; + break; + case "-c": + case "--condition": + var condition = args.shift(); + parsed.conditions = parsed.conditions || []; + var scriptName = 'bunyan-condition-'+parsed.conditions.length; + var script = vm.createScript(condDefines + condition, scriptName); + parsed.conditions.push(script); + break; + default: // arguments + if (!endOfOptions && arg.length > 0 && arg[0] === '-') { + throw new Error("unknown option '"+arg+"'"); + } + parsed.args.push(arg); + break; + } + } + //TODO: '--' handling and error on a first arg that looks like an option. + + return parsed; +} + + +function isInteger(s) { + return (s.search(/^-?[0-9]+$/) == 0); +} + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +// Suggested colors (some are unreadable in common cases): +// - Good: cyan, yellow (limited use), grey, bold, green, magenta, red +// - Bad: blue (not visible on cmd.exe) +var colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +function stylizeWithColor(str, color) { + if (!str) + return ''; + var codes = colors[color]; + if (codes) { + return '\033[' + codes[0] + 'm' + str + + '\033[' + codes[1] + 'm'; + } else { + return str; + } +} + +function stylizeWithoutColor(str, color) { + return str; +} + + +/** + * Is this a valid Bunyan log record. + */ +function isValidRecord(rec) { + if (rec.v == null || + rec.level == null || + rec.name == null || + rec.hostname == null || + rec.pid == null || + rec.time == null || + rec.msg == null) { + // Not valid Bunyan log. + return false; + } else { + return true; + } +} + + +/** + * Parses the given log line and either emits it right away (for invalid + * records) or enqueues it for emitting later when it's the next line to show. + */ +function handleLogLine(file, line, opts, stylize) { + // Emit non-JSON lines immediately. + var rec; + if (!line) { + if (!opts.strict) emit(line + '\n'); + return; + } else if (line[0] !== '{') { + if (!opts.strict) emit(line + '\n'); // not JSON + return; + } else { + try { + rec = JSON.parse(line); + } catch(e) { + if (!opts.strict) emit(line + '\n'); + return; + } + } + + if (!isValidRecord(rec)) { + if (!opts.strict) emitRecord(rec, line, opts, stylize); + return; + } + + if (!filterRecord(rec, opts)) + return; + + if (file === null) + return emitRecord(rec, line, opts, stylize); + + return gotRecord(file, line, rec, opts, stylize); +} + +/** + * Print out a single result, considering input options. + */ +function emitRecord(rec, line, opts, stylize) { + var short = false; + + switch (opts.outputMode) { + case OM_SHORT: + short = true; + /* jsl:fall-thru */ + + case OM_PAUL: + // [time] LEVEL: name[/component]/pid on hostname (src): msg* (extras...) + // msg* + // -- + // long and multi-line extras + // ... + // If 'msg' is single-line, then it goes in the top line. + // If 'req', show the request. + // If 'res', show the response. + // If 'err' and 'err.stack' then show that. + if (!isValidRecord(rec)) { + return emit(line + '\n'); + } + + delete rec.v; + + /* + * We assume the Date is formatted according to ISO8601, in which case we + * can safely chop off the date information. + */ + if (short && rec.time[10] == 'T') { + var time = rec.time.substr(11); + time = stylize(time, 'XXX'); + } else { + var time = stylize('[' + rec.time + ']', 'XXX'); + } + + delete rec.time; + + var nameStr = rec.name; + delete rec.name; + + if (rec.component) { + nameStr += '/' + rec.component; + } + delete rec.component; + + if (!short) + nameStr += '/' + rec.pid; + delete rec.pid; + + var level = (upperPaddedNameFromLevel[rec.level] || "LVL" + rec.level); + if (opts.color) { + var colorFromLevel = { + 10: 'grey', // TRACE + 20: 'grey', // DEBUG + 30: 'cyan', // INFO + 40: 'magenta', // WARN + 50: 'red', // ERROR + 60: 'inverse', // FATAL + }; + level = stylize(level, colorFromLevel[rec.level]); + } + delete rec.level; + + var src = ""; + if (rec.src && rec.src.file) { + var s = rec.src; + if (s.func) { + src = format(" (%s:%d in %s)", s.file, s.line, s.func); + } else { + src = format(" (%s:%d)", s.file, s.line); + } + src = stylize(src, 'green'); + } + delete rec.src; + + var hostname = rec.hostname; + delete rec.hostname; + + var extras = []; + var details = []; + + if (rec.req_id) { + extras.push("req_id=" + rec.req_id); + } + delete rec.req_id; + + if (rec.latency) { + extras.push(rec.latency + "ms"); + } + delete rec.latency; + + var onelineMsg; + if (rec.msg.indexOf('\n') !== -1) { + onelineMsg = ''; + details.push(indent(stylize(rec.msg, 'cyan'))); + } else { + onelineMsg = ' ' + stylize(rec.msg, 'cyan'); + } + delete rec.msg; + + if (rec.req) { + var req = rec.req; + var headers = req.headers; + var s = format("%s %s HTTP/%s%s", req.method, + req.url, + req.httpVersion || "1.1", + (headers + ? '\n' + Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n') + : '') + ); + delete req.url; + delete req.method; + delete req.httpVersion; + delete req.headers; + if (req.body) { + s += '\n\n' + (typeof(req.body) === 'object' + ? JSON.stringify(req.body, null, 2) : req.body); + delete req.body; + } + if (req.trailers && Object.keys(req.trailers) > 0) { + s += '\n' + Object.keys(req.trailers).map( + function (t) { return t + ': ' + req.trailers[t]; }).join('\n'); + } + delete req.trailers; + details.push(indent(s)); + // E.g. for extra 'foo' field on 'req', add 'req.foo' at top-level. + // This *does* have the potential to stomp on a literal 'req.foo' key. + Object.keys(req).forEach(function (k) { + rec["req." + k] = req[k]; + }) + } + delete rec.req; + + if (rec.client_req) { + var client_req = rec.client_req; + var headers = client_req.headers; + var hostHeaderLine = ''; + var s = ''; + if (client_req.address) { + hostHeaderLine = 'Host: ' + client_req.address; + if (client_req.port) + hostHeaderLine += ':' + client_req.port; + hostHeaderLine += '\n'; + } + delete client_req.headers; + delete client_req.address; + delete client_req.port; + s += format("%s %s HTTP/%s\n%s%s", client_req.method, + client_req.url, + client_req.httpVersion || "1.1", + hostHeaderLine, + Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n')); + delete client_req.method; + delete client_req.url; + delete client_req.httpVersion; + if (client_req.body) { + s += '\n\n' + (typeof(client_req.body) === 'object' + ? JSON.stringify(client_req.body, null, 2) : client_req.body); + delete client_req.body; + } + // E.g. for extra 'foo' field on 'client_req', add 'client_req.foo' at + // top-level. This *does* have the potential to stomp on a literal + // 'client_req.foo' key. + Object.keys(client_req).forEach(function (k) { + rec["client_req." + k] = client_req[k]; + }) + details.push(indent(s)); + } + delete rec.client_req; + + if (rec.res) { + var res = rec.res; + var s = ''; + if (res.header) { + s += res.header.trimRight(); + } else if (res.headers) { + if (res.statusCode) { + s += format("HTTP/1.1 %s %s\n", res.statusCode, + http.STATUS_CODES[res.statusCode]); + } + var headers = res.headers; + s += Object.keys(headers).map( + function (h) { return h + ': ' + headers[h]; }).join('\n'); + } + delete res.header; + delete res.headers; + delete res.statusCode; + if (res.body) { + s += '\n\n' + res.body + delete res.body; + } + if (res.trailer) { + s += '\n' + res.trailer; + } + delete res.trailer; + if (s) { + details.push(indent(s)); + } + // E.g. for extra 'foo' field on 'res', add 'res.foo' at top-level. + // This *does* have the potential to stomp on a literal 'res.foo' key. + Object.keys(res).forEach(function (k) { + rec["res." + k] = res[k]; + }) + } + delete rec.res; + + if (rec.err && rec.err.stack) { + details.push(indent(rec.err.stack)); + delete rec.err; + } + + var leftover = Object.keys(rec); + for (var i = 0; i < leftover.length; i++) { + var key = leftover[i]; + var value = rec[key]; + var stringified = false; + if (typeof(value) !== 'string') { + value = JSON.stringify(value, null, 2); + stringified = true; + } + if (value.indexOf('\n') !== -1 || value.length > 50) { + details.push(indent(key + ': ' + value)); + } else if (!stringified && (value.indexOf(' ') != -1 || + value.length === 0)) { + extras.push(key + '=' + JSON.stringify(value)); + } else { + extras.push(key + '=' + value); + } + } + + extras = stylize( + (extras.length ? ' (' + extras.join(', ') + ')' : ''), 'grey'); + details = stylize( + (details.length ? details.join('\n --\n') + '\n' : ''), 'grey'); + if (!short) + emit(format("%s %s: %s on %s%s:%s%s\n%s", + time, + level, + nameStr, + hostname || "", + src, + onelineMsg, + extras, + details)); + else + emit(format("%s %s %s:%s%s\n%s", + time, + level, + nameStr, + onelineMsg, + extras, + details)); + break; + + case OM_INSPECT: + emit(util.inspect(rec, false, Infinity, true) + '\n'); + break; + + case OM_JSON: + emit(JSON.stringify(rec, null, opts.jsonIndent) + '\n'); + break; + + case OM_SIMPLE: + // + if (!isValidRecord(rec)) { + return emit(line + '\n'); + } + emit(format("%s - %s\n", upperNameFromLevel[rec.level] || "LVL" + rec.level, + rec.msg)); + break; + default: + throw new Error("unknown output mode: "+opts.outputMode); + } +} + + +var stdoutFlushed = true; +function emit(s) { + // TODO:PERF If this is try/catch is too slow (too granular): move up to + // mainline and be sure to only catch this particular error. + try { + stdoutFlushed = process.stdout.write(s); + } catch (e) { + // Handle any exceptions in stdout writing in the "error" event above. + } +} + +process.stdout.on("error", function (err) { + if (err.code === "EPIPE") { + // Pass. See . + } else { + warn(err); + drainStdoutAndExit(1); + } +}); + + +/** + * A hacked up version of "process.exit" that will first drain stdout + * before exiting. *WARNING: This doesn't stop event processing.* IOW, + * callers have to be careful that code following this call isn't + * accidentally executed. + * + * In node v0.6 "process.stdout and process.stderr are blocking when they + * refer to regular files or TTY file descriptors." However, this hack might + * still be necessary in a shell pipeline. + */ +function drainStdoutAndExit(code) { + process.stdout.on('drain', function () { + process.exit(code); + }); + if (stdoutFlushed) { + process.exit(code); + } +} + + +/** + * Process all input from stdin. + * + * @params opts {Object} Bunyan options object. + * @param stylize {Function} Output stylize function to use. + * @param callback {Function} `function ()` + */ +function processStdin(opts, stylize, callback) { + var leftover = ""; // Left-over partial line from last chunk. + var stdin = process.stdin; + stdin.resume(); + stdin.setEncoding('utf8'); + stdin.on('data', function (chunk) { + var lines = chunk.split(/\r\n|\n/); + var length = lines.length; + if (length === 1) { + leftover += lines[0]; + return; + } + + if (length > 1) { + handleLogLine(null, leftover + lines[0], opts, stylize); + } + leftover = lines.pop(); + length -= 1; + for (var i=1; i < length; i++) { + handleLogLine(null, lines[i], opts, stylize); + } + }); + stdin.on('end', function () { + if (leftover) { + handleLogLine(null, leftover, opts, stylize); + leftover = ''; + } + callback(); + }); +} + + +/** + * Process all input from the given log file. + * + * @param file {String} Log file path to process. + * @params opts {Object} Bunyan options object. + * @param stylize {Function} Output stylize function to use. + * @param callback {Function} `function ()` + */ +function processFile(file, opts, stylize, callback) { + var stream = fs.createReadStream(file); + if (/\.gz$/.test(file)) { + stream = stream.pipe(require('zlib').createGunzip()); + } + // Manually decode streams - lazy load here as per node/lib/fs.js + var decoder = new (require('string_decoder').StringDecoder)('utf8'); + + streams[file].stream = stream; + + stream.on('error', function (err) { + streams[file].done = true; + callback(err); + }); + + var leftover = ''; // Left-over partial line from last chunk. + stream.on('data', function (data) { + var chunk = decoder.write(data); + if (!chunk.length) { + return; + } + var lines = chunk.split(/\r\n|\n/); + var length = lines.length; + if (length === 1) { + leftover += lines[0]; + return; + } + + if (length > 1) { + handleLogLine(file, leftover + lines[0], opts, stylize); + } + leftover = lines.pop(); + length -= 1; + for (var i=1; i < length; i++) { + handleLogLine(file, lines[i], opts, stylize); + } + }); + + stream.on('end', function () { + streams[file].done = true; + if (leftover) { + handleLogLine(file, leftover, opts, stylize); + leftover = ''; + } else { + emitNextRecord(opts, stylize); + } + callback(); + }); +} + + +/** + * From node async module. + */ +function asyncForEach(arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + arr.forEach(function (x) { + iterator(x, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(); + } + } + }); + }); +}; + + + +//---- mainline + +function main(argv) { + var opts; + try { + opts = parseArgv(argv); + } catch (e) { + warn("bunyan: error: %s", e.message); + return drainStdoutAndExit(1); + } + if (opts.help) { + printHelp(); + return; + } + if (opts.version) { + console.log("bunyan " + getVersion()); + return; + } + var stylize = (opts.color ? stylizeWithColor : stylizeWithoutColor); + + var retval = 0; + if (opts.args.length > 0) { + var files = opts.args; + files.forEach(function (file) { + streams[file] = { stream: null, records: [], done: false } + }); + asyncForEach(files, + function (file, next) { + processFile(file, opts, stylize, function (err) { + if (err) { + warn('bunyan: %s', err.message); + retval += 1; + } + next(); + }); + }, + function (err) { + if (err) { + warn("bunyan: unexpected error: %s", err.stack || err); + return drainStdoutAndExit(1); + } + process.exit(retval); + } + ); + } else { + processStdin(opts, stylize, function () { + process.exit(retval); + }); + } +} + +if (require.main === module) { + // HACK guard for . + // We override the `process.stdout.end` guard that core node.js puts in + // place. The real fix is that `.end()` shouldn't be called on stdout + // in node core. Node v0.6.9 fixes that. Only guard for v0.6.0..v0.6.8. + var nodeVer = process.versions.node.split('.').map(Number); + if ([0,6,0] <= nodeVer && nodeVer <= [0,6,8]) { + var stdout = process.stdout; + stdout.end = stdout.destroy = stdout.destroySoon = function() { + /* pass */ + }; + } + + main(process.argv); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/err.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/err.js new file mode 100644 index 0000000..2eacc7f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/err.js @@ -0,0 +1,58 @@ +// Example logging an error: + +var http = require('http'); +var Logger = require('../lib/bunyan'); +var util = require('util'); + +var log = new Logger({ + name: 'myserver', + serializers: { + err: Logger.stdSerializers.err, // <--- use this + } +}); + +try { + throw new TypeError('boom'); +} catch (err) { + log.warn({err: err}, 'operation went boom: %s', err) // <--- here +} + +log.info(new TypeError('how about this?')) // <--- alternatively this + + +try { + throw 'boom string'; +} catch (err) { + log.error(err) +} + +/* BEGIN JSSTYLED */ +/** + * + * $ node err.js | ../bin/bunyan -j + * { + * "name": "myserver", + * "hostname": "banana.local", + * "err": { + * "stack": "TypeError: boom\n at Object. (/Users/trentm/tm/node-bunyan/examples/err.js:15:9)\n at Module._compile (module.js:411:26)\n at Object..js (module.js:417:10)\n at Module.load (module.js:343:31)\n at Function._load (module.js:302:12)\n at Array.0 (module.js:430:10)\n at EventEmitter._tickCallback (node.js:126:26)", + * "name": "TypeError", + * "message": "boom" + * }, + * "level": 4, + * "msg": "operation went boom: TypeError: boom", + * "time": "2012-02-02T04:42:53.206Z", + * "v": 0 + * } + * $ node err.js | ../bin/bunyan + * [2012-02-02T05:02:39.412Z] WARN: myserver on banana.local: operation went boom: TypeError: boom + * TypeError: boom + * at Object. (/Users/trentm/tm/node-bunyan/examples/err.js:15:9) + * at Module._compile (module.js:411:26) + * at Object..js (module.js:417:10) + * at Module.load (module.js:343:31) + * at Function._load (module.js:302:12) + * at Array.0 (module.js:430:10) + * at EventEmitter._tickCallback (node.js:126:26) + * + */ +/* END JSSTYLED */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/handle-fs-error.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/handle-fs-error.js new file mode 100644 index 0000000..af08056 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/handle-fs-error.js @@ -0,0 +1,36 @@ +// Example handling an fs error for a Bunyan-created +// stream: we create a logger to a file that is read-only. + +var fs = require('fs'); +var path = require('path'); +var Logger = require('../lib/bunyan'); + +var FILENAME = 'handle-fs-error.log'; +var S_IWUSR = 00200; // mask for owner write permission in stat mode + +console.warn('- Log file is "%s".', FILENAME); +if (!path.existsSync(FILENAME)) { + console.warn('- Touch log file.'); + fs.writeFileSync(FILENAME, 'touch\n'); +} +if (fs.statSync(FILENAME).mode & S_IWUSR) { + console.warn('- Make log file read-only.'); + fs.chmodSync(FILENAME, 0444); +} + +console.warn('- Create logger.') +var log = new Logger({name: 'handle-fs-error', streams: [{path: FILENAME}]}); + +log.on('error', function (err) { + console.warn('- The logger emitted an error:', err); +}); + +console.warn('- Call log.info(...).') +log.info('info log message'); +console.warn('- Called log.info(...).') + +setTimeout(function () { + console.warn('- Call log.warn(...).') + log.warn('warn log message'); + console.warn('- Called log.warn(...).') +}, 1000); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/hi.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/hi.js new file mode 100644 index 0000000..0f5f21b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/hi.js @@ -0,0 +1,32 @@ +var Logger = require('../lib/bunyan'); + +// Basic usage. +var log = new Logger({name: 'myapp', level: 'info', src: true}); + +// isInfoEnabled replacement +console.log('log.info() is:', log.info()) + +// `util.format`-based printf handling +log.info('hi'); +log.info('hi', 'trent'); +log.info('hi %s there', true); + +// First arg as an object adds fields to the log record. +log.info({foo:'bar', multiline:'one\ntwo\nthree'}, 'hi %d', 1, 'two', 3); + + +// Shows `log.child(...)` to specialize a logger for a sub-component. +console.log('\n') + +function Wuzzle(options) { + this.log = options.log; + this.log.info('creating a wuzzle') +} + +Wuzzle.prototype.woos = function () { + this.log.warn('This wuzzle is woosey.') +} + +var wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})}); +wuzzle.woos(); +log.info('done with the wuzzle') diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/level.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/level.js new file mode 100644 index 0000000..80bb563 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/level.js @@ -0,0 +1,46 @@ +// Play with setting levels. +// +// TODO: put this in a damn test suite + +var Logger = require('../lib/bunyan'), + DEBUG = Logger.DEBUG, + INFO = Logger.INFO, + WARN = Logger.WARN; +var assert = require('assert'); + +// Basic usage. +var log = new Logger({ + name: 'example-level', + streams: [ + { + name: 'stdout', + stream: process.stdout, + level: 'debug' + }, + { + name: 'stderr', + stream: process.stderr + } + ] +}); + +assert.equal(log.level(), DEBUG); +assert.equal(log.levels()[0], DEBUG); +assert.equal(log.levels()[1], INFO); +assert.equal(log.levels(0), DEBUG); +assert.equal(log.levels(1), INFO); + +assert.equal(log.levels('stdout'), DEBUG) +try { + log.levels('foo') +} catch (e) { + assert.ok(e.message.indexOf('name') !== -1) +} + +log.trace('no one should see this') +log.debug('should see this once (on stdout)') +log.info('should see this twice') +log.levels('stdout', INFO) +log.debug('no one should see this either') +log.level('trace') +log.trace('should see this twice as 4th and 5th emitted log messages') diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/multi.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/multi.js new file mode 100644 index 0000000..81400ee --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/multi.js @@ -0,0 +1,20 @@ +var Logger = require('../lib/bunyan'); +log = new Logger({ + name: 'amon', + streams: [ + { + level: 'info', + stream: process.stdout, + }, + { + level: 'error', + path: 'multi.log' + } + ] +}); + + +log.debug('hi nobody on debug'); +log.info('hi stdout on info'); +log.error('hi both on error'); +log.fatal('hi both on fatal'); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/raw-stream.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/raw-stream.js new file mode 100644 index 0000000..32b905b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/raw-stream.js @@ -0,0 +1,37 @@ +// Example of a "raw" stream in a Bunyan Logger. A raw stream is one to +// which log record *objects* are written instead of the JSON-serialized +// string. + +var Logger = require('../lib/bunyan'); + + +/** + * A raw Bunyan Logger stream object. It takes raw log records and writes + * them to stdout with an added "yo": "yo" field. + */ +function MyRawStream() {} +MyRawStream.prototype.write = function (rec) { + if (typeof (rec) !== 'object') { + console.error('error: raw stream got a non-object record: %j', rec) + } else { + rec.yo = 'yo'; + process.stdout.write(JSON.stringify(rec) + '\n'); + } +} + + +// A Logger using the raw stream. +var log = new Logger({ + name: 'raw-example', + streams: [ + { + level: 'info', + stream: new MyRawStream(), + type: 'raw' + }, + ] +}); + + +log.info('hi raw stream'); +log.info({foo: 'bar'}, 'added "foo" key'); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/ringbuffer.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/ringbuffer.js new file mode 100644 index 0000000..74ffb31 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/ringbuffer.js @@ -0,0 +1,14 @@ +/* Create a ring buffer that stores the last 100 records. */ +var bunyan = require('..'); +var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); +var log = new bunyan({ + name: 'foo', + streams: [{ + type: 'raw', + stream: ringbuffer, + level: 'debug' + }] +}); + +log.info('hello world'); +console.log(ringbuffer.records); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/server.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/server.js new file mode 100644 index 0000000..c8bdac1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/server.js @@ -0,0 +1,63 @@ +// Example logging HTTP server request and response objects. + +var http = require('http'); +var Logger = require('../lib/bunyan'); + +var log = new Logger({ + name: 'myserver', + serializers: { + req: Logger.stdSerializers.req, + res: Logger.stdSerializers.res + } +}); + +var server = http.createServer(function (req, res) { + log.info({req: req}, 'start request'); // <-- this is the guy we're testing + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Hello World\n'); + log.info({res: res}, 'done response'); // <-- this is the guy we're testing +}); +server.listen(1337, '127.0.0.1', function () { + log.info('server listening'); + var options = { + port: 1337, + hostname: '127.0.0.1', + path: '/path?q=1#anchor', + headers: { + 'X-Hi': 'Mom' + } + }; + var req = http.request(options); + req.on('response', function (res) { + res.on('end', function () { + process.exit(); + }) + }); + req.write('hi from the client'); + req.end(); +}); + + +/* BEGIN JSSTYLED */ +/** + * + * $ node server.js + * {"service":"myserver","hostname":"banana.local","level":3,"msg":"server listening","time":"2012-02-02T05:32:13.257Z","v":0} + * {"service":"myserver","hostname":"banana.local","req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","connection":"close"}},"level":3,"msg":"start request","time":"2012-02-02T05:32:13.260Z","v":0} + * {"service":"myserver","hostname":"banana.local","res":{"statusCode":200,"_hasBody":true,"_header":"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\nTransfer-Encoding: chunked\r\n\r\n","_trailer":""},"level":3,"msg":"done response","time":"2012-02-02T05:32:13.261Z","v":0} + * + * $ node server.js | ../bin/bunyan + * [2012-02-02T05:32:16.006Z] INFO: myserver on banana.local: server listening + * [2012-02-02T05:32:16.010Z] INFO: myserver on banana.local: start request + * GET /path?q=1#anchor + * x-hi: Mom + * connection: close + * [2012-02-02T05:32:16.011Z] INFO: myserver on banana.local: done response + * HTTP/1.1 200 OK + * Content-Type: text/plain + * Connection: close + * Transfer-Encoding: chunked + * (body) + * + */ +/* END JSSTYLED */ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/src.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/src.js new file mode 100644 index 0000000..fe4ab50 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/src.js @@ -0,0 +1,25 @@ +// Show the usage of `src: true` config option to get log call source info in +// log records (the `src` field). + +var Logger = require('../lib/bunyan'); + +var log = new Logger({name: 'src-example', src: true}); + +log.info('one'); +log.info('two'); +function doSomeFoo() { + log.info({foo:'bar'}, 'three'); +} +doSomeFoo(); + +function Wuzzle(options) { + this.log = options.log; + this.log.info('creating a wuzzle') +} +Wuzzle.prototype.woos = function () { + this.log.warn('This wuzzle is woosey.') +} + +var wuzzle = new Wuzzle({log: log.child({component: 'wuzzle'})}); +wuzzle.woos(); +log.info('done with the wuzzle') diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/unstringifyable.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/unstringifyable.js new file mode 100644 index 0000000..ddad545 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/examples/unstringifyable.js @@ -0,0 +1,12 @@ +// See how bunyan behaves with an un-stringify-able object. +var Logger = require('../lib/bunyan'); + +var log = new Logger({src: true, name: 'foo'}); + +// Make a circular object (cannot be JSON-ified). +var myobj = { + foo: 'bar' +}; +myobj.myobj = myobj; + +log.info({obj: myobj}, 'hi there'); // <--- here diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/follow.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/follow.js new file mode 100644 index 0000000..113dcaa --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/follow.js @@ -0,0 +1,25 @@ +//var fs = require('fs'); + +//fs.watch('a.log', {}, function (ev, fn) { +// console.log('event', ev, fn); +//}); + +// fs.open(path, flags, [mode], [callback]) +// fs.openSync(path, flags, [mode]) + +// fs.read(fd, buffer, offset, length, position, [callback]) + + +//var tailf = require('tailf'); +// +//var watchinglog = new tailf.simpleTailf('a.log'); +// +//watchinglog.on('data', function (data) { +// console.log('Data arrived: ' , data.toString()); +//}); + +var tail = require('tailfd').tail, +watcher = tail('a.log',function(line,tailInfo){ + //default line listener. optional. + console.log('line of data> ',line); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/foo.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/foo.js new file mode 100644 index 0000000..8f769a2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/foo.js @@ -0,0 +1,6 @@ +var b = require('./lib/bunyan'); +var log = b.createLogger({name: 'foo'}) +var data = new Buffer([72,84,84,80,47,49,46,49]); +log.info({buf: data}, 'my message'); + +//console.log(data.length, data.toString()) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/lib/bunyan.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/lib/bunyan.js new file mode 100644 index 0000000..76efcf3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/lib/bunyan.js @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * The bunyan logging library for node.js. + */ + +var VERSION = '0.14.0'; + +// Bunyan log format version. This becomes the 'v' field on all log records. +// `0` is until I release a version '1.0.0' of node-bunyan. Thereafter, +// starting with `1`, this will be incremented if there is any backward +// incompatible change to the log record format. Details will be in +// 'CHANGES.md' (the change log). +var LOG_VERSION = 0; + + +var xxx = function xxx(s) { // internal dev/debug logging + var args = ['XX' + 'X: '+s].concat(Array.prototype.slice.call(arguments, 1)); + console.error.apply(this, args); +}; +var xxx = function xxx() {}; // uncomment to turn of debug logging + + +var os = require('os'); +var fs = require('fs'); +var util = require('util'); +var assert = require('assert'); +var EventEmitter = require('events').EventEmitter; + + + +//---- Internal support stuff + +function objCopy(obj) { + if (obj === null) { + return null; + } else if (Array.isArray(obj)) { + return obj.slice(); + } else { + var copy = {}; + Object.keys(obj).forEach(function (k) { + copy[k] = obj[k]; + }); + return copy; + } +} + +var format = util.format; +if (!format) { + // If not node 0.6, then use its `util.format`: + // : + var inspect = util.inspect; + var formatRegExp = /%[sdj%]/g; + format = function format(f) { + if (typeof (f) !== 'string') { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function (x) { + if (i >= len) + return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': return JSON.stringify(args[i++], safeCycles()); + case '%%': return '%'; + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (x === null || typeof (x) !== 'object') { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; + }; +} + + +/** + * Gather some caller info 3 stack levels up. + * See . + */ +function getCaller3Info() { + var obj = {}; + var saveLimit = Error.stackTraceLimit; + var savePrepare = Error.prepareStackTrace; + Error.stackTraceLimit = 3; + Error.captureStackTrace(this, getCaller3Info); + Error.prepareStackTrace = function (_, stack) { + var caller = stack[2]; + obj.file = caller.getFileName(); + obj.line = caller.getLineNumber(); + var func = caller.getFunctionName(); + if (func) + obj.func = func; + }; + this.stack; + Error.stackTraceLimit = saveLimit; + Error.prepareStackTrace = savePrepare; + return obj; +} + + +/** + * Warn about an bunyan processing error. + * + * If file/line are given, this makes an attempt to warn on stderr only once. + * + * @param msg {String} Message with which to warn. + * @param file {String} Optional. File path relevant for the warning. + * @param line {String} Optional. Line number in `file` path relevant for + * the warning. + */ +function _warn(msg, file, line) { + assert.ok(msg); + var key; + if (file && line) { + key = file + ':' + line; + if (_warned[key]) { + return; + } + _warned[key] = true; + } + process.stderr.write(msg + '\n'); +} +var _warned = {}; + + + +//---- Levels + +var TRACE = 10; +var DEBUG = 20; +var INFO = 30; +var WARN = 40; +var ERROR = 50; +var FATAL = 60; + +var levelFromName = { + 'trace': TRACE, + 'debug': DEBUG, + 'info': INFO, + 'warn': WARN, + 'error': ERROR, + 'fatal': FATAL +}; + + +/** + * Resolve a level number, name (upper or lowercase) to a level number value. + * + * @api public + */ +function resolveLevel(nameOrNum) { + var level = (typeof (nameOrNum) === 'string' + ? levelFromName[nameOrNum.toLowerCase()] + : nameOrNum); + if (! (TRACE <= level && level <= FATAL)) { + throw new Error('invalid level: ' + nameOrNum); + } + return level; +} + + + +//---- Logger class + +/** + * Create a Logger instance. + * + * @param options {Object} See documentation for full details. At minimum + * this must include a 'name' string key. Configuration keys: + * - `streams`: specify the logger output streams. This is an array of + * objects with these fields: + * - `type`: The stream type. See README.md for full details. + * Often this is implied by the other fields. Examples are + * "file", "stream" and "raw". + * - `level`: Defaults to "info". + * - `path` or `stream`: The specify the file path or writeable + * stream to which log records are written. E.g. + * `stream: process.stdout`. + * - `closeOnExit` (boolean): Optional. Default is true for a + * "file" stream when `path` is given, false otherwise. + * See README.md for full details. + * - `level`: set the level for a single output stream (cannot be used + * with `streams`) + * - `stream`: the output stream for a logger with just one, e.g. + * `process.stdout` (cannot be used with `streams`) + * - `serializers`: object mapping log record field names to + * serializing functions. See README.md for details. + * - `src`: Boolean (default false). Set true to enable 'src' automatic + * field with log call source info. + * All other keys are log record fields. + * + * An alternative *internal* call signature is used for creating a child: + * new Logger(, [, ]); + * + * @param _childSimple (Boolean) An assertion that the given `_childOptions` + * (a) only add fields (no config) and (b) no serialization handling is + * required for them. IOW, this is a fast path for frequent child + * creation. + */ +function Logger(options, _childOptions, _childSimple) { + xxx('Logger start:', options) + if (! this instanceof Logger) { + return new Logger(options, _childOptions); + } + + // Input arg validation. + var parent; + if (_childOptions !== undefined) { + parent = options; + options = _childOptions; + if (! parent instanceof Logger) { + throw new TypeError('invalid Logger creation: do not pass a second arg'); + } + } + if (!options) { + throw new TypeError('options (object) is required'); + } + if (!parent) { + if (!options.name) { + throw new TypeError('options.name (string) is required'); + } + } else { + if (options.name) { + throw new TypeError('invalid options.name: child cannot set logger name'); + } + } + if ((options.stream || options.level) && options.streams) { + throw new TypeError( + 'cannot mix "streams" with "stream" or "level" options'); + } + if (options.streams && !Array.isArray(options.streams)) { + throw new TypeError('invalid options.streams: must be an array') + } + if (options.serializers && (typeof (options.serializers) !== 'object' || + Array.isArray(options.serializers))) { + throw new TypeError('invalid options.serializers: must be an object') + } + + EventEmitter.call(this); + + // Fast path for simple child creation. + if (parent && _childSimple) { + // `_isSimpleChild` is a signal to stream close handling that this child + // owns none of its streams. + this._isSimpleChild = true; + + this._level = parent._level; + this.streams = parent.streams; + this.serializers = parent.serializers; + this.src = parent.src; + var fields = this.fields = {}; + var parentFieldNames = Object.keys(parent.fields); + for (var i = 0; i < parentFieldNames.length; i++) { + var name = parentFieldNames[i]; + fields[name] = parent.fields[name]; + } + var names = Object.keys(options); + for (var i = 0; i < names.length; i++) { + var name = names[i]; + fields[name] = options[name]; + } + return; + } + + // Null values. + var self = this; + if (parent) { + this._level = parent._level; + this.streams = []; + for (var i = 0; i < parent.streams.length; i++) { + var s = objCopy(parent.streams[i]); + s.closeOnExit = false; // Don't own parent stream. + this.streams.push(s); + } + this.serializers = objCopy(parent.serializers); + this.src = parent.src; + this.fields = objCopy(parent.fields); + } else { + this._level = Number.POSITIVE_INFINITY; + this.streams = []; + this.serializers = null; + this.src = false; + this.fields = {}; + } + + // Helpers + function addStream(s) { + s = objCopy(s); + + // Implicit 'type' from other args. + var type = s.type; + if (!s.type) { + if (s.stream) { + s.type = 'stream'; + } else if (s.path) { + s.type = 'file' + } + } + s.raw = (s.type === 'raw'); // PERF: Allow for faster check in `_emit`. + + if (s.level) { + s.level = resolveLevel(s.level); + } else { + s.level = INFO; + } + if (s.level < self._level) { + self._level = s.level; + } + + switch (s.type) { + case 'stream': + if (!s.closeOnExit) { + s.closeOnExit = false; + } + break; + case 'file': + if (!s.stream) { + s.stream = fs.createWriteStream(s.path, + {flags: 'a', encoding: 'utf8'}); + s.stream.on('error', function (err) { + self.emit('error', err, s); + }); + if (!s.closeOnExit) { + s.closeOnExit = true; + } + } else { + if (!s.closeOnExit) { + s.closeOnExit = false; + } + } + break; + case 'raw': + if (!s.closeOnExit) { + s.closeOnExit = false; + } + break; + default: + throw new TypeError('unknown stream type "' + s.type + '"'); + } + + self.streams.push(s); + } + + function addSerializers(serializers) { + if (!self.serializers) { + self.serializers = {}; + } + Object.keys(serializers).forEach(function (field) { + var serializer = serializers[field]; + if (typeof (serializer) !== 'function') { + throw new TypeError(format( + 'invalid serializer for "%s" field: must be a function', field)); + } else { + self.serializers[field] = serializer; + } + }); + } + + // Handle *config* options. + if (options.stream) { + addStream({ + type: 'stream', + stream: options.stream, + closeOnExit: false, + level: (options.level ? resolveLevel(options.level) : INFO) + }); + } else if (options.streams) { + options.streams.forEach(addStream); + } else if (!parent) { + addStream({ + type: 'stream', + stream: process.stdout, + closeOnExit: false, + level: (options.level ? resolveLevel(options.level) : INFO) + }); + } + if (options.serializers) { + addSerializers(options.serializers); + } + if (options.src) { + this.src = true; + } + xxx('Logger: ', self) + + // Fields. + // These are the default fields for log records (minus the attributes + // removed in this constructor). To allow storing raw log records + // (unrendered), `this.fields` must never be mutated. Create a copy for + // any changes. + var fields = objCopy(options); + delete fields.stream; + delete fields.level; + delete fields.streams; + delete fields.serializers; + delete fields.src; + if (this.serializers) { + this._applySerializers(fields); + } + if (!fields.hostname) { + fields.hostname = os.hostname(); + } + if (!fields.pid) { + fields.pid = process.pid; + } + Object.keys(fields).forEach(function (k) { + self.fields[k] = fields[k]; + }); +} + +util.inherits(Logger, EventEmitter); + + +/** + * Create a child logger, typically to add a few log record fields. + * + * This can be useful when passing a logger to a sub-component, e.g. a + * 'wuzzle' component of your service: + * + * var wuzzleLog = log.child({component: 'wuzzle'}) + * var wuzzle = new Wuzzle({..., log: wuzzleLog}) + * + * Then log records from the wuzzle code will have the same structure as + * the app log, *plus the component='wuzzle' field*. + * + * @param options {Object} Optional. Set of options to apply to the child. + * All of the same options for a new Logger apply here. Notes: + * - The parent's streams are inherited and cannot be removed in this + * call. + * - The parent's serializers are inherited, though can effectively be + * overwritten by using duplicate keys. + * @param simple {Boolean} Optional. Set to true to assert that `options` + * (a) only add fields (no config) and (b) no serialization handling is + * required for them. IOW, this is a fast path for frequent child + * creation. See 'tools/timechild.js' for numbers. + */ +Logger.prototype.child = function (options, simple) { + return new Logger(this, options || {}, simple); +} + + +/* BEGIN JSSTYLED */ +/** + * Close this logger. + * + * This closes streams (that it owns, as per 'endOnClose' attributes on + * streams), etc. Typically you **don't** need to bother calling this. +Logger.prototype.close = function () { + if (this._closed) { + return; + } + if (!this._isSimpleChild) { + self.streams.forEach(function (s) { + if (s.endOnClose) { + xxx('closing stream s:', s); + s.stream.end(); + s.endOnClose = false; + } + }); + } + this._closed = true; +} + */ +/* END JSSTYLED */ + + +/** + * Get/set the level of all streams on this logger. + * + * Get Usage: + * // Returns the current log level (lowest level of all its streams). + * log.level() -> INFO + * + * Set Usage: + * log.level(INFO) // set all streams to level INFO + * log.level('info') // can use 'info' et al aliases + */ +Logger.prototype.level = function level(value) { + if (value === undefined) { + return this._level; + } + var newLevel = resolveLevel(value); + var len = this.streams.length; + for (var i = 0; i < len; i++) { + this.streams[i].level = newLevel; + } + this._level = newLevel; +} + + +/** + * Get/set the level of a particular stream on this logger. + * + * Get Usage: + * // Returns an array of the levels of each stream. + * log.levels() -> [TRACE, INFO] + * + * // Returns a level of the identified stream. + * log.levels(0) -> TRACE // level of stream at index 0 + * log.levels('foo') // level of stream with name 'foo' + * + * Set Usage: + * log.levels(0, INFO) // set level of stream 0 to INFO + * log.levels(0, 'info') // can use 'info' et al aliases + * log.levels('foo', WARN) // set stream named 'foo' to WARN + * + * Stream names: When streams are defined, they can optionally be given + * a name. For example, + * log = new Logger({ + * streams: [ + * { + * name: 'foo', + * path: '/var/log/my-service/foo.log' + * level: 'trace' + * }, + * ... + * + * @param name {String|Number} The stream index or name. + * @param value {Number|String} The level value (INFO) or alias ('info'). + * If not given, this is a 'get' operation. + * @throws {Error} If there is no stream with the given name. + */ +Logger.prototype.levels = function levels(name, value) { + if (name === undefined) { + assert.equal(value, undefined); + return this.streams.map( + function (s) { return s.level }); + } + var stream; + if (typeof (name) === 'number') { + stream = this.streams[name]; + if (stream === undefined) { + throw new Error('invalid stream index: ' + name); + } + } else { + var len = this.streams.length; + for (var i = 0; i < len; i++) { + var s = this.streams[i]; + if (s.name === name) { + stream = s; + break; + } + } + if (!stream) { + throw new Error(format('no stream with name "%s"', name)); + } + } + if (value === undefined) { + return stream.level; + } else { + var newLevel = resolveLevel(value); + stream.level = newLevel; + if (newLevel < this._level) { + this._level = newLevel; + } + } +} + + +/** + * Apply registered serializers to the appropriate keys in the given fields. + * + * Pre-condition: This is only called if there is at least one serializer. + * + * @param fields (Object) The log record fields. + * @param keys (Array) Optional array of keys to which to limit processing. + */ +Logger.prototype._applySerializers = function (fields, keys) { + var self = this; + + // Mapping of keys to potentially serialize. + var applyKeys = fields; + if (keys) { + applyKeys = {}; + for (var i = 0; i < keys.length; i++) { + applyKeys[keys[i]] = true; + } + } + + xxx('_applySerializers: applyKeys', applyKeys); + + // Check each serializer against these (presuming number of serializers + // is typically less than number of fields). + Object.keys(this.serializers).forEach(function (name) { + if (applyKeys[name]) { + xxx('_applySerializers; apply to "%s" key', name) + try { + fields[name] = self.serializers[name](fields[name]); + } catch (err) { + _warn(format('bunyan: ERROR: This should never happen. ' + + 'This is a bug in or ' + + 'in this application. Exception from "%s" Logger serializer: %s', + name, err.stack || err)); + fields[name] = format('(Error in Bunyan log "%s" serializer ' + + 'broke field. See stderr for details.)', name); + } + } + }); +} + + +/** + * A log record is a 4-tuple: + * [, + * , + * , + * ] + * For Perf reasons, we only render this down to a single object when + * it is emitted. + */ +Logger.prototype._mkRecord = function (fields, level, msgArgs) { + var recFields = (fields ? objCopy(fields) : null); + return [this.fields, recFields, level, msgArgs]; +} + + +/** + * Emit a log record. + * + * @param rec {log record} + */ +Logger.prototype._emit = function (rec) { + var i; + + var obj = objCopy(rec[0]); + var level = obj.level = rec[2]; + var recFields = rec[1]; + if (recFields) { + if (this.serializers) { + this._applySerializers(recFields); + } + Object.keys(recFields).forEach(function (k) { + obj[k] = recFields[k]; + }); + } + xxx('Record:', rec) + obj.msg = format.apply(this, rec[3]); + if (!obj.time) { + obj.time = (new Date()); + } + // Get call source info + if (this.src && !obj.src) { + obj.src = getCaller3Info() + } + obj.v = LOG_VERSION; + + // Lazily determine if this Logger has non-"raw" streams. If there are + // any, then we need to stringify the log record. + if (this.haveNonRawStreams === undefined) { + this.haveNonRawStreams = false; + for (i = 0; i < this.streams.length; i++) { + if (!this.streams[i].raw) { + this.haveNonRawStreams = true; + break; + } + } + } + + // Stringify the object. Attempt to warn/recover on error. + var str; + if (this.haveNonRawStreams) { + str = JSON.stringify(obj, safeCycles()) + '\n'; + } + + for (i = 0; i < this.streams.length; i++) { + var s = this.streams[i]; + if (s.level <= level) { + xxx('writing log rec "%s" to "%s" stream (%d <= %d): %j', + obj.msg, s.type, s.level, level, obj); + s.stream.write(s.raw ? obj : str); + } + }; +} + + +/** + * Log a record at TRACE level. + * + * Usages: + * log.trace() -> boolean is-trace-enabled + * log.trace( err, [ msg, ...]) + * log.trace( msg, ...) + * log.trace( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.trace = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.trace()` + return (this._level <= TRACE); + } else if (this._level > TRACE) { + return; + } else if (arguments[0] instanceof Error) { + // `log.trace(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.trace(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.trace(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.trace(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, TRACE, msgArgs); + this._emit(rec); +} + +/** + * Log a record at DEBUG level. + * + * Usages: + * log.debug() -> boolean is-debug-enabled + * log.debug( err, [ msg, ...]) + * log.debug( msg, ...) + * log.debug( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.debug = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.debug()` + return (this._level <= DEBUG); + } else if (this._level > DEBUG) { + return; + } else if (arguments[0] instanceof Error) { + // `log.debug(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.debug(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.debug(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.debug(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, DEBUG, msgArgs); + this._emit(rec); +} + +/** + * Log a record at INFO level. + * + * Usages: + * log.info() -> boolean is-info-enabled + * log.info( err, [ msg, ...]) + * log.info( msg, ...) + * log.info( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.info = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.info()` + return (this._level <= INFO); + } else if (this._level > INFO) { + return; + } else if (arguments[0] instanceof Error) { + // `log.info(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.info(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.info(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.info(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, INFO, msgArgs); + this._emit(rec); +} + +/** + * Log a record at WARN level. + * + * Usages: + * log.warn() -> boolean is-warn-enabled + * log.warn( err, [ msg, ...]) + * log.warn( msg, ...) + * log.warn( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.warn = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.warn()` + return (this._level <= WARN); + } else if (this._level > WARN) { + return; + } else if (arguments[0] instanceof Error) { + // `log.warn(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.warn(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.warn(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.warn(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, WARN, msgArgs); + this._emit(rec); +} + +/** + * Log a record at ERROR level. + * + * Usages: + * log.error() -> boolean is-error-enabled + * log.error( err, [ msg, ...]) + * log.error( msg, ...) + * log.error( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.error = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.error()` + return (this._level <= ERROR); + } else if (this._level > ERROR) { + return; + } else if (arguments[0] instanceof Error) { + // `log.error(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.error(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.error(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.error(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, ERROR, msgArgs); + this._emit(rec); +} + +/** + * Log a record at FATAL level. + * + * Usages: + * log.fatal() -> boolean is-fatal-enabled + * log.fatal( err, [ msg, ...]) + * log.fatal( msg, ...) + * log.fatal( fields, msg, ...) + * + * @params fields {Object} Optional set of additional fields to log. + * @params msg {String} Log message. This can be followed by additional + * arguments that are handled like + * [util.format](http://nodejs.org/docs/latest/api/all.html#util.format). + */ +Logger.prototype.fatal = function () { + var fields = null, msgArgs = null; + if (arguments.length === 0) { // `log.fatal()` + return (this._level <= FATAL); + } else if (this._level > FATAL) { + return; + } else if (arguments[0] instanceof Error) { + // `log.fatal(err, ...)` + fields = {err: errSerializer(arguments[0])}; + if (arguments.length === 1) { + msgArgs = [fields.err.message]; + } else { + msgArgs = Array.prototype.slice.call(arguments, 1); + } + } else if (typeof (arguments[0]) === 'string') { // `log.fatal(msg, ...)` + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + } else if (Buffer.isBuffer(arguments[0])) { // `log.fatal(buf, ...)` + // Almost certainly an error, show `inspect(buf)`. See bunyan issue #35. + fields = null; + msgArgs = Array.prototype.slice.call(arguments); + msgArgs[0] = util.inspect(msgArgs[0]); + } else { // `log.fatal(fields, msg, ...)` + fields = arguments[0]; + msgArgs = Array.prototype.slice.call(arguments, 1); + } + var rec = this._mkRecord(fields, FATAL, msgArgs); + this._emit(rec); +} + + + +//---- Standard serializers +// A serializer is a function that serializes a JavaScript object to a +// JSON representation for logging. There is a standard set of presumed +// interesting objects in node.js-land. + +Logger.stdSerializers = {}; + +// Serialize an HTTP request. +Logger.stdSerializers.req = function req(req) { + if (!req || !req.connection) + return req; + return { + method: req.method, + url: req.url, + headers: req.headers, + remoteAddress: req.connection.remoteAddress, + remotePort: req.connection.remotePort + }; + // Trailers: Skipping for speed. If you need trailers in your app, then + // make a custom serializer. + //if (Object.keys(trailers).length > 0) { + // obj.trailers = req.trailers; + //} +}; + +// Serialize an HTTP response. +Logger.stdSerializers.res = function res(res) { + if (!res || !res.statusCode) + return res; + return { + statusCode: res.statusCode, + header: res._header + } +}; + +// Serialize an Error object +// (Core error properties are enumerable in node 0.4, not in 0.6). +var errSerializer = Logger.stdSerializers.err = function err(err) { + if (!err || !err.stack) + return err; + var obj = { + message: err.message, + name: err.name, + stack: err.stack + } + Object.keys(err).forEach(function (k) { + if (err[k] !== undefined) { + obj[k] = err[k]; + } + }); + return obj; +}; + +// A JSON stringifier that handles cycles safely. +// Usage: JSON.stringify(obj, safeCycles()) +function safeCycles() { + var seen = []; + return function(key, val) { + if (!val || typeof val !== 'object') { + return val; + } + if (seen.indexOf(val) !== -1) { + return '[Circular]'; + } + seen.push(val); + return val; + }; +} + +/** + * RingBuffer is a Writable Stream that just stores the last N records in + * memory. + * + * @param options {Object}, with the following fields: + * + * - limit: number of records to keep in memory + */ +function RingBuffer(options) { + this.limit = options && options.limit ? options.limit : 100; + this.writable = true; + this.records = []; + EventEmitter.call(this); +} + +util.inherits(RingBuffer, EventEmitter); + +RingBuffer.prototype.write = function (record) { + if (!this.writable) + throw (new Error('RingBuffer has been ended already')); + + this.records.push(record); + + if (this.records.length > this.limit) + this.records.shift(); + + return (true); +}; + +RingBuffer.prototype.end = function () { + if (arguments.length > 0) + this.write.apply(this, Array.prototype.slice.call(arguments)); + this.writable = false; +}; + +RingBuffer.prototype.destroy = function () { + this.writable = false; + this.emit('close'); +}; + +RingBuffer.prototype.destroySoon = function () { + this.destroy(); +}; + + +//---- Exports + +module.exports = Logger; + +module.exports.TRACE = TRACE; +module.exports.DEBUG = DEBUG; +module.exports.INFO = INFO; +module.exports.WARN = WARN; +module.exports.ERROR = ERROR; +module.exports.FATAL = FATAL; +module.exports.resolveLevel = resolveLevel; + +module.exports.VERSION = VERSION; +module.exports.LOG_VERSION = LOG_VERSION; + +module.exports.createLogger = function createLogger(options) { + return new Logger(options); +}; + +module.exports.RingBuffer = RingBuffer; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/package.json new file mode 100644 index 0000000..1e42eac --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/package.json @@ -0,0 +1,26 @@ +{ + "name": "bunyan", + "version": "0.14.0", + "description": "a JSON Logger library for node.js services", + "author": "Trent Mick (http://trentm.com)", + "main": "./lib/bunyan.js", + "bin": { + "bunyan": "./bin/bunyan" + }, + + "repository": { + "type": "git", + "url": "git://github.com/trentm/node-bunyan.git" + }, + "engines": ["node >=0.6.0"], + "keywords": ["log", "logging", "log4j", "json"], + + "devDependencies": { + "tap": "0.2.0", + "ben": "0.0.0" + }, + + "scripts": { + "test": "tap test/*.js" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/buffer.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/buffer.test.js new file mode 100644 index 0000000..13c374a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/buffer.test.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * Copyright (c) 2012 Joyent Inc. All rights reserved. + * + * Test logging with (accidental) usage of buffers. + */ + +var util = require('util'), + inspect = util.inspect, + format = util.format; +var test = require('tap').test; +var bunyan = require('../lib/bunyan'); + + +function Catcher() { + this.records = []; +} +Catcher.prototype.write = function (record) { + this.records.push(record); +} + +var catcher = new Catcher(); +var log = new bunyan.createLogger({ + name: 'buffer.test', + streams: [ + { + type: 'raw', + stream: catcher, + level: 'trace' + } + ] +}); + + +test('log.info(BUFFER)', function (t) { + var b = new Buffer('foo'); + + ['trace', 'debug', 'info', 'warn', 'error', 'fatal'].forEach(function (lvl) { + log[lvl].call(log, b); + var rec = catcher.records[catcher.records.length - 1]; + t.equal(rec.msg, inspect(b), + format('log.%s msg is inspect(BUFFER)', lvl)); + t.ok(rec["0"] === undefined, + 'no "0" array index key in record: ' + inspect(rec["0"])); + t.ok(rec["parent"] === undefined, + 'no "parent" array index key in record: ' + inspect(rec["parent"])); + + log[lvl].call(log, b, 'bar'); + var rec = catcher.records[catcher.records.length - 1]; + t.equal(rec.msg, inspect(b) + ' bar', + format('log.%s(BUFFER, "bar") msg is inspect(BUFFER) + " bar"', lvl)); + }); + + t.end(); +}); + + +//test('log.info({buf: BUFFER})', function (t) { +// var b = new Buffer('foo'); +// +// // Really there isn't much Bunyan can do here. See +// // . An unwelcome hack would +// // be to monkey-patch in Buffer.toJSON. Bletch. +// log.info({buf: b}, 'my message'); +// var rec = catcher.records[catcher.records.length - 1]; +// +// t.end(); +//}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/cli.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/cli.test.js new file mode 100644 index 0000000..99bdfb5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/cli.test.js @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test the `bunyan` CLI. + */ + +var path = require('path'); +var exec = require('child_process').exec; +var format = require('util').format; +var test = require('tap').test; +var debug = console.warn; + +var BUNYAN = path.resolve(__dirname, '../bin/bunyan'); + +//child = exec('cat *.js bad_file | wc -l', +// function (error, stdout, stderr) { +// console.log('stdout: ' + stdout); +// console.log('stderr: ' + stderr); +// if (error !== null) { +// console.log('exec error: ' + error); +// } +//}); + +test('--version', function (t) { + var version = require('../package.json').version; + exec(BUNYAN + ' --version', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, 'bunyan ' + version + '\n'); + t.end(); + }); +}); + +test('--help', function (t) { + exec(BUNYAN + ' --help', function (err, stdout, stderr) { + t.error(err) + t.ok(stdout.indexOf('General options:') !== -1); + t.end(); + }); +}); + +test('-h', function (t) { + exec(BUNYAN + ' -h', function (err, stdout, stderr) { + t.error(err) + t.ok(stdout.indexOf('General options:') !== -1); + t.end(); + }); +}); + +test('--bogus', function (t) { + exec(BUNYAN + ' --bogus', function (err, stdout, stderr) { + t.ok(err, 'should error out') + t.equal(err.code, 1, '... with exit code 1') + t.end(); + }); +}); + +test('simple.log', function (t) { + exec(BUNYAN + ' corpus/simple.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message\n'); + t.end(); + }); +}); +test('cat simple.log', function (t) { + exec(format('cat corpus/simple.log | %s', BUNYAN), + function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message\n'); + t.end(); + } + ); +}); +test('simple.log with color', function (t) { + exec(BUNYAN + ' --color corpus/simple.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] \u001b[36m INFO\u001b[39m: myservice/123 ' + + 'on example.com: \u001b[36mMy message\u001b[39m\n'); + t.end(); + }); +}); + +test('extrafield.log', function (t) { + exec(BUNYAN + ' corpus/extrafield.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message (extra=field)\n'); + t.end(); + }); +}); +test('extrafield.log with color', function (t) { + exec(BUNYAN + ' --color corpus/extrafield.log', + function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] \u001b[36m INFO\u001b[39m: myservice/123 ' + + 'on example.com: \u001b[36mMy message\u001b[39m' + + '\u001b[90m (extra=field)\u001b[39m\n'); + t.end(); + }); +}); + +test('bogus.log', function (t) { + exec(BUNYAN + ' corpus/bogus.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, 'not a JSON line\n{"hi": "there"}\n'); + t.end(); + }); +}); + +test('bogus.log -j', function (t) { + exec(BUNYAN + ' -j corpus/bogus.log', function (err, stdout, stderr) { + t.error(err) + t.equal(stdout, 'not a JSON line\n{\n "hi": "there"\n}\n'); + t.end(); + }); +}); + +test('all.log', function (t) { + exec(BUNYAN + ' corpus/all.log', function (err, stdout, stderr) { + // Just make sure don't blow up on this. + t.error(err) + t.end(); + }); +}); + +test('simple.log doesnotexist1.log doesnotexist2.log', function (t) { + exec(BUNYAN + ' corpus/simple.log doesnotexist1.log doesnotexist2.log', + function (err, stdout, stderr) { + t.ok(err) + t.equal(err.code, 2) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message\n'); + // Note: node v0.6.10: + // ENOENT, no such file or directory 'asdf.log' + // but node v0.6.14: + // ENOENT, open 'asdf.log' + // Somewhat annoying change. + t.equal(stderr, + 'bunyan: ENOENT, open \'doesnotexist1.log\'\nbunyan: ENOENT, ' + + 'open \'doesnotexist2.log\'\n'); + t.end(); + } + ); +}); + +test('multiple logs', function (t) { + exec(BUNYAN + ' corpus/log1.log corpus/log2.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, [ + '[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n', + ].join('')); + t.end(); + }); +}); + +test('log1.log.gz', function (t) { + exec(BUNYAN + ' corpus/log1.log.gz', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, [ + '[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + ].join('')); + t.end(); + }); +}); + +test('mixed text and gzip logs', function (t) { + exec(BUNYAN + ' corpus/log1.log.gz corpus/log2.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, [ + '[2012-05-08T16:57:55.586Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T16:58:55.586Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:01:49.339Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:47.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:02:49.339Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:49.404Z] INFO: agent1/73267 on headnode: message\n', + '[2012-05-08T17:02:57.404Z] INFO: agent2/73267 on headnode: message\n', + '[2012-05-08T17:08:01.105Z] INFO: agent2/76156 on headnode: message\n', + ].join('')); + t.end(); + }); +}); + +test('--level 40', function (t) { + expect = [ + '# levels\n', + '[2012-02-08T22:56:53.856Z] WARN: myservice/123 on example.com: My message\n', + '[2012-02-08T22:56:54.856Z] ERROR: myservice/123 on example.com: My message\n', + '[2012-02-08T22:56:55.856Z] LVL55: myservice/123 on example.com: My message\n', + '[2012-02-08T22:56:56.856Z] FATAL: myservice/123 on example.com: My message\n', + '\n', + '# extra fields\n', + '\n', + '# bogus\n', + 'not a JSON line\n', + '{"hi": "there"}\n' + ].join(''); + exec(BUNYAN + ' -l 40 corpus/all.log', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + exec(BUNYAN + ' --level 40 corpus/all.log', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); + }); +}); + +test('--condition "level === 10 && pid === 123"', function (t) { + var expect = [ + '# levels\n', + '[2012-02-08T22:56:50.856Z] TRACE: myservice/123 on example.com: My message\n', + '\n', + '# extra fields\n', + '\n', + '# bogus\n', + 'not a JSON line\n', + '{"hi": "there"}\n' + ].join(''); + exec(BUNYAN + ' -c "level === 10 && pid === 123" corpus/all.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + exec(BUNYAN + ' --condition "level === 10 && pid === 123" corpus/all.log', + function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); + }); +}); + +// multiple +// not sure if this is a bug or a feature. let's call it a feature! +test('multiple --conditions', function (t) { + var expect = [ + '# levels\n', + '[2012-02-08T22:56:53.856Z] WARN: myservice/1 on example.com: My message\n', + '\n', + '# extra fields\n', + '\n', + '# bogus\n', + 'not a JSON line\n', + '{"hi": "there"}\n' + ].join(''); + exec(BUNYAN + ' corpus/all.log ' + + '-c "if (level === 40) pid = 1; true" ' + + '-c "pid === 1"', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); +}); + +// https://github.com/trentm/node-bunyan/issues/30 +// +// One of the records in corpus/withreq.log has a 'req' +// field with no 'headers'. Ditto for the 'res' field. +test('robust req handling', function (t) { + var expect = [ + '[2012-08-08T10:25:47.636Z] DEBUG: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: headAgentProbes respond (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, route=HeadAgentProbes, contentMD5=11FxOYiYfpMxmANj4kGJzg==)', + '[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, 3ms, audit=true, remoteAddress=10.2.207.2, remotePort=50394, secure=false, _audit=true, req.version=*)', + ' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1', + ' accept: application/json', + ' content-type: application/json', + ' host: 10.2.207.16', + ' connection: keep-alive', + ' --', + ' HTTP/1.1 200 OK', + ' content-md5: 11FxOYiYfpMxmANj4kGJzg==', + ' access-control-allow-origin: *', + ' access-control-allow-headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version', + ' access-control-allow-methods: HEAD', + ' access-control-expose-headers: X-Api-Version, X-Request-Id, X-Response-Time', + ' connection: Keep-Alive', + ' date: Wed, 08 Aug 2012 10:25:47 GMT', + ' server: Amon Master/1.0.0', + ' x-request-id: cce79d15-ffc2-487c-a4e4-e940bdaac31e', + ' x-response-time: 3', + ' --', + ' route: {', + ' "name": "HeadAgentProbes",', + ' "version": false', + ' }', + '[2012-08-08T10:25:47.637Z] INFO: amon-master/12859 on 9724a190-27b6-4fd8-830b-a574f839c67d: HeadAgentProbes handled: 200 (req_id=cce79d15-ffc2-487c-a4e4-e940bdaac31e, 3ms, audit=true, remoteAddress=10.2.207.2, remotePort=50394, secure=false, _audit=true, req.version=*)', + ' HEAD /agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037 HTTP/1.1', + ' --', + ' route: {', + ' "name": "HeadAgentProbes",', + ' "version": false', + ' }' + ].join('\n') + '\n'; + exec(BUNYAN + ' corpus/withreq.log', function (err, stdout, stderr) { + t.error(err); + t.equal(stdout, expect); + t.end(); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/all.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/all.log new file mode 100644 index 0000000..5d59151 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/all.log @@ -0,0 +1,20 @@ +# levels +{"name":"myservice","pid":123,"hostname":"example.com","level":10,"msg":"My message","time":"2012-02-08T22:56:50.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":20,"msg":"My message","time":"2012-02-08T22:56:51.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":40,"msg":"My message","time":"2012-02-08T22:56:53.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":50,"msg":"My message","time":"2012-02-08T22:56:54.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":55,"msg":"My message","time":"2012-02-08T22:56:55.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":60,"msg":"My message","time":"2012-02-08T22:56:56.856Z","v":0} + +# extra fields +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"one":"short","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"two":"short with space","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"three":"multi\nline","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"four":"over 50 chars long long long long long long long long long","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"five":{"a": "json object"},"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"six":["a", "json", "array"],"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} + +# bogus +not a JSON line +{"hi": "there"} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/bogus.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/bogus.log new file mode 100644 index 0000000..1c9e5fd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/bogus.log @@ -0,0 +1,2 @@ +not a JSON line +{"hi": "there"} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/extrafield.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/extrafield.log new file mode 100644 index 0000000..aac64b9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/extrafield.log @@ -0,0 +1 @@ +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"extra":"field","msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log1.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log1.log new file mode 100644 index 0000000..1482e98 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log1.log @@ -0,0 +1,4 @@ +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:57:55.586Z","v":0} +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.339Z","v":0} +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0} +{"name":"agent1","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:49.404Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log1.log.gz b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log1.log.gz new file mode 100644 index 0000000..43068dc Binary files /dev/null and b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log1.log.gz differ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log2.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log2.log new file mode 100644 index 0000000..e636081 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/log2.log @@ -0,0 +1,5 @@ +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T16:58:55.586Z","v":0} +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:01:49.339Z","v":0} +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:47.404Z","v":0} +{"name":"agent2","pid":73267,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:02:57.404Z","v":0} +{"name":"agent2","pid":76156,"hostname":"headnode","level":30,"msg":"message","time":"2012-05-08T17:08:01.105Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/simple.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/simple.log new file mode 100644 index 0000000..ebccc0d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/simple.log @@ -0,0 +1 @@ +{"name":"myservice","pid":123,"hostname":"example.com","level":30,"msg":"My message","time":"2012-02-08T22:56:52.856Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/withreq.log b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/withreq.log new file mode 100644 index 0000000..e5ece7a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/corpus/withreq.log @@ -0,0 +1,3 @@ +{"name":"amon-master","hostname":"9724a190-27b6-4fd8-830b-a574f839c67d","pid":12859,"route":"HeadAgentProbes","req_id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","level":20,"contentMD5":"11FxOYiYfpMxmANj4kGJzg==","msg":"headAgentProbes respond","time":"2012-08-08T10:25:47.636Z","v":0} +{"name":"amon-master","hostname":"9724a190-27b6-4fd8-830b-a574f839c67d","pid":12859,"audit":true,"level":30,"remoteAddress":"10.2.207.2","remotePort":50394,"req_id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","req":{"method":"HEAD","url":"/agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037","headers":{"accept":"application/json","content-type":"application/json","host":"10.2.207.16","connection":"keep-alive"},"httpVersion":"1.1","trailers":{},"version":"*"},"res":{"statusCode":200,"headers":{"content-md5":"11FxOYiYfpMxmANj4kGJzg==","access-control-allow-origin":"*","access-control-allow-headers":"Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version","access-control-allow-methods":"HEAD","access-control-expose-headers":"X-Api-Version, X-Request-Id, X-Response-Time","connection":"Keep-Alive","date":"Wed, 08 Aug 2012 10:25:47 GMT","server":"Amon Master/1.0.0","x-request-id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","x-response-time":3},"trailer":false},"route":{"name":"HeadAgentProbes","version":false},"latency":3,"secure":false,"_audit":true,"msg":"HeadAgentProbes handled: 200","time":"2012-08-08T10:25:47.637Z","v":0} +{"name":"amon-master","hostname":"9724a190-27b6-4fd8-830b-a574f839c67d","pid":12859,"audit":true,"level":30,"remoteAddress":"10.2.207.2","remotePort":50394,"req_id":"cce79d15-ffc2-487c-a4e4-e940bdaac31e","req":{"method":"HEAD","url":"/agentprobes?agent=ccf92af9-0b24-46b6-ab60-65095fdd3037","httpVersion":"1.1","trailers":{},"version":"*"},"res":{"statusCode":200,"trailer":false},"route":{"name":"HeadAgentProbes","version":false},"latency":3,"secure":false,"_audit":true,"msg":"HeadAgentProbes handled: 200","time":"2012-08-08T10:25:47.637Z","v":0} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/ctor.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/ctor.test.js new file mode 100644 index 0000000..9db59be --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/ctor.test.js @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test type checking on creation of the Logger. + */ + +var test = require('tap').test; +var bunyan = require('../lib/bunyan'), + Logger = bunyan; + + + +test('ensure Logger creation options', function (t) { + t.throws(function () { new Logger(); }, + {name: 'TypeError', message: 'options (object) is required'}, + 'no options should throw'); + + t.throws(function () { new Logger({}); }, + {name: 'TypeError', message: 'options.name (string) is required'}, + 'no options.name should throw'); + + t.doesNotThrow(function () { new Logger({name: 'foo'}); }, + 'just options.name should be sufficient'); + + var options = {name: 'foo', stream: process.stdout, streams: []}; + t.throws(function () { new Logger(options); }, + 'cannot use "stream" and "streams"'); + + options = {name: 'foo', level: 'info', streams: []}; + t.throws(function () { new Logger(options); }, + 'cannot use "level" and "streams"'); + + // https://github.com/trentm/node-bunyan/issues/3 + options = {name: 'foo', streams: {}}; + t.throws(function () { new Logger(options); }, + {name: 'TypeError', message: 'invalid options.streams: must be an array'}, + '"streams" must be an array'); + + options = {name: 'foo', serializers: 'a string'}; + t.throws(function () { new Logger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be a string'); + + options = {name: 'foo', serializers: [1, 2, 3]}; + t.throws(function () { new Logger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be an array'); + + t.end(); +}); + + +test('ensure Logger creation options (createLogger)', function (t) { + t.throws(function () { bunyan.createLogger(); }, + {name: 'TypeError', message: 'options (object) is required'}, + 'no options should throw'); + + t.throws(function () { bunyan.createLogger({}); }, + {name: 'TypeError', message: 'options.name (string) is required'}, + 'no options.name should throw'); + + t.doesNotThrow(function () { bunyan.createLogger({name: 'foo'}); }, + 'just options.name should be sufficient'); + + var options = {name: 'foo', stream: process.stdout, streams: []}; + t.throws(function () { bunyan.createLogger(options); }, + 'cannot use "stream" and "streams"'); + + options = {name: 'foo', level: 'info', streams: []}; + t.throws(function () { bunyan.createLogger(options); }, + 'cannot use "level" and "streams"'); + + // https://github.com/trentm/node-bunyan/issues/3 + options = {name: 'foo', streams: {}}; + t.throws(function () { bunyan.createLogger(options); }, + {name: 'TypeError', message: 'invalid options.streams: must be an array'}, + '"streams" must be an array'); + + options = {name: 'foo', serializers: 'a string'}; + t.throws(function () { bunyan.createLogger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be a string'); + + options = {name: 'foo', serializers: [1, 2, 3]}; + t.throws(function () { bunyan.createLogger(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be an array'); + + t.end(); +}); + + +test('ensure Logger child() options', function (t) { + var log = new Logger({name: 'foo'}); + + t.doesNotThrow(function () { log.child(); }, + 'no options should be fine'); + + t.doesNotThrow(function () { log.child({}); }, + 'empty options should be fine too'); + + t.throws(function () { log.child({name: 'foo'}); }, + { + name: 'TypeError', + message: 'invalid options.name: child cannot set logger name' + }, + 'child cannot change name'); + + var options = {stream: process.stdout, streams: []}; + t.throws(function () { log.child(options); }, + 'cannot use "stream" and "streams"'); + + options = {level: 'info', streams: []}; + t.throws(function () { log.child(options); }, + 'cannot use "level" and "streams"'); + + // https://github.com/trentm/node-bunyan/issues/3 + options = {streams: {}}; + t.throws(function () { log.child(options); }, + {name: 'TypeError', message: 'invalid options.streams: must be an array'}, + '"streams" must be an array'); + + options = {serializers: 'a string'}; + t.throws(function () { log.child(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be a string'); + + options = {serializers: [1, 2, 3]}; + t.throws(function () { log.child(options); }, + { + name: 'TypeError', + message: 'invalid options.serializers: must be an object' + }, + '"serializers" cannot be an array'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/cycles.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/cycles.test.js new file mode 100644 index 0000000..c845aae --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/cycles.test.js @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Make sure cycles are safe. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan.js'); + +var Stream = require('stream').Stream; +var outstr = new Stream; +outstr.writable = true; +var output = []; +outstr.write = function (c) { + output.push(JSON.parse(c + '')); +}; +outstr.end = function (c) { + if (c) this.write(c); + this.emit('end'); +}; + +// these are lacking a few fields that will probably never match +var expect = + [ + { + "name": "blammo", + "level": 30, + "msg": "bango { bang: 'boom', KABOOM: [Circular] }", + "v": 0 + }, + { + "name": "blammo", + "level": 30, + "msg": "kaboom { bang: 'boom', KABOOM: [Circular] }", + "v": 0 + }, + { + "name": "blammo", + "level": 30, + "bang": "boom", + "KABOOM": { + "bang": "boom", + "KABOOM": "[Circular]" + }, + "msg": "", + "v": 0 + } + ]; + +var log = new Logger({ + name: 'blammo', + streams: [ + { + type: 'stream', + level: 'info', + stream: outstr + } + ] +}); + +test('cycles', function (t) { + outstr.on('end', function () { + output.forEach(function (o, i) { + t.has(o, expect[i], 'log item ' + i + ' matches'); + }); + t.end(); + }); + + var obj = { bang: 'boom' }; + obj.KABOOM = obj; + log.info('bango', obj); + log.info('kaboom', obj.KABOOM); + log.info(obj); + outstr.end(); + t.pass('did not throw'); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/error-event.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/error-event.test.js new file mode 100644 index 0000000..6c8f0cf --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/error-event.test.js @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test emission and handling of 'error' event in a logger with a 'path' + * stream. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan'); + +test('error event on log write', function (t) { + LOG_PATH = '/this/path/is/bogus.log' + var log = new Logger({name: 'error-event', streams: [{path: LOG_PATH}]}); + t.plan(5); + log.on('error', function (err, stream) { + t.ok(err, 'got err in error event: ' + err); + t.equal(err.code, 'ENOENT', 'error code is ENOENT'); + t.ok(stream, 'got a stream argument'); + t.equal(stream.path, LOG_PATH); + t.equal(stream.type, 'file'); + t.end(); + }); + log.info('info log message'); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/log.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/log.test.js new file mode 100644 index 0000000..264d946 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/log.test.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test the `log.trace(...)`, `log.debug(...)`, ..., `log.fatal(...)` API. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan'); + +var log1 = new Logger({ + name: 'log1', + streams: [ + { + path: __dirname + '/log.test.log1.log', + level: 'info' + } + ] +}); + +var log2 = new Logger({ + name: 'log2', + streams: [ + { + path: __dirname + '/log.test.log2a.log', + level: 'error' + }, + { + path: __dirname + '/log.test.log2b.log', + level: 'debug' + } + ] +}) + +test('log.LEVEL() -> boolean', function (t) { + t.equal(log1.trace(), false) + t.equal(log1.debug(), false) + t.equal(log1.info(), true) + t.equal(log1.warn(), true) + t.equal(log1.error(), true) + t.equal(log1.fatal(), true) + + // Level is the *lowest* level of all streams. + t.equal(log2.trace(), false) + t.equal(log2.debug(), true) + t.equal(log2.info(), true) + t.equal(log2.warn(), true) + t.equal(log2.error(), true) + t.equal(log2.fatal(), true) + t.end(); +}); + +//TODO: +// - rest of api diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/other-api.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/other-api.test.js new file mode 100644 index 0000000..25bee68 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/other-api.test.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test other parts of the exported API. + */ + +var test = require('tap').test; +var bunyan = require('../lib/bunyan'); + +test('bunyan.s', function (t) { + t.ok(bunyan.TRACE, 'TRACE'); + t.ok(bunyan.DEBUG, 'DEBUG'); + t.ok(bunyan.INFO, 'INFO'); + t.ok(bunyan.WARN, 'WARN'); + t.ok(bunyan.ERROR, 'ERROR'); + t.ok(bunyan.FATAL, 'FATAL'); + t.end(); +}); + +test('bunyan.resolveLevel()', function (t) { + t.equal(bunyan.resolveLevel('trace'), bunyan.TRACE, 'TRACE'); + t.equal(bunyan.resolveLevel('TRACE'), bunyan.TRACE, 'TRACE'); + t.equal(bunyan.resolveLevel('debug'), bunyan.DEBUG, 'DEBUG'); + t.equal(bunyan.resolveLevel('DEBUG'), bunyan.DEBUG, 'DEBUG'); + t.equal(bunyan.resolveLevel('info'), bunyan.INFO, 'INFO'); + t.equal(bunyan.resolveLevel('INFO'), bunyan.INFO, 'INFO'); + t.equal(bunyan.resolveLevel('warn'), bunyan.WARN, 'WARN'); + t.equal(bunyan.resolveLevel('WARN'), bunyan.WARN, 'WARN'); + t.equal(bunyan.resolveLevel('error'), bunyan.ERROR, 'ERROR'); + t.equal(bunyan.resolveLevel('ERROR'), bunyan.ERROR, 'ERROR'); + t.equal(bunyan.resolveLevel('fatal'), bunyan.FATAL, 'FATAL'); + t.equal(bunyan.resolveLevel('FATAL'), bunyan.FATAL, 'FATAL'); + t.end(); +}); + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/raw-stream.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/raw-stream.test.js new file mode 100644 index 0000000..8eb1336 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/raw-stream.test.js @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test `type: 'raw'` Logger streams. + */ + +var format = require('util').format; +var test = require('tap').test; +var Logger = require('../lib/bunyan'); + + + +function CapturingStream(recs) { + this.recs = recs; +} +CapturingStream.prototype.write = function (rec) { + this.recs.push(rec); +} + + +test('raw stream', function (t) { + var recs = []; + + var log = new Logger({ + name: 'raw-stream-test', + streams: [ + { + stream: new CapturingStream(recs), + type: 'raw' + } + ] + }); + log.info('first'); + log.info({two: 'deux'}, 'second'); + + t.equal(recs.length, 2); + t.equal(typeof (recs[0]), 'object', 'first rec is an object'); + t.equal(recs[1].two, 'deux', '"two" field made it through'); + t.end(); +}); + + +test('raw streams and regular streams can mix', function (t) { + var rawRecs = []; + var nonRawRecs = []; + + var log = new Logger({ + name: 'raw-stream-test', + streams: [ + { + stream: new CapturingStream(rawRecs), + type: 'raw' + }, + { + stream: new CapturingStream(nonRawRecs) + } + ] + }); + log.info('first'); + log.info({two: 'deux'}, 'second'); + + t.equal(rawRecs.length, 2); + t.equal(typeof (rawRecs[0]), 'object', 'first rawRec is an object'); + t.equal(rawRecs[1].two, 'deux', '"two" field made it through'); + + t.equal(nonRawRecs.length, 2); + t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string'); + + t.end(); +}); + + +test('child adding a non-raw stream works', function (t) { + var parentRawRecs = []; + var rawRecs = []; + var nonRawRecs = []; + + var logParent = new Logger({ + name: 'raw-stream-test', + streams: [ + { + stream: new CapturingStream(parentRawRecs), + type: 'raw' + } + ] + }); + var logChild = logParent.child({ + child: true, + streams: [ + { + stream: new CapturingStream(rawRecs), + type: 'raw' + }, + { + stream: new CapturingStream(nonRawRecs) + } + ] + }); + logParent.info('first'); + logChild.info({two: 'deux'}, 'second'); + + t.equal(rawRecs.length, 1, + format('rawRecs length should be 1 (is %d)', rawRecs.length)); + t.equal(typeof (rawRecs[0]), 'object', 'rawRec entry is an object'); + t.equal(rawRecs[0].two, 'deux', '"two" field made it through'); + + t.equal(nonRawRecs.length, 1); + t.equal(typeof (nonRawRecs[0]), 'string', 'first nonRawRec is a string'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/ringbuffer.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/ringbuffer.test.js new file mode 100644 index 0000000..75a6a12 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/ringbuffer.test.js @@ -0,0 +1,39 @@ +/* + * Test the RingBuffer output stream. + */ + +var test = require('tap').test; +var Logger = require('../lib/bunyan'); +var ringbuffer = new Logger.RingBuffer({ 'limit': 5 }); + +var log1 = new Logger({ + name: 'log1', + streams: [ + { + stream: ringbuffer, + type: 'raw', + level: 'info' + } + ] +}); + +test('ringbuffer', function (t) { + log1.info('hello'); + log1.trace('there'); + log1.error('android'); + t.equal(ringbuffer.records.length, 2); + t.equal(ringbuffer.records[0]['msg'], 'hello'); + t.equal(ringbuffer.records[1]['msg'], 'android'); + log1.error('one'); + log1.error('two'); + log1.error('three'); + t.equal(ringbuffer.records.length, 5); + log1.error('four'); + t.equal(ringbuffer.records.length, 5); + t.equal(ringbuffer.records[0]['msg'], 'android'); + t.equal(ringbuffer.records[1]['msg'], 'one'); + t.equal(ringbuffer.records[2]['msg'], 'two'); + t.equal(ringbuffer.records[3]['msg'], 'three'); + t.equal(ringbuffer.records[4]['msg'], 'four'); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/std-serializers.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/std-serializers.test.js new file mode 100644 index 0000000..132bb4f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/test/std-serializers.test.js @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2012 Trent Mick. All rights reserved. + * + * Test the standard serializers in Bunyan. + */ + +var test = require('tap').test; +var bunyan = require('../lib/bunyan'); +var http = require('http'); + + +function CapturingStream(recs) { + this.recs = recs; +} +CapturingStream.prototype.write = function (rec) { + this.recs.push(rec); +} + + +test('req serializer', function (t) { + var records = []; + var log = bunyan.createLogger({ + name: 'serializer-test', + streams: [ + { + stream: new CapturingStream(records), + type: 'raw' + } + ], + serializers: { + req: bunyan.stdSerializers.req + } + }); + + // None of these should blow up. + var bogusReqs = [ + undefined, + null, + {}, + 1, + 'string', + [1,2,3], + {'foo':'bar'} + ]; + for (var i = 0; i < bogusReqs.length; i++) { + log.info({req: bogusReqs[i]}, "hi"); + t.equal(records[i].req, bogusReqs[i]); + } + + // Get http request and response objects to play with and test. + var theReq, theRes; + var server = http.createServer(function (req, res) { + theReq = req; + theRes = res; + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Hello World\n'); + }) + server.listen(8765, function () { + http.get({host: '127.0.0.1', port: 8765, path: '/'}, function(res) { + log.info({req: theReq}, 'the request'); + var lastRecord = records[records.length-1]; + t.equal(lastRecord.req.method, 'GET'); + t.equal(lastRecord.req.url, theReq.url); + t.equal(lastRecord.req.remoteAddress, theReq.connection.remoteAddress); + t.equal(lastRecord.req.remotePort, theReq.connection.remotePort); + server.close(); + t.end(); + }).on('error', function (err) { + t.ok(false, 'error requesting to our test server: ' + err); + server.close(); + t.end(); + }); + }); +}); + + +test('res serializer', function (t) { + var records = []; + var log = bunyan.createLogger({ + name: 'serializer-test', + streams: [ + { + stream: new CapturingStream(records), + type: 'raw' + } + ], + serializers: { + res: bunyan.stdSerializers.res + } + }); + + // None of these should blow up. + var bogusRess = [ + undefined, + null, + {}, + 1, + 'string', + [1,2,3], + {'foo':'bar'} + ]; + for (var i = 0; i < bogusRess.length; i++) { + log.info({res: bogusRess[i]}, "hi"); + t.equal(records[i].res, bogusRess[i]); + } + + // Get http request and response objects to play with and test. + var theReq, theRes; + var server = http.createServer(function (req, res) { + theReq = req; + theRes = res; + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('Hello World\n'); + }) + server.listen(8765, function () { + http.get({host: '127.0.0.1', port: 8765, path: '/'}, function(res) { + log.info({res: theRes}, 'the response'); + var lastRecord = records[records.length-1]; + t.equal(lastRecord.res.statusCode, theRes.statusCode); + t.equal(lastRecord.res.header, theRes._header); + server.close(); + t.end(); + }).on('error', function (err) { + t.ok(false, 'error requesting to our test server: ' + err); + server.close(); + t.end(); + }); + }); +}); + + +test('err serializer', function (t) { + var records = []; + var log = bunyan.createLogger({ + name: 'serializer-test', + streams: [ + { + stream: new CapturingStream(records), + type: 'raw' + } + ], + serializers: { + err: bunyan.stdSerializers.err + } + }); + + // None of these should blow up. + var bogusErrs = [ + undefined, + null, + {}, + 1, + 'string', + [1,2,3], + {'foo':'bar'} + ]; + for (var i = 0; i < bogusErrs.length; i++) { + log.info({err: bogusErrs[i]}, "hi"); + t.equal(records[i].err, bogusErrs[i]); + } + + var theErr = new TypeError('blah'); + + log.info(theErr, 'the error'); + var lastRecord = records[records.length-1]; + t.equal(lastRecord.err.message, theErr.message); + t.equal(lastRecord.err.name, theErr.name); + t.equal(lastRecord.err.stack, theErr.stack); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/tools/cutarelease.py b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/tools/cutarelease.py new file mode 100755 index 0000000..2cfeb98 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/tools/cutarelease.py @@ -0,0 +1,609 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2012 Trent Mick + +"""cutarelease -- Cut a release of your project. + +A script that will help cut a release for a git-based project that follows +a few conventions. It'll update your changelog (CHANGES.md), add a git +tag, push those changes, update your version to the next patch level release +and create a new changelog section for that new version. + +Conventions: +- XXX +""" + +__version_info__ = (1, 0, 7) +__version__ = '.'.join(map(str, __version_info__)) + +import sys +import os +from os.path import join, dirname, normpath, abspath, exists, basename, splitext +from glob import glob +from pprint import pprint +import re +import codecs +import logging +import optparse +import json + + + +#---- globals and config + +log = logging.getLogger("cutarelease") + +class Error(Exception): + pass + + + +#---- main functionality + +def cutarelease(project_name, version_files, dry_run=False): + """Cut a release. + + @param project_name {str} + @param version_files {list} List of paths to files holding the version + info for this project. + + If none are given it attempts to guess the version file: + package.json or VERSION.txt or VERSION or $project_name.py + or lib/$project_name.py or $project_name.js or lib/$project_name.js. + + The version file can be in one of the following forms: + + - A .py file, in which case the file is expect to have a top-level + global called "__version_info__" as follows. [1] + + __version_info__ = (0, 7, 6) + + Note that I typically follow that with the following to get a + string version attribute on my modules: + + __version__ = '.'.join(map(str, __version_info__)) + + - A .js file, in which case the file is expected to have a top-level + global called "VERSION" as follows: + + ver VERSION = "1.2.3"; + + - A "package.json" file, typical of a node.js npm-using project. + The package.json file must have a "version" field. + + - TODO: A simple version file whose only content is a "1.2.3"-style version + string. + + [1]: This is a convention I tend to follow in my projects. + Granted it might not be your cup of tea. I should add support for + just `__version__ = "1.2.3"`. I'm open to other suggestions too. + """ + dry_run_str = dry_run and " (dry-run)" or "" + + if not version_files: + log.info("guessing version file") + candidates = [ + "package.json", + "VERSION.txt", + "VERSION", + "%s.py" % project_name, + "lib/%s.py" % project_name, + "%s.js" % project_name, + "lib/%s.js" % project_name, + ] + for candidate in candidates: + if exists(candidate): + version_files = [candidate] + break + else: + raise Error("could not find a version file: specify its path or " + "add one of the following to your project: '%s'" + % "', '".join(candidates)) + log.info("using '%s' as version file", version_files[0]) + + parsed_version_files = [_parse_version_file(f) for f in version_files] + version_file_type, version_info = parsed_version_files[0] + version = _version_from_version_info(version_info) + + # Confirm + if not dry_run: + answer = query_yes_no("* * *\n" + "Are you sure you want cut a %s release?\n" + "This will involved commits and a push." % version, + default="no") + print "* * *" + if answer != "yes": + log.info("user abort") + return + log.info("cutting a %s release%s", version, dry_run_str) + + # Checks: Ensure there is a section in changes for this version. + + + + changes_path = "CHANGES.md" + changes_txt, changes, nyr = parse_changelog(changes_path) + #pprint(changes) + top_ver = changes[0]["version"] + if top_ver != version: + raise Error("changelog '%s' top section says " + "version %r, expected version %r: aborting" + % (changes_path, top_ver, version)) + top_verline = changes[0]["verline"] + if not top_verline.endswith(nyr): + answer = query_yes_no("\n* * *\n" + "The changelog '%s' top section doesn't have the expected\n" + "'%s' marker. Has this been released already?" + % (changes_path, nyr), default="yes") + print "* * *" + if answer != "no": + log.info("abort") + return + top_body = changes[0]["body"] + if top_body.strip() == "(nothing yet)": + raise Error("top section body is `(nothing yet)': it looks like " + "nothing has been added to this release") + + # Commits to prepare release. + changes_txt_before = changes_txt + changes_txt = changes_txt.replace(" (not yet released)", "", 1) + if not dry_run and changes_txt != changes_txt_before: + log.info("prepare `%s' for release", changes_path) + f = codecs.open(changes_path, 'w', 'utf-8') + f.write(changes_txt) + f.close() + run('git commit %s -m "prepare for %s release"' + % (changes_path, version)) + + # Tag version and push. + curr_tags = set(t for t in _capture_stdout(["git", "tag", "-l"]).split('\n') if t) + if not dry_run and version not in curr_tags: + log.info("tag the release") + run('git tag -a "%s" -m "version %s"' % (version, version)) + run('git push --tags') + + # Optionally release. + if exists("package.json"): + answer = query_yes_no("\n* * *\nPublish to npm?", default="yes") + print "* * *" + if answer == "yes": + if dry_run: + log.info("skipping npm publish (dry-run)") + else: + run('npm publish') + elif exists("setup.py"): + answer = query_yes_no("\n* * *\nPublish to pypi?", default="yes") + print "* * *" + if answer == "yes": + if dry_run: + log.info("skipping pypi publish (dry-run)") + else: + run("%spython setup.py sdist --formats zip upload" + % _setup_command_prefix()) + + # Commits to prepare for future dev and push. + # - update changelog file + next_version_info = _get_next_version_info(version_info) + next_version = _version_from_version_info(next_version_info) + log.info("prepare for future dev (version %s)", next_version) + marker = "## " + changes[0]["verline"] + if marker.endswith(nyr): + marker = marker[0:-len(nyr)] + if marker not in changes_txt: + raise Error("couldn't find `%s' marker in `%s' " + "content: can't prep for subsequent dev" % (marker, changes_path)) + next_verline = "%s %s%s" % (marker.rsplit(None, 1)[0], next_version, nyr) + changes_txt = changes_txt.replace(marker + '\n', + "%s\n\n(nothing yet)\n\n\n%s\n" % (next_verline, marker)) + if not dry_run: + f = codecs.open(changes_path, 'w', 'utf-8') + f.write(changes_txt) + f.close() + + # - update version file + next_version_tuple = _tuple_from_version(next_version) + for i, ver_file in enumerate(version_files): + ver_content = codecs.open(ver_file, 'r', 'utf-8').read() + ver_file_type, ver_info = parsed_version_files[i] + if ver_file_type == "json": + marker = '"version": "%s"' % version + if marker not in ver_content: + raise Error("couldn't find `%s' version marker in `%s' " + "content: can't prep for subsequent dev" % (marker, ver_file)) + ver_content = ver_content.replace(marker, + '"version": "%s"' % next_version) + elif ver_file_type == "javascript": + candidates = [ + ("single", "var VERSION = '%s';" % version), + ("double", 'var VERSION = "%s";' % version), + ] + for quote_type, marker in candidates: + if marker in ver_content: + break + else: + raise Error("couldn't find any candidate version marker in " + "`%s' content: can't prep for subsequent dev: %r" + % (ver_file, candidates)) + if quote_type == "single": + ver_content = ver_content.replace(marker, + "var VERSION = '%s';" % next_version) + else: + ver_content = ver_content.replace(marker, + 'var VERSION = "%s";' % next_version) + elif ver_file_type == "python": + marker = "__version_info__ = %r" % (version_info,) + if marker not in ver_content: + raise Error("couldn't find `%s' version marker in `%s' " + "content: can't prep for subsequent dev" % (marker, ver_file)) + ver_content = ver_content.replace(marker, + "__version_info__ = %r" % (next_version_tuple,)) + elif ver_file_type == "version": + ver_content = next_version + else: + raise Error("unknown ver_file_type: %r" % ver_file_type) + if not dry_run: + log.info("update version to '%s' in '%s'", next_version, ver_file) + f = codecs.open(ver_file, 'w', 'utf-8') + f.write(ver_content) + f.close() + + if not dry_run: + run('git commit %s %s -m "prep for future dev"' % ( + changes_path, ' '.join(version_files))) + run('git push') + + + +#---- internal support routines + +def _indent(s, indent=' '): + return indent + indent.join(s.splitlines(True)) + +def _tuple_from_version(version): + def _intify(s): + try: + return int(s) + except ValueError: + return s + return tuple(_intify(b) for b in version.split('.')) + +def _get_next_version_info(version_info): + next = list(version_info[:]) + next[-1] += 1 + return tuple(next) + +def _version_from_version_info(version_info): + v = str(version_info[0]) + state_dot_join = True + for i in version_info[1:]: + if state_dot_join: + try: + int(i) + except ValueError: + state_dot_join = False + else: + pass + if state_dot_join: + v += "." + str(i) + else: + v += str(i) + return v + +_version_re = re.compile(r"^(\d+)\.(\d+)(?:\.(\d+)([abc](\d+)?)?)?$") +def _version_info_from_version(version): + m = _version_re.match(version) + if not m: + raise Error("could not convert '%s' version to version info" % version) + version_info = [] + for g in m.groups(): + if g is None: + break + try: + version_info.append(int(g)) + except ValueError: + version_info.append(g) + return tuple(version_info) + +def _parse_version_file(version_file): + """Get version info from the given file. It can be any of: + + Supported version file types (i.e. types of files from which we know + how to parse the version string/number -- often by some convention): + - json: use the "version" key + - javascript: look for a `var VERSION = "1.2.3";` or + `var VERSION = '1.2.3';` + - python: Python script/module with `__version_info__ = (1, 2, 3)` + - version: a VERSION.txt or VERSION file where the whole contents are + the version string + + @param version_file {str} Can be a path or "type:path", where "type" + is one of the supported types. + """ + # Get version file *type*. + version_file_type = None + match = re.compile("^([a-z]+):(.*)$").search(version_file) + if match: + version_file = match.group(2) + version_file_type = match.group(1) + aliases = { + "js": "javascript" + } + if version_file_type in aliases: + version_file_type = aliases[version_file_type] + + f = codecs.open(version_file, 'r', 'utf-8') + content = f.read() + f.close() + + if not version_file_type: + # Guess the type. + base = basename(version_file) + ext = splitext(base)[1] + if ext == ".json": + version_file_type = "json" + elif ext == ".py": + version_file_type = "python" + elif ext == ".js": + version_file_type = "javascript" + elif content.startswith("#!"): + shebang = content.splitlines(False)[0] + shebang_bits = re.split(r'[/ \t]', shebang) + for name, typ in {"python": "python", "node": "javascript"}.items(): + if name in shebang_bits: + version_file_type = typ + break + elif base in ("VERSION", "VERSION.txt"): + version_file_type = "version" + if not version_file_type: + raise RuntimeError("can't extract version from '%s': no idea " + "what type of file it it" % version_file) + + if version_file_type == "json": + obj = json.loads(content) + version_info = _version_info_from_version(obj["version"]) + elif version_file_type == "python": + m = re.search(r'^__version_info__ = (.*?)$', content, re.M) + version_info = eval(m.group(1)) + elif version_file_type == "javascript": + m = re.search(r'^var VERSION = (\'|")(.*?)\1;$', content, re.M) + version_info = _version_info_from_version(m.group(2)) + elif version_file_type == "version": + version_info = _version_info_from_version(content.strip()) + else: + raise RuntimeError("unexpected version_file_type: %r" + % version_file_type) + return version_file_type, version_info + + +def parse_changelog(changes_path): + """Parse the given changelog path and return `(content, parsed, nyr)` + where `nyr` is the ' (not yet released)' marker and `parsed` looks like: + + [{'body': u'\n(nothing yet)\n\n', + 'verline': u'restify 1.0.1 (not yet released)', + 'version': u'1.0.1'}, # version is parsed out for top section only + {'body': u'...', + 'verline': u'1.0.0'}, + {'body': u'...', + 'verline': u'1.0.0-rc2'}, + {'body': u'...', + 'verline': u'1.0.0-rc1'}] + + A changelog (CHANGES.md) is expected to look like this: + + # $project Changelog + + ## $next_version (not yet released) + + ... + + ## $version1 + + ... + + ## $version2 + + ... and so on + + The version lines are enforced as follows: + + - The top entry should have a " (not yet released)" suffix. "Should" + because recovery from half-cutarelease failures is supported. + - A version string must be extractable from there, but it tries to + be loose (though strict "X.Y.Z" versioning is preferred). Allowed + + ## 1.0.0 + ## my project 1.0.1 + ## foo 1.2.3-rc2 + + Basically, (a) the " (not yet released)" is stripped, (b) the + last token is the version, and (c) that version must start with + a digit (sanity check). + """ + if not exists(changes_path): + raise Error("changelog file '%s' not found" % changes_path) + content = codecs.open(changes_path, 'r', 'utf-8').read() + + parser = re.compile( + r'^##\s*(?P[^\n]*?)\s*$(?P.*?)(?=^##|\Z)', + re.M | re.S) + sections = parser.findall(content) + + # Sanity checks on changelog format. + if not sections: + template = "## 1.0.0 (not yet released)\n\n(nothing yet)\n" + raise Error("changelog '%s' must have at least one section, " + "suggestion:\n\n%s" % (changes_path, _indent(template))) + first_section_verline = sections[0][0] + nyr = ' (not yet released)' + #if not first_section_verline.endswith(nyr): + # eg = "## %s%s" % (first_section_verline, nyr) + # raise Error("changelog '%s' top section must end with %r, " + # "naive e.g.: '%s'" % (changes_path, nyr, eg)) + + items = [] + for i, section in enumerate(sections): + item = { + "verline": section[0], + "body": section[1] + } + if i == 0: + # We only bother to pull out 'version' for the top section. + verline = section[0] + if verline.endswith(nyr): + verline = verline[0:-len(nyr)] + version = verline.split()[-1] + try: + int(version[0]) + except ValueError: + msg = '' + if version.endswith(')'): + msg = " (cutarelease is picky about the trailing %r " \ + "on the top version line. Perhaps you misspelled " \ + "that?)" % nyr + raise Error("changelog '%s' top section version '%s' is " + "invalid: first char isn't a number%s" + % (changes_path, version, msg)) + item["version"] = version + items.append(item) + + return content, items, nyr + +## {{{ http://code.activestate.com/recipes/577058/ (r2) +def query_yes_no(question, default="yes"): + """Ask a yes/no question via raw_input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is one of "yes" or "no". + """ + valid = {"yes":"yes", "y":"yes", "ye":"yes", + "no":"no", "n":"no"} + if default == None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while 1: + sys.stdout.write(question + prompt) + choice = raw_input().lower() + if default is not None and choice == '': + return default + elif choice in valid.keys(): + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' "\ + "(or 'y' or 'n').\n") +## end of http://code.activestate.com/recipes/577058/ }}} + +def _capture_stdout(argv): + import subprocess + p = subprocess.Popen(argv, stdout=subprocess.PIPE) + return p.communicate()[0] + +class _NoReflowFormatter(optparse.IndentedHelpFormatter): + """An optparse formatter that does NOT reflow the description.""" + def format_description(self, description): + return description or "" + +def run(cmd): + """Run the given command. + + Raises OSError is the command returns a non-zero exit status. + """ + log.debug("running '%s'", cmd) + fixed_cmd = cmd + if sys.platform == "win32" and cmd.count('"') > 2: + fixed_cmd = '"' + cmd + '"' + retval = os.system(fixed_cmd) + if hasattr(os, "WEXITSTATUS"): + status = os.WEXITSTATUS(retval) + else: + status = retval + if status: + raise OSError(status, "error running '%s'" % cmd) + +def _setup_command_prefix(): + prefix = "" + if sys.platform == "darwin": + # http://forums.macosxhints.com/archive/index.php/t-43243.html + # This is an Apple customization to `tar` to avoid creating + # '._foo' files for extended-attributes for archived files. + prefix = "COPY_EXTENDED_ATTRIBUTES_DISABLE=1 " + return prefix + + +#---- mainline + +def main(argv): + logging.basicConfig(format="%(name)s: %(levelname)s: %(message)s") + log.setLevel(logging.INFO) + + # Parse options. + parser = optparse.OptionParser(prog="cutarelease", usage='', + version="%prog " + __version__, description=__doc__, + formatter=_NoReflowFormatter()) + parser.add_option("-v", "--verbose", dest="log_level", + action="store_const", const=logging.DEBUG, + help="more verbose output") + parser.add_option("-q", "--quiet", dest="log_level", + action="store_const", const=logging.WARNING, + help="quieter output (just warnings and errors)") + parser.set_default("log_level", logging.INFO) + parser.add_option("--test", action="store_true", + help="run self-test and exit (use 'eol.py -v --test' for verbose test output)") + parser.add_option("-p", "--project-name", metavar="NAME", + help='the name of this project (default is the base dir name)', + default=basename(os.getcwd())) + parser.add_option("-f", "--version-file", metavar="[TYPE:]PATH", + action='append', dest="version_files", + help='The path to the project file holding the version info. Can be ' + 'specified multiple times if more than one file should be updated ' + 'with new version info. If excluded, it will be guessed.') + parser.add_option("-n", "--dry-run", action="store_true", + help='Do a dry-run', default=False) + opts, args = parser.parse_args() + log.setLevel(opts.log_level) + + cutarelease(opts.project_name, opts.version_files, dry_run=opts.dry_run) + + +## {{{ http://code.activestate.com/recipes/577258/ (r5+) +if __name__ == "__main__": + try: + retval = main(sys.argv) + except KeyboardInterrupt: + sys.exit(1) + except SystemExit: + raise + except: + import traceback, logging + if not log.handlers and not logging.root.handlers: + logging.basicConfig() + skip_it = False + exc_info = sys.exc_info() + if hasattr(exc_info[0], "__name__"): + exc_class, exc, tb = exc_info + if isinstance(exc, IOError) and exc.args[0] == 32: + # Skip 'IOError: [Errno 32] Broken pipe': often a cancelling of `less`. + skip_it = True + if not skip_it: + tb_path, tb_lineno, tb_func = traceback.extract_tb(tb)[-1][:3] + log.error("%s (%s:%s in %s)", exc_info[1], tb_path, + tb_lineno, tb_func) + else: # string exception + log.error(exc_info[0]) + if not skip_it: + if log.isEnabledFor(logging.DEBUG): + traceback.print_exception(*exc_info) + sys.exit(1) + else: + sys.exit(retval) +## end of http://code.activestate.com/recipes/577258/ }}} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/tools/jsstyle b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/tools/jsstyle new file mode 100755 index 0000000..9df33c9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/bunyan/tools/jsstyle @@ -0,0 +1,953 @@ +#!/usr/bin/env perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# +# jsstyle - check for some common stylistic errors. +# +# jsstyle is a sort of "lint" for Javascript coding style. This tool is +# derived from the cstyle tool, used to check for the style used in the +# Solaris kernel, sometimes known as "Bill Joy Normal Form". +# +# There's a lot this can't check for, like proper indentation of code +# blocks. There's also a lot more this could check for. +# +# A note to the non perl literate: +# +# perl regular expressions are pretty much like egrep +# regular expressions, with the following special symbols +# +# \s any space character +# \S any non-space character +# \w any "word" character [a-zA-Z0-9_] +# \W any non-word character +# \d a digit [0-9] +# \D a non-digit +# \b word boundary (between \w and \W) +# \B non-word boundary +# + +require 5.0; +use IO::File; +use Getopt::Std; +use strict; + +my $usage = +"Usage: jsstyle [-h?vcC] [-t ] [-f ] [-o ] file ... + +Check your JavaScript file for style. +See for details on config options. +Report bugs to . + +Options: + -h print this help and exit + -v verbose + + -c check continuation indentation inside functions + -t specify tab width for line length calculation + -C don't check anything in header block comments + + -f PATH + path to a jsstyle config file + -o OPTION1,OPTION2 + set config options, e.g. '-o doxygen,indent=2' + +"; + +my %opts; + +if (!getopts("ch?o:t:f:vC", \%opts)) { + print $usage; + exit 2; +} + +if (defined($opts{'h'}) || defined($opts{'?'})) { + print $usage; + exit; +} + +my $check_continuation = $opts{'c'}; +my $verbose = $opts{'v'}; +my $ignore_hdr_comment = $opts{'C'}; +my $tab_width = $opts{'t'}; + +# By default, tabs are 8 characters wide +if (! defined($opts{'t'})) { + $tab_width = 8; +} + + +# Load config +my %config = ( + indent => "tab", + doxygen => 0, # doxygen comments: /** ... */ + splint => 0, # splint comments. Needed? + "unparenthesized-return" => 1, + "literal-string-quote" => "single", # 'single' or 'double' + "blank-after-start-comment" => 1, + "continuation-at-front" => 0, + "leading-right-paren-ok" => 0, + "strict-indent" => 0 +); +sub add_config_var ($$) { + my ($scope, $str) = @_; + + if ($str !~ /^([\w-]+)(?:\s*=\s*(.*?))?$/) { + die "$scope: invalid option: '$str'"; + } + my $name = $1; + my $value = ($2 eq '' ? 1 : $2); + #print "scope: '$scope', str: '$str', name: '$name', value: '$value'\n"; + + # Validate config var. + if ($name eq "indent") { + # A number of spaces or "tab". + if ($value !~ /^\d+$/ && $value ne "tab") { + die "$scope: invalid '$name': must be a number (of ". + "spaces) or 'tab'"; + } + } elsif ($name eq "doxygen" || # boolean vars + $name eq "splint" || + $name eq "unparenthesized-return" || + $name eq "continuation-at-front" || + $name eq "leading-right-paren-ok" || + $name eq "leading-comma-ok" || + $name eq "uncuddled-else-ok" || + $name eq "whitespace-after-left-paren-ok" || + $name eq "strict-indent" || + $name eq "blank-after-start-comment") { + + if ($value != 1 && $value != 0) { + die "$scope: invalid '$name': don't give a value"; + } + } elsif ($name eq "literal-string-quote") { + if ($value !~ /single|double/) { + die "$scope: invalid '$name': must be 'single' ". + "or 'double'"; + } + } else { + die "$scope: unknown config var: $name"; + } + $config{$name} = $value; +} + +if (defined($opts{'f'})) { + my $path = $opts{'f'}; + my $fh = new IO::File $path, "r"; + if (!defined($fh)) { + die "cannot open config path '$path'"; + } + my $line = 0; + while (<$fh>) { + $line++; + s/^\s*//; # drop leading space + s/\s*$//; # drop trailing space + next if ! $_; # skip empty line + next if /^#/; # skip comments + add_config_var "$path:$line", $_; + } +} + +if (defined($opts{'o'})) { + for my $x (split /,/, $opts{'o'}) { + add_config_var "'-o' option", $x; + } +} + + +my ($filename, $line, $prev); # shared globals + +my $fmt; +my $hdr_comment_start; + +if ($verbose) { + $fmt = "%s: %d: %s\n%s\n"; +} else { + $fmt = "%s: %d: %s\n"; +} + +if ($config{"doxygen"}) { + # doxygen comments look like "/*!" or "/**"; allow them. + $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/; +} else { + $hdr_comment_start = qr/^\s*\/\*$/; +} + +# Note, following must be in single quotes so that \s and \w work right. +my $lint_re = qr/\/\*(?: + jsl:\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*| + CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY| + FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*| + PROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*? + )\*\//x; + +my $splint_re = qr/\/\*@.*?@\*\//x; + +my $err_stat = 0; # exit status + +if ($#ARGV >= 0) { + foreach my $arg (@ARGV) { + my $fh = new IO::File $arg, "r"; + if (!defined($fh)) { + printf "%s: cannot open\n", $arg; + } else { + &jsstyle($arg, $fh); + close $fh; + } + } +} else { + &jsstyle("", *STDIN); +} +exit $err_stat; + +my $no_errs = 0; # set for JSSTYLED-protected lines + +sub err($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $line; + $err_stat = 1; + } +} + +sub err_prefix($$) { + my ($prevline, $error) = @_; + my $out = $prevline."\n".$line; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $out; + $err_stat = 1; + } +} + +sub err_prev($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $. - 1, $error, $prev; + $err_stat = 1; + } +} + +sub jsstyle($$) { + +my ($fn, $filehandle) = @_; +$filename = $fn; # share it globally + +my $in_cpp = 0; +my $next_in_cpp = 0; + +my $in_comment = 0; +my $in_header_comment = 0; +my $comment_done = 0; +my $in_function = 0; +my $in_function_header = 0; +my $in_declaration = 0; +my $note_level = 0; +my $nextok = 0; +my $nocheck = 0; + +my $in_string = 0; + +my ($okmsg, $comment_prefix); + +$line = ''; +$prev = ''; +reset_indent(); + +line: while (<$filehandle>) { + s/\r?\n$//; # strip return and newline + + # save the original line, then remove all text from within + # double or single quotes, we do not want to check such text. + + $line = $_; + + # + # C allows strings to be continued with a backslash at the end of + # the line. We translate that into a quoted string on the previous + # line followed by an initial quote on the next line. + # + # (we assume that no-one will use backslash-continuation with character + # constants) + # + $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); + + # + # normal strings and characters + # + s/'([^\\']|\\.)*'/\'\'/g; + s/"([^\\"]|\\.)*"/\"\"/g; + + # + # detect string continuation + # + if ($nocheck || $in_comment) { + $in_string = 0; + } else { + # + # Now that all full strings are replaced with "", we check + # for unfinished strings continuing onto the next line. + # + $in_string = + (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || + s/^("")*"([^\\"]|\\.)*\\$/""/); + } + + # + # figure out if we are in a cpp directive + # + $in_cpp = $next_in_cpp || /^\s*#/; # continued or started + $next_in_cpp = $in_cpp && /\\$/; # only if continued + + # strip off trailing backslashes, which appear in long macros + s/\s*\\$//; + + # an /* END JSSTYLED */ comment ends a no-check block. + if ($nocheck) { + if (/\/\* *END *JSSTYLED *\*\//) { + $nocheck = 0; + } else { + reset_indent(); + next line; + } + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if ($nextok) { + if ($okmsg) { + err($okmsg); + } + $nextok = 0; + $okmsg = 0; + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + $no_errs = 1; + } elsif ($no_errs) { + $no_errs = 0; + } + + # check length of line. + # first, a quick check to see if there is any chance of being too long. + if ((($line =~ tr/\t/\t/) * ($tab_width - 1)) + length($line) > 80) { + # yes, there is a chance. + # replace tabs with spaces and check again. + my $eline = $line; + 1 while $eline =~ + s/\t+/' ' x + (length($&) * $tab_width - length($`) % $tab_width)/e; + if (length($eline) > 80) { + err("line > 80 characters"); + } + } + + # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). + if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE + s/[^()]//g; # eliminate all non-parens + $note_level += s/\(//g - length; # update paren nest level + next; + } + + # a /* BEGIN JSSTYLED */ comment starts a no-check block. + if (/\/\* *BEGIN *JSSTYLED *\*\//) { + $nocheck = 1; + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + if (/\/\/ *JSSTYLED/) { + /^.*\/\/ *JSSTYLED *(.*)$/; + $okmsg = $1; + $nextok = 1; + } + + # universal checks; apply to everything + if (/\t +\t/) { + err("spaces between tabs"); + } + if (/ \t+ /) { + err("tabs between spaces"); + } + if (/\s$/) { + err("space or tab at end of line"); + } + if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { + err("comment preceded by non-blank"); + } + + # is this the beginning or ending of a function? + # (not if "struct foo\n{\n") + if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { + $in_function = 1; + $in_declaration = 1; + $in_function_header = 0; + $prev = $line; + next line; + } + if (/^}\s*(\/\*.*\*\/\s*)*$/) { + if ($prev =~ /^\s*return\s*;/) { + err_prev("unneeded return at end of function"); + } + $in_function = 0; + reset_indent(); # we don't check between functions + $prev = $line; + next line; + } + if (/^\w*\($/) { + $in_function_header = 1; + } + + # a blank line terminates the declarations within a function. + # XXX - but still a problem in sub-blocks. + if ($in_declaration && /^$/) { + $in_declaration = 0; + } + + if ($comment_done) { + $in_comment = 0; + $in_header_comment = 0; + $comment_done = 0; + } + # does this looks like the start of a block comment? + if (/$hdr_comment_start/) { + if ($config{"indent"} eq "tab") { + if (!/^\t*\/\*/) { + err("block comment not indented by tabs"); + } + } elsif (!/^ *\/\*/) { + err("block comment not indented by spaces"); + } + $in_comment = 1; + /^(\s*)\//; + $comment_prefix = $1; + if ($comment_prefix eq "") { + $in_header_comment = 1; + } + $prev = $line; + next line; + } + # are we still in the block comment? + if ($in_comment) { + if (/^$comment_prefix \*\/$/) { + $comment_done = 1; + } elsif (/\*\//) { + $comment_done = 1; + err("improper block comment close") + unless ($ignore_hdr_comment && $in_header_comment); + } elsif (!/^$comment_prefix \*[ \t]/ && + !/^$comment_prefix \*$/) { + err("improper block comment") + unless ($ignore_hdr_comment && $in_header_comment); + } + } + + if ($in_header_comment && $ignore_hdr_comment) { + $prev = $line; + next line; + } + + # check for errors that might occur in comments and in code. + + # allow spaces to be used to draw pictures in header comments. + #if (/[^ ] / && !/".* .*"/ && !$in_header_comment) { + # err("spaces instead of tabs"); + #} + #if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && + # (!/^ \w/ || $in_function != 0)) { + # err("indent by spaces instead of tabs"); + #} + if ($config{"indent"} eq "tab") { + if (/^ {2,}/ && !/^ [^ ]/) { + err("indent by spaces instead of tabs"); + } + } elsif (/^\t/) { + err("indent by tabs instead of spaces") + } elsif (/^( +)/ && !$in_comment) { + my $indent = $1; + if (length($indent) < $config{"indent"}) { + err("indent of " . length($indent) . + " space(s) instead of " . $config{"indent"}); + } elsif ($config{"strict-indent"} && + length($indent) % $config{"indent"} != 0) { + err("indent is " . length($indent) . + " not a multiple of " . $config{'indent'} . " spaces"); + } + } + if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { + err("continuation line not indented by 4 spaces"); + } + + # A multi-line block comment must not have content on the first line. + if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) { + err("improper first line of block comment"); + } + + if ($in_comment) { # still in comment, don't do further checks + $prev = $line; + next line; + } + + if ((/[^(]\/\*\S/ || /^\/\*\S/) && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank after open comment"); + } + if (/\S\*\/[^)]|\S\*\/$/ && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank before close comment"); + } + if ($config{"blank-after-start-comment"} && /(?\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\s,=]/ || + (/[^->]>[^,=>\s]/ && !/[^->]>$/) || + (/[^<]<[^,=<\s]/ && !/[^<]<$/) || + /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { + err("missing space around relational operator"); + } + if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || + (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || + (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { + # XXX - should only check this for C++ code + # XXX - there are probably other forms that should be allowed + if (!/\soperator=/) { + err("missing space around assignment operator"); + } + } + if (/[,;]\S/ && !/\bfor \(;;\)/ && + # Allow a comma in a regex quantifier. + !/\/.*?\{\d+,?\d*\}.*?\//) { + err("comma or semicolon followed by non-blank"); + } + # check for commas preceded by blanks + if ((!$config{"leading-comma-ok"} && /^\s*,/) || (!/^\s*,/ && /\s,/)) { + err("comma preceded by blank"); + } + # check for semicolons preceded by blanks + # allow "for" statements to have empty "while" clauses + if (/\s;/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { + err("semicolon preceded by blank"); + } + if (!$config{"continuation-at-front"} && /^\s*(&&|\|\|)/) { + err("improper boolean continuation"); + } elsif ($config{"continuation-at-front"} && /(&&|\|\||\+)$/) { + err("improper continuation"); + } + if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { + err("more than one space around boolean operator"); + } + # We allow methods which look like obj.delete() but not keywords without + # spaces ala: delete(obj) + if (/(? $@ + +# +# The "docs" target is complicated because we do several things here: +# +# (1) Use restdown to build HTML and JSON files from each of DOC_FILES. +# +# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which +# functions as a complete copy of the documentation that could be +# mirrored or served over HTTP. +# +# (3) Then copy any directories and media from docs/media into +# $(DOC_BUILD)/media. This allows projects to include their own media, +# including files that will override same-named files provided by +# restdown. +# +# Step (3) is the surprisingly complex part: in order to do this, we need to +# identify the subdirectories in docs/media, recreate them in +# $(DOC_BUILD)/media, then do the same with the files. +# +DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$") +DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%) +DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%) + +DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null) +DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%) +DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%) + +# +# Like the other targets, "docs" just depends on the final files we want to +# create in $(DOC_BUILD), leveraging other targets and recipes to define how +# to get there. +# +.PHONY: docs +docs: \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html) \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json) \ + $(DOC_MEDIA_FILES_BUILD) + +# +# We keep the intermediate files so that the next build can see whether the +# files in DOC_BUILD are up to date. +# +.PRECIOUS: \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%json) + +# +# We do clean those intermediate files, as well as all of DOC_BUILD. +# +CLEAN_FILES += \ + $(DOC_BUILD) \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%.json) + +# +# Before installing the files, we must make sure the directories exist. The | +# syntax tells make that the dependency need only exist, not be up to date. +# Otherwise, it might try to rebuild spuriously because the directory itself +# appears out of date. +# +$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD) + +$(DOC_BUILD)/%: docs/% | $(DOC_BUILD) + $(CP) $< $@ + +docs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC) + $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $< + +$(DOC_BUILD): + $(MKDIR) $@ + +$(DOC_MEDIA_DIRS_BUILD): + $(MKDIR) $@ + +# +# The default "test" target does nothing. This should usually be overridden by +# the parent Makefile. It's included here so we can define "prepush" without +# requiring the repo to define "test". +# +.PHONY: test +test: + +.PHONY: prepush +prepush: check test diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/README.md new file mode 100644 index 0000000..78ea546 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/README.md @@ -0,0 +1,441 @@ +# vasync: utilities for observable asynchronous control flow + +This module provides facilities for asynchronous control flow. There are many +modules that do this already (notably async.js). This one's claim to fame is +aided debuggability: each of the contained functions return a "status" object +with the following fields: + + operations array corresponding to the input functions, with + + func input function + + status "pending", "ok", or "fail" + + err returned "err" value, if any + + result returned "result" value, if any + + successes "result" field for each of "operations" where + "status" == "ok" + + ndone number of input operations that have completed + + nerrors number of input operations that have failed + +You can use this from a debugger (or your own monitoring code) to understand +the state of an ongoing asynchronous operation. For example, you could see how +far into a pipeline some particular operation is. + + +### parallel(args, callback): invoke N functions in parallel and merge the results + +This function takes a list of input functions (specified by the "funcs" property +of "args") and runs them all. These input functions are expected to be +asynchronous: they get a "callback" argument and should invoke it as +callback(err, result). The error and result will be saved and made available to +the original caller when all of these functions complete. + +All errors are combined into a single "err" parameter to the final callback (see +below). You can also observe the progress of the operation as it goes by +examining the object returned synchronously by this function. + +Example usage: + + status = mod_vasync.parallel({ + 'funcs': [ + function f1 (callback) { mod_fs.stat('/tmp', callback); }, + function f2 (callback) { mod_fs.stat('/noexist', callback); }, + function f3 (callback) { mod_fs.stat('/var', callback); } + ] + }, function (err, results) { + console.log('error: %s', err.message); + console.log('results: %s', mod_util.inspect(results, null, 3)); + }); + + console.log('status: %s', mod_sys.inspect(status, null, 3)); + +In the first tick, this outputs: + + status: { operations: + [ { func: [Function: f1], status: 'pending' }, + { func: [Function: f2], status: 'pending' }, + { func: [Function: f3], status: 'pending' } ], + successes: [], + ndone: 0, + nerrors: 0 } + +showing that there are three operations pending and none has yet been started. +When the program finishes, it outputs this error: + + error: first of 1 error: ENOENT, no such file or directory '/noexist' + +which encapsulates all of the intermediate failures. This model allows you to +write the final callback like you normally would: + + if (err) + return (callback(err)); + +and still propagate useful information to callers that don't deal with multiple +errors (i.e. most callers). + +The example also prints out the detailed final status, including all of the +errors and return values: + + results: { operations: + [ { func: [Function: f1], + status: 'ok', + err: null, + result: + { dev: 140247096, + ino: 879368309, + mode: 17407, + nlink: 9, + uid: 0, + gid: 3, + rdev: 0, + size: 754, + blksize: 4096, + blocks: 8, + atime: Thu, 12 Apr 2012 23:18:57 GMT, + mtime: Tue, 17 Apr 2012 23:56:34 GMT, + ctime: Tue, 17 Apr 2012 23:56:34 GMT } }, + { func: [Function: f2], + status: 'fail', + err: { [Error: ENOENT, no such file or directory '/noexist'] errno: 34, code: 'ENOENT', path: '/noexist' }, + result: undefined }, + { func: [Function: f3], + status: 'ok', + err: null, + result: + { dev: 23658528, + ino: 5, + mode: 16877, + nlink: 27, + uid: 0, + gid: 0, + rdev: -1, + size: 27, + blksize: 2560, + blocks: 3, + atime: Fri, 09 Sep 2011 14:28:55 GMT, + mtime: Wed, 04 Apr 2012 17:51:20 GMT, + ctime: Wed, 04 Apr 2012 17:51:20 GMT } } ], + successes: + [ { dev: 234881026, + ino: 24965, + mode: 17407, + nlink: 8, + uid: 0, + gid: 0, + rdev: 0, + size: 272, + blksize: 4096, + blocks: 0, + atime: Tue, 01 May 2012 16:02:24 GMT, + mtime: Tue, 01 May 2012 19:10:35 GMT, + ctime: Tue, 01 May 2012 19:10:35 GMT }, + { dev: 234881026, + ino: 216, + mode: 16877, + nlink: 26, + uid: 0, + gid: 0, + rdev: 0, + size: 884, + blksize: 4096, + blocks: 0, + atime: Tue, 01 May 2012 16:02:24 GMT, + mtime: Fri, 14 Aug 2009 21:23:03 GMT, + ctime: Thu, 28 Oct 2010 21:51:39 GMT } ], + ndone: 3, + nerrors: 1 } + +You can use this if you want to handle all of the errors individually or to get +at all of the individual return values. + + +### forEachParallel(args, callback): invoke the same function on N inputs in parallel + +This function is exactly like `parallel`, except that the input is specified as +a *single* function ("func") and a list of inputs ("inputs"). The function is +invoked on each input in parallel. + +This example is exactly equivalent to the one above: + + mod_vasync.forEachParallel({ + 'func': mod_fs.stat, + 'inputs': [ '/var', '/nonexistent', '/tmp' ] + }, function (err, results) { + console.log('error: %s', err.message); + console.log('results: %s', mod_util.inspect(results, null, 3)); + }); + + +### pipeline(args, callback): invoke N functions in series (and stop on failure) + +The arguments for this function are: + +* funcs: input functions, to be invoked in series +* arg: arbitrary argument that will be passed to each function + +The functions are invoked in order as `func(arg, callback)`, where "arg" is the +user-supplied argument from "args" and "callback" should be invoked in the usual +way. If any function emits an error, the whole pipeline stops. + +The return value and the arguments to the final callback are exactly the same as +for `parallel`. The error object for the final callback is just the error +returned by whatever pipeline function failed (if any). + +This example is similar to the one above, except that it runs the steps in +sequence and stops early because `pipeline` stops on the first error: + + console.log(mod_vasync.pipeline({ + 'funcs': [ + function f1 (_, callback) { mod_fs.stat('/tmp', callback); }, + function f2 (_, callback) { mod_fs.stat('/noexist', callback); }, + function f3 (_, callback) { mod_fs.stat('/var', callback); } + ] + }, function (err, results) { + console.log('error: %s', err.message); + console.log('results: %s', mod_util.inspect(results, null, 3)); + })); + +As a result, the status after the first tick looks like this: + + { operations: + [ { func: [Function: f1], status: 'pending' }, + { func: [Function: f2], status: 'waiting' }, + { func: [Function: f3], status: 'waiting' } ], + successes: [], + ndone: 0, + nerrors: 0 } + +(Note that the second and third stages are now "waiting", rather than "pending" +in the `parallel` case.) The error reported is: + + error: ENOENT, no such file or directory '/noexist' + +and the complete result is: + + results: { operations: + [ { func: [Function: f1], + status: 'ok', + err: null, + result: + { dev: 140247096, + ino: 879368309, + mode: 17407, + nlink: 9, + uid: 0, + gid: 3, + rdev: 0, + size: 754, + blksize: 4096, + blocks: 8, + atime: Thu, 12 Apr 2012 23:18:57 GMT, + mtime: Tue, 17 Apr 2012 23:56:34 GMT, + ctime: Tue, 17 Apr 2012 23:56:34 GMT } }, + { func: [Function: f2], + status: 'fail', + err: { [Error: ENOENT, no such file or directory '/noexist'] errno: 34, code: 'ENOENT', path: '/noexist' }, + result: undefined }, + { func: [Function: f3], status: 'waiting' } ], + successes: + [ { dev: 234881026, + ino: 24965, + mode: 17407, + nlink: 8, + uid: 0, + gid: 0, + rdev: 0, + size: 272, + blksize: 4096, + blocks: 0, + atime: Tue, 01 May 2012 16:02:24 GMT, + mtime: Tue, 01 May 2012 19:10:35 GMT, + ctime: Tue, 01 May 2012 19:10:35 GMT } ], + ndone: 2, + nerrors: 1 } + + +### queue(worker, concurrency): fixed-size worker queue +### queuev(args) + +This function returns an object that allows up to a fixed number of tasks to be +dispatched at any given time. The interface is compatible with that provided +by the "async" Node library, except that the returned object's fields represent +a public interface you can use to introspect what's going on. + +The arguments are: + +* worker: a function invoked as `worker(task, callback)`, where `task` is a + task dispatched to this queue and `callback` should be invoked when the task + completes. +* concurrency: a positive integer indicating the maximum number of tasks that + may be dispatched at any time. + +With concurrency = 1, the queue serializes all operations. + +The object provides the following method: + +* push(task, [callback]): add a task (or array of tasks) to the queue, with an + optional callback to be invoked when each task completes. If a list of tasks + are added, the callback is invoked for each one. + +The object also provides the length() method, the "concurrency" field, and +hooks for "saturated", "empty", and "drain" for compatibility with node-async. +In addition, several fields may be inspected to see what's currently going on +with the queue, but **these fields must not be modified directly**: + +* worker: worker function, as passed into "queue"/"queuev" +* worker_name: worker function's "name" field +* npending: the number of tasks currently being processed +* pending: an object (*not* an array) describing the tasks currently being processed +* queued: array of tasks currently queued for processing + +If the tasks are themselves simple objects, then the entire queue may be +serialized (as via JSON.stringify) for debugging and monitoring tools. Using +the above fields, you can see what this queue is doing (worker_name), which +tasks are queued, which tasks are being processed, and so on. + +### Example 1: Stat several files + +Here's an example demonstrating the queue: + + var mod_fs = require('fs'); + var mod_vasync = require('../lib/vasync'); + + var queue; + + function doneOne() + { + console.log('task completed; queue state:\n%s\n', + JSON.stringify(queue, null, 4)); + } + + queue = mod_vasync.queue(mod_fs.stat, 2); + + console.log('initial queue state:\n%s\n', JSON.stringify(queue, null, 4)); + + queue.push('/tmp/file1', doneOne); + queue.push('/tmp/file2', doneOne); + queue.push('/tmp/file3', doneOne); + queue.push('/tmp/file4', doneOne); + + console.log('all tasks dispatched:\n%s\n', JSON.stringify(queue, null, 4)); + +The initial queue state looks like this: + + initial queue state: + { + "nextid": 0, + "worker_name": "anon", + "npending": 0, + "pending": {}, + "queued": [], + "concurrency": 2 + } + +After four tasks have been pushed, we see that two of them have been dispatched +and the remaining two are queued up: + + all tasks pushed: + { + "nextid": 4, + "worker_name": "anon", + "npending": 2, + "pending": { + "1": { + "id": 1, + "task": "/tmp/file1" + }, + "2": { + "id": 2, + "task": "/tmp/file2" + } + }, + "queued": [ + { + "id": 3, + "task": "/tmp/file3" + }, + { + "id": 4, + "task": "/tmp/file4" + } + ], + "concurrency": 2 + } + +As they complete, we see tasks moving from "queued" to "pending", and completed +tasks disappear: + + task completed; queue state: + { + "nextid": 4, + "worker_name": "anon", + "npending": 1, + "pending": { + "3": { + "id": 3, + "task": "/tmp/file3" + } + }, + "queued": [ + { + "id": 4, + "task": "/tmp/file4" + } + ], + "concurrency": 2 + } + +When all tasks have completed, the queue state looks like it started: + + task completed; queue state: + { + "nextid": 4, + "worker_name": "anon", + "npending": 0, + "pending": {}, + "queued": [], + "concurrency": 2 + } + + +### Example 2: A simple serializer + +You can use a queue with concurrency 1 and where the tasks are themselves +functions to ensure that an arbitrary asynchronous function never runs +concurrently with another one, no matter what each one does. Since the tasks +are the actual functions to be invoked, the worker function just invokes each +one: + + var mod_vasync = require('../lib/vasync'); + + var queue = mod_vasync.queue( + function (task, callback) { task(callback); }, 1); + + queue.push(function (callback) { + console.log('first task begins'); + setTimeout(function () { + console.log('first task ends'); + callback(); + }, 500); + }); + + queue.push(function (callback) { + console.log('second task begins'); + process.nextTick(function () { + console.log('second task ends'); + callback(); + }); + }); + +This example outputs: + + $ node examples/queue-serializer.js + first task begins + first task ends + second task begins + second task ends diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/foreach-parallel.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/foreach-parallel.js new file mode 100644 index 0000000..809a78f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/foreach-parallel.js @@ -0,0 +1,11 @@ +var mod_fs = require('fs'); +var mod_util = require('util'); +var mod_vasync = require('../lib/vasync'); + +console.log(mod_vasync.forEachParallel({ + 'func': mod_fs.stat, + 'inputs': [ '/var', '/nonexistent', '/tmp' ] +}, function (err, results) { + console.log('error: %s', err.message); + console.log('results: %s', mod_util.inspect(results, null, 3)); +})); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/nofail.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/nofail.js new file mode 100644 index 0000000..7d2038b --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/nofail.js @@ -0,0 +1,13 @@ +var mod_vasync = require('../lib/vasync'); +var mod_util = require('util'); +var mod_fs = require('fs'); + +var status = mod_vasync.parallel({ + funcs: [ + function f1 (callback) { mod_fs.stat('/tmp', callback); }, + function f2 (callback) { mod_fs.stat('/var', callback); } + ] +}, function (err, results) { + console.log(err); + console.log(mod_util.inspect(results, false, 8)); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/parallel.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/parallel.js new file mode 100644 index 0000000..eb88a1d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/parallel.js @@ -0,0 +1,14 @@ +var mod_fs = require('fs'); +var mod_util = require('util'); +var mod_vasync = require('../lib/vasync'); + +console.log(mod_vasync.parallel({ + 'funcs': [ + function f1 (callback) { mod_fs.stat('/tmp', callback); }, + function f2 (callback) { mod_fs.stat('/noexist', callback); }, + function f3 (callback) { mod_fs.stat('/var', callback); } + ] +}, function (err, results) { + console.log('error: %s', err.message); + console.log('results: %s', mod_util.inspect(results, null, 3)); +})); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/pipeline.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/pipeline.js new file mode 100644 index 0000000..9b4f087 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/pipeline.js @@ -0,0 +1,14 @@ +var mod_fs = require('fs'); +var mod_util = require('util'); +var mod_vasync = require('../lib/vasync'); + +console.log(mod_vasync.pipeline({ + 'funcs': [ + function f1 (_, callback) { mod_fs.stat('/tmp', callback); }, + function f2 (_, callback) { mod_fs.stat('/noexist', callback); }, + function f3 (_, callback) { mod_fs.stat('/var', callback); } + ] +}, function (err, results) { + console.log('error: %s', err.message); + console.log('results: %s', mod_util.inspect(results, null, 3)); +})); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/queue-serializer.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/queue-serializer.js new file mode 100644 index 0000000..5fc6d38 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/queue-serializer.js @@ -0,0 +1,19 @@ +var mod_vasync = require('../lib/vasync'); + +var queue = mod_vasync.queue(function (task, callback) { task(callback); }, 1); + +queue.push(function (callback) { + console.log('first task begins'); + setTimeout(function () { + console.log('first task ends'); + callback(); + }, 500); +}); + +queue.push(function (callback) { + console.log('second task begins'); + process.nextTick(function () { + console.log('second task ends'); + callback(); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/queue-stat.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/queue-stat.js new file mode 100644 index 0000000..c8e9585 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/examples/queue-stat.js @@ -0,0 +1,21 @@ +var mod_fs = require('fs'); +var mod_vasync = require('../lib/vasync'); + +var queue; + +function doneOne() +{ + console.log('task completed; queue state:\n%s\n', + JSON.stringify(queue, null, 4)); +} + +queue = mod_vasync.queue(mod_fs.stat, 2); + +console.log('initial queue state:\n%s\n', JSON.stringify(queue, null, 4)); + +queue.push('/tmp/file1', doneOne); +queue.push('/tmp/file2', doneOne); +queue.push('/tmp/file3', doneOne); +queue.push('/tmp/file4', doneOne); + +console.log('all tasks pushed:\n%s\n', JSON.stringify(queue, null, 4)); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/jsl.node.conf b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/jsl.node.conf new file mode 100644 index 0000000..03f787f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/jsl.node.conf @@ -0,0 +1,137 @@ +# +# Configuration File for JavaScript Lint +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# ++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent ++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity ++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement ++anon_no_return_value # anonymous function does not always return value ++assign_to_function_call # assignment to a function call +-block_without_braces # block statement without curly braces ++comma_separated_stmts # multiple statements separated by commas (use semicolons?) ++comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) ++default_not_at_end # the default case is not at the end of the switch statement ++dup_option_explicit # duplicate "option explicit" control comment ++duplicate_case_in_switch # duplicate case in switch statement ++duplicate_formal # duplicate formal argument {name} ++empty_statement # empty statement or extra semicolon ++identifier_hides_another # identifer {name} hides an identifier in a parent scope +-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement ++incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. ++invalid_fallthru # unexpected "fallthru" control comment ++invalid_pass # unexpected "pass" control comment ++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax ++leading_decimal_point # leading decimal point may indicate a number or an object member ++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax ++meaningless_block # meaningless block; curly braces have no impact ++mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence ++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma ++missing_break # missing break statement ++missing_break_for_last_case # missing break statement for last case in switch ++missing_default_case # missing default case in switch statement ++missing_option_explicit # the "option explicit" control comment is missing ++missing_semicolon # missing semicolon ++missing_semicolon_for_lambda # missing semicolon for lambda assignment ++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs ++nested_comment # nested comment ++no_return_value # function {name} does not always return a value ++octal_number # leading zeros make an octal number ++parseint_missing_radix # parseInt missing radix parameter ++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag ++redeclared_var # redeclaration of {name} ++trailing_comma_in_array # extra comma is not recommended in array initializers ++trailing_decimal_point # trailing decimal point may indicate a number or an object member ++undeclared_identifier # undeclared identifier: {name} ++unreachable_code # unreachable code +-unreferenced_argument # argument declared but never referenced: {name} +-unreferenced_function # function is declared but never referenced: {name} ++unreferenced_variable # variable is declared but never referenced: {name} ++unsupported_version # JavaScript {version} is not supported ++use_of_label # use of label ++useless_assign # useless assignment ++useless_comparison # useless comparison; comparing identical expressions +-useless_quotes # the quotation marks are unnecessary ++useless_void # use of the void type may be unnecessary (void is always undefined) ++var_hides_arg # variable {name} hides argument ++want_assign_or_call # expected an assignment or function call ++with_statement # with statement hides undeclared variables; use temporary variable instead + + +### Output format +# Customize the format of the error message. +# __FILE__ indicates current file path +# __FILENAME__ indicates current file name +# __LINE__ indicates current line +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message +# +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# +-legacy_control_comments + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" +-always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: ++define __dirname ++define clearInterval ++define clearTimeout ++define console ++define exports ++define global ++define process ++define require ++define setInterval ++define setTimeout ++define Buffer ++define JSON ++define Math + +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/lib/vasync.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/lib/vasync.js new file mode 100644 index 0000000..38e060f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/lib/vasync.js @@ -0,0 +1,308 @@ +/* + * vasync.js: utilities for observable asynchronous control flow + */ + +var mod_assert = require('assert'); +var mod_util = require('util'); +var mod_verror = require('verror'); + +/* + * Public interface + */ +exports.parallel = parallel; +exports.forEachParallel = forEachParallel; +exports.pipeline = pipeline; +exports.queue = queue; +exports.queuev = queuev; + +/* + * Given a set of functions that complete asynchronously using the standard + * callback(err, result) pattern, invoke them all and merge the results. See + * README.md for details. + */ +function parallel(args, callback) +{ + var funcs, rv, doneOne, i; + + mod_assert.equal(typeof (args), 'object', '"args" must be an object'); + mod_assert.ok(Array.isArray(args['funcs']), + '"args.funcs" must be specified and must be an array'); + mod_assert.equal(typeof (callback), 'function', + 'callback argument must be specified and must be a function'); + + funcs = args['funcs']; + + rv = { + 'operations': new Array(funcs.length), + 'successes': [], + 'ndone': 0, + 'nerrors': 0 + }; + + if (funcs.length === 0) { + process.nextTick(function () { callback(null, rv); }); + return (rv); + } + + doneOne = function (entry) { + return (function (err, result) { + mod_assert.equal(entry['status'], 'pending'); + + entry['err'] = err; + entry['result'] = result; + entry['status'] = err ? 'fail' : 'ok'; + + if (err) + rv['nerrors']++; + else + rv['successes'].push(result); + + if (++rv['ndone'] < funcs.length) + return; + + var errors = rv['operations'].filter(function (ent) { + return (ent['status'] == 'fail'); + }).map(function (ent) { return (ent['err']); }); + + if (errors.length > 0) + callback(new mod_verror.MultiError(errors), rv); + else + callback(null, rv); + }); + }; + + for (i = 0; i < funcs.length; i++) { + rv['operations'][i] = { + 'func': funcs[i], + 'funcname': funcs[i].name || '(anon)', + 'status': 'pending' + }; + + funcs[i](doneOne(rv['operations'][i])); + } + + return (rv); +} + +/* + * Exactly like parallel, except that the input is specified as a single + * function to invoke on N different inputs (rather than N functions). "args" + * must have the following fields: + * + * func asynchronous function to invoke on each input value + * + * inputs array of input values + */ +function forEachParallel(args, callback) +{ + var func, funcs; + + mod_assert.equal(typeof (args), 'object', '"args" must be an object'); + mod_assert.equal(typeof (args['func']), 'function', + '"args.func" must be specified and must be a function'); + mod_assert.ok(Array.isArray(args['inputs']), + '"args.inputs" must be specified and must be an array'); + + func = args['func']; + funcs = args['inputs'].map(function (input) { + return (function (subcallback) { + return (func(input, subcallback)); + }); + }); + + return (parallel({ 'funcs': funcs }, callback)); +} + +/* + * Like parallel, but invokes functions in sequence rather than in parallel + * and aborts if any function exits with failure. Arguments include: + * + * funcs invoke the functions in parallel + * + * arg first argument to each pipeline function + */ +function pipeline(args, callback) +{ + var funcs, uarg, rv, next; + + mod_assert.equal(typeof (args), 'object', '"args" must be an object'); + mod_assert.ok(Array.isArray(args['funcs']), + '"args.funcs" must be specified and must be an array'); + + funcs = args['funcs']; + uarg = args['arg']; + + rv = { + 'operations': funcs.map(function (func) { + return ({ + 'func': func, + 'funcname': func.name || '(anon)', + 'status': 'waiting' + }); + }), + 'successes': [], + 'ndone': 0, + 'nerrors': 0 + }; + + if (funcs.length === 0) { + process.nextTick(function () { callback(null, rv); }); + return (rv); + } + + next = function (err, result) { + if (rv['nerrors'] > 0 || + rv['ndone'] >= rv['operations'].length) { + throw new mod_verror.VError('pipeline callback ' + + 'invoked after the pipeline has already ' + + 'completed (%j)', rv); + } + + var entry = rv['operations'][rv['ndone']++]; + + mod_assert.equal(entry['status'], 'pending'); + + entry['status'] = err ? 'fail' : 'ok'; + entry['err'] = err; + entry['result'] = result; + + if (err) + rv['nerrors']++; + else + rv['successes'].push(result); + + if (err || rv['ndone'] == funcs.length) { + callback(err, rv); + } else { + var nextent = rv['operations'][rv['ndone']]; + nextent['status'] = 'pending'; + + /* + * We invoke the next function on the next tick so that + * the caller (stage N) need not worry about the case + * that the next stage (stage N + 1) runs in its own + * context. + */ + process.nextTick(function () { + nextent['func'](uarg, next); + }); + } + }; + + rv['operations'][0]['status'] = 'pending'; + funcs[0](uarg, next); + + return (rv); +} + +/* + * async-compatible "queue" function. + */ +function queue(worker, concurrency) +{ + return (new WorkQueue({ + 'worker': worker, + 'concurrency': concurrency + })); +} + +function queuev(args) +{ + return (new WorkQueue(args)); +} + +function WorkQueue(args) +{ + mod_assert.ok(args.hasOwnProperty('worker')); + mod_assert.equal(typeof (args['worker']), 'function'); + mod_assert.ok(args.hasOwnProperty('concurrency')); + mod_assert.equal(typeof (args['concurrency']), 'number'); + mod_assert.equal(Math.floor(args['concurrency']), args['concurrency']); + mod_assert.ok(args['concurrency'] > 0); + + this.nextid = 0; + this.worker = args['worker']; + this.worker_name = args['worker'].name || 'anon'; + this.npending = 0; + this.pending = {}; + this.queued = []; + + /* user-settable fields inherited from "async" interface */ + this.concurrency = args['concurrency']; + this.saturated = undefined; + this.empty = undefined; + this.drain = undefined; +} + +WorkQueue.prototype.push = function (tasks, callback) +{ + if (!Array.isArray(tasks)) + return (this.pushOne(tasks, callback)); + + var wq = this; + return (tasks.map(function (task) { + return (wq.pushOne(task, callback)); + })); +}; + +/* private */ +WorkQueue.prototype.pushOne = function (task, callback) +{ + var id = ++this.nextid; + var entry = { 'id': id, 'task': task, 'callback': callback }; + + if (this.npending < this.concurrency) + this.dispatch(entry); + else + this.queued.push(entry); + + return (id); +}; + +/* private */ +WorkQueue.prototype.dispatch = function (entry) +{ + var wq = this; + + mod_assert.ok(!this.pending.hasOwnProperty(entry['id'])); + mod_assert.ok(this.npending < this.concurrency); + + this.npending++; + this.pending[entry['id']] = entry; + + if (this.npending == this.concurrency && this.saturated) + this.saturated(); + + /* + * We invoke the worker function on the next tick so that callers can + * always assume that the callback is NOT invoked during the call to + * push() even if the queue is not at capacity. It also avoids O(n) + * stack usage when used with synchronous worker functions. + */ + process.nextTick(function () { + wq.worker(entry['task'], function (err) { + --wq.npending; + delete (wq.pending[entry['id']]); + + if (entry['callback']) + entry['callback'](err); + + if (wq.drain && wq.npending === 0 && + wq.queued.length === 0) + wq.drain(); + + if (wq.queued.length > 0) { + var next = wq.queued.shift(); + wq.dispatch(next); + + if (wq.queued.length === 0 && wq.empty) + wq.empty(); + } + }); + }); +}; + +WorkQueue.prototype.length = function () +{ + return (this.queued.length); +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/.gitmodules b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/.gitmodules new file mode 100644 index 0000000..d775f69 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/.gitmodules @@ -0,0 +1,6 @@ +[submodule "deps/javascriptlint"] + path = deps/javascriptlint + url = git://github.com/davepacheco/javascriptlint +[submodule "deps/jsstyle"] + path = deps/jsstyle + url = git://github.com/davepacheco/jsstyle diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/.npmignore b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/LICENSE b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/LICENSE new file mode 100644 index 0000000..cbc0bb3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012, Joyent, Inc. All rights reserved. + +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/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile new file mode 100644 index 0000000..1deeb5f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile @@ -0,0 +1,23 @@ +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile: top-level Makefile +# +# This Makefile contains only repo-specific logic and uses included makefiles +# to supply common targets (javascriptlint, jsstyle, restdown, etc.), which are +# used by other repos as well. +# + +# +# Files +# +JS_FILES := $(shell find lib -name '*.js') +JSL_FILES_NODE = $(JS_FILES) +JSSTYLE_FILES = $(JS_FILES) +JSL_CONF_NODE = jsl.node.conf + +# Default target is "check" +check: + +include ./Makefile.deps +include ./Makefile.targ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile.deps b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile.deps new file mode 100644 index 0000000..2811bde --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile.deps @@ -0,0 +1,39 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.deps: Makefile for including common tools as dependencies +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This file is separate from Makefile.targ so that teams can choose +# independently whether to use the common targets in Makefile.targ and the +# common tools here. +# + +# +# javascriptlint +# +JSL_EXEC ?= deps/javascriptlint/build/install/jsl +JSL ?= python2.6 $(JSL_EXEC) + +$(JSL_EXEC): | deps/javascriptlint/.git + cd deps/javascriptlint && make install + +# +# jsstyle +# +JSSTYLE_EXEC ?= deps/jsstyle/jsstyle +JSSTYLE ?= $(JSSTYLE_EXEC) + +$(JSSTYLE_EXEC): | deps/jsstyle/.git + +# +# restdown +# +RESTDOWN_EXEC ?= deps/restdown/bin/restdown +RESTDOWN ?= python2.6 $(RESTDOWN_EXEC) +$(RESTDOWN_EXEC): | deps/restdown/.git diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile.targ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile.targ new file mode 100644 index 0000000..2a64fe7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/Makefile.targ @@ -0,0 +1,285 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.targ: common targets. +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This Makefile defines several useful targets and rules. You can use it by +# including it from a Makefile that specifies some of the variables below. +# +# Targets defined in this Makefile: +# +# check Checks JavaScript files for lint and style +# Checks bash scripts for syntax +# Checks SMF manifests for validity against the SMF DTD +# +# clean Removes built files +# +# docs Builds restdown documentation in docs/ +# +# prepush Depends on "check" and "test" +# +# test Does nothing (you should override this) +# +# xref Generates cscope (source cross-reference index) +# +# For details on what these targets are supposed to do, see the Joyent +# Engineering Guide. +# +# To make use of these targets, you'll need to set some of these variables. Any +# variables left unset will simply not be used. +# +# BASH_FILES Bash scripts to check for syntax +# (paths relative to top-level Makefile) +# +# CLEAN_FILES Files to remove as part of the "clean" target. Note +# that files generated by targets in this Makefile are +# automatically included in CLEAN_FILES. These include +# restdown-generated HTML and JSON files. +# +# DOC_FILES Restdown (documentation source) files. These are +# assumed to be contained in "docs/", and must NOT +# contain the "docs/" prefix. +# +# JSL_CONF_NODE Specify JavaScriptLint configuration files +# JSL_CONF_WEB (paths relative to top-level Makefile) +# +# Node.js and Web configuration files are separate +# because you'll usually want different global variable +# configurations. If no file is specified, none is given +# to jsl, which causes it to use a default configuration, +# which probably isn't what you want. +# +# JSL_FILES_NODE JavaScript files to check with Node config file. +# JSL_FILES_WEB JavaScript files to check with Web config file. +# +# You can also override these variables: +# +# BASH Path to bash (default: bash) +# +# CSCOPE_DIRS Directories to search for source files for the cscope +# index. (default: ".") +# +# JSL Path to JavaScriptLint (default: "jsl") +# +# JSL_FLAGS_NODE Additional flags to pass through to JSL +# JSL_FLAGS_WEB +# JSL_FLAGS +# +# JSSTYLE Path to jsstyle (default: jsstyle) +# +# JSSTYLE_FLAGS Additional flags to pass through to jsstyle +# + +# +# Defaults for the various tools we use. +# +BASH ?= bash +BASHSTYLE ?= tools/bashstyle +CP ?= cp +CSCOPE ?= cscope +CSCOPE_DIRS ?= . +JSL ?= jsl +JSSTYLE ?= jsstyle +MKDIR ?= mkdir -p +MV ?= mv +RESTDOWN_FLAGS ?= +RMTREE ?= rm -rf +JSL_FLAGS ?= --nologo --nosummary + +ifeq ($(shell uname -s),SunOS) + TAR ?= gtar +else + TAR ?= tar +endif + + +# +# Defaults for other fixed values. +# +BUILD = build +DISTCLEAN_FILES += $(BUILD) +DOC_BUILD = $(BUILD)/docs/public + +# +# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}. +# +ifneq ($(origin JSL_CONF_NODE), undefined) + JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE) +endif + +ifneq ($(origin JSL_CONF_WEB), undefined) + JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB) +endif + +# +# Targets. For descriptions on what these are supposed to do, see the +# Joyent Engineering Guide. +# + +# +# Instruct make to keep around temporary files. We have rules below that +# automatically update git submodules as needed, but they employ a deps/*/.git +# temporary file. Without this directive, make tries to remove these .git +# directories after the build has completed. +# +.SECONDARY: $($(wildcard deps/*):%=%/.git) + +# +# This rule enables other rules that use files from a git submodule to have +# those files depend on deps/module/.git and have "make" automatically check +# out the submodule as needed. +# +deps/%/.git: + git submodule update --init deps/$* + +# +# These recipes make heavy use of dynamically-created phony targets. The parent +# Makefile defines a list of input files like BASH_FILES. We then say that each +# of these files depends on a fake target called filename.bashchk, and then we +# define a pattern rule for those targets that runs bash in check-syntax-only +# mode. This mechanism has the nice properties that if you specify zero files, +# the rule becomes a noop (unlike a single rule to check all bash files, which +# would invoke bash with zero files), and you can check individual files from +# the command line with "make filename.bashchk". +# +.PHONY: check-bash +check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle) + +%.bashchk: % + $(BASH) -n $^ + +%.bashstyle: % + $(BASHSTYLE) $^ + +.PHONY: check-jsl check-jsl-node check-jsl-web +check-jsl: check-jsl-node check-jsl-web + +check-jsl-node: $(JSL_FILES_NODE:%=%.jslnodechk) + +check-jsl-web: $(JSL_FILES_WEB:%=%.jslwebchk) + +%.jslnodechk: % $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $< + +%.jslwebchk: % $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $< + +.PHONY: check-jsstyle +check-jsstyle: $(JSSTYLE_FILES:%=%.jsstylechk) + +%.jsstylechk: % $(JSSTYLE_EXEC) + $(JSSTYLE) $(JSSTYLE_FLAGS) $< + +.PHONY: check +check: check-jsl check-jsstyle check-bash + @echo check ok + +.PHONY: clean +clean:: + -$(RMTREE) $(CLEAN_FILES) + +.PHONY: distclean +distclean:: clean + -$(RMTREE) $(DISTCLEAN_FILES) + +CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out +CLEAN_FILES += $(CSCOPE_FILES) + +.PHONY: xref +xref: cscope.files + $(CSCOPE) -bqR + +.PHONY: cscope.files +cscope.files: + find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \ + -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@ + +# +# The "docs" target is complicated because we do several things here: +# +# (1) Use restdown to build HTML and JSON files from each of DOC_FILES. +# +# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which +# functions as a complete copy of the documentation that could be +# mirrored or served over HTTP. +# +# (3) Then copy any directories and media from docs/media into +# $(DOC_BUILD)/media. This allows projects to include their own media, +# including files that will override same-named files provided by +# restdown. +# +# Step (3) is the surprisingly complex part: in order to do this, we need to +# identify the subdirectories in docs/media, recreate them in +# $(DOC_BUILD)/media, then do the same with the files. +# +DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$") +DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%) +DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%) + +DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null) +DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%) +DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%) + +# +# Like the other targets, "docs" just depends on the final files we want to +# create in $(DOC_BUILD), leveraging other targets and recipes to define how +# to get there. +# +.PHONY: docs +docs: \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html) \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json) \ + $(DOC_MEDIA_FILES_BUILD) + +# +# We keep the intermediate files so that the next build can see whether the +# files in DOC_BUILD are up to date. +# +.PRECIOUS: \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%json) + +# +# We do clean those intermediate files, as well as all of DOC_BUILD. +# +CLEAN_FILES += \ + $(DOC_BUILD) \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%.json) + +# +# Before installing the files, we must make sure the directories exist. The | +# syntax tells make that the dependency need only exist, not be up to date. +# Otherwise, it might try to rebuild spuriously because the directory itself +# appears out of date. +# +$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD) + +$(DOC_BUILD)/%: docs/% | $(DOC_BUILD) + $(CP) $< $@ + +docs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC) + $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $< + +$(DOC_BUILD): + $(MKDIR) $@ + +$(DOC_MEDIA_DIRS_BUILD): + $(MKDIR) $@ + +# +# The default "test" target does nothing. This should usually be overridden by +# the parent Makefile. It's included here so we can define "prepush" without +# requiring the repo to define "test". +# +.PHONY: test +test: + +.PHONY: prepush +prepush: check test diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/README.md new file mode 100644 index 0000000..e0764c4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/README.md @@ -0,0 +1,44 @@ +# verror: richer JavaScript errors + +This module provides VError, which is like JavaScript's built-in Error class, +but supports a "cause" argument and a printf-style message. The cause argument +can be null. For example: + + if (err) + throw (new VError(err, 'operation "%s" failed', opname)); + +If err.message is "file not found" and "opname" is "rm", then the thrown +exception's toString() would return: + + operation "rm" failed: file not found + +This is useful for annotating exceptions up the stack, rather than getting an +extremely low-level error (like "file not found") for a potentially much higher +level operation. + +Additionally, when printed using node-extsprintf using %r, each exception's +stack is printed. + + +# Example + +First, install it: + + # npm install verror + +Now, use it: + + var mod_fs = require('fs'); + var mod_extsprintf = require('extsprintf'); + var mod_verror = require('../lib/verror'); + + mod_fs.stat('/nonexistent', function (err) { + console.log(mod_extsprintf.sprintf('%r', + new mod_verror.VError(err, 'operation failed'))); + }); + +outputs: + + EXCEPTION: VError: operation failed: ENOENT, no such file or directory '/nonexistent' + at Object.oncomplete (/home/dap/node-verror/examples/simple.js:7:6) + Caused by: EXCEPTION: Error: Error: ENOENT, no such file or directory '/nonexistent' diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/examples/missingcause.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/examples/missingcause.js new file mode 100644 index 0000000..994d3dc --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/examples/missingcause.js @@ -0,0 +1,6 @@ +var mod_extsprintf = require('extsprintf'); +var mod_verror = require('../lib/verror'); + +console.log(mod_extsprintf.sprintf('%r', new mod_verror.VError())); +console.log(mod_extsprintf.sprintf('%r', + new mod_verror.VError('operation failed: %s', 'hello'))); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/examples/simple.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/examples/simple.js new file mode 100644 index 0000000..c847b62 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/examples/simple.js @@ -0,0 +1,8 @@ +var mod_fs = require('fs'); +var mod_extsprintf = require('extsprintf'); +var mod_verror = require('../lib/verror'); + +mod_fs.stat('/nonexistent', function (err) { + console.log(mod_extsprintf.sprintf('%r', + new mod_verror.VError(err, 'operation failed'))); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/jsl.node.conf b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/jsl.node.conf new file mode 100644 index 0000000..03f787f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/jsl.node.conf @@ -0,0 +1,137 @@ +# +# Configuration File for JavaScript Lint +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# ++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent ++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity ++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement ++anon_no_return_value # anonymous function does not always return value ++assign_to_function_call # assignment to a function call +-block_without_braces # block statement without curly braces ++comma_separated_stmts # multiple statements separated by commas (use semicolons?) ++comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) ++default_not_at_end # the default case is not at the end of the switch statement ++dup_option_explicit # duplicate "option explicit" control comment ++duplicate_case_in_switch # duplicate case in switch statement ++duplicate_formal # duplicate formal argument {name} ++empty_statement # empty statement or extra semicolon ++identifier_hides_another # identifer {name} hides an identifier in a parent scope +-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement ++incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. ++invalid_fallthru # unexpected "fallthru" control comment ++invalid_pass # unexpected "pass" control comment ++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax ++leading_decimal_point # leading decimal point may indicate a number or an object member ++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax ++meaningless_block # meaningless block; curly braces have no impact ++mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence ++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma ++missing_break # missing break statement ++missing_break_for_last_case # missing break statement for last case in switch ++missing_default_case # missing default case in switch statement ++missing_option_explicit # the "option explicit" control comment is missing ++missing_semicolon # missing semicolon ++missing_semicolon_for_lambda # missing semicolon for lambda assignment ++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs ++nested_comment # nested comment ++no_return_value # function {name} does not always return a value ++octal_number # leading zeros make an octal number ++parseint_missing_radix # parseInt missing radix parameter ++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag ++redeclared_var # redeclaration of {name} ++trailing_comma_in_array # extra comma is not recommended in array initializers ++trailing_decimal_point # trailing decimal point may indicate a number or an object member ++undeclared_identifier # undeclared identifier: {name} ++unreachable_code # unreachable code +-unreferenced_argument # argument declared but never referenced: {name} +-unreferenced_function # function is declared but never referenced: {name} ++unreferenced_variable # variable is declared but never referenced: {name} ++unsupported_version # JavaScript {version} is not supported ++use_of_label # use of label ++useless_assign # useless assignment ++useless_comparison # useless comparison; comparing identical expressions +-useless_quotes # the quotation marks are unnecessary ++useless_void # use of the void type may be unnecessary (void is always undefined) ++var_hides_arg # variable {name} hides argument ++want_assign_or_call # expected an assignment or function call ++with_statement # with statement hides undeclared variables; use temporary variable instead + + +### Output format +# Customize the format of the error message. +# __FILE__ indicates current file path +# __FILENAME__ indicates current file name +# __LINE__ indicates current line +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message +# +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# +-legacy_control_comments + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" +-always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: ++define __dirname ++define clearInterval ++define clearTimeout ++define console ++define exports ++define global ++define process ++define require ++define setInterval ++define setTimeout ++define Buffer ++define JSON ++define Math + +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/lib/verror.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/lib/verror.js new file mode 100644 index 0000000..a5018e0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/lib/verror.js @@ -0,0 +1,80 @@ +/* + * verror.js: richer JavaScript errors + */ + +var mod_assert = require('assert'); +var mod_util = require('util'); + +var mod_extsprintf = require('extsprintf'); + +/* + * Public interface + */ +exports.VError = VError; +exports.MultiError = MultiError; + +/* + * Like JavaScript's built-in Error class, but supports a "cause" argument and a + * printf-style message. The cause argument can be null. + */ +function VError(cause) +{ + var args, tailmsg; + + if (cause instanceof Error) { + args = Array.prototype.slice.call(arguments, 1); + } else { + args = Array.prototype.slice.call(arguments, 0); + cause = undefined; + } + + tailmsg = args.length > 0 ? + mod_extsprintf.sprintf.apply(null, args) : ''; + this.jse_shortmsg = tailmsg; + + if (cause) { + mod_assert.ok(cause instanceof Error); + this.jse_cause = cause; + this.jse_summary = tailmsg + ': ' + cause.message; + } else { + this.jse_summary = tailmsg; + } + + this.message = this.jse_summary; + Error.apply(this, [ this.jse_summary ]); + + if (Error.captureStackTrace) + Error.captureStackTrace(this, arguments.callee); +} + +VError.prototype = new Error(); +VError.prototype.constructor = VError; +VError.prototype.name = VError; + +VError.prototype.toString = function () +{ + return (this.jse_summary); +}; + +VError.prototype.cause = function () +{ + return (this.jse_cause); +}; + + +/* + * Represents a collection of errors for the purpose of consumers that generally + * only deal with one error. Callers can extract the individual errors + * contained in this object, but may also just treat it as a normal single + * error, in which case a summary message will be printed. + */ +function MultiError(errors) +{ + mod_assert.ok(errors.length > 0); + this.ase_errors = errors; + + VError.call(this, errors[0], 'first of %d error%s', + errors.length, errors.length == 1 ? '' : 's'); +} + +mod_util.inherits(MultiError, VError); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/.gitmodules b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/.gitmodules new file mode 100644 index 0000000..4e0f5e2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/.gitmodules @@ -0,0 +1,6 @@ +[submodule "deps/jsstyle"] + path = deps/jsstyle + url = git://github.com/davepacheco/jsstyle +[submodule "deps/javascriptlint"] + path = deps/javascriptlint + url = git://github.com/davepacheco/javascriptlint diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/LICENSE b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/LICENSE new file mode 100644 index 0000000..cbc0bb3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012, Joyent, Inc. All rights reserved. + +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/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile new file mode 100644 index 0000000..1deeb5f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile @@ -0,0 +1,23 @@ +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile: top-level Makefile +# +# This Makefile contains only repo-specific logic and uses included makefiles +# to supply common targets (javascriptlint, jsstyle, restdown, etc.), which are +# used by other repos as well. +# + +# +# Files +# +JS_FILES := $(shell find lib -name '*.js') +JSL_FILES_NODE = $(JS_FILES) +JSSTYLE_FILES = $(JS_FILES) +JSL_CONF_NODE = jsl.node.conf + +# Default target is "check" +check: + +include ./Makefile.deps +include ./Makefile.targ diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile.deps b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile.deps new file mode 100644 index 0000000..2811bde --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile.deps @@ -0,0 +1,39 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.deps: Makefile for including common tools as dependencies +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This file is separate from Makefile.targ so that teams can choose +# independently whether to use the common targets in Makefile.targ and the +# common tools here. +# + +# +# javascriptlint +# +JSL_EXEC ?= deps/javascriptlint/build/install/jsl +JSL ?= python2.6 $(JSL_EXEC) + +$(JSL_EXEC): | deps/javascriptlint/.git + cd deps/javascriptlint && make install + +# +# jsstyle +# +JSSTYLE_EXEC ?= deps/jsstyle/jsstyle +JSSTYLE ?= $(JSSTYLE_EXEC) + +$(JSSTYLE_EXEC): | deps/jsstyle/.git + +# +# restdown +# +RESTDOWN_EXEC ?= deps/restdown/bin/restdown +RESTDOWN ?= python2.6 $(RESTDOWN_EXEC) +$(RESTDOWN_EXEC): | deps/restdown/.git diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile.targ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile.targ new file mode 100644 index 0000000..2a64fe7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/Makefile.targ @@ -0,0 +1,285 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.targ: common targets. +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This Makefile defines several useful targets and rules. You can use it by +# including it from a Makefile that specifies some of the variables below. +# +# Targets defined in this Makefile: +# +# check Checks JavaScript files for lint and style +# Checks bash scripts for syntax +# Checks SMF manifests for validity against the SMF DTD +# +# clean Removes built files +# +# docs Builds restdown documentation in docs/ +# +# prepush Depends on "check" and "test" +# +# test Does nothing (you should override this) +# +# xref Generates cscope (source cross-reference index) +# +# For details on what these targets are supposed to do, see the Joyent +# Engineering Guide. +# +# To make use of these targets, you'll need to set some of these variables. Any +# variables left unset will simply not be used. +# +# BASH_FILES Bash scripts to check for syntax +# (paths relative to top-level Makefile) +# +# CLEAN_FILES Files to remove as part of the "clean" target. Note +# that files generated by targets in this Makefile are +# automatically included in CLEAN_FILES. These include +# restdown-generated HTML and JSON files. +# +# DOC_FILES Restdown (documentation source) files. These are +# assumed to be contained in "docs/", and must NOT +# contain the "docs/" prefix. +# +# JSL_CONF_NODE Specify JavaScriptLint configuration files +# JSL_CONF_WEB (paths relative to top-level Makefile) +# +# Node.js and Web configuration files are separate +# because you'll usually want different global variable +# configurations. If no file is specified, none is given +# to jsl, which causes it to use a default configuration, +# which probably isn't what you want. +# +# JSL_FILES_NODE JavaScript files to check with Node config file. +# JSL_FILES_WEB JavaScript files to check with Web config file. +# +# You can also override these variables: +# +# BASH Path to bash (default: bash) +# +# CSCOPE_DIRS Directories to search for source files for the cscope +# index. (default: ".") +# +# JSL Path to JavaScriptLint (default: "jsl") +# +# JSL_FLAGS_NODE Additional flags to pass through to JSL +# JSL_FLAGS_WEB +# JSL_FLAGS +# +# JSSTYLE Path to jsstyle (default: jsstyle) +# +# JSSTYLE_FLAGS Additional flags to pass through to jsstyle +# + +# +# Defaults for the various tools we use. +# +BASH ?= bash +BASHSTYLE ?= tools/bashstyle +CP ?= cp +CSCOPE ?= cscope +CSCOPE_DIRS ?= . +JSL ?= jsl +JSSTYLE ?= jsstyle +MKDIR ?= mkdir -p +MV ?= mv +RESTDOWN_FLAGS ?= +RMTREE ?= rm -rf +JSL_FLAGS ?= --nologo --nosummary + +ifeq ($(shell uname -s),SunOS) + TAR ?= gtar +else + TAR ?= tar +endif + + +# +# Defaults for other fixed values. +# +BUILD = build +DISTCLEAN_FILES += $(BUILD) +DOC_BUILD = $(BUILD)/docs/public + +# +# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}. +# +ifneq ($(origin JSL_CONF_NODE), undefined) + JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE) +endif + +ifneq ($(origin JSL_CONF_WEB), undefined) + JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB) +endif + +# +# Targets. For descriptions on what these are supposed to do, see the +# Joyent Engineering Guide. +# + +# +# Instruct make to keep around temporary files. We have rules below that +# automatically update git submodules as needed, but they employ a deps/*/.git +# temporary file. Without this directive, make tries to remove these .git +# directories after the build has completed. +# +.SECONDARY: $($(wildcard deps/*):%=%/.git) + +# +# This rule enables other rules that use files from a git submodule to have +# those files depend on deps/module/.git and have "make" automatically check +# out the submodule as needed. +# +deps/%/.git: + git submodule update --init deps/$* + +# +# These recipes make heavy use of dynamically-created phony targets. The parent +# Makefile defines a list of input files like BASH_FILES. We then say that each +# of these files depends on a fake target called filename.bashchk, and then we +# define a pattern rule for those targets that runs bash in check-syntax-only +# mode. This mechanism has the nice properties that if you specify zero files, +# the rule becomes a noop (unlike a single rule to check all bash files, which +# would invoke bash with zero files), and you can check individual files from +# the command line with "make filename.bashchk". +# +.PHONY: check-bash +check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle) + +%.bashchk: % + $(BASH) -n $^ + +%.bashstyle: % + $(BASHSTYLE) $^ + +.PHONY: check-jsl check-jsl-node check-jsl-web +check-jsl: check-jsl-node check-jsl-web + +check-jsl-node: $(JSL_FILES_NODE:%=%.jslnodechk) + +check-jsl-web: $(JSL_FILES_WEB:%=%.jslwebchk) + +%.jslnodechk: % $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $< + +%.jslwebchk: % $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $< + +.PHONY: check-jsstyle +check-jsstyle: $(JSSTYLE_FILES:%=%.jsstylechk) + +%.jsstylechk: % $(JSSTYLE_EXEC) + $(JSSTYLE) $(JSSTYLE_FLAGS) $< + +.PHONY: check +check: check-jsl check-jsstyle check-bash + @echo check ok + +.PHONY: clean +clean:: + -$(RMTREE) $(CLEAN_FILES) + +.PHONY: distclean +distclean:: clean + -$(RMTREE) $(DISTCLEAN_FILES) + +CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out +CLEAN_FILES += $(CSCOPE_FILES) + +.PHONY: xref +xref: cscope.files + $(CSCOPE) -bqR + +.PHONY: cscope.files +cscope.files: + find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \ + -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@ + +# +# The "docs" target is complicated because we do several things here: +# +# (1) Use restdown to build HTML and JSON files from each of DOC_FILES. +# +# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which +# functions as a complete copy of the documentation that could be +# mirrored or served over HTTP. +# +# (3) Then copy any directories and media from docs/media into +# $(DOC_BUILD)/media. This allows projects to include their own media, +# including files that will override same-named files provided by +# restdown. +# +# Step (3) is the surprisingly complex part: in order to do this, we need to +# identify the subdirectories in docs/media, recreate them in +# $(DOC_BUILD)/media, then do the same with the files. +# +DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$") +DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%) +DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%) + +DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null) +DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%) +DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%) + +# +# Like the other targets, "docs" just depends on the final files we want to +# create in $(DOC_BUILD), leveraging other targets and recipes to define how +# to get there. +# +.PHONY: docs +docs: \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html) \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json) \ + $(DOC_MEDIA_FILES_BUILD) + +# +# We keep the intermediate files so that the next build can see whether the +# files in DOC_BUILD are up to date. +# +.PRECIOUS: \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%json) + +# +# We do clean those intermediate files, as well as all of DOC_BUILD. +# +CLEAN_FILES += \ + $(DOC_BUILD) \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%.json) + +# +# Before installing the files, we must make sure the directories exist. The | +# syntax tells make that the dependency need only exist, not be up to date. +# Otherwise, it might try to rebuild spuriously because the directory itself +# appears out of date. +# +$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD) + +$(DOC_BUILD)/%: docs/% | $(DOC_BUILD) + $(CP) $< $@ + +docs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC) + $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $< + +$(DOC_BUILD): + $(MKDIR) $@ + +$(DOC_MEDIA_DIRS_BUILD): + $(MKDIR) $@ + +# +# The default "test" target does nothing. This should usually be overridden by +# the parent Makefile. It's included here so we can define "prepush" without +# requiring the repo to define "test". +# +.PHONY: test +test: + +.PHONY: prepush +prepush: check test diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/README.md b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/README.md new file mode 100644 index 0000000..702e4e2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/README.md @@ -0,0 +1,39 @@ +# extsprintf: extended POSIX-style sprintf + +Stripped down version of s[n]printf(3c). We make a best effort to throw an +exception when given a format string we don't understand, rather than ignoring +it, so that we won't break existing programs if/when we go implement the rest +of this. + +This implementation currently supports specifying + +* field alignment ('-' flag), +* zero-pad ('0' flag) +* always show numeric sign ('+' flag), +* field width +* conversions for strings, decimal integers, and floats (numbers). +* argument size specifiers. These are all accepted but ignored, since + Javascript has no notion of the physical size of an argument. + +Everything else is currently unsupported, most notably: precision, unsigned +numbers, non-decimal numbers, and characters. + +Besides the usual POSIX conversions, this implementation supports: + +* `%j`: pretty-print a JSON object (using node's "inspect") +* `%r`: pretty-print an Error object + +# Example + +First, install it: + + # npm install extsprintf + +Now, use it: + + var mod_extsprintf = require('extsprintf'); + console.log(mod_extsprintf.sprintf('hello %25s', 'world')); + +outputs: + + hello world diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/examples/simple.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/examples/simple.js new file mode 100644 index 0000000..9f342f5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/examples/simple.js @@ -0,0 +1,2 @@ +var mod_extsprintf = require('extsprintf'); +console.log(mod_extsprintf.sprintf('hello %25s', 'world')); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/jsl.node.conf b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/jsl.node.conf new file mode 100644 index 0000000..03f787f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/jsl.node.conf @@ -0,0 +1,137 @@ +# +# Configuration File for JavaScript Lint +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# ++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent ++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity ++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement ++anon_no_return_value # anonymous function does not always return value ++assign_to_function_call # assignment to a function call +-block_without_braces # block statement without curly braces ++comma_separated_stmts # multiple statements separated by commas (use semicolons?) ++comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) ++default_not_at_end # the default case is not at the end of the switch statement ++dup_option_explicit # duplicate "option explicit" control comment ++duplicate_case_in_switch # duplicate case in switch statement ++duplicate_formal # duplicate formal argument {name} ++empty_statement # empty statement or extra semicolon ++identifier_hides_another # identifer {name} hides an identifier in a parent scope +-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement ++incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. ++invalid_fallthru # unexpected "fallthru" control comment ++invalid_pass # unexpected "pass" control comment ++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax ++leading_decimal_point # leading decimal point may indicate a number or an object member ++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax ++meaningless_block # meaningless block; curly braces have no impact ++mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence ++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma ++missing_break # missing break statement ++missing_break_for_last_case # missing break statement for last case in switch ++missing_default_case # missing default case in switch statement ++missing_option_explicit # the "option explicit" control comment is missing ++missing_semicolon # missing semicolon ++missing_semicolon_for_lambda # missing semicolon for lambda assignment ++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs ++nested_comment # nested comment ++no_return_value # function {name} does not always return a value ++octal_number # leading zeros make an octal number ++parseint_missing_radix # parseInt missing radix parameter ++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag ++redeclared_var # redeclaration of {name} ++trailing_comma_in_array # extra comma is not recommended in array initializers ++trailing_decimal_point # trailing decimal point may indicate a number or an object member ++undeclared_identifier # undeclared identifier: {name} ++unreachable_code # unreachable code +-unreferenced_argument # argument declared but never referenced: {name} +-unreferenced_function # function is declared but never referenced: {name} ++unreferenced_variable # variable is declared but never referenced: {name} ++unsupported_version # JavaScript {version} is not supported ++use_of_label # use of label ++useless_assign # useless assignment ++useless_comparison # useless comparison; comparing identical expressions +-useless_quotes # the quotation marks are unnecessary ++useless_void # use of the void type may be unnecessary (void is always undefined) ++var_hides_arg # variable {name} hides argument ++want_assign_or_call # expected an assignment or function call ++with_statement # with statement hides undeclared variables; use temporary variable instead + + +### Output format +# Customize the format of the error message. +# __FILE__ indicates current file path +# __FILENAME__ indicates current file name +# __LINE__ indicates current line +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message +# +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# +-legacy_control_comments + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" +-always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: ++define __dirname ++define clearInterval ++define clearTimeout ++define console ++define exports ++define global ++define process ++define require ++define setInterval ++define setTimeout ++define Buffer ++define JSON ++define Math + +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/lib/extsprintf.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/lib/extsprintf.js new file mode 100644 index 0000000..bfac955 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/lib/extsprintf.js @@ -0,0 +1,165 @@ +/* + * extsprintf.js: extended POSIX-style sprintf + */ + +var mod_assert = require('assert'); +var mod_util = require('util'); + +/* + * Public interface + */ +exports.sprintf = jsSprintf; + +/* + * Stripped down version of s[n]printf(3c). We make a best effort to throw an + * exception when given a format string we don't understand, rather than + * ignoring it, so that we won't break existing programs if/when we go implement + * the rest of this. + * + * This implementation currently supports specifying + * - field alignment ('-' flag), + * - zero-pad ('0' flag) + * - always show numeric sign ('+' flag), + * - field width + * - conversions for strings, decimal integers, and floats (numbers). + * - argument size specifiers. These are all accepted but ignored, since + * Javascript has no notion of the physical size of an argument. + * + * Everything else is currently unsupported, most notably precision, unsigned + * numbers, non-decimal numbers, and characters. + */ +function jsSprintf(fmt) +{ + var regex = [ + '([^%]*)', /* normal text */ + '%', /* start of format */ + '([\'\\-+ #0]*?)', /* flags (optional) */ + '([1-9]\\d*)?', /* width (optional) */ + '(\\.([1-9]\\d*))?', /* precision (optional) */ + '[lhjztL]*?', /* length mods (ignored) */ + '([diouxXfFeEgGaAcCsSp%jr])' /* conversion */ + ].join(''); + + var re = new RegExp(regex); + var args = Array.prototype.slice.call(arguments, 1); + var flags, width, precision, conversion; + var left, pad, sign, arg, match; + var ret = ''; + var argn = 1; + + mod_assert.equal('string', typeof (fmt)); + + while ((match = re.exec(fmt)) !== null) { + ret += match[1]; + fmt = fmt.substring(match[0].length); + + flags = match[2] || ''; + width = match[3] || 0; + precision = match[4] || ''; + conversion = match[6]; + left = false; + sign = false; + pad = ' '; + + if (conversion == '%') { + ret += '%'; + continue; + } + + if (args.length === 0) + throw (new Error('too few args to sprintf')); + + arg = args.shift(); + argn++; + + if (flags.match(/[\' #]/)) + throw (new Error( + 'unsupported flags: ' + flags)); + + if (precision.length > 0) + throw (new Error( + 'non-zero precision not supported')); + + if (flags.match(/-/)) + left = true; + + if (flags.match(/0/)) + pad = '0'; + + if (flags.match(/\+/)) + sign = true; + + switch (conversion) { + case 's': + if (arg === undefined || arg === null) + throw (new Error('argument ' + argn + + ': attempted to print undefined or null ' + + 'as a string')); + ret += doPad(pad, width, left, arg); + break; + + case 'd': + arg = Math.floor(arg); + /*jsl:fallthru*/ + case 'f': + sign = sign && arg > 0 ? '+' : ''; + ret += sign + doPad(pad, width, left, + arg.toString()); + break; + + case 'j': /* non-standard */ + if (width === 0) + width = 10; + ret += mod_util.inspect(arg, false, width); + break; + + case 'r': /* non-standard */ + ret += dumpException(arg); + break; + + default: + throw (new Error('unsupported conversion: ' + + conversion)); + } + } + + ret += fmt; + return (ret); +} + +function doPad(chr, width, left, str) +{ + var ret = str; + + while (ret.length < width) { + if (left) + ret += chr; + else + ret = chr + ret; + } + + return (ret); +} + +/* + * This function dumps long stack traces for exceptions having a cause() method. + * See node-verror for an example. + */ +function dumpException(ex) +{ + var ret; + + if (!(ex instanceof Error)) + throw (new Error(jsSprintf('invalid type for %%r: %j', ex))); + + /* Note that V8 prepends "ex.stack" with ex.toString(). */ + ret = 'EXCEPTION: ' + ex.constructor.name + ': ' + ex.stack; + + if (!ex.cause) + return (ret); + + for (ex = ex.cause(); ex; ex = ex.cause ? ex.cause() : null) + ret += '\nCaused by: ' + dumpException(ex); + + return (ret); +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/package.json new file mode 100644 index 0000000..af56bd3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/node_modules/extsprintf/package.json @@ -0,0 +1,13 @@ +{ + "name": "extsprintf", + "version": "1.0.0", + "description": "extended POSIX-style sprintf", + "main": "./lib/extsprintf.js", + + "repository": { + "type": "git", + "url": "git://github.com/davepacheco/node-extsprintf.git" + }, + + "engines": [ "node >=0.6.0" ] +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/package.json new file mode 100644 index 0000000..81ad476 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/node_modules/verror/package.json @@ -0,0 +1,17 @@ +{ + "name": "verror", + "version": "1.1.0", + "description": "richer JavaScript errors", + "main": "./lib/verror.js", + + "repository": { + "type": "git", + "url": "git://github.com/davepacheco/node-verror.git" + }, + + "dependencies": { + "extsprintf": "1.0.0" + }, + + "engines": [ "node >=0.6.0" ] +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/package.json new file mode 100644 index 0000000..975d7cf --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/node_modules/vasync/package.json @@ -0,0 +1,17 @@ +{ + "name": "vasync", + "version": "1.3.2", + "description": "utilities for observable asynchronous control flow", + "main": "./lib/vasync.js", + + "repository": { + "type": "git", + "url": "git://github.com/davepacheco/node-vasync.git" + }, + + "dependencies": { + "verror": "1.1.0" + }, + + "engines": [ "node >=0.6.0" ] +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/package.json new file mode 100644 index 0000000..c3e1286 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/package.json @@ -0,0 +1,26 @@ +{ + "name": "pooling", + "description": "General purpose resource pool API", + "version": "0.3.1", + "author": "Mark Cavage ", + "main": "lib/index.js", + "repository": { + "type": "git", + "url": "git://github.com/mcavage/node-pooling.git" + }, + "engines": { + "node": ">=0.8" + }, + "dependencies": { + "assert-plus": "0.1.2", + "bunyan": "0.14.0", + "vasync": "1.3.2" + }, + "devDependencies": { + "cover": "0.2.8", + "nodeunit": "0.7.4" + }, + "scripts": { + "test": "./node_modules/.bin/nodeunit ./test/*.test.js | ./node_modules/.bin/bunyan" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/test/helper.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/test/helper.js new file mode 100644 index 0000000..ac7cdc5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/test/helper.js @@ -0,0 +1,46 @@ +// Copyright 2012 Mark Cavage. All rights reserved. +// +// Just a simple wrapper over nodeunit's exports syntax. Also exposes +// a common logger for all tests. +// + +var Logger = require('bunyan'); + + + +///--- Exports + +module.exports = { + + after: function after(teardown) { + module.parent.exports.tearDown = teardown; + }, + + before: function before(setup) { + module.parent.exports.setUp = setup; + }, + + test: function test(name, tester) { + module.parent.exports[name] = function _(t) { + var _done = false; + t.end = function end() { + if (!_done) { + _done = true; + t.done(); + } + }; + return (tester(t)); + }; + }, + + log: new Logger({ + level: (process.env.LOG_LEVEL || 'info'), + name: process.argv[1], + stream: process.stdout, + src: true, + serializers: { + err: Logger.stdSerializers.err + } + }) + +}; diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/test/pool.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/test/pool.test.js new file mode 100644 index 0000000..7f024e9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/test/pool.test.js @@ -0,0 +1,242 @@ +// Copyright (c) 2012, Mark Cavage. All rights reserved. + +var EventEmitter = require('events').EventEmitter; + +var mod_pool = require('../lib'); + +if (require.cache[__dirname + '/helper.js']) + delete require.cache[__dirname + '/helper.js']; +var helper = require('./helper.js'); + + + +///--- Globals + +var after = helper.after; +var before = helper.before; +var test = helper.test; + +var POOL; + +var IDLE_TIMEOUT = 30; +var MAX_CLIENTS = 10; +var POOL_NAME = 'pool_test'; +var REAP_INTERVAL = 15; + + + +///--- Tests + +before(function setup(callback) { + var id = 0; + POOL = mod_pool.createPool({ + checkInterval: REAP_INTERVAL, + log: helper.log, + max: MAX_CLIENTS, + maxIdleTime: IDLE_TIMEOUT, + name: POOL_NAME, + + check: function check(client, cb) { + if ((client.id % 2) !== 0) + return (cb(new Error())); + + return (cb(null)); + }, + + create: function create(cb) { + var client = new EventEmitter(); + client.id = ++id; + return (cb(null, client)); + }, + + destroy: function destroy(client) { + client.killed = true; + } + }); + + return (callback()); +}); + + +after(function teardown(callback) { + POOL.shutdown(function () { + POOL = null; + return (callback()); + }); +}); + + +test('check pool ok', function (t) { + t.ok(POOL); + t.equal(POOL.checkInterval, REAP_INTERVAL); + t.ok(POOL.log); + t.equal(POOL.max, MAX_CLIENTS); + t.equal(POOL.maxIdleTime, IDLE_TIMEOUT); + t.equal(POOL.name, POOL_NAME); + t.end(); +}); + + +test('acquire and release ok', function (t) { + POOL.acquire(function (err, client) { + t.ifError(err); + t.ok(client); + t.equal(client.id, 1); + POOL.release(client); + t.equal(POOL.available.length, 1); + t.end(); + }); +}); + +test('acquire with queue', function (t) { + var finished = 0; + var clients = []; + for (var i = 0; i <= MAX_CLIENTS; i++) { + POOL.acquire(function (err, client) { + t.ifError(err); + clients.push(client); + if (finished++ === MAX_CLIENTS) { + t.equal(1, client.id); + clients.forEach(function (c) { + POOL.release(c); + }); + t.end(); + } + }); + } + + t.equal(POOL.queue.length, 1); + t.equal(POOL.resources.length, MAX_CLIENTS); + POOL.release(clients.shift()); +}); + + +test('acquire after releasing (no queue)', function (t) { + var clients = []; + + for (var i = 0; i < MAX_CLIENTS; i++) { + POOL.acquire(function (err, client) { + t.ifError(err); + clients.push(client); + }); + } + + t.equal(POOL.available.length, 0); + t.equal(POOL.resources.length, MAX_CLIENTS); + t.equal(POOL.queue.length, 0); + + clients.reverse(); + clients.forEach(function (c) { + POOL.release(c); + }); + + t.equal(POOL.available.length, MAX_CLIENTS); + POOL.acquire(function (err, client) { + t.ifError(err); + t.equal(client.id, MAX_CLIENTS); + POOL.release(client); + t.end(); + }); +}); + + +test('health check reaping', function (t) { + for (var i = 0; i < MAX_CLIENTS; i++) { + POOL.acquire(function (err, client) { + t.ifError(err); + process.nextTick(function () { + POOL.release(client); + }); + }); + } + + var killed = 0; + POOL.on('death', function (client) { + t.ok(client); + t.ok(client.killed); + if (++killed === 2) + t.end(); + }); +}); + + +test('onError reaping (while acquired)', function (t) { + for (var i = 0; i < MAX_CLIENTS; i++) { + POOL.acquire(function (err, client) { + t.ifError(err); + process.nextTick(function () { + POOL.release(client); + }); + }); + } + + POOL.on('death', function (client) { + t.ok(client); + t.ok(client.killed); + t.end(); + }); + + POOL.acquire(function (err, client) { + t.ifError(err); + client.emit('error', new Error()); + }); +}); + + +test('onError reaping (while idle)', function (t) { + for (var i = 0; i < MAX_CLIENTS; i++) { + POOL.acquire(function (err, client) { + t.ifError(err); + process.nextTick(function () { + POOL.release(client); + }); + }); + } + + POOL.on('death', function (client) { + t.ok(client); + t.ok(client.killed); + t.end(); + }); + + process.nextTick(function () { + // This is a little icky reaching in, but meh. + POOL.available[0].client.emit('error', new Error()); + }); +}); + + +test('drain event', function (t) { + for (var i = 0; i < MAX_CLIENTS; i++) { + POOL.acquire(function (err, client) { + t.ifError(err); + process.nextTick(function () { + POOL.release(client); + }); + }); + } + + POOL.on('drain', function () { + t.end(); + }); +}); + + +test('shutdown blocks acquire', function (t) { + for (var i = 0; i < MAX_CLIENTS; i++) { + POOL.acquire(function (err, client) { + t.ifError(err); + process.nextTick(function () { + POOL.release(client); + }); + }); + } + + POOL.shutdown(function () { + t.end(); + }); + + POOL.acquire(function (err) { + t.ok(err); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/jsl.node.conf b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/jsl.node.conf new file mode 100644 index 0000000..0b8ae95 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/jsl.node.conf @@ -0,0 +1,138 @@ +# +# Configuration File for JavaScript Lint +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# ++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent ++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity ++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement ++anon_no_return_value # anonymous function does not always return value ++assign_to_function_call # assignment to a function call +-block_without_braces # block statement without curly braces ++comma_separated_stmts # multiple statements separated by commas (use semicolons?) ++comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) ++default_not_at_end # the default case is not at the end of the switch statement ++dup_option_explicit # duplicate "option explicit" control comment ++duplicate_case_in_switch # duplicate case in switch statement ++duplicate_formal # duplicate formal argument {name} ++empty_statement # empty statement or extra semicolon ++identifier_hides_another # identifer {name} hides an identifier in a parent scope +-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement ++incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. ++invalid_fallthru # unexpected "fallthru" control comment ++invalid_pass # unexpected "pass" control comment ++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax ++leading_decimal_point # leading decimal point may indicate a number or an object member ++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax ++meaningless_block # meaningless block; curly braces have no impact ++mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence ++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma ++missing_break # missing break statement ++missing_break_for_last_case # missing break statement for last case in switch ++missing_default_case # missing default case in switch statement ++missing_option_explicit # the "option explicit" control comment is missing ++missing_semicolon # missing semicolon ++missing_semicolon_for_lambda # missing semicolon for lambda assignment ++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs ++nested_comment # nested comment ++no_return_value # function {name} does not always return a value ++octal_number # leading zeros make an octal number ++parseint_missing_radix # parseInt missing radix parameter ++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag ++redeclared_var # redeclaration of {name} ++trailing_comma_in_array # extra comma is not recommended in array initializers ++trailing_decimal_point # trailing decimal point may indicate a number or an object member ++undeclared_identifier # undeclared identifier: {name} ++unreachable_code # unreachable code +-unreferenced_argument # argument declared but never referenced: {name} +-unreferenced_function # function is declared but never referenced: {name} ++unreferenced_variable # variable is declared but never referenced: {name} ++unsupported_version # JavaScript {version} is not supported ++use_of_label # use of label ++useless_assign # useless assignment ++useless_comparison # useless comparison; comparing identical expressions +-useless_quotes # the quotation marks are unnecessary ++useless_void # use of the void type may be unnecessary (void is always undefined) ++var_hides_arg # variable {name} hides argument ++want_assign_or_call # expected an assignment or function call ++with_statement # with statement hides undeclared variables; use temporary variable instead + + +### Output format +# Customize the format of the error message. +# __FILE__ indicates current file path +# __FILENAME__ indicates current file name +# __LINE__ indicates current line +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message +# +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# +-legacy_control_comments + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" +-always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: ++define __dirname ++define clearInterval ++define clearTimeout ++define console ++define exports ++define global ++define module ++define process ++define require ++define setInterval ++define setTimeout ++define Buffer ++define JSON ++define Math + +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/jsstyle.conf b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/jsstyle.conf new file mode 100644 index 0000000..351736a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/jsstyle.conf @@ -0,0 +1,4 @@ +indent=8 +doxygen +unparenthesized-return=1 +blank-after-start-comment=0 \ No newline at end of file diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.defs b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.defs new file mode 100644 index 0000000..ffb91e4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.defs @@ -0,0 +1,40 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.defs: common defines. +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This makefile defines some useful defines. Include it at the top of +# your Makefile. +# +# Definitions in this Makefile: +# +# TOP The absolute path to the project directory. The top dir. +# BRANCH The current git branch. +# TIMESTAMP The timestamp for the build. This can be set via +# the TIMESTAMP envvar (used by MG-based builds). +# STAMP A build stamp to use in built package names. +# + +TOP := $(shell pwd) + +# +# Mountain Gorilla-spec'd versioning. +# See "Package Versioning" in MG's README.md: +# +# +# Need GNU awk for multi-char arg to "-F". +_AWK := $(shell (which gawk >/dev/null && echo gawk) \ + || (which nawk >/dev/null && echo nawk) \ + || echo awk) +BRANCH := $(shell git log -n 1 --pretty=%d HEAD | $(_AWK) '{print $$NF}' | sed -e 's/.$$//' | cut -d/ -f2) +ifeq ($(TIMESTAMP),) + TIMESTAMP := $(shell date -u "+%Y%m%dT%H%M%SZ") +endif +_GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $$NF}') +STAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE) diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.deps b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.deps new file mode 100644 index 0000000..eb2c471 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.deps @@ -0,0 +1,39 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.deps: Makefile for including common tools as dependencies +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This file is separate from Makefile.targ so that teams can choose +# independently whether to use the common targets in Makefile.targ and the +# common tools here. +# + +# +# javascriptlint +# +JSL_EXEC ?= deps/javascriptlint/build/install/jsl +JSL ?= $(JSL_EXEC) + +$(JSL_EXEC): | deps/javascriptlint/.git + cd deps/javascriptlint && make install + +# +# jsstyle +# +JSSTYLE_EXEC ?= deps/jsstyle/jsstyle +JSSTYLE ?= $(JSSTYLE_EXEC) + +$(JSSTYLE_EXEC): | deps/jsstyle/.git + +# +# restdown +# +RESTDOWN_EXEC ?= deps/restdown/bin/restdown +RESTDOWN ?= python2.6 $(RESTDOWN_EXEC) +$(RESTDOWN_EXEC): | deps/restdown/.git diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.targ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.targ new file mode 100644 index 0000000..2a64fe7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/node_modules/pooling/tools/mk/Makefile.targ @@ -0,0 +1,285 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.targ: common targets. +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This Makefile defines several useful targets and rules. You can use it by +# including it from a Makefile that specifies some of the variables below. +# +# Targets defined in this Makefile: +# +# check Checks JavaScript files for lint and style +# Checks bash scripts for syntax +# Checks SMF manifests for validity against the SMF DTD +# +# clean Removes built files +# +# docs Builds restdown documentation in docs/ +# +# prepush Depends on "check" and "test" +# +# test Does nothing (you should override this) +# +# xref Generates cscope (source cross-reference index) +# +# For details on what these targets are supposed to do, see the Joyent +# Engineering Guide. +# +# To make use of these targets, you'll need to set some of these variables. Any +# variables left unset will simply not be used. +# +# BASH_FILES Bash scripts to check for syntax +# (paths relative to top-level Makefile) +# +# CLEAN_FILES Files to remove as part of the "clean" target. Note +# that files generated by targets in this Makefile are +# automatically included in CLEAN_FILES. These include +# restdown-generated HTML and JSON files. +# +# DOC_FILES Restdown (documentation source) files. These are +# assumed to be contained in "docs/", and must NOT +# contain the "docs/" prefix. +# +# JSL_CONF_NODE Specify JavaScriptLint configuration files +# JSL_CONF_WEB (paths relative to top-level Makefile) +# +# Node.js and Web configuration files are separate +# because you'll usually want different global variable +# configurations. If no file is specified, none is given +# to jsl, which causes it to use a default configuration, +# which probably isn't what you want. +# +# JSL_FILES_NODE JavaScript files to check with Node config file. +# JSL_FILES_WEB JavaScript files to check with Web config file. +# +# You can also override these variables: +# +# BASH Path to bash (default: bash) +# +# CSCOPE_DIRS Directories to search for source files for the cscope +# index. (default: ".") +# +# JSL Path to JavaScriptLint (default: "jsl") +# +# JSL_FLAGS_NODE Additional flags to pass through to JSL +# JSL_FLAGS_WEB +# JSL_FLAGS +# +# JSSTYLE Path to jsstyle (default: jsstyle) +# +# JSSTYLE_FLAGS Additional flags to pass through to jsstyle +# + +# +# Defaults for the various tools we use. +# +BASH ?= bash +BASHSTYLE ?= tools/bashstyle +CP ?= cp +CSCOPE ?= cscope +CSCOPE_DIRS ?= . +JSL ?= jsl +JSSTYLE ?= jsstyle +MKDIR ?= mkdir -p +MV ?= mv +RESTDOWN_FLAGS ?= +RMTREE ?= rm -rf +JSL_FLAGS ?= --nologo --nosummary + +ifeq ($(shell uname -s),SunOS) + TAR ?= gtar +else + TAR ?= tar +endif + + +# +# Defaults for other fixed values. +# +BUILD = build +DISTCLEAN_FILES += $(BUILD) +DOC_BUILD = $(BUILD)/docs/public + +# +# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}. +# +ifneq ($(origin JSL_CONF_NODE), undefined) + JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE) +endif + +ifneq ($(origin JSL_CONF_WEB), undefined) + JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB) +endif + +# +# Targets. For descriptions on what these are supposed to do, see the +# Joyent Engineering Guide. +# + +# +# Instruct make to keep around temporary files. We have rules below that +# automatically update git submodules as needed, but they employ a deps/*/.git +# temporary file. Without this directive, make tries to remove these .git +# directories after the build has completed. +# +.SECONDARY: $($(wildcard deps/*):%=%/.git) + +# +# This rule enables other rules that use files from a git submodule to have +# those files depend on deps/module/.git and have "make" automatically check +# out the submodule as needed. +# +deps/%/.git: + git submodule update --init deps/$* + +# +# These recipes make heavy use of dynamically-created phony targets. The parent +# Makefile defines a list of input files like BASH_FILES. We then say that each +# of these files depends on a fake target called filename.bashchk, and then we +# define a pattern rule for those targets that runs bash in check-syntax-only +# mode. This mechanism has the nice properties that if you specify zero files, +# the rule becomes a noop (unlike a single rule to check all bash files, which +# would invoke bash with zero files), and you can check individual files from +# the command line with "make filename.bashchk". +# +.PHONY: check-bash +check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle) + +%.bashchk: % + $(BASH) -n $^ + +%.bashstyle: % + $(BASHSTYLE) $^ + +.PHONY: check-jsl check-jsl-node check-jsl-web +check-jsl: check-jsl-node check-jsl-web + +check-jsl-node: $(JSL_FILES_NODE:%=%.jslnodechk) + +check-jsl-web: $(JSL_FILES_WEB:%=%.jslwebchk) + +%.jslnodechk: % $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $< + +%.jslwebchk: % $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $< + +.PHONY: check-jsstyle +check-jsstyle: $(JSSTYLE_FILES:%=%.jsstylechk) + +%.jsstylechk: % $(JSSTYLE_EXEC) + $(JSSTYLE) $(JSSTYLE_FLAGS) $< + +.PHONY: check +check: check-jsl check-jsstyle check-bash + @echo check ok + +.PHONY: clean +clean:: + -$(RMTREE) $(CLEAN_FILES) + +.PHONY: distclean +distclean:: clean + -$(RMTREE) $(DISTCLEAN_FILES) + +CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out +CLEAN_FILES += $(CSCOPE_FILES) + +.PHONY: xref +xref: cscope.files + $(CSCOPE) -bqR + +.PHONY: cscope.files +cscope.files: + find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \ + -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@ + +# +# The "docs" target is complicated because we do several things here: +# +# (1) Use restdown to build HTML and JSON files from each of DOC_FILES. +# +# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which +# functions as a complete copy of the documentation that could be +# mirrored or served over HTTP. +# +# (3) Then copy any directories and media from docs/media into +# $(DOC_BUILD)/media. This allows projects to include their own media, +# including files that will override same-named files provided by +# restdown. +# +# Step (3) is the surprisingly complex part: in order to do this, we need to +# identify the subdirectories in docs/media, recreate them in +# $(DOC_BUILD)/media, then do the same with the files. +# +DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$") +DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%) +DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%) + +DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null) +DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%) +DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%) + +# +# Like the other targets, "docs" just depends on the final files we want to +# create in $(DOC_BUILD), leveraging other targets and recipes to define how +# to get there. +# +.PHONY: docs +docs: \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html) \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json) \ + $(DOC_MEDIA_FILES_BUILD) + +# +# We keep the intermediate files so that the next build can see whether the +# files in DOC_BUILD are up to date. +# +.PRECIOUS: \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%json) + +# +# We do clean those intermediate files, as well as all of DOC_BUILD. +# +CLEAN_FILES += \ + $(DOC_BUILD) \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%.json) + +# +# Before installing the files, we must make sure the directories exist. The | +# syntax tells make that the dependency need only exist, not be up to date. +# Otherwise, it might try to rebuild spuriously because the directory itself +# appears out of date. +# +$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD) + +$(DOC_BUILD)/%: docs/% | $(DOC_BUILD) + $(CP) $< $@ + +docs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC) + $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $< + +$(DOC_BUILD): + $(MKDIR) $@ + +$(DOC_MEDIA_DIRS_BUILD): + $(MKDIR) $@ + +# +# The default "test" target does nothing. This should usually be overridden by +# the parent Makefile. It's included here so we can define "prepush" without +# requiring the repo to define "test". +# +.PHONY: test +test: + +.PHONY: prepush +prepush: check test diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/package.json b/dist/node_modules/ldapauth/node_modules/ldapjs/package.json new file mode 100644 index 0000000..b938017 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/package.json @@ -0,0 +1,42 @@ +{ + "author": "Mark Cavage ", + "contributors": [ + "Craig Baker", + "Austin King ", + "Mathieu Lecarme >", + "Trent Mick ", + "Yunong Xiao " + ], + "name": "ldapjs", + "homepage": "http://ldapjs.org", + "description": "LDAP client and server APIs", + "version": "0.5.6", + "repository": { + "type": "git", + "url": "git://github.com/mcavage/node-ldapjs.git" + }, + "main": "lib/index.js", + "directories": { + "bin": "./bin", + "lib": "./lib" + }, + "engines": { + "node": ">=0.6" + }, + "dependencies": { + "asn1": "0.1.11", + "assert-plus": "0.1.2", + "buffertools": "1.1.0", + "bunyan": "0.14.0", + "dtrace-provider": "0.2.1", + "nopt": "1.0.10", + "pooling": "0.3.1" + }, + "devDependencies": { + "tap": "0.3.1", + "node-uuid": "1.3.3" + }, + "scripts": { + "test": "./node_modules/.bin/tap ./test" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/attribute.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/attribute.test.js new file mode 100644 index 0000000..c97e8e7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/attribute.test.js @@ -0,0 +1,82 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var Attribute; + + +///--- Tests + +test('load library', function (t) { + Attribute = require('../lib/index').Attribute; + t.ok(Attribute); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new Attribute()); + t.end(); +}); + + +test('new with args', function (t) { + var attr = new Attribute({ + type: 'cn', + vals: ['foo', 'bar'] + }); + t.ok(attr); + attr.addValue('baz'); + t.equal(attr.type, 'cn'); + t.equal(attr.vals.length, 3); + t.equal(attr.vals[0], 'foo'); + t.equal(attr.vals[1], 'bar'); + t.equal(attr.vals[2], 'baz'); + t.end(); +}); + + +test('toBer', function (t) { + var attr = new Attribute({ + type: 'cn', + vals: ['foo', 'bar'] + }); + t.ok(attr); + var ber = new BerWriter(); + attr.toBer(ber); + var reader = new BerReader(ber.buffer); + t.ok(reader.readSequence()); + t.equal(reader.readString(), 'cn'); + t.equal(reader.readSequence(), 0x31); // lber set + t.equal(reader.readString(), 'foo'); + t.equal(reader.readString(), 'bar'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.startSequence(); + ber.writeString('cn'); + ber.startSequence(0x31); + ber.writeStringArray(['foo', 'bar']); + ber.endSequence(); + ber.endSequence(); + + var attr = new Attribute(); + t.ok(attr); + t.ok(attr.parse(new BerReader(ber.buffer))); + + t.equal(attr.type, 'cn'); + t.equal(attr.vals.length, 2); + t.equal(attr.vals[0], 'foo'); + t.equal(attr.vals[1], 'bar'); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/change.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/change.test.js new file mode 100644 index 0000000..b9b3b81 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/change.test.js @@ -0,0 +1,116 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var Attribute; +var Change; + + +///--- Tests + +test('load library', function (t) { + Attribute = require('../lib/index').Attribute; + Change = require('../lib/index').Change; + t.ok(Attribute); + t.ok(Change); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new Change()); + t.end(); +}); + + +test('new with args', function (t) { + var change = new Change({ + operation: 0x00, + modification: new Attribute({ + type: 'cn', + vals: ['foo', 'bar'] + }) + }); + t.ok(change); + + t.equal(change.operation, 'add'); + t.equal(change.modification.type, 'cn'); + t.equal(change.modification.vals.length, 2); + t.equal(change.modification.vals[0], 'foo'); + t.equal(change.modification.vals[1], 'bar'); + + t.end(); +}); + + +test('GH-31 (multiple attributes per Change)', function (t) { + try { + t.notOk(new Change({ + operation: 'replace', + modification: { + cn: 'foo', + sn: 'bar' + } + }), 'should have thrown'); + } catch (e) { + t.ok(e); + t.end(); + } +}); + + +test('toBer', function (t) { + var change = new Change({ + operation: 'Add', + modification: new Attribute({ + type: 'cn', + vals: ['foo', 'bar'] + }) + }); + t.ok(change); + + var ber = new BerWriter(); + change.toBer(ber); + var reader = new BerReader(ber.buffer); + t.ok(reader.readSequence()); + t.equal(reader.readEnumeration(), 0x00); + t.ok(reader.readSequence()); + t.equal(reader.readString(), 'cn'); + t.equal(reader.readSequence(), 0x31); // lber set + t.equal(reader.readString(), 'foo'); + t.equal(reader.readString(), 'bar'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.startSequence(); + ber.writeEnumeration(0x00); + ber.startSequence(); + ber.writeString('cn'); + ber.startSequence(0x31); + ber.writeStringArray(['foo', 'bar']); + ber.endSequence(); + ber.endSequence(); + ber.endSequence(); + + var change = new Change(); + t.ok(change); + t.ok(change.parse(new BerReader(ber.buffer))); + + t.equal(change.operation, 'add'); + t.equal(change.modification.type, 'cn'); + t.equal(change.modification.vals.length, 2); + t.equal(change.modification.vals[0], 'foo'); + t.equal(change.modification.vals[1], 'bar'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/client.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/client.test.js new file mode 100644 index 0000000..72dc77c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/client.test.js @@ -0,0 +1,607 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var Logger = require('bunyan'); + +var test = require('tap').test; +var uuid = require('node-uuid'); + + +///--- Globals + +var BIND_DN = 'cn=root'; +var BIND_PW = 'secret'; +var SOCKET = '/tmp/.' + uuid(); + +var SUFFIX = 'dc=test'; + +var ldap; +var Attribute; +var Change; +var client; +var server; + + +///--- Tests + +test('setup', function (t) { + ldap = require('../lib/index'); + t.ok(ldap); + t.ok(ldap.createClient); + t.ok(ldap.createServer); + t.ok(ldap.Attribute); + t.ok(ldap.Change); + + Attribute = ldap.Attribute; + Change = ldap.Change; + + server = ldap.createServer(); + t.ok(server); + + server.bind(BIND_DN, function (req, res, next) { + if (req.credentials !== BIND_PW) + return next(new ldap.InvalidCredentialsError('Invalid password')); + + res.end(); + return next(); + }); + + server.add(SUFFIX, function (req, res, next) { + res.end(); + return next(); + }); + + server.compare(SUFFIX, function (req, res, next) { + res.end(req.value === 'test'); + return next(); + }); + + server.del(SUFFIX, function (req, res, next) { + res.end(); + return next(); + }); + + // LDAP whoami + server.exop('1.3.6.1.4.1.4203.1.11.3', function (req, res, next) { + res.value = 'u:xxyyz@EXAMPLE.NET'; + res.end(); + return next(); + }); + + server.modify(SUFFIX, function (req, res, next) { + res.end(); + return next(); + }); + + server.modifyDN(SUFFIX, function (req, res, next) { + res.end(); + return next(); + }); + + server.search('dc=timeout', function (req, res, next) { + // Haha client! + }); + + server.search(SUFFIX, function (req, res, next) { + + if (req.dn.equals('cn=ref,' + SUFFIX)) { + res.send(res.createSearchReference('ldap://localhost')); + } else if (req.dn.equals('cn=bin,' + SUFFIX)) { + res.send(res.createSearchEntry({ + objectName: req.dn, + attributes: { + 'foo;binary': 'wr0gKyDCvCA9IMK+', + 'gb18030': new Buffer([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]), + 'objectclass': 'binary' + } + })); + } else { + var e = res.createSearchEntry({ + objectName: req.dn, + attributes: { + cn: ['unit', 'test'], + SN: 'testy' + } + }); + res.send(e); + res.send(e); + } + + + res.end(); + return next(); + }); + + server.search('dc=empty', function (req, res, next) { + res.send({ + dn: 'dc=empty', + attributes: { + member: [], + 'member;range=0-1': ['cn=user1, dc=empty', 'cn=user2, dc=empty'] + } + }); + res.end(); + return next(); + }); + + server.unbind(function (req, res, next) { + res.end(); + return next(); + }); + + server.listen(SOCKET, function () { + client = ldap.createClient({ + connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), + socketPath: SOCKET, + maxConnections: parseInt(process.env.LDAP_MAX_CONNS || 5, 10), + idleTimeoutMillis: 10, + log: new Logger({ + name: 'ldapjs_unit_test', + stream: process.stderr, + level: (process.env.LOG_LEVEL || 'info'), + serializers: Logger.stdSerializers, + src: true + }) + }); + t.ok(client); + t.end(); + }); + +}); + + +test('simple bind failure', function (t) { + client.bind(BIND_DN, uuid(), function (err, res) { + t.ok(err); + t.notOk(res); + + t.ok(err instanceof ldap.InvalidCredentialsError); + t.ok(err instanceof Error); + t.ok(err.dn); + t.ok(err.message); + t.ok(err.stack); + + t.end(); + }); +}); + + +test('simple bind success', function (t) { + client.bind(BIND_DN, BIND_PW, function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('add success', function (t) { + var attrs = [ + new Attribute({ + type: 'cn', + vals: ['test'] + }) + ]; + client.add('cn=add, ' + SUFFIX, attrs, function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('add success with object', function (t) { + var entry = { + cn: ['unit', 'add'], + sn: 'test' + }; + client.add('cn=add, ' + SUFFIX, entry, function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('compare success', function (t) { + client.compare('cn=compare, ' + SUFFIX, 'cn', 'test', function (err, + matched, + res) { + t.ifError(err); + t.ok(matched); + t.ok(res); + t.end(); + }); +}); + + +test('compare false', function (t) { + client.compare('cn=compare, ' + SUFFIX, 'cn', 'foo', function (err, + matched, + res) { + t.ifError(err); + t.notOk(matched); + t.ok(res); + t.end(); + }); +}); + + +test('compare bad suffix', function (t) { + client.compare('cn=' + uuid(), 'cn', 'foo', function (err, + matched, + res) { + t.ok(err); + t.ok(err instanceof ldap.NoSuchObjectError); + t.notOk(matched); + t.notOk(res); + t.end(); + }); +}); + + +test('delete success', function (t) { + client.del('cn=delete, ' + SUFFIX, function (err, res) { + t.ifError(err); + t.ok(res); + t.end(); + }); +}); + + +test('exop success', function (t) { + client.exop('1.3.6.1.4.1.4203.1.11.3', function (err, value, res) { + t.ifError(err); + t.ok(value); + t.ok(res); + t.equal(value, 'u:xxyyz@EXAMPLE.NET'); + t.end(); + }); +}); + + +test('exop invalid', function (t) { + client.exop('1.2.3.4', function (err, res) { + t.ok(err); + t.ok(err instanceof ldap.ProtocolError); + t.notOk(res); + t.end(); + }); +}); + + +test('bogus exop (GH-17)', function (t) { + client.exop('cn=root', function (err, value) { + t.ok(err); + t.end(); + }); +}); + + +test('modify success', function (t) { + var change = new Change({ + type: 'Replace', + modification: new Attribute({ + type: 'cn', + vals: ['test'] + }) + }); + client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('modify change plain object success', function (t) { + var change = new Change({ + type: 'Replace', + modification: { + cn: 'test' + } + }); + client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('modify array success', function (t) { + var changes = [ + new Change({ + operation: 'Replace', + modification: new Attribute({ + type: 'cn', + vals: ['test'] + }) + }), + new Change({ + operation: 'Delete', + modification: new Attribute({ + type: 'sn' + }) + }) + ]; + client.modify('cn=modify, ' + SUFFIX, changes, function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('modify change plain object success (GH-31)', function (t) { + var change = { + type: 'replace', + modification: { + cn: 'test', + sn: 'bar' + } + }; + client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('modify DN new RDN only', function (t) { + client.modifyDN('cn=old, ' + SUFFIX, 'cn=new', function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('modify DN new superior', function (t) { + client.modifyDN('cn=old, ' + SUFFIX, 'cn=new, dc=foo', function (err, res) { + t.ifError(err); + t.ok(res); + t.equal(res.status, 0); + t.end(); + }); +}); + + +test('search basic', function (t) { + client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function (err, res) { + t.ifError(err); + t.ok(res); + var gotEntry = 0; + res.on('searchEntry', function (entry) { + t.ok(entry); + t.ok(entry instanceof ldap.SearchEntry); + t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX); + t.ok(entry.attributes); + t.ok(entry.attributes.length); + t.equal(entry.attributes[0].type, 'cn'); + t.equal(entry.attributes[1].type, 'SN'); + t.ok(entry.object); + gotEntry++; + }); + res.on('error', function (err) { + t.fail(err); + }); + res.on('end', function (res) { + t.ok(res); + t.ok(res instanceof ldap.SearchResponse); + t.equal(res.status, 0); + t.equal(gotEntry, 2); + t.end(); + }); + }); +}); + + +test('search referral', function (t) { + client.search('cn=ref, ' + SUFFIX, '(objectclass=*)', function (err, res) { + t.ifError(err); + t.ok(res); + var gotEntry = 0; + var gotReferral = false; + res.on('searchEntry', function (entry) { + gotEntry++; + }); + res.on('searchReference', function (referral) { + gotReferral = true; + t.ok(referral); + t.ok(referral instanceof ldap.SearchReference); + t.ok(referral.uris); + t.ok(referral.uris.length); + }); + res.on('error', function (err) { + t.fail(err); + }); + res.on('end', function (res) { + t.ok(res); + t.ok(res instanceof ldap.SearchResponse); + t.equal(res.status, 0); + t.equal(gotEntry, 0); + t.ok(gotReferral); + t.end(); + }); + }); +}); + + +test('search empty attribute', function (t) { + client.search('dc=empty', '(objectclass=*)', function (err, res) { + t.ifError(err); + t.ok(res); + var gotEntry = 0; + res.on('searchEntry', function (entry) { + var obj = entry.toObject(); + t.equal('dc=empty', obj.dn); + t.ok(obj.member); + t.equal(obj.member.length, 0); + t.ok(obj['member;range=0-1']); + t.ok(obj['member;range=0-1'].length); + gotEntry++; + }); + res.on('error', function (err) { + t.fail(err); + }); + res.on('end', function (res) { + t.ok(res); + t.ok(res instanceof ldap.SearchResponse); + t.equal(res.status, 0); + t.equal(gotEntry, 1); + t.end(); + }); + }); +}); + + +test('GH-21 binary attributes', function (t) { + client.search('cn=bin, ' + SUFFIX, '(objectclass=*)', function (err, res) { + t.ifError(err); + t.ok(res); + var gotEntry = 0; + var expect = new Buffer('\u00bd + \u00bc = \u00be', 'utf8'); + var expect2 = new Buffer([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]); + res.on('searchEntry', function (entry) { + t.ok(entry); + t.ok(entry instanceof ldap.SearchEntry); + t.equal(entry.dn.toString(), 'cn=bin, ' + SUFFIX); + t.ok(entry.attributes); + t.ok(entry.attributes.length); + t.equal(entry.attributes[0].type, 'foo;binary'); + t.equal(entry.attributes[0].vals[0], expect.toString('base64')); + t.equal(entry.attributes[0].buffers[0].toString('base64'), + expect.toString('base64')); + + t.ok(entry.attributes[1].type, 'gb18030'); + t.equal(entry.attributes[1].buffers.length, 1); + t.equal(expect2.length, entry.attributes[1].buffers[0].length); + for (var i = 0; i < expect2.length; i++) + t.equal(expect2[i], entry.attributes[1].buffers[0][i]); + + t.ok(entry.object); + gotEntry++; + }); + res.on('error', function (err) { + t.fail(err); + }); + res.on('end', function (res) { + t.ok(res); + t.ok(res instanceof ldap.SearchResponse); + t.equal(res.status, 0); + t.equal(gotEntry, 1); + t.end(); + }); + }); +}); + + +test('GH-23 case insensitive attribute filtering', function (t) { + var opts = { + filter: '(objectclass=*)', + attributes: ['Cn'] + }; + client.search('cn=test, ' + SUFFIX, opts, function (err, res) { + t.ifError(err); + t.ok(res); + var gotEntry = 0; + res.on('searchEntry', function (entry) { + t.ok(entry); + t.ok(entry instanceof ldap.SearchEntry); + t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX); + t.ok(entry.attributes); + t.ok(entry.attributes.length); + t.equal(entry.attributes[0].type, 'cn'); + t.ok(entry.object); + gotEntry++; + }); + res.on('error', function (err) { + t.fail(err); + }); + res.on('end', function (res) { + t.ok(res); + t.ok(res instanceof ldap.SearchResponse); + t.equal(res.status, 0); + t.equal(gotEntry, 2); + t.end(); + }); + }); +}); + + +test('GH-24 attribute selection of *', function (t) { + var opts = { + filter: '(objectclass=*)', + attributes: ['*'] + }; + client.search('cn=test, ' + SUFFIX, opts, function (err, res) { + t.ifError(err); + t.ok(res); + var gotEntry = 0; + res.on('searchEntry', function (entry) { + t.ok(entry); + t.ok(entry instanceof ldap.SearchEntry); + t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX); + t.ok(entry.attributes); + t.ok(entry.attributes.length); + t.equal(entry.attributes[0].type, 'cn'); + t.equal(entry.attributes[1].type, 'SN'); + t.ok(entry.object); + gotEntry++; + }); + res.on('error', function (err) { + t.fail(err); + }); + res.on('end', function (res) { + t.ok(res); + t.ok(res instanceof ldap.SearchResponse); + t.equal(res.status, 0); + t.equal(gotEntry, 2); + t.end(); + }); + }); +}); + + +test('abandon (GH-27)', function (t) { + client.abandon(401876543, function (err) { + t.ifError(err); + t.end(); + }); +}); + + +test('search timeout (GH-51)', function (t) { + client.timeout = 250; + client.search('dc=timeout', 'objectclass=*', function (err, res) { + t.ifError(err); + res.on('error', function () { + t.end(); + }); + }); +}); + + +test('unbind (GH-30)', function (t) { + client.unbind(function (err) { + t.ifError(err); + t.end(); + }); +}); + + +test('shutdown', function (t) { + server.on('close', function () { + t.end(); + }); + server.close(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/control.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/control.test.js new file mode 100644 index 0000000..0943f15 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/control.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var Control; +var getControl; + + +///--- Tests + +test('load library', function (t) { + Control = require('../../lib/index').Control; + t.ok(Control); + getControl = require('../../lib/index').getControl; + t.ok(getControl); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new Control()); + t.end(); +}); + + +test('new with args', function (t) { + var c = new Control({ + type: '2.16.840.1.113730.3.4.2', + criticality: true + }); + t.ok(c); + t.equal(c.type, '2.16.840.1.113730.3.4.2'); + t.ok(c.criticality); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.startSequence(); + ber.writeString('2.16.840.1.113730.3.4.2'); + ber.writeBoolean(true); + ber.writeString('foo'); + ber.endSequence(); + + var c = getControl(new BerReader(ber.buffer)); + + t.ok(c); + t.equal(c.type, '2.16.840.1.113730.3.4.2'); + t.ok(c.criticality); + t.equal(c.value, 'foo'); + t.end(); +}); + + +test('parse no value', function (t) { + var ber = new BerWriter(); + ber.startSequence(); + ber.writeString('2.16.840.1.113730.3.4.2'); + ber.endSequence(); + + var c = getControl(new BerReader(ber.buffer)); + + t.ok(c); + t.equal(c.type, '2.16.840.1.113730.3.4.2'); + t.equal(c.criticality, false); + t.notOk(c.value, null); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/entry_change_notification_control_test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/entry_change_notification_control_test.js new file mode 100644 index 0000000..e9a9965 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/entry_change_notification_control_test.js @@ -0,0 +1,88 @@ + +var test = require('tap').test; + +var asn1 = require('asn1'); + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var getControl; +var EntryChangeNotificationControl; + + + +///--- Tests + + +test('load library', function (t) { + EntryChangeNotificationControl = + require('../../lib').EntryChangeNotificationControl; + t.ok(EntryChangeNotificationControl); + getControl = require('../../lib').getControl; + t.ok(getControl); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new EntryChangeNotificationControl()); + t.end(); +}); + + +test('new with args', function (t) { + var c = new EntryChangeNotificationControl({ + type: '2.16.840.1.113730.3.4.7', + criticality: true, + value: { + changeType: 8, + previousDN: 'cn=foobarbazcar', + changeNumber: 123456789 + } + }); + t.ok(c); + t.equal(c.type, '2.16.840.1.113730.3.4.7'); + t.ok(c.criticality); + t.equal(c.value.changeType, 8); + t.equal(c.value.previousDN, 'cn=foobarbazcar'); + t.equal(c.value.changeNumber, 123456789); + + + var writer = new BerWriter(); + c.toBer(writer); + var reader = new BerReader(writer.buffer); + var psc = getControl(reader); + t.ok(psc); + console.log('psc', psc.value); + t.equal(psc.type, '2.16.840.1.113730.3.4.7'); + t.ok(psc.criticality); + t.equal(psc.value.changeType, 8); + t.equal(psc.value.previousDN, 'cn=foobarbazcar'); + t.equal(psc.value.changeNumber, 123456789); + + t.end(); +}); + +test('tober', function (t) { + var psc = new EntryChangeNotificationControl({ + type: '2.16.840.1.113730.3.4.7', + criticality: true, + value: { + changeType: 8, + previousDN: 'cn=foobarbazcar', + changeNumber: 123456789 + } + }); + + var ber = new BerWriter(); + psc.toBer(ber); + + var c = getControl(new BerReader(ber.buffer)); + t.ok(c); + t.equal(c.type, '2.16.840.1.113730.3.4.7'); + t.ok(c.criticality); + t.equal(c.value.changeType, 8); + t.equal(c.value.previousDN, 'cn=foobarbazcar'); + t.equal(c.value.changeNumber, 123456789); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/paged_results_control_test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/paged_results_control_test.js new file mode 100644 index 0000000..e8c2f07 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/paged_results_control_test.js @@ -0,0 +1,86 @@ + +var test = require('tap').test; + +var asn1 = require('asn1'); + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var getControl; +var PagedResultsControl; + +function bufferEqual(t, a, b) { + t.equal(a.toString('hex'), b.toString('hex')); +} + + +///--- Tests + + +test('load library', function (t) { + PagedResultsControl = + require('../../lib').PagedResultsControl; + t.ok(PagedResultsControl); + getControl = require('../../lib').getControl; + t.ok(getControl); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new PagedResultsControl()); + t.end(); +}); + + +test('new with args', function (t) { + var c = new PagedResultsControl({ + type: '1.2.840.113556.1.4.319', + criticality: true, + value: { + size: 1000, + cookie: new Buffer([1, 2, 3]) + } + }); + t.ok(c); + t.equal(c.type, '1.2.840.113556.1.4.319'); + t.ok(c.criticality); + t.equal(c.value.size, 1000); + bufferEqual(t, c.value.cookie, new Buffer([1, 2, 3])); + + + var writer = new BerWriter(); + c.toBer(writer); + var reader = new BerReader(writer.buffer); + var psc = getControl(reader); + t.ok(psc); + console.log('psc', psc.value); + t.equal(psc.type, '1.2.840.113556.1.4.319'); + t.ok(psc.criticality); + t.equal(psc.value.size, 1000); + bufferEqual(t, psc.value.cookie, new Buffer([1, 2, 3])); + + t.end(); +}); + +test('tober', function (t) { + var psc = new PagedResultsControl({ + type: '1.2.840.113556.1.4.319', + criticality: true, + value: { + size: 20, + cookie: new Buffer(0) + } + }); + + var ber = new BerWriter(); + psc.toBer(ber); + + var c = getControl(new BerReader(ber.buffer)); + t.ok(c); + t.equal(c.type, '1.2.840.113556.1.4.319'); + t.ok(c.criticality); + t.equal(c.value.size, 20); + bufferEqual(t, c.value.cookie, new Buffer(0)); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/persistent_search_control.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/persistent_search_control.test.js new file mode 100644 index 0000000..37a7667 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/controls/persistent_search_control.test.js @@ -0,0 +1,104 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var getControl; +var PersistentSearchControl; + + + +///--- Tests + +test('load library', function (t) { + PersistentSearchControl = require('../../lib').PersistentSearchControl; + t.ok(PersistentSearchControl); + getControl = require('../../lib').getControl; + t.ok(getControl); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new PersistentSearchControl()); + t.end(); +}); + + +test('new with args', function (t) { + var c = new PersistentSearchControl({ + type: '2.16.840.1.113730.3.4.3', + criticality: true, + value: { + changeTypes: 15, + changesOnly: false, + returnECs: false + } + }); + t.ok(c); + t.equal(c.type, '2.16.840.1.113730.3.4.3'); + t.ok(c.criticality); + + t.equal(c.value.changeTypes, 15); + t.equal(c.value.changesOnly, false); + t.equal(c.value.returnECs, false); + + + var writer = new BerWriter(); + c.toBer(writer); + var reader = new BerReader(writer.buffer); + var psc = getControl(reader); + t.ok(psc); + t.equal(psc.type, '2.16.840.1.113730.3.4.3'); + t.ok(psc.criticality); + t.equal(psc.value.changeTypes, 15); + t.equal(psc.value.changesOnly, false); + t.equal(psc.value.returnECs, false); + + t.end(); +}); + +test('getControl with args', function (t) { + var buf = new Buffer([ + 0x30, 0x26, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, + 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, + 0x34, 0x2e, 0x33, 0x04, 0x0b, 0x30, 0x09, 0x02, 0x01, 0x0f, 0x01, 0x01, + 0xff, 0x01, 0x01, 0xff]); + + var ber = new BerReader(buf); + var psc = getControl(ber); + t.ok(psc); + t.equal(psc.type, '2.16.840.1.113730.3.4.3'); + t.equal(psc.criticality, false); + t.equal(psc.value.changeTypes, 15); + t.equal(psc.value.changesOnly, true); + t.equal(psc.value.returnECs, true); + t.end(); +}); + +test('tober', function (t) { + var psc = new PersistentSearchControl({ + type: '2.16.840.1.113730.3.4.3', + criticality: true, + value: { + changeTypes: 15, + changesOnly: false, + returnECs: false + } + }); + + var ber = new BerWriter(); + psc.toBer(ber); + + var c = getControl(new BerReader(ber.buffer)); + t.ok(c); + t.equal(c.type, '2.16.840.1.113730.3.4.3'); + t.ok(c.criticality); + t.equal(c.value.changeTypes, 15); + t.equal(c.value.changesOnly, false); + t.equal(c.value.returnECs, false); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/dn.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/dn.test.js new file mode 100644 index 0000000..aa3b243 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/dn.test.js @@ -0,0 +1,133 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + + + +///--- Globals + +var dn; + + + +///--- Tests + +test('load library', function (t) { + dn = require('../lib/index').dn; + t.ok(dn); + t.end(); +}); + + +test('parse basic', function (t) { + var DN_STR = 'cn=mark, ou=people, o=joyent'; + var name = dn.parse(DN_STR); + t.ok(name); + t.ok(name.rdns); + t.ok(Array.isArray(name.rdns)); + t.equal(3, name.rdns.length); + name.rdns.forEach(function (rdn) { + t.equal('object', typeof (rdn)); + }); + t.equal(name.toString(), DN_STR); + t.end(); +}); + + +test('parse escaped', function (t) { + var DN_STR = 'cn=m\\,ark, ou=people, o=joyent'; + var name = dn.parse(DN_STR); + t.ok(name); + t.ok(name.rdns); + t.ok(Array.isArray(name.rdns)); + t.equal(3, name.rdns.length); + name.rdns.forEach(function (rdn) { + t.equal('object', typeof (rdn)); + }); + t.equal(name.toString(), DN_STR); + t.end(); +}); + + +test('parse compound', function (t) { + var DN_STR = 'cn=mark+sn=cavage, ou=people, o=joyent'; + var name = dn.parse(DN_STR); + t.ok(name); + t.ok(name.rdns); + t.ok(Array.isArray(name.rdns)); + t.equal(3, name.rdns.length); + name.rdns.forEach(function (rdn) { + t.equal('object', typeof (rdn)); + }); + t.equal(name.toString(), DN_STR); + t.end(); +}); + + +test('parse quoted', function (t) { + var DN_STR = 'cn="mark+sn=cavage", ou=people, o=joyent'; + var name = dn.parse(DN_STR); + t.ok(name); + t.ok(name.rdns); + t.ok(Array.isArray(name.rdns)); + t.equal(3, name.rdns.length); + name.rdns.forEach(function (rdn) { + t.equal('object', typeof (rdn)); + }); + t.equal(name.toString(), DN_STR); + t.end(); +}); + + +test('equals', function (t) { + var dn1 = dn.parse('cn=foo, dc=bar'); + t.ok(dn1.equals('cn=foo, dc=bar')); + t.ok(!dn1.equals('cn=foo1, dc=bar')); + t.ok(dn1.equals(dn.parse('cn=foo, dc=bar'))); + t.ok(!dn1.equals(dn.parse('cn=foo2, dc=bar'))); + t.end(); +}); + + +test('child of', function (t) { + var dn1 = dn.parse('cn=foo, dc=bar'); + t.ok(dn1.childOf('dc=bar')); + t.ok(!dn1.childOf('dc=moo')); + t.ok(!dn1.childOf('dc=foo')); + t.ok(!dn1.childOf('cn=foo, dc=bar')); + + t.ok(dn1.childOf(dn.parse('dc=bar'))); + t.end(); +}); + + +test('parent of', function (t) { + var dn1 = dn.parse('cn=foo, dc=bar'); + t.ok(dn1.parentOf('cn=moo, cn=foo, dc=bar')); + t.ok(!dn1.parentOf('cn=moo, cn=bar, dc=foo')); + t.ok(!dn1.parentOf('cn=foo, dc=bar')); + + t.ok(dn1.parentOf(dn.parse('cn=moo, cn=foo, dc=bar'))); + t.end(); +}); + + +test('empty DNs GH-16', function (t) { + var _dn = dn.parse(''); + var _dn2 = dn.parse('cn=foo'); + t.notOk(_dn.equals('cn=foo')); + t.notOk(_dn2.equals('')); + t.notOk(_dn.parentOf('cn=foo')); + t.notOk(_dn.childOf('cn=foo')); + t.notOk(_dn2.parentOf('')); + t.notOk(_dn2.childOf('')); + t.end(); +}); + + +test('case insensitive attribute names GH-20', function (t) { + var dn1 = dn.parse('CN=foo, dc=bar'); + t.ok(dn1.equals('cn=foo, dc=bar')); + t.ok(dn1.equals(dn.parse('cn=foo, DC=bar'))); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/and.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/and.test.js new file mode 100644 index 0000000..c7b22c3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/and.test.js @@ -0,0 +1,79 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var EqualityFilter; +var AndFilter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + EqualityFilter = filters.EqualityFilter; + AndFilter = filters.AndFilter; + t.ok(EqualityFilter); + t.ok(AndFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + t.ok(new AndFilter()); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new AndFilter(); + f.addFilter(new EqualityFilter({ + attribute: 'foo', + value: 'bar' + })); + f.addFilter(new EqualityFilter({ + attribute: 'zig', + value: 'zag' + })); + t.ok(f); + t.equal(f.toString(), '(&(foo=bar)(zig=zag))'); + t.end(); +}); + + +test('match true', function (t) { + var f = new AndFilter(); + f.addFilter(new EqualityFilter({ + attribute: 'foo', + value: 'bar' + })); + f.addFilter(new EqualityFilter({ + attribute: 'zig', + value: 'zag' + })); + t.ok(f); + t.ok(f.matches({ foo: 'bar', zig: 'zag' })); + t.end(); +}); + + +test('match false', function (t) { + var f = new AndFilter(); + f.addFilter(new EqualityFilter({ + attribute: 'foo', + value: 'bar' + })); + f.addFilter(new EqualityFilter({ + attribute: 'zig', + value: 'zag' + })); + t.ok(f); + t.ok(!f.matches({ foo: 'bar', zig: 'zonk' })); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/approx.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/approx.test.js new file mode 100644 index 0000000..d0d66b4 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/approx.test.js @@ -0,0 +1,109 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var ApproximateFilter; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + ApproximateFilter = filters.ApproximateFilter; + t.ok(ApproximateFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + var f = new ApproximateFilter(); + t.ok(f); + t.ok(!f.attribute); + t.ok(!f.value); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new ApproximateFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar'); + t.equal(f.toString(), '(foo~=bar)'); + t.end(); +}); + + +test('match true', function (t) { + var f = new ApproximateFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); + + +test('match multiple', function (t) { + var f = new ApproximateFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: ['steak', 'bar']})); + t.ok(!f.matches({ foo: ['nihhh', 'rabbit']})); + t.end(); +}); + +test('match false', function (t) { + var f = new ApproximateFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(!f.matches({ foo: 'baz' })); + t.end(); +}); + + +test('parse ok', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeString('bar'); + + var f = new ApproximateFilter(); + t.ok(f); + t.ok(f.parse(new BerReader(writer.buffer))); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); + + +test('parse bad', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeInt(20); + + var f = new ApproximateFilter(); + t.ok(f); + try { + f.parse(new BerReader(writer.buffer)); + t.fail('Should have thrown InvalidAsn1Error'); + } catch (e) { + t.equal(e.name, 'InvalidAsn1Error'); + } + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/eq.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/eq.test.js new file mode 100644 index 0000000..58958ff --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/eq.test.js @@ -0,0 +1,120 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var EqualityFilter; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + EqualityFilter = filters.EqualityFilter; + t.ok(EqualityFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + var f = new EqualityFilter(); + t.ok(f); + t.ok(!f.attribute); + t.ok(!f.value); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new EqualityFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar'); + t.equal(f.toString(), '(foo=bar)'); + t.end(); +}); + + +test('match true', function (t) { + var f = new EqualityFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); + + +test('match multiple', function (t) { + var f = new EqualityFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: ['plop', 'bar'] })); + t.end(); +}); + + +test('match false', function (t) { + var f = new EqualityFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(!f.matches({ foo: 'baz' })); + t.end(); +}); + + +test('parse ok', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeString('bar'); + + var f = new EqualityFilter(); + t.ok(f); + t.ok(f.parse(new BerReader(writer.buffer))); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); + +test('escape EqualityFilter inputs', function (t) { + var f = new EqualityFilter({ + attribute: '(|(foo', + value: 'bar))(' + }); + + t.equal(f.attribute, '\\28|\\28foo'); + t.equal(f.value, 'bar\\29\\29\\28'); + t.end(); +}); + + +test('parse bad', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeInt(20); + + var f = new EqualityFilter(); + t.ok(f); + try { + f.parse(new BerReader(writer.buffer)); + t.fail('Should have thrown InvalidAsn1Error'); + } catch (e) { + t.equal(e.name, 'InvalidAsn1Error'); + } + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/ext.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/ext.test.js new file mode 100644 index 0000000..a84f1fd --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/ext.test.js @@ -0,0 +1,110 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var ExtensibleFilter; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var filters; + + +///--- Tests + +test('load library', function (t) { + filters = require('../../lib/index').filters; + t.ok(filters); + ExtensibleFilter = filters.ExtensibleFilter; + t.ok(ExtensibleFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + var f = new ExtensibleFilter(); + t.ok(f); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new ExtensibleFilter({ + matchType: 'foo', + value: 'bar' + }); + t.ok(f); + t.equal(f.matchType, 'foo'); + t.equal(f.value, 'bar'); + t.equal(f.toString(), '(foo:=bar)'); + t.end(); +}); + + +test('parse RFC example 1', function (t) { + var f = filters.parseString('(cn:caseExactMatch:=Fred Flintstone)'); + t.ok(f); + t.equal(f.matchType, 'cn'); + t.equal(f.matchingRule, 'caseExactMatch'); + t.equal(f.matchValue, 'Fred Flintstone'); + t.notOk(f.dnAttributes); + t.end(); +}); + + +test('parse RFC example 2', function (t) { + var f = filters.parseString('(cn:=Betty Rubble)'); + t.ok(f); + t.equal(f.matchType, 'cn'); + t.equal(f.matchValue, 'Betty Rubble'); + t.notOk(f.dnAttributes); + t.notOk(f.matchingRule); + t.end(); +}); + + +test('parse RFC example 3', function (t) { + var f = filters.parseString('(sn:dn:2.4.6.8.10:=Barney Rubble)'); + t.ok(f); + t.equal(f.matchType, 'sn'); + t.equal(f.matchingRule, '2.4.6.8.10'); + t.equal(f.matchValue, 'Barney Rubble'); + t.ok(f.dnAttributes); + t.end(); +}); + + +test('parse RFC example 3', function (t) { + var f = filters.parseString('(o:dn:=Ace Industry)'); + t.ok(f); + t.equal(f.matchType, 'o'); + t.notOk(f.matchingRule); + t.equal(f.matchValue, 'Ace Industry'); + t.ok(f.dnAttributes); + t.end(); +}); + + +test('parse RFC example 4', function (t) { + var f = filters.parseString('(:1.2.3:=Wilma Flintstone)'); + t.ok(f); + t.notOk(f.matchType); + t.equal(f.matchingRule, '1.2.3'); + t.equal(f.matchValue, 'Wilma Flintstone'); + t.notOk(f.dnAttributes); + t.end(); +}); + + +test('parse RFC example 5', function (t) { + var f = filters.parseString('(:DN:2.4.6.8.10:=Dino)'); + t.ok(f); + t.notOk(f.matchType); + t.equal(f.matchingRule, '2.4.6.8.10'); + t.equal(f.matchValue, 'Dino'); + t.ok(f.dnAttributes); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/ge.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/ge.test.js new file mode 100644 index 0000000..b04b4c0 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/ge.test.js @@ -0,0 +1,108 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var GreaterThanEqualsFilter; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + GreaterThanEqualsFilter = filters.GreaterThanEqualsFilter; + t.ok(GreaterThanEqualsFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + var f = new GreaterThanEqualsFilter(); + t.ok(f); + t.ok(!f.attribute); + t.ok(!f.value); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new GreaterThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar'); + t.equal(f.toString(), '(foo>=bar)'); + t.end(); +}); + + +test('match true', function (t) { + var f = new GreaterThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: 'baz' })); + t.end(); +}); + + +test('match multiple', function (t) { + var f = new GreaterThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: ['beuha', 'baz'] })); + t.end(); +}); + +test('match false', function (t) { + var f = new GreaterThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(!f.matches({ foo: 'abc' })); + t.end(); +}); + + +test('parse ok', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeString('bar'); + + var f = new GreaterThanEqualsFilter(); + t.ok(f); + t.ok(f.parse(new BerReader(writer.buffer))); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); + + +test('parse bad', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeInt(20); + + var f = new GreaterThanEqualsFilter(); + t.ok(f); + try { + f.parse(new BerReader(writer.buffer)); + t.fail('Should have thrown InvalidAsn1Error'); + } catch (e) { + t.equal(e.name, 'InvalidAsn1Error'); + } + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/le.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/le.test.js new file mode 100644 index 0000000..f4a0f57 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/le.test.js @@ -0,0 +1,109 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var LessThanEqualsFilter; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + LessThanEqualsFilter = filters.LessThanEqualsFilter; + t.ok(LessThanEqualsFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + var f = new LessThanEqualsFilter(); + t.ok(f); + t.ok(!f.attribute); + t.ok(!f.value); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new LessThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar'); + t.equal(f.toString(), '(foo<=bar)'); + t.end(); +}); + + +test('match true', function (t) { + var f = new LessThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: 'abc' })); + t.end(); +}); + + +test('match multiple', function (t) { + var f = new LessThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: ['abc', 'beuha'] })); + t.end(); +}); + + +test('match false', function (t) { + var f = new LessThanEqualsFilter({ + attribute: 'foo', + value: 'bar' + }); + t.ok(f); + t.ok(!f.matches({ foo: 'baz' })); + t.end(); +}); + + +test('parse ok', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeString('bar'); + + var f = new LessThanEqualsFilter(); + t.ok(f); + t.ok(f.parse(new BerReader(writer.buffer))); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); + + +test('parse bad', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeInt(20); + + var f = new LessThanEqualsFilter(); + t.ok(f); + try { + f.parse(new BerReader(writer.buffer)); + t.fail('Should have thrown InvalidAsn1Error'); + } catch (e) { + t.equal(e.name, 'InvalidAsn1Error'); + } + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/not.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/not.test.js new file mode 100644 index 0000000..5fc9566 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/not.test.js @@ -0,0 +1,70 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var EqualityFilter; +var NotFilter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + EqualityFilter = filters.EqualityFilter; + NotFilter = filters.NotFilter; + t.ok(EqualityFilter); + t.ok(NotFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + t.ok(new NotFilter()); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new NotFilter({ + filter: new EqualityFilter({ + attribute: 'foo', + value: 'bar' + }) + }); + t.ok(f); + t.equal(f.toString(), '(!(foo=bar))'); + t.end(); +}); + + +test('match true', function (t) { + var f = new NotFilter({ + filter: new EqualityFilter({ + attribute: 'foo', + value: 'bar' + }) + }); + t.ok(f); + t.ok(f.matches({ foo: 'baz' })); + t.end(); +}); + + +test('match false', function (t) { + var f = new NotFilter({ + filter: new EqualityFilter({ + attribute: 'foo', + value: 'bar' + }) + }); + t.ok(f); + t.ok(!f.matches({ foo: 'bar' })); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/or.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/or.test.js new file mode 100644 index 0000000..8622f78 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/or.test.js @@ -0,0 +1,79 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var EqualityFilter; +var OrFilter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + EqualityFilter = filters.EqualityFilter; + OrFilter = filters.OrFilter; + t.ok(EqualityFilter); + t.ok(OrFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + t.ok(new OrFilter()); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new OrFilter(); + f.addFilter(new EqualityFilter({ + attribute: 'foo', + value: 'bar' + })); + f.addFilter(new EqualityFilter({ + attribute: 'zig', + value: 'zag' + })); + t.ok(f); + t.equal(f.toString(), '(|(foo=bar)(zig=zag))'); + t.end(); +}); + + +test('match true', function (t) { + var f = new OrFilter(); + f.addFilter(new EqualityFilter({ + attribute: 'foo', + value: 'bar' + })); + f.addFilter(new EqualityFilter({ + attribute: 'zig', + value: 'zag' + })); + t.ok(f); + t.ok(f.matches({ foo: 'bar', zig: 'zonk' })); + t.end(); +}); + + +test('match false', function (t) { + var f = new OrFilter(); + f.addFilter(new EqualityFilter({ + attribute: 'foo', + value: 'bar' + })); + f.addFilter(new EqualityFilter({ + attribute: 'zig', + value: 'zag' + })); + t.ok(f); + t.ok(!f.matches({ foo: 'baz', zig: 'zonk' })); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/parse.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/parse.test.js new file mode 100644 index 0000000..6e7ab8c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/parse.test.js @@ -0,0 +1,125 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var parse = require('../../lib/index').parseFilter; + + + +test('GH-48 XML Strings in filter', function (t) { + var str = '(&(CentralUIEnrollments=\\*)(objectClass=User))'; + var f = parse(str); + t.ok(f); + t.ok(f.filters); + t.equal(f.filters.length, 2); + f.filters.forEach(function (filter) { + t.ok(filter.attribute); + }); + t.end(); +}); + + +test('GH-50 = in filter', function (t) { + var str = '(uniquemember=uuid=930896af-bf8c-48d4-885c-6573a94b1853, ' + + 'ou=users, o=smartdc)'; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'uniquemember'); + t.equal(f.value, + 'uuid=930896af-bf8c-48d4-885c-6573a94b1853, ou=users, o=smartdc'); + t.end(); +}); + + +test('( in filter', function (t) { + var str = '(foo=bar\\()'; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar\\28'); + t.end(); +}); + +test(') in filter', function (t) { + var str = '(foo=bar\\))'; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar\\29'); + t.end(); +}); + + +test('( in filter', function (t) { + var str = 'foo(bar=baz\\()'; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'foo\\28bar'); + t.equal(f.value, 'baz\\28\\29'); + t.end(); +}); + + +test('( in filter', function (t) { + var str = 'foo)(&(bar=baz)('; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'foo\\29\\28&\\28bar'); + t.equal(f.value, 'baz\\29\\28'); + t.end(); +}); + + +test('\\ in filter', function (t) { + var str = '(foo=bar\\\\)'; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar\\5c'); + t.end(); +}); + + +test('* in equality filter', function (t) { + var str = '(foo=bar\\*)'; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.value, 'bar\\2a'); + t.end(); +}); + + +test('* substr filter (prefix)', function (t) { + var str = '(foo=bar*)'; + var f = parse(str); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.initial, 'bar'); + t.end(); +}); + + +test('GH-53 NotFilter', function (t) { + var str = '(&(objectClass=person)(!(objectClass=shadowAccount)))'; + var f = parse(str); + t.ok(f); + t.equal(f.type, 'and'); + t.equal(f.filters.length, 2); + t.equal(f.filters[0].type, 'equal'); + t.equal(f.filters[1].type, 'not'); + t.equal(f.filters[1].filter.type, 'equal'); + t.equal(f.filters[1].filter.attribute, 'objectClass'); + t.equal(f.filters[1].filter.value, 'shadowAccount'); + t.end(); +}); + + +test('presence filter', function (t) { + var str = '(foo=*)'; + var f = parse(str); + t.ok(f); + t.equal(f.type, 'present'); + t.equal(f.attribute, 'foo'); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/presence.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/presence.test.js new file mode 100644 index 0000000..e97b28e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/presence.test.js @@ -0,0 +1,78 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var PresenceFilter; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + PresenceFilter = filters.PresenceFilter; + t.ok(PresenceFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + var f = new PresenceFilter(); + t.ok(f); + t.ok(!f.attribute); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new PresenceFilter({ + attribute: 'foo' + }); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.toString(), '(foo=*)'); + t.end(); +}); + + +test('match true', function (t) { + var f = new PresenceFilter({ + attribute: 'foo' + }); + t.ok(f); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); + + +test('match false', function (t) { + var f = new PresenceFilter({ + attribute: 'foo' + }); + t.ok(f); + t.ok(!f.matches({ bar: 'foo' })); + t.end(); +}); + + +test('parse ok', function (t) { + var writer = new BerWriter(); + writer.writeString('foo', 0x87); + + var f = new PresenceFilter(); + t.ok(f); + + var reader = new BerReader(writer.buffer); + reader.readSequence(); + t.ok(f.parse(reader)); + t.ok(f.matches({ foo: 'bar' })); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/substr.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/substr.test.js new file mode 100644 index 0000000..df8af38 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/filters/substr.test.js @@ -0,0 +1,120 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var SubstringFilter; +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; + + + +///--- Tests + +test('load library', function (t) { + var filters = require('../../lib/index').filters; + t.ok(filters); + SubstringFilter = filters.SubstringFilter; + t.ok(SubstringFilter); + t.end(); +}); + + +test('Construct no args', function (t) { + var f = new SubstringFilter(); + t.ok(f); + t.ok(!f.attribute); + t.ok(!f.value); + t.end(); +}); + + +test('Construct args', function (t) { + var f = new SubstringFilter({ + attribute: 'foo', + initial: 'bar', + any: ['zig', 'zag'], + 'final': 'baz' + }); + t.ok(f); + t.equal(f.attribute, 'foo'); + t.equal(f.initial, 'bar'); + t.equal(f.any.length, 2); + t.equal(f.any[0], 'zig'); + t.equal(f.any[1], 'zag'); + t.equal(f['final'], 'baz'); + t.equal(f.toString(), '(foo=bar*zig*zag*baz)'); + t.end(); +}); + + +test('match true', function (t) { + var f = new SubstringFilter({ + attribute: 'foo', + initial: 'bar', + any: ['zig', 'zag'], + 'final': 'baz' + }); + t.ok(f); + t.ok(f.matches({ foo: 'barmoozigbarzagblahbaz' })); + t.end(); +}); + + +test('match false', function (t) { + var f = new SubstringFilter({ + attribute: 'foo', + initial: 'bar', + foo: ['zig', 'zag'], + 'final': 'baz' + }); + t.ok(f); + t.ok(!f.matches({ foo: 'bafmoozigbarzagblahbaz' })); + t.end(); +}); + + +test('match any', function (t) { + var f = new SubstringFilter({ + attribute: 'foo', + initial: 'bar' + }); + t.ok(f); + t.ok(f.matches({ foo: ['beuha', 'barista']})); + t.end(); +}); + +test('parse ok', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.startSequence(); + writer.writeString('bar', 0x80); + writer.writeString('bad', 0x81); + writer.writeString('baz', 0x82); + writer.endSequence(); + var f = new SubstringFilter(); + t.ok(f); + t.ok(f.parse(new BerReader(writer.buffer))); + t.ok(f.matches({ foo: 'bargoobadgoobaz' })); + t.end(); +}); + + +test('parse bad', function (t) { + var writer = new BerWriter(); + writer.writeString('foo'); + writer.writeInt(20); + + var f = new SubstringFilter(); + t.ok(f); + try { + f.parse(new BerReader(writer.buffer)); + t.fail('Should have thrown InvalidAsn1Error'); + } catch (e) { + } + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/laundry.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/laundry.test.js new file mode 100644 index 0000000..0bdc47e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/laundry.test.js @@ -0,0 +1,140 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; +var uuid = require('node-uuid'); + +var ldap = require('../lib/index'); + + +///--- Globals + +var SOCKET = '/tmp/.' + uuid(); +var SUFFIX = 'dc=' + uuid(); + +var client; +var server; + + + +///--- Helper + +function search(t, options, callback) { + client.search(SUFFIX, options, function (err, res) { + t.ifError(err); + t.ok(res); + var found = false; + res.on('searchEntry', function (entry) { + t.ok(entry); + found = true; + }); + res.on('end', function () { + t.ok(found); + if (callback) + return callback(); + + return t.end(); + }); + }); +} + + + +///--- Tests + +test('setup', function (t) { + server = ldap.createServer(); + t.ok(server); + server.listen(SOCKET, function () { + client = ldap.createClient({ + socketPath: SOCKET + }); + t.ok(client); + t.end(); + }); + + server.bind('cn=root', function (req, res, next) { + res.end(); + return next(); + }); + server.search(SUFFIX, function (req, res, next) { + var entry = { + dn: 'cn=foo, ' + SUFFIX, + attributes: { + objectclass: ['person', 'top'], + cn: 'Pogo Stick', + sn: 'Stick', + givenname: 'ogo', + mail: uuid() + '@pogostick.org' + } + }; + + if (req.filter.matches(entry.attributes)) + res.send(entry); + + res.end(); + }); +}); + + +test('Evolution search filter (GH-3)', function (t) { + // This is what Evolution sends, when searching for a contact 'ogo'. Wow. + var filter = + '(|(cn=ogo*)(givenname=ogo*)(sn=ogo*)(mail=ogo*)(member=ogo*)' + + '(primaryphone=ogo*)(telephonenumber=ogo*)(homephone=ogo*)(mobile=ogo*)' + + '(carphone=ogo*)(facsimiletelephonenumber=ogo*)' + + '(homefacsimiletelephonenumber=ogo*)(otherphone=ogo*)' + + '(otherfacsimiletelephonenumber=ogo*)(internationalisdnnumber=ogo*)' + + '(pager=ogo*)(radio=ogo*)(telex=ogo*)(assistantphone=ogo*)' + + '(companyphone=ogo*)(callbackphone=ogo*)(tty=ogo*)(o=ogo*)(ou=ogo*)' + + '(roomnumber=ogo*)(title=ogo*)(businessrole=ogo*)(managername=ogo*)' + + '(assistantname=ogo*)(postaladdress=ogo*)(l=ogo*)(st=ogo*)' + + '(postofficebox=ogo*)(postalcode=ogo*)(c=ogo*)(homepostaladdress=ogo*)' + + '(mozillahomelocalityname=ogo*)(mozillahomestate=ogo*)' + + '(mozillahomepostalcode=ogo*)(mozillahomecountryname=ogo*)' + + '(otherpostaladdress=ogo*)(jpegphoto=ogo*)(usercertificate=ogo*)' + + '(labeleduri=ogo*)(displayname=ogo*)(spousename=ogo*)(note=ogo*)' + + '(anniversary=ogo*)(birthdate=ogo*)(mailer=ogo*)(fileas=ogo*)' + + '(category=ogo*)(calcaluri=ogo*)(calfburl=ogo*)(icscalendar=ogo*))'; + + return search(t, filter); +}); + + +test('GH-49 Client errors on bad attributes', function (t) { + var searchOpts = { + filter: 'cn=*ogo*', + scope: 'one', + attributes: 'dn' + }; + return search(t, searchOpts); +}); + + +test('GH-55 Client emits connect multiple times', function (t) { + var c = ldap.createClient({ + socketPath: SOCKET + }); + + var count = 0; + c.on('connect', function (socket) { + t.ok(socket); + count++; + c.bind('cn=root', 'secret', function (err, res) { + t.ifError(err); + c.unbind(function () { + t.equal(count, 1); + t.end(); + }); + }); + }); +}); + + +test('shutdown', function (t) { + client.unbind(function () { + server.on('close', function () { + t.end(); + }); + server.close(); + }); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/add_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/add_request.test.js new file mode 100644 index 0000000..fe68e68 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/add_request.test.js @@ -0,0 +1,144 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var AddRequest; +var Attribute; +var dn; + +///--- Tests + +test('load library', function (t) { + AddRequest = require('../../lib/index').AddRequest; + Attribute = require('../../lib/index').Attribute; + dn = require('../../lib/index').dn; + t.ok(AddRequest); + t.ok(Attribute); + t.ok(dn); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new AddRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new AddRequest({ + entry: dn.parse('cn=foo, o=test'), + attributes: [new Attribute({type: 'cn', vals: ['foo']}), + new Attribute({type: 'objectclass', vals: ['person']})] + }); + t.ok(req); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.attributes.length, 2); + t.equal(req.attributes[0].type, 'cn'); + t.equal(req.attributes[0].vals[0], 'foo'); + t.equal(req.attributes[1].type, 'objectclass'); + t.equal(req.attributes[1].vals[0], 'person'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeString('cn=foo,o=test'); + + ber.startSequence(); + + ber.startSequence(); + ber.writeString('cn'); + ber.startSequence(0x31); + ber.writeString('foo'); + ber.endSequence(); + ber.endSequence(); + + ber.startSequence(); + ber.writeString('objectclass'); + ber.startSequence(0x31); + ber.writeString('person'); + ber.endSequence(); + ber.endSequence(); + + ber.endSequence(); + + var req = new AddRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.attributes.length, 2); + t.equal(req.attributes[0].type, 'cn'); + t.equal(req.attributes[0].vals[0], 'foo'); + t.equal(req.attributes[1].type, 'objectclass'); + t.equal(req.attributes[1].vals[0], 'person'); + t.end(); +}); + + +test('toBer', function (t) { + var req = new AddRequest({ + messageID: 123, + entry: dn.parse('cn=foo, o=test'), + attributes: [new Attribute({type: 'cn', vals: ['foo']}), + new Attribute({type: 'objectclass', vals: ['person']})] + }); + + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x68); + t.equal(ber.readString(), 'cn=foo, o=test'); + t.ok(ber.readSequence()); + + t.ok(ber.readSequence()); + t.equal(ber.readString(), 'cn'); + t.equal(ber.readSequence(), 0x31); + t.equal(ber.readString(), 'foo'); + + t.ok(ber.readSequence()); + t.equal(ber.readString(), 'objectclass'); + t.equal(ber.readSequence(), 0x31); + t.equal(ber.readString(), 'person'); + + t.end(); +}); + + +test('toObject', function (t) { + var req = new AddRequest({ + entry: dn.parse('cn=foo, o=test'), + attributes: [new Attribute({type: 'cn', vals: ['foo', 'bar']}), + new Attribute({type: 'objectclass', vals: ['person']})] + }); + + t.ok(req); + + var obj = req.toObject(); + t.ok(obj); + + t.ok(obj.dn); + t.equal(obj.dn, 'cn=foo, o=test'); + t.ok(obj.attributes); + t.ok(obj.attributes.cn); + t.ok(Array.isArray(obj.attributes.cn)); + t.equal(obj.attributes.cn.length, 2); + t.equal(obj.attributes.cn[0], 'foo'); + t.equal(obj.attributes.cn[1], 'bar'); + t.ok(obj.attributes.objectclass); + t.ok(Array.isArray(obj.attributes.objectclass)); + t.equal(obj.attributes.objectclass.length, 1); + t.equal(obj.attributes.objectclass[0], 'person'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/add_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/add_response.test.js new file mode 100644 index 0000000..4b362e1 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/add_response.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var AddResponse; + + +///--- Tests + +test('load library', function (t) { + AddResponse = require('../../lib/index').AddResponse; + t.ok(AddResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new AddResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new AddResponse({ + messageID: 123, + status: 0 + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + + var res = new AddResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new AddResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x69); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/bind_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/bind_request.test.js new file mode 100644 index 0000000..0acbb6e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/bind_request.test.js @@ -0,0 +1,82 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var BindRequest; +var dn; + +///--- Tests + +test('load library', function (t) { + BindRequest = require('../../lib/index').BindRequest; + dn = require('../../lib/index').dn; + t.ok(BindRequest); + t.ok(dn); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new BindRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new BindRequest({ + version: 3, + name: dn.parse('cn=root'), + credentials: 'secret' + }); + t.ok(req); + t.equal(req.version, 3); + t.equal(req.name.toString(), 'cn=root'); + t.equal(req.credentials, 'secret'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeInt(3); + ber.writeString('cn=root'); + ber.writeString('secret', 0x80); + + var req = new BindRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.equal(req.version, 3); + t.equal(req.dn.toString(), 'cn=root'); + t.ok(req.name.constructor); + t.equal(req.name.constructor.name, 'DN'); + t.equal(req.credentials, 'secret'); + t.end(); +}); + + +test('toBer', function (t) { + var req = new BindRequest({ + messageID: 123, + version: 3, + name: dn.parse('cn=root'), + credentials: 'secret' + }); + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x60); + t.equal(ber.readInt(), 0x03); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(0x80), 'secret'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/bind_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/bind_response.test.js new file mode 100644 index 0000000..fd8caf7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/bind_response.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var BindResponse; + + +///--- Tests + +test('load library', function (t) { + BindResponse = require('../../lib/index').BindResponse; + t.ok(BindResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new BindResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new BindResponse({ + messageID: 123, + status: 0 + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + + var res = new BindResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new BindResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x61); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/compare_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/compare_request.test.js new file mode 100644 index 0000000..716d008 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/compare_request.test.js @@ -0,0 +1,87 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var CompareRequest; +var dn; + +///--- Tests + +test('load library', function (t) { + CompareRequest = require('../../lib/index').CompareRequest; + dn = require('../../lib/index').dn; + t.ok(CompareRequest); + t.ok(dn); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new CompareRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new CompareRequest({ + entry: dn.parse('cn=foo, o=test'), + attribute: 'sn', + value: 'testy' + }); + t.ok(req); + t.equal(req.dn, 'cn=foo, o=test'); + t.equal(req.attribute, 'sn'); + t.equal(req.value, 'testy'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeString('cn=foo,o=test'); + + ber.startSequence(); + ber.writeString('sn'); + ber.writeString('testy'); + ber.endSequence(); + + + var req = new CompareRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.equal(req.dn, 'cn=foo, o=test'); + t.equal(req.attribute, 'sn'); + t.equal(req.value, 'testy'); + t.end(); +}); + + +test('toBer', function (t) { + var req = new CompareRequest({ + messageID: 123, + entry: dn.parse('cn=foo, o=test'), + attribute: 'sn', + value: 'testy' + }); + + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x6e); + t.equal(ber.readString(), 'cn=foo, o=test'); + t.ok(ber.readSequence()); + + t.equal(ber.readString(), 'sn'); + t.equal(ber.readString(), 'testy'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/compare_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/compare_response.test.js new file mode 100644 index 0000000..fd4594c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/compare_response.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var CompareResponse; + + +///--- Tests + +test('load library', function (t) { + CompareResponse = require('../../lib/index').CompareResponse; + t.ok(CompareResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new CompareResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new CompareResponse({ + messageID: 123, + status: 0 + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + + var res = new CompareResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new CompareResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x6f); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/del_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/del_request.test.js new file mode 100644 index 0000000..9b7d527 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/del_request.test.js @@ -0,0 +1,72 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + + +var asn1 = require('asn1'); +var Logger = require('bunyan'); +var test = require('tap').test; + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var DeleteRequest; +var dn; + + +///--- Tests + +test('load library', function (t) { + DeleteRequest = require('../../lib/index').DeleteRequest; + dn = require('../../lib/index').dn; + t.ok(DeleteRequest); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new DeleteRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new DeleteRequest({ + entry: dn.parse('cn=test') + }); + t.ok(req); + t.equal(req.dn.toString(), 'cn=test'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeString('cn=test', 0x4a); + + var req = new DeleteRequest({ + log: new Logger({name: 'del_request.test.js'}) + }); + var reader = new BerReader(ber.buffer); + reader.readSequence(0x4a); + t.ok(req.parse(reader, reader.length)); + t.equal(req.dn.toString(), 'cn=test'); + t.end(); +}); + + +test('toBer', function (t) { + var req = new DeleteRequest({ + messageID: 123, + entry: dn.parse('cn=test') + }); + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readString(0x4a), 'cn=test'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/del_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/del_response.test.js new file mode 100644 index 0000000..ebecb5c --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/del_response.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var DeleteResponse; + + +///--- Tests + +test('load library', function (t) { + DeleteResponse = require('../../lib/index').DeleteResponse; + t.ok(DeleteResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new DeleteResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new DeleteResponse({ + messageID: 123, + status: 0 + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + + var res = new DeleteResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new DeleteResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x6b); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/ext_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/ext_request.test.js new file mode 100644 index 0000000..656dbf3 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/ext_request.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var ExtendedRequest; +var dn; + +///--- Tests + +test('load library', function (t) { + ExtendedRequest = require('../../lib/index').ExtendedRequest; + dn = require('../../lib/index').dn; + t.ok(ExtendedRequest); + t.ok(dn); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new ExtendedRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new ExtendedRequest({ + requestName: '1.2.3.4', + requestValue: 'test' + }); + t.ok(req); + t.equal(req.requestName, '1.2.3.4'); + t.equal(req.requestValue, 'test'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeString('1.2.3.4', 0x80); + ber.writeString('test', 0x81); + + + var req = new ExtendedRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.equal(req.requestName, '1.2.3.4'); + t.equal(req.requestValue, 'test'); + t.end(); +}); + + +test('toBer', function (t) { + var req = new ExtendedRequest({ + messageID: 123, + requestName: '1.2.3.4', + requestValue: 'test' + }); + + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x77); + t.equal(ber.readString(0x80), '1.2.3.4'); + t.equal(ber.readString(0x81), 'test'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/ext_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/ext_response.test.js new file mode 100644 index 0000000..b31559a --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/ext_response.test.js @@ -0,0 +1,88 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var ExtendedResponse; + + +///--- Tests + +test('load library', function (t) { + ExtendedResponse = require('../../lib/index').ExtendedResponse; + t.ok(ExtendedResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new ExtendedResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new ExtendedResponse({ + messageID: 123, + status: 0, + responseName: '1.2.3.4', + responseValue: 'test' + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.equal(res.responseName, '1.2.3.4'); + t.equal(res.responseValue, 'test'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + ber.writeString('1.2.3.4', 0x8a); + ber.writeString('test', 0x8b); + + var res = new ExtendedResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.equal(res.responseName, '1.2.3.4'); + t.equal(res.responseValue, 'test'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new ExtendedResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo', + responseName: '1.2.3.4', + responseValue: 'test' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x78); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + t.equal(ber.readString(0x8a), '1.2.3.4'); + t.equal(ber.readString(0x8b), 'test'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/moddn_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/moddn_request.test.js new file mode 100644 index 0000000..375acff --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/moddn_request.test.js @@ -0,0 +1,82 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var ModifyDNRequest; +var dn; + +///--- Tests + +test('load library', function (t) { + ModifyDNRequest = require('../../lib/index').ModifyDNRequest; + dn = require('../../lib/index').dn; + t.ok(ModifyDNRequest); + t.ok(dn); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new ModifyDNRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new ModifyDNRequest({ + entry: dn.parse('cn=foo, o=test'), + newRdn: dn.parse('cn=foo2'), + deleteOldRdn: true + }); + t.ok(req); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.newRdn.toString(), 'cn=foo2'); + t.equal(req.deleteOldRdn, true); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeString('cn=foo,o=test'); + ber.writeString('cn=foo2'); + ber.writeBoolean(true); + + var req = new ModifyDNRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.newRdn.toString(), 'cn=foo2'); + t.equal(req.deleteOldRdn, true); + + t.end(); +}); + + +test('toBer', function (t) { + var req = new ModifyDNRequest({ + messageID: 123, + entry: dn.parse('cn=foo, o=test'), + newRdn: dn.parse('cn=foo2'), + deleteOldRdn: true + }); + + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x6c); + t.equal(ber.readString(), 'cn=foo, o=test'); + t.equal(ber.readString(), 'cn=foo2'); + t.equal(ber.readBoolean(), true); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/moddn_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/moddn_response.test.js new file mode 100644 index 0000000..e257c8d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/moddn_response.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var ModifyDNResponse; + + +///--- Tests + +test('load library', function (t) { + ModifyDNResponse = require('../../lib/index').ModifyDNResponse; + t.ok(ModifyDNResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new ModifyDNResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new ModifyDNResponse({ + messageID: 123, + status: 0 + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + + var res = new ModifyDNResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new ModifyDNResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x6d); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/modify_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/modify_request.test.js new file mode 100644 index 0000000..8b62727 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/modify_request.test.js @@ -0,0 +1,114 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var ModifyRequest; +var Attribute; +var Change; +var dn; + +///--- Tests + +test('load library', function (t) { + ModifyRequest = require('../../lib/index').ModifyRequest; + Attribute = require('../../lib/index').Attribute; + Change = require('../../lib/index').Change; + dn = require('../../lib/index').dn; + t.ok(ModifyRequest); + t.ok(Attribute); + t.ok(Change); + t.ok(dn); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new ModifyRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new ModifyRequest({ + object: dn.parse('cn=foo, o=test'), + changes: [new Change({ + operation: 'Replace', + modification: new Attribute({type: 'objectclass', vals: ['person']}) + })] + }); + t.ok(req); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.changes.length, 1); + t.equal(req.changes[0].operation, 'replace'); + t.equal(req.changes[0].modification.type, 'objectclass'); + t.equal(req.changes[0].modification.vals[0], 'person'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeString('cn=foo,o=test'); + ber.startSequence(); + + ber.startSequence(); + ber.writeEnumeration(0x02); + + ber.startSequence(); + ber.writeString('objectclass'); + ber.startSequence(0x31); + ber.writeString('person'); + ber.endSequence(); + ber.endSequence(); + + ber.endSequence(); + + ber.endSequence(); + + var req = new ModifyRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.changes.length, 1); + t.equal(req.changes[0].operation, 'replace'); + t.equal(req.changes[0].modification.type, 'objectclass'); + t.equal(req.changes[0].modification.vals[0], 'person'); + t.end(); +}); + + +test('toBer', function (t) { + var req = new ModifyRequest({ + messageID: 123, + object: dn.parse('cn=foo, o=test'), + changes: [new Change({ + operation: 'Replace', + modification: new Attribute({type: 'objectclass', vals: ['person']}) + })] + }); + + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x66); + t.equal(ber.readString(), 'cn=foo, o=test'); + t.ok(ber.readSequence()); + t.ok(ber.readSequence()); + t.equal(ber.readEnumeration(), 0x02); + + t.ok(ber.readSequence()); + t.equal(ber.readString(), 'objectclass'); + t.equal(ber.readSequence(), 0x31); + t.equal(ber.readString(), 'person'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/modify_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/modify_response.test.js new file mode 100644 index 0000000..f63993e --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/modify_response.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var ModifyResponse; + + +///--- Tests + +test('load library', function (t) { + ModifyResponse = require('../../lib/index').ModifyResponse; + t.ok(ModifyResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new ModifyResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new ModifyResponse({ + messageID: 123, + status: 0 + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + + var res = new ModifyResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new ModifyResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x67); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_entry.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_entry.test.js new file mode 100644 index 0000000..7b41d53 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_entry.test.js @@ -0,0 +1,116 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var SearchEntry; +var Attribute; +var dn; + +///--- Tests + +test('load library', function (t) { + SearchEntry = require('../../lib/index').SearchEntry; + Attribute = require('../../lib/index').Attribute; + dn = require('../../lib/index').dn; + t.ok(SearchEntry); + t.ok(dn); + t.ok(Attribute); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new SearchEntry()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new SearchEntry({ + messageID: 123, + objectName: dn.parse('cn=foo, o=test'), + attributes: [new Attribute({type: 'cn', vals: ['foo']}), + new Attribute({type: 'objectclass', vals: ['person']})] + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.dn.toString(), 'cn=foo, o=test'); + t.equal(res.attributes.length, 2); + t.equal(res.attributes[0].type, 'cn'); + t.equal(res.attributes[0].vals[0], 'foo'); + t.equal(res.attributes[1].type, 'objectclass'); + t.equal(res.attributes[1].vals[0], 'person'); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeString('cn=foo, o=test'); + + ber.startSequence(); + + ber.startSequence(); + ber.writeString('cn'); + ber.startSequence(0x31); + ber.writeString('foo'); + ber.endSequence(); + ber.endSequence(); + + ber.startSequence(); + ber.writeString('objectclass'); + ber.startSequence(0x31); + ber.writeString('person'); + ber.endSequence(); + ber.endSequence(); + + ber.endSequence(); + + var res = new SearchEntry(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.dn, 'cn=foo, o=test'); + t.equal(res.attributes.length, 2); + t.equal(res.attributes[0].type, 'cn'); + t.equal(res.attributes[0].vals[0], 'foo'); + t.equal(res.attributes[1].type, 'objectclass'); + t.equal(res.attributes[1].vals[0], 'person'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new SearchEntry({ + messageID: 123, + objectName: dn.parse('cn=foo, o=test'), + attributes: [new Attribute({type: 'cn', vals: ['foo']}), + new Attribute({type: 'objectclass', vals: ['person']})] + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x64); + t.equal(ber.readString(), 'cn=foo, o=test'); + t.ok(ber.readSequence()); + + t.ok(ber.readSequence()); + t.equal(ber.readString(), 'cn'); + t.equal(ber.readSequence(), 0x31); + t.equal(ber.readString(), 'foo'); + + t.ok(ber.readSequence()); + t.equal(ber.readString(), 'objectclass'); + t.equal(ber.readSequence(), 0x31); + t.equal(ber.readString(), 'person'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_request.test.js new file mode 100644 index 0000000..6975731 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_request.test.js @@ -0,0 +1,120 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var SearchRequest; +var EqualityFilter; +var dn; + +///--- Tests + +test('load library', function (t) { + SearchRequest = require('../../lib/index').SearchRequest; + EqualityFilter = require('../../lib/index').EqualityFilter; + dn = require('../../lib/index').dn; + t.ok(SearchRequest); + t.ok(EqualityFilter); + t.ok(dn); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new SearchRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new SearchRequest({ + baseObject: dn.parse('cn=foo, o=test'), + filter: new EqualityFilter({ + attribute: 'email', + value: 'foo@bar.com' + }), + attributes: ['cn', 'sn'] + }); + t.ok(req); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.filter.toString(), '(email=foo@bar.com)'); + t.equal(req.attributes.length, 2); + t.equal(req.attributes[0], 'cn'); + t.equal(req.attributes[1], 'sn'); + t.end(); +}); + + +test('parse', function (t) { + var f = new EqualityFilter({ + attribute: 'email', + value: 'foo@bar.com' + }); + + var ber = new BerWriter(); + ber.writeString('cn=foo,o=test'); + ber.writeEnumeration(0); + ber.writeEnumeration(0); + ber.writeInt(1); + ber.writeInt(2); + ber.writeBoolean(false); + ber = f.toBer(ber); + + var req = new SearchRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.equal(req.dn.toString(), 'cn=foo, o=test'); + t.equal(req.scope, 'base'); + t.equal(req.derefAliases, 0); + t.equal(req.sizeLimit, 1); + t.equal(req.timeLimit, 2); + t.equal(req.typesOnly, false); + t.equal(req.filter.toString(), '(email=foo@bar.com)'); + t.equal(req.attributes.length, 0); + t.end(); +}); + + +test('toBer', function (t) { + var req = new SearchRequest({ + messageID: 123, + baseObject: dn.parse('cn=foo, o=test'), + scope: 1, + derefAliases: 2, + sizeLimit: 10, + timeLimit: 20, + typesOnly: true, + filter: new EqualityFilter({ + attribute: 'email', + value: 'foo@bar.com' + }), + attributes: ['cn', 'sn'] + }); + + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x63); + t.equal(ber.readString(), 'cn=foo, o=test'); + t.equal(ber.readEnumeration(), 1); + t.equal(ber.readEnumeration(), 2); + t.equal(ber.readInt(), 10); + t.equal(ber.readInt(), 20); + t.ok(ber.readBoolean()); + t.equal(ber.readSequence(), 0xa3); + t.equal(ber.readString(), 'email'); + t.equal(ber.readString(), 'foo@bar.com'); + t.ok(ber.readSequence()); + t.equal(ber.readString(), 'cn'); + t.equal(ber.readString(), 'sn'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_response.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_response.test.js new file mode 100644 index 0000000..83ffeb7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/search_response.test.js @@ -0,0 +1,76 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var SearchResponse; + + +///--- Tests + +test('load library', function (t) { + SearchResponse = require('../../lib/index').SearchResponse; + t.ok(SearchResponse); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new SearchResponse()); + t.end(); +}); + + +test('new with args', function (t) { + var res = new SearchResponse({ + messageID: 123, + status: 0 + }); + t.ok(res); + t.equal(res.messageID, 123); + t.equal(res.status, 0); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + ber.writeEnumeration(0); + ber.writeString('cn=root'); + ber.writeString('foo'); + + var res = new SearchResponse(); + t.ok(res._parse(new BerReader(ber.buffer))); + t.equal(res.status, 0); + t.equal(res.matchedDN, 'cn=root'); + t.equal(res.errorMessage, 'foo'); + t.end(); +}); + + +test('toBer', function (t) { + var res = new SearchResponse({ + messageID: 123, + status: 3, + matchedDN: 'cn=root', + errorMessage: 'foo' + }); + t.ok(res); + + var ber = new BerReader(res.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.equal(ber.readSequence(), 0x65); + t.equal(ber.readEnumeration(), 3); + t.equal(ber.readString(), 'cn=root'); + t.equal(ber.readString(), 'foo'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/unbind_request.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/unbind_request.test.js new file mode 100644 index 0000000..058a124 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/messages/unbind_request.test.js @@ -0,0 +1,57 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + +var asn1 = require('asn1'); + + +///--- Globals + +var BerReader = asn1.BerReader; +var BerWriter = asn1.BerWriter; +var UnbindRequest; + + +///--- Tests + +test('load library', function (t) { + UnbindRequest = require('../../lib/index').UnbindRequest; + t.ok(UnbindRequest); + t.end(); +}); + + +test('new no args', function (t) { + t.ok(new UnbindRequest()); + t.end(); +}); + + +test('new with args', function (t) { + var req = new UnbindRequest({}); + t.ok(req); + t.end(); +}); + + +test('parse', function (t) { + var ber = new BerWriter(); + + var req = new UnbindRequest(); + t.ok(req._parse(new BerReader(ber.buffer))); + t.end(); +}); + + +test('toBer', function (t) { + var req = new UnbindRequest({ + messageID: 123 + }); + t.ok(req); + + var ber = new BerReader(req.toBer()); + t.ok(ber); + t.equal(ber.readSequence(), 0x30); + t.equal(ber.readInt(), 123); + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/test/url.test.js b/dist/node_modules/ldapauth/node_modules/ldapjs/test/url.test.js new file mode 100644 index 0000000..47882c7 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/test/url.test.js @@ -0,0 +1,74 @@ +// Copyright 2011 Mark Cavage, Inc. All rights reserved. + +var test = require('tap').test; + + + +///--- Globals + +var url; +var parseURL; + + + +///--- Tests + +test('load library', function (t) { + parseURL = require('../lib/index').parseURL; + t.ok(parseURL); + + t.end(); +}); + + +test('parse empty', function (t) { + var u = parseURL('ldap:///'); + t.equal(u.hostname, 'localhost'); + t.equal(u.port, 389); + t.ok(!u.DN); + t.ok(!u.attributes); + t.equal(u.secure, false); + t.end(); +}); + + +test('parse hostname', function (t) { + var u = parseURL('ldap://example.com/'); + t.equal(u.hostname, 'example.com'); + t.equal(u.port, 389); + t.ok(!u.DN); + t.ok(!u.attributes); + t.equal(u.secure, false); + t.end(); +}); + + +test('parse host and port', function (t) { + var u = parseURL('ldap://example.com:1389/'); + t.equal(u.hostname, 'example.com'); + t.equal(u.port, 1389); + t.ok(!u.DN); + t.ok(!u.attributes); + t.equal(u.secure, false); + t.end(); +}); + + +test('parse full', function (t) { + + var u = parseURL('ldaps://ldap.example.com:1389/dc=example%20,dc=com' + + '?cn,sn?sub?(cn=Babs%20Jensen)'); + + t.equal(u.secure, true); + t.equal(u.hostname, 'ldap.example.com'); + t.equal(u.port, 1389); + t.equal(u.DN, 'dc=example ,dc=com'); + t.ok(u.attributes); + t.equal(u.attributes.length, 2); + t.equal(u.attributes[0], 'cn'); + t.equal(u.attributes[1], 'sn'); + t.equal(u.scope, 'sub'); + t.equal(u.filter.toString(), '(cn=Babs Jensen)'); + + t.end(); +}); diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/tools/jsl.node.conf b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/jsl.node.conf new file mode 100644 index 0000000..74b817d --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/jsl.node.conf @@ -0,0 +1,138 @@ +# +# Configuration File for JavaScript Lint +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# ++ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent ++ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity ++ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement ++anon_no_return_value # anonymous function does not always return value ++assign_to_function_call # assignment to a function call +-block_without_braces # block statement without curly braces ++comma_separated_stmts # multiple statements separated by commas (use semicolons?) ++comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) ++default_not_at_end # the default case is not at the end of the switch statement ++dup_option_explicit # duplicate "option explicit" control comment ++duplicate_case_in_switch # duplicate case in switch statement ++duplicate_formal # duplicate formal argument {name} ++empty_statement # empty statement or extra semicolon ++identifier_hides_another # identifer {name} hides an identifier in a parent scope +-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement ++incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. ++invalid_fallthru # unexpected "fallthru" control comment ++invalid_pass # unexpected "pass" control comment ++jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax ++leading_decimal_point # leading decimal point may indicate a number or an object member ++legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax ++meaningless_block # meaningless block; curly braces have no impact ++mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence ++misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma ++missing_break # missing break statement ++missing_break_for_last_case # missing break statement for last case in switch ++missing_default_case # missing default case in switch statement ++missing_option_explicit # the "option explicit" control comment is missing ++missing_semicolon # missing semicolon ++missing_semicolon_for_lambda # missing semicolon for lambda assignment ++multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs ++nested_comment # nested comment ++no_return_value # function {name} does not always return a value ++octal_number # leading zeros make an octal number ++parseint_missing_radix # parseInt missing radix parameter ++partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag ++redeclared_var # redeclaration of {name} ++trailing_comma_in_array # extra comma is not recommended in array initializers ++trailing_decimal_point # trailing decimal point may indicate a number or an object member ++undeclared_identifier # undeclared identifier: {name} ++unreachable_code # unreachable code +-unreferenced_argument # argument declared but never referenced: {name} +-unreferenced_function # function is declared but never referenced: {name} ++unreferenced_variable # variable is declared but never referenced: {name} ++unsupported_version # JavaScript {version} is not supported ++use_of_label # use of label ++useless_assign # useless assignment ++useless_comparison # useless comparison; comparing identical expressions +-useless_quotes # the quotation marks are unnecessary ++useless_void # use of the void type may be unnecessary (void is always undefined) ++var_hides_arg # variable {name} hides argument ++want_assign_or_call # expected an assignment or function call ++with_statement # with statement hides undeclared variables; use temporary variable instead +-identifier_hides_another + +### Output format +# Customize the format of the error message. +# __FILE__ indicates current file path +# __FILENAME__ indicates current file name +# __LINE__ indicates current line +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message +# +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# +-legacy_control_comments + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" +-always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: ++define __dirname ++define clearInterval ++define clearTimeout ++define console ++define exports ++define global ++define module ++define process ++define require ++define setInterval ++define setTimeout ++define Buffer ++define JSON ++define Math + +### JavaScript Version +# To change the default JavaScript version: +#+default-type text/javascript;version=1.5 +#+default-type text/javascript;e4x=1 + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# + diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/tools/jsstyle.conf b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/jsstyle.conf new file mode 100644 index 0000000..2f11f48 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/jsstyle.conf @@ -0,0 +1,4 @@ +indent=2 +doxygen +unparenthesized-return=0 +blank-after-start-comment=0 \ No newline at end of file diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.defs b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.defs new file mode 100644 index 0000000..50a13c5 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.defs @@ -0,0 +1,43 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.defs: common defines. +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This makefile defines some useful defines. Include it at the top of +# your Makefile. +# +# Definitions in this Makefile: +# +# TOP The absolute path to the project directory. The top dir. +# BRANCH The current git branch. +# TIMESTAMP The timestamp for the build. This can be set via +# the TIMESTAMP envvar (used by MG-based builds). +# STAMP A build stamp to use in built package names. +# + +TOP := $(shell pwd) + +# +# Mountain Gorilla-spec'd versioning. +# See "Package Versioning" in MG's README.md: +# +# +# Need GNU awk for multi-char arg to "-F". +_AWK := $(shell (which gawk >/dev/null && echo gawk) \ + || (which nawk >/dev/null && echo nawk) \ + || echo awk) +BRANCH := $(shell git symbolic-ref HEAD | $(_AWK) -F/ '{print $$3}') +ifeq ($(TIMESTAMP),) + TIMESTAMP := $(shell date -u "+%Y%m%dT%H%M%SZ") +endif +_GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $$NF}') +STAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE) + +# node-gyp will print build info useful for debugging with V=1 +export V=1 diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.deps b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.deps new file mode 100644 index 0000000..9317daf --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.deps @@ -0,0 +1,44 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.deps: Makefile for including common tools as dependencies +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This file is separate from Makefile.targ so that teams can choose +# independently whether to use the common targets in Makefile.targ and the +# common tools here. +# + +# +# javascriptlint +# +JSL_EXEC ?= deps/javascriptlint/build/install/jsl +JSL ?= $(JSL_EXEC) + +$(JSL_EXEC): | deps/javascriptlint/.git + cd deps/javascriptlint && make install + +distclean:: + if [[ -f deps/javascriptlint/Makefile ]]; then \ + cd deps/javascriptlint && make clean; \ + fi + +# +# jsstyle +# +JSSTYLE_EXEC ?= deps/jsstyle/jsstyle +JSSTYLE ?= $(JSSTYLE_EXEC) + +$(JSSTYLE_EXEC): | deps/jsstyle/.git + +# +# restdown +# +RESTDOWN_EXEC ?= deps/restdown/bin/restdown +RESTDOWN ?= python2.6 $(RESTDOWN_EXEC) +$(RESTDOWN_EXEC): | deps/restdown/.git diff --git a/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.targ b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.targ new file mode 100644 index 0000000..22c9bef --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/ldapjs/tools/mk/Makefile.targ @@ -0,0 +1,291 @@ +# -*- mode: makefile -*- +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# +# Makefile.targ: common targets. +# +# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped +# into other repos as-is without requiring any modifications. If you find +# yourself changing this file, you should instead update the original copy in +# eng.git and then update your repo to use the new version. +# +# This Makefile defines several useful targets and rules. You can use it by +# including it from a Makefile that specifies some of the variables below. +# +# Targets defined in this Makefile: +# +# check Checks JavaScript files for lint and style +# Checks bash scripts for syntax +# Checks SMF manifests for validity against the SMF DTD +# +# clean Removes built files +# +# docs Builds restdown documentation in docs/ +# +# prepush Depends on "check" and "test" +# +# test Does nothing (you should override this) +# +# xref Generates cscope (source cross-reference index) +# +# For details on what these targets are supposed to do, see the Joyent +# Engineering Guide. +# +# To make use of these targets, you'll need to set some of these variables. Any +# variables left unset will simply not be used. +# +# BASH_FILES Bash scripts to check for syntax +# (paths relative to top-level Makefile) +# +# CLEAN_FILES Files to remove as part of the "clean" target. Note +# that files generated by targets in this Makefile are +# automatically included in CLEAN_FILES. These include +# restdown-generated HTML and JSON files. +# +# DOC_FILES Restdown (documentation source) files. These are +# assumed to be contained in "docs/", and must NOT +# contain the "docs/" prefix. +# +# JSL_CONF_NODE Specify JavaScriptLint configuration files +# JSL_CONF_WEB (paths relative to top-level Makefile) +# +# Node.js and Web configuration files are separate +# because you'll usually want different global variable +# configurations. If no file is specified, none is given +# to jsl, which causes it to use a default configuration, +# which probably isn't what you want. +# +# JSL_FILES_NODE JavaScript files to check with Node config file. +# JSL_FILES_WEB JavaScript files to check with Web config file. +# +# You can also override these variables: +# +# BASH Path to bash (default: bash) +# +# CSCOPE_DIRS Directories to search for source files for the cscope +# index. (default: ".") +# +# JSL Path to JavaScriptLint (default: "jsl") +# +# JSL_FLAGS_NODE Additional flags to pass through to JSL +# JSL_FLAGS_WEB +# JSL_FLAGS +# +# JSSTYLE Path to jsstyle (default: jsstyle) +# +# JSSTYLE_FLAGS Additional flags to pass through to jsstyle +# + +# +# Defaults for the various tools we use. +# +BASH ?= bash +BASHSTYLE ?= tools/bashstyle +CP ?= cp +CSCOPE ?= cscope +CSCOPE_DIRS ?= . +JSL ?= jsl +JSSTYLE ?= jsstyle +MKDIR ?= mkdir -p +MV ?= mv +RESTDOWN_FLAGS ?= +RMTREE ?= rm -rf +JSL_FLAGS ?= --nologo --nosummary + +ifeq ($(shell uname -s),SunOS) + TAR ?= gtar +else + TAR ?= tar +endif + + +# +# Defaults for other fixed values. +# +BUILD = build +DISTCLEAN_FILES += $(BUILD) +DOC_BUILD = $(BUILD)/docs/public + +# +# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}. +# +ifneq ($(origin JSL_CONF_NODE), undefined) + JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE) +endif + +ifneq ($(origin JSL_CONF_WEB), undefined) + JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB) +endif + +# +# Targets. For descriptions on what these are supposed to do, see the +# Joyent Engineering Guide. +# + +# +# Instruct make to keep around temporary files. We have rules below that +# automatically update git submodules as needed, but they employ a deps/*/.git +# temporary file. Without this directive, make tries to remove these .git +# directories after the build has completed. +# +.SECONDARY: $($(wildcard deps/*):%=%/.git) + +# +# This rule enables other rules that use files from a git submodule to have +# those files depend on deps/module/.git and have "make" automatically check +# out the submodule as needed. +# +deps/%/.git: + git submodule update --init deps/$* + +# +# These recipes make heavy use of dynamically-created phony targets. The parent +# Makefile defines a list of input files like BASH_FILES. We then say that each +# of these files depends on a fake target called filename.bashchk, and then we +# define a pattern rule for those targets that runs bash in check-syntax-only +# mode. This mechanism has the nice properties that if you specify zero files, +# the rule becomes a noop (unlike a single rule to check all bash files, which +# would invoke bash with zero files), and you can check individual files from +# the command line with "make filename.bashchk". +# +.PHONY: check-bash +check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle) + +%.bashchk: % + $(BASH) -n $^ + +%.bashstyle: % + $(BASHSTYLE) $^ + +# +# The above approach can be slow when there are many files to check because it +# requires that "make" invoke the check tool once for each file, rather than +# passing in several files at once. For the JavaScript check targets, we define +# a variable for the target itself *only if* the list of input files is +# non-empty. This avoids invoking the tool if there are no files to check. +# +JSL_NODE_TARGET = $(if $(JSL_FILES_NODE), check-jsl-node) +.PHONY: check-jsl-node +check-jsl-node: $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $(JSL_FILES_NODE) + +JSL_WEB_TARGET = $(if $(JSL_FILES_WEB), check-jsl-web) +.PHONY: check-jsl-web +check-jsl-web: $(JSL_EXEC) + $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $(JSL_FILES_WEB) + +.PHONY: check-jsl +check-jsl: $(JSL_NODE_TARGET) $(JSL_WEB_TARGET) + +JSSTYLE_TARGET = $(if $(JSSTYLE_FILES), check-jsstyle) +.PHONY: check-jsstyle +check-jsstyle: $(JSSTYLE_EXEC) + $(JSSTYLE) $(JSSTYLE_FLAGS) $(JSSTYLE_FILES) + +.PHONY: check +check: check-jsl $(JSSTYLE_TARGET) check-bash + @echo check ok + +.PHONY: clean +clean:: + -$(RMTREE) $(CLEAN_FILES) + +.PHONY: distclean +distclean:: clean + -$(RMTREE) $(DISTCLEAN_FILES) + +CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out +CLEAN_FILES += $(CSCOPE_FILES) + +.PHONY: xref +xref: cscope.files + $(CSCOPE) -bqR + +.PHONY: cscope.files +cscope.files: + find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \ + -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@ + +# +# The "docs" target is complicated because we do several things here: +# +# (1) Use restdown to build HTML and JSON files from each of DOC_FILES. +# +# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which +# functions as a complete copy of the documentation that could be +# mirrored or served over HTTP. +# +# (3) Then copy any directories and media from docs/media into +# $(DOC_BUILD)/media. This allows projects to include their own media, +# including files that will override same-named files provided by +# restdown. +# +# Step (3) is the surprisingly complex part: in order to do this, we need to +# identify the subdirectories in docs/media, recreate them in +# $(DOC_BUILD)/media, then do the same with the files. +# +DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$") +DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%) +DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%) + +DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null) +DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%) +DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%) + +# +# Like the other targets, "docs" just depends on the final files we want to +# create in $(DOC_BUILD), leveraging other targets and recipes to define how +# to get there. +# +.PHONY: docs +docs: \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html) \ + $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json) \ + $(DOC_MEDIA_FILES_BUILD) + +# +# We keep the intermediate files so that the next build can see whether the +# files in DOC_BUILD are up to date. +# +.PRECIOUS: \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%json) + +# +# We do clean those intermediate files, as well as all of DOC_BUILD. +# +CLEAN_FILES += \ + $(DOC_BUILD) \ + $(DOC_FILES:%.restdown=docs/%.html) \ + $(DOC_FILES:%.restdown=docs/%.json) + +# +# Before installing the files, we must make sure the directories exist. The | +# syntax tells make that the dependency need only exist, not be up to date. +# Otherwise, it might try to rebuild spuriously because the directory itself +# appears out of date. +# +$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD) + +$(DOC_BUILD)/%: docs/% | $(DOC_BUILD) + $(CP) $< $@ + +docs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC) + $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $< + +$(DOC_BUILD): + $(MKDIR) $@ + +$(DOC_MEDIA_DIRS_BUILD): + $(MKDIR) $@ + +# +# The default "test" target does nothing. This should usually be overridden by +# the parent Makefile. It's included here so we can define "prepush" without +# requiring the repo to define "test". +# +.PHONY: test +test: + +.PHONY: prepush +prepush: check test diff --git a/dist/node_modules/ldapauth/node_modules/lru-cache/.npmignore b/dist/node_modules/ldapauth/node_modules/lru-cache/.npmignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/lru-cache/.npmignore @@ -0,0 +1 @@ +/node_modules diff --git a/dist/node_modules/ldapauth/node_modules/lru-cache/AUTHORS b/dist/node_modules/ldapauth/node_modules/lru-cache/AUTHORS new file mode 100644 index 0000000..016d7fb --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/lru-cache/AUTHORS @@ -0,0 +1,8 @@ +# Authors, sorted by whether or not they are me +Isaac Z. Schlueter +Carlos Brito Lage +Marko Mikulicic +Trent Mick +Kevin O'Hara +Marco Rogers +Jesse Dailey diff --git a/dist/node_modules/ldapauth/node_modules/lru-cache/LICENSE b/dist/node_modules/ldapauth/node_modules/lru-cache/LICENSE new file mode 100644 index 0000000..05a4010 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/lru-cache/LICENSE @@ -0,0 +1,23 @@ +Copyright 2009, 2010, 2011 Isaac Z. Schlueter. +All rights reserved. + +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/dist/node_modules/ldapauth/node_modules/lru-cache/README.md b/dist/node_modules/ldapauth/node_modules/lru-cache/README.md new file mode 100644 index 0000000..8ea0dd9 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/lru-cache/README.md @@ -0,0 +1,46 @@ +# lru cache + +A cache object that deletes the least-recently-used items. + +## Usage: + +```javascript +var LRU = require("lru-cache") + , options = { max: 500 + , length: function (n) { return n * 2 } + , dispose: function (key, n) { n.close() } + , maxAge: 1000 * 60 * 60 } + , cache = LRU(options) + , otherCache = LRU(50) // sets just the max size + +cache.set("key", "value") +cache.get("key") // "value" + +cache.reset() // empty the cache +``` + +If you put more stuff in it, then items will fall out. + +If you try to put an oversized thing in it, then it'll fall out right +away. + +## Options + +* `max` The maximum number of items. Not setting this is kind of + silly, since that's the whole purpose of this lib, but it defaults + to `Infinity`. +* `maxAge` Maximum age in ms. Items are not pro-actively pruned out + as they age, but if you try to get an item that is too old, it'll + drop it and return undefined instead of giving it to you. +* `length` Function that is used to calculate the length of stored + items. If you're storing strings or buffers, then you probably want + to do something like `function(n){return n.length}`. The default is + `function(n){return 1}`, which is fine if you want to store `n` + like-sized things. +* `dispose` Function that is called on items when they are dropped + from the cache. This can be handy if you want to close file + descriptors or do other cleanup tasks when items are no longer + accessible. Called with `key, value`. It's called *before* + actually removing the item from the internal cache, so if you want + to immediately put it back in, you'll have to do that in a + `nextTick` or `setTimeout` callback or it won't do anything. diff --git a/dist/node_modules/ldapauth/node_modules/lru-cache/lib/lru-cache.js b/dist/node_modules/ldapauth/node_modules/lru-cache/lib/lru-cache.js new file mode 100644 index 0000000..4bec3e2 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/lru-cache/lib/lru-cache.js @@ -0,0 +1,191 @@ +;(function () { // closure for web browsers + +if (typeof module === 'object' && module.exports) { + module.exports = LRUCache +} else { + // just set the global for non-node platforms. + this.LRUCache = LRUCache +} + +function hOP (obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key) +} + +function naiveLength () { return 1 } + +function LRUCache (options) { + if (!(this instanceof LRUCache)) { + return new LRUCache(options) + } + + var max + if (typeof options === 'number') { + max = options + options = { max: max } + } + max = options.max + + if (!options) options = {} + + var lengthCalculator = options.length || naiveLength + + if (typeof lengthCalculator !== "function") { + lengthCalculator = naiveLength + } + if (!max || !(typeof max === "number") || max <= 0 ) { + // a little bit silly. maybe this should throw? + max = Infinity + } + + var maxAge = options.maxAge || null + + var dispose = options.dispose + + var cache = Object.create(null) // hash of items by key + , lruList = Object.create(null) // list of items in order of use recency + , mru = 0 // most recently used + , length = 0 // number of items in the list + , itemCount = 0 + + + // resize the cache when the max changes. + Object.defineProperty(this, "max", + { set : function (mL) { + if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity + max = mL + // if it gets above double max, trim right away. + // otherwise, do it whenever it's convenient. + if (length > max) trim() + } + , get : function () { return max } + , enumerable : true + }) + + // resize the cache when the lengthCalculator changes. + Object.defineProperty(this, "lengthCalculator", + { set : function (lC) { + if (typeof lC !== "function") { + lengthCalculator = naiveLength + length = itemCount + for (var key in cache) { + cache[key].length = 1 + } + } else { + lengthCalculator = lC + length = 0 + for (var key in cache) { + cache[key].length = lengthCalculator(cache[key].value) + length += cache[key].length + } + } + + if (length > max) trim() + } + , get : function () { return lengthCalculator } + , enumerable : true + }) + + Object.defineProperty(this, "length", + { get : function () { return length } + , enumerable : true + }) + + + Object.defineProperty(this, "itemCount", + { get : function () { return itemCount } + , enumerable : true + }) + + this.reset = function () { + if (dispose) { + for (var k in cache) { + dispose(k, cache[k].value) + } + } + cache = {} + lruList = {} + mru = 0 + length = 0 + itemCount = 0 + } + + // Provided for debugging/dev purposes only. No promises whatsoever that + // this API stays stable. + this.dump = function () { + return cache + } + + this.set = function (key, value) { + if (hOP(cache, key)) { + // dispose of the old one before overwriting + if (dispose) dispose(key, cache[key].value) + if (maxAge) cache[key].now = Date.now() + cache[key].value = value + this.get(key) + return true + } + + var len = lengthCalculator(value) + var age = maxAge ? Date.now() : 0 + var hit = new Entry(key, value, mru++, len, age) + + // oversized objects fall out of cache automatically. + if (hit.length > max) { + if (dispose) dispose(key, value) + return false + } + + length += hit.length + lruList[hit.lu] = cache[key] = hit + itemCount ++ + + if (length > max) trim() + return true + } + + this.get = function (key) { + if (!hOP(cache, key)) return + var hit = cache[key] + if (maxAge && (Date.now() - hit.now > maxAge)) { + this.del(key) + return + } + delete lruList[hit.lu] + hit.lu = mru ++ + lruList[hit.lu] = hit + return hit.value + } + + this.del = function (key) { + if (!hOP(cache, key)) return + var hit = cache[key] + if (dispose) dispose(key, hit.value) + delete cache[key] + delete lruList[hit.lu] + length -= hit.length + itemCount -- + } + + function trim () { + if (length <= max) return + for (var k in lruList) { + if (length <= max) break; + var hit = lruList[k] + if (dispose) dispose(hit.key, hit.value) + length -= hit.length + delete cache[ hit.key ] + delete lruList[k] + } + } +} + +// classy, since V8 prefers predictable objects. +function Entry (key, value, mru, len, age) { + this.key = key + this.value = value + this.lu = mru + this.length = len + this.now = age +} + +})() diff --git a/dist/node_modules/ldapauth/node_modules/lru-cache/package.json b/dist/node_modules/ldapauth/node_modules/lru-cache/package.json new file mode 100644 index 0000000..50ca24f --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/lru-cache/package.json @@ -0,0 +1,18 @@ +{ + "name": "lru-cache", + "description": "A cache object that deletes the least-recently-used items.", + "version": "2.0.4", + "author": "Isaac Z. Schlueter ", + "scripts": { + "test": "tap test" + }, + "main": "lib/lru-cache.js", + "repository": "git://github.com/isaacs/node-lru-cache.git", + "devDependencies": { + "tap": "" + }, + "license": { + "type": "MIT", + "url": "http://github.com/isaacs/node-lru-cache/raw/master/LICENSE" + } +} diff --git a/dist/node_modules/ldapauth/node_modules/lru-cache/test/basic.js b/dist/node_modules/ldapauth/node_modules/lru-cache/test/basic.js new file mode 100644 index 0000000..4200272 --- /dev/null +++ b/dist/node_modules/ldapauth/node_modules/lru-cache/test/basic.js @@ -0,0 +1,267 @@ +var test = require("tap").test + , LRU = require("../") + +test("basic", function (t) { + var cache = new LRU({max: 10}) + cache.set("key", "value") + t.equal(cache.get("key"), "value") + t.equal(cache.get("nada"), undefined) + t.equal(cache.length, 1) + t.equal(cache.max, 10) + t.end() +}) + +test("least recently set", function (t) { + var cache = new LRU(2) + cache.set("a", "A") + cache.set("b", "B") + cache.set("c", "C") + t.equal(cache.get("c"), "C") + t.equal(cache.get("b"), "B") + t.equal(cache.get("a"), undefined) + t.end() +}) + +test("lru recently gotten", function (t) { + var cache = new LRU(2) + cache.set("a", "A") + cache.set("b", "B") + cache.get("a") + cache.set("c", "C") + t.equal(cache.get("c"), "C") + t.equal(cache.get("b"), undefined) + t.equal(cache.get("a"), "A") + t.end() +}) + +test("del", function (t) { + var cache = new LRU(2) + cache.set("a", "A") + cache.del("a") + t.equal(cache.get("a"), undefined) + t.end() +}) + +test("max", function (t) { + var cache = new LRU(3) + + // test changing the max, verify that the LRU items get dropped. + cache.max = 100 + for (var i = 0; i < 100; i ++) cache.set(i, i) + t.equal(cache.length, 100) + for (var i = 0; i < 100; i ++) { + t.equal(cache.get(i), i) + } + cache.max = 3 + t.equal(cache.length, 3) + for (var i = 0; i < 97; i ++) { + t.equal(cache.get(i), undefined) + } + for (var i = 98; i < 100; i ++) { + t.equal(cache.get(i), i) + } + + // now remove the max restriction, and try again. + cache.max = "hello" + for (var i = 0; i < 100; i ++) cache.set(i, i) + t.equal(cache.length, 100) + for (var i = 0; i < 100; i ++) { + t.equal(cache.get(i), i) + } + // should trigger an immediate resize + cache.max = 3 + t.equal(cache.length, 3) + for (var i = 0; i < 97; i ++) { + t.equal(cache.get(i), undefined) + } + for (var i = 98; i < 100; i ++) { + t.equal(cache.get(i), i) + } + t.end() +}) + +test("reset", function (t) { + var cache = new LRU(10) + cache.set("a", "A") + cache.set("b", "B") + cache.reset() + t.equal(cache.length, 0) + t.equal(cache.max, 10) + t.equal(cache.get("a"), undefined) + t.equal(cache.get("b"), undefined) + t.end() +}) + + +// Note: `.dump()` is a debugging tool only. No guarantees are made +// about the format/layout of the response. +test("dump", function (t) { + var cache = new LRU(10) + var d = cache.dump(); + t.equal(Object.keys(d).length, 0, "nothing in dump for empty cache") + cache.set("a", "A") + var d = cache.dump() // { a: { key: "a", value: "A", lu: 0 } } + t.ok(d.a) + t.equal(d.a.key, "a") + t.equal(d.a.value, "A") + t.equal(d.a.lu, 0) + + cache.set("b", "B") + cache.get("b") + d = cache.dump() + t.ok(d.b) + t.equal(d.b.key, "b") + t.equal(d.b.value, "B") + t.equal(d.b.lu, 2) + + t.end() +}) + + +test("basic with weighed length", function (t) { + var cache = new LRU({ + max: 100, + length: function (item) { return item.size } + }) + cache.set("key", {val: "value", size: 50}) + t.equal(cache.get("key").val, "value") + t.equal(cache.get("nada"), undefined) + t.equal(cache.lengthCalculator(cache.get("key")), 50) + t.equal(cache.length, 50) + t.equal(cache.max, 100) + t.end() +}) + + +test("weighed length item too large", function (t) { + var cache = new LRU({ + max: 10, + length: function (item) { return item.size } + }) + t.equal(cache.max, 10) + + // should fall out immediately + cache.set("key", {val: "value", size: 50}) + + t.equal(cache.length, 0) + t.equal(cache.get("key"), undefined) + t.end() +}) + +test("least recently set with weighed length", function (t) { + var cache = new LRU({ + max:8, + length: function (item) { return item.length } + }) + cache.set("a", "A") + cache.set("b", "BB") + cache.set("c", "CCC") + cache.set("d", "DDDD") + t.equal(cache.get("d"), "DDDD") + t.equal(cache.get("c"), "CCC") + t.equal(cache.get("b"), undefined) + t.equal(cache.get("a"), undefined) + t.end() +}) + +test("lru recently gotten with weighed length", function (t) { + var cache = new LRU({ + max: 8, + length: function (item) { return item.length } + }) + cache.set("a", "A") + cache.set("b", "BB") + cache.set("c", "CCC") + cache.get("a") + cache.get("b") + cache.set("d", "DDDD") + t.equal(cache.get("c"), undefined) + t.equal(cache.get("d"), "DDDD") + t.equal(cache.get("b"), "BB") + t.equal(cache.get("a"), "A") + t.end() +}) + +test("set returns proper booleans", function(t) { + var cache = new LRU({ + max: 5, + length: function (item) { return item.length } + }) + + t.equal(cache.set("a", "A"), true) + + // should return false for max exceeded + t.equal(cache.set("b", "donuts"), false) + + t.equal(cache.set("b", "B"), true) + t.equal(cache.set("c", "CCCC"), true) + t.end() +}) + +test("drop the old items", function(t) { + var cache = new LRU({ + max: 5, + maxAge: 50 + }) + + cache.set("a", "A") + + setTimeout(function () { + cache.set("b", "b") + t.equal(cache.get("a"), "A") + }, 25) + + setTimeout(function () { + cache.set("c", "C") + // timed out + t.notOk(cache.get("a")) + }, 60) + + setTimeout(function () { + t.notOk(cache.get("b")) + t.equal(cache.get("c"), "C") + }, 90) + + setTimeout(function () { + t.notOk(cache.get("c")) + t.end() + }, 155) +}) + +test("disposal function", function(t) { + var disposed = false + var cache = new LRU({ + max: 1, + dispose: function (k, n) { + disposed = n + } + }) + + cache.set(1, 1) + cache.set(2, 2) + t.equal(disposed, 1) + cache.set(3, 3) + t.equal(disposed, 2) + cache.reset() + t.equal(disposed, 3) + t.end() +}) + +test("disposal function on too big of item", function(t) { + var disposed = false + var cache = new LRU({ + max: 1, + length: function (k) { + return k.length + }, + dispose: function (k, n) { + disposed = n + } + }) + var obj = [ 1, 2 ] + + t.equal(disposed, false) + cache.set("obj", obj) + t.equal(disposed, obj) + t.end() +}) diff --git a/dist/node_modules/ldapauth/package.json b/dist/node_modules/ldapauth/package.json new file mode 100644 index 0000000..f930864 --- /dev/null +++ b/dist/node_modules/ldapauth/package.json @@ -0,0 +1,21 @@ +{ + "name": "ldapauth", + "version": "2.2.0", + "main": "./lib/ldapauth.js", + "description": "Authenticate against an LDAP server", + "author": "Trent Mick (http://trentm.com)", + "license": { + "type": "MIT", + "url": "https://github.com/trentm/node-ldapauth/raw/master/LICENSE" + }, + "keywords": ["authenticate", "ldap", "authentication", "auth"], + "repository": { + "type": "git", + "url": "git://github.com/trentm/node-ldapauth.git" + }, + "dependencies": { + "ldapjs": "0.5.6", + "bcrypt": "0.7.2", + "lru-cache": "2.0.4" + } +} diff --git a/dist/node_modules/ldapauth/tools/cutarelease.py b/dist/node_modules/ldapauth/tools/cutarelease.py new file mode 100755 index 0000000..5d2fb15 --- /dev/null +++ b/dist/node_modules/ldapauth/tools/cutarelease.py @@ -0,0 +1,597 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2009-2012 Trent Mick + +"""cutarelease -- Cut a release of your project. + +A script that will help cut a release for a git-based project that follows +a few conventions. It'll update your changelog (CHANGES.md), add a git +tag, push those changes, update your version to the next patch level release +and create a new changelog section for that new version. + +Conventions: +- XXX +""" + +__version_info__ = (1, 0, 6) +__version__ = '.'.join(map(str, __version_info__)) + +import sys +import os +from os.path import join, dirname, normpath, abspath, exists, basename, splitext +from glob import glob +from pprint import pprint +import re +import codecs +import logging +import optparse +import json + + + +#---- globals and config + +log = logging.getLogger("cutarelease") + +class Error(Exception): + pass + + + +#---- main functionality + +def cutarelease(project_name, version_files, dry_run=False): + """Cut a release. + + @param project_name {str} + @param version_files {list} List of paths to files holding the version + info for this project. + + If none are given it attempts to guess the version file: + package.json or VERSION.txt or VERSION or $project_name.py + or lib/$project_name.py or $project_name.js or lib/$project_name.js. + + The version file can be in one of the following forms: + + - A .py file, in which case the file is expect to have a top-level + global called "__version_info__" as follows. [1] + + __version_info__ = (0, 7, 6) + + Note that I typically follow that with the following to get a + string version attribute on my modules: + + __version__ = '.'.join(map(str, __version_info__)) + + - A .js file, in which case the file is expected to have a top-level + global called "VERSION" as follows: + + ver VERSION = "1.2.3"; + + - A "package.json" file, typical of a node.js npm-using project. + The package.json file must have a "version" field. + + - TODO: A simple version file whose only content is a "1.2.3"-style version + string. + + [1]: This is a convention I tend to follow in my projects. + Granted it might not be your cup of tea. I should add support for + just `__version__ = "1.2.3"`. I'm open to other suggestions too. + """ + dry_run_str = dry_run and " (dry-run)" or "" + + if not version_files: + log.info("guessing version file") + candidates = [ + "package.json", + "VERSION.txt", + "VERSION", + "%s.py" % project_name, + "lib/%s.py" % project_name, + "%s.js" % project_name, + "lib/%s.js" % project_name, + ] + for candidate in candidates: + if exists(candidate): + version_files = [candidate] + break + else: + raise Error("could not find a version file: specify its path or " + "add one of the following to your project: '%s'" + % "', '".join(candidates)) + log.info("using '%s' as version file", version_files[0]) + + parsed_version_files = [_parse_version_file(f) for f in version_files] + version_file_type, version_info = parsed_version_files[0] + version = _version_from_version_info(version_info) + + # Confirm + if not dry_run: + answer = query_yes_no("* * *\n" + "Are you sure you want cut a %s release?\n" + "This will involved commits and a push." % version, + default="no") + print "* * *" + if answer != "yes": + log.info("user abort") + return + log.info("cutting a %s release%s", version, dry_run_str) + + # Checks: Ensure there is a section in changes for this version. + + + + changes_path = "CHANGES.md" + changes_txt, changes, nyr = parse_changelog(changes_path) + #pprint(changes) + top_ver = changes[0]["version"] + if top_ver != version: + raise Error("changelog '%s' top section says " + "version %r, expected version %r: aborting" + % (changes_path, top_ver, version)) + top_verline = changes[0]["verline"] + if not top_verline.endswith(nyr): + answer = query_yes_no("\n* * *\n" + "The changelog '%s' top section doesn't have the expected\n" + "'%s' marker. Has this been released already?" + % (changes_path, nyr), default="yes") + print "* * *" + if answer != "no": + log.info("abort") + return + top_body = changes[0]["body"] + if top_body.strip() == "(nothing yet)": + raise Error("top section body is `(nothing yet)': it looks like " + "nothing has been added to this release") + + # Commits to prepare release. + changes_txt_before = changes_txt + changes_txt = changes_txt.replace(" (not yet released)", "", 1) + if not dry_run and changes_txt != changes_txt_before: + log.info("prepare `%s' for release", changes_path) + f = codecs.open(changes_path, 'w', 'utf-8') + f.write(changes_txt) + f.close() + run('git commit %s -m "prepare for %s release"' + % (changes_path, version)) + + # Tag version and push. + curr_tags = set(t for t in _capture_stdout(["git", "tag", "-l"]).split('\n') if t) + if not dry_run and version not in curr_tags: + log.info("tag the release") + run('git tag -a "%s" -m "version %s"' % (version, version)) + run('git push --tags') + + # Optionally release. + if exists("package.json"): + answer = query_yes_no("\n* * *\nPublish to npm?", default="yes") + print "* * *" + if answer == "yes": + if dry_run: + log.info("skipping npm publish (dry-run)") + else: + run('npm publish') + elif exists("setup.py"): + answer = query_yes_no("\n* * *\nPublish to pypi?", default="yes") + print "* * *" + if answer == "yes": + if dry_run: + log.info("skipping pypi publish (dry-run)") + else: + run("%spython setup.py sdist --formats zip upload" + % _setup_command_prefix()) + + # Commits to prepare for future dev and push. + # - update changelog file + next_version_info = _get_next_version_info(version_info) + next_version = _version_from_version_info(next_version_info) + log.info("prepare for future dev (version %s)", next_version) + marker = "## " + changes[0]["verline"] + if marker.endswith(nyr): + marker = marker[0:-len(nyr)] + if marker not in changes_txt: + raise Error("couldn't find `%s' marker in `%s' " + "content: can't prep for subsequent dev" % (marker, changes_path)) + next_verline = "%s %s%s" % (marker.rsplit(None, 1)[0], next_version, nyr) + changes_txt = changes_txt.replace(marker + '\n', + "%s\n\n(nothing yet)\n\n\n%s\n" % (next_verline, marker)) + if not dry_run: + f = codecs.open(changes_path, 'w', 'utf-8') + f.write(changes_txt) + f.close() + + # - update version file + next_version_tuple = _tuple_from_version(next_version) + for i, ver_file in enumerate(version_files): + ver_content = codecs.open(ver_file, 'r', 'utf-8').read() + ver_file_type, ver_info = parsed_version_files[i] + if ver_file_type == "json": + marker = '"version": "%s"' % version + if marker not in ver_content: + raise Error("couldn't find `%s' version marker in `%s' " + "content: can't prep for subsequent dev" % (marker, ver_file)) + ver_content = ver_content.replace(marker, + '"version": "%s"' % next_version) + elif ver_file_type == "javascript": + marker = 'var VERSION = "%s";' % version + if marker not in ver_content: + raise Error("couldn't find `%s' version marker in `%s' " + "content: can't prep for subsequent dev" % (marker, ver_file)) + ver_content = ver_content.replace(marker, + 'var VERSION = "%s";' % next_version) + elif ver_file_type == "python": + marker = "__version_info__ = %r" % (version_info,) + if marker not in ver_content: + raise Error("couldn't find `%s' version marker in `%s' " + "content: can't prep for subsequent dev" % (marker, ver_file)) + ver_content = ver_content.replace(marker, + "__version_info__ = %r" % (next_version_tuple,)) + elif ver_file_type == "version": + ver_content = next_version + else: + raise Error("unknown ver_file_type: %r" % ver_file_type) + if not dry_run: + log.info("update version to '%s' in '%s'", next_version, ver_file) + f = codecs.open(ver_file, 'w', 'utf-8') + f.write(ver_content) + f.close() + + if not dry_run: + run('git commit %s %s -m "prep for future dev"' % ( + changes_path, ' '.join(version_files))) + run('git push') + + + +#---- internal support routines + +def _indent(s, indent=' '): + return indent + indent.join(s.splitlines(True)) + +def _tuple_from_version(version): + def _intify(s): + try: + return int(s) + except ValueError: + return s + return tuple(_intify(b) for b in version.split('.')) + +def _get_next_version_info(version_info): + next = list(version_info[:]) + next[-1] += 1 + return tuple(next) + +def _version_from_version_info(version_info): + v = str(version_info[0]) + state_dot_join = True + for i in version_info[1:]: + if state_dot_join: + try: + int(i) + except ValueError: + state_dot_join = False + else: + pass + if state_dot_join: + v += "." + str(i) + else: + v += str(i) + return v + +_version_re = re.compile(r"^(\d+)\.(\d+)(?:\.(\d+)([abc](\d+)?)?)?$") +def _version_info_from_version(version): + m = _version_re.match(version) + if not m: + raise Error("could not convert '%s' version to version info" % version) + version_info = [] + for g in m.groups(): + if g is None: + break + try: + version_info.append(int(g)) + except ValueError: + version_info.append(g) + return tuple(version_info) + +def _parse_version_file(version_file): + """Get version info from the given file. It can be any of: + + Supported version file types (i.e. types of files from which we know + how to parse the version string/number -- often by some convention): + - json: use the "version" key + - javascript: look for a `var VERSION = "1.2.3";` + - python: Python script/module with `__version_info__ = (1, 2, 3)` + - version: a VERSION.txt or VERSION file where the whole contents are + the version string + + @param version_file {str} Can be a path or "type:path", where "type" + is one of the supported types. + """ + # Get version file *type*. + version_file_type = None + match = re.compile("^([a-z]+):(.*)$").search(version_file) + if match: + version_file = match.group(2) + version_file_type = match.group(1) + aliases = { + "js": "javascript" + } + if version_file_type in aliases: + version_file_type = aliases[version_file_type] + + f = codecs.open(version_file, 'r', 'utf-8') + content = f.read() + f.close() + + if not version_file_type: + # Guess the type. + base = basename(version_file) + ext = splitext(base)[1] + if ext == ".json": + version_file_type = "json" + elif ext == ".py": + version_file_type = "python" + elif ext == ".js": + version_file_type = "javascript" + elif content.startswith("#!"): + shebang = content.splitlines(False)[0] + shebang_bits = re.split(r'[/ \t]', shebang) + for name, typ in {"python": "python", "node": "javascript"}.items(): + if name in shebang_bits: + version_file_type = typ + break + elif base in ("VERSION", "VERSION.txt"): + version_file_type = "version" + if not version_file_type: + raise RuntimeError("can't extract version from '%s': no idea " + "what type of file it it" % version_file) + + if version_file_type == "json": + obj = json.loads(content) + version_info = _version_info_from_version(obj["version"]) + elif version_file_type == "python": + m = re.search(r'^__version_info__ = (.*?)$', content, re.M) + version_info = eval(m.group(1)) + elif version_file_type == "javascript": + m = re.search(r'^var VERSION = "(.*?)";$', content, re.M) + version_info = _version_info_from_version(m.group(1)) + elif version_file_type == "version": + version_info = _version_info_from_version(content.strip()) + else: + raise RuntimeError("unexpected version_file_type: %r" + % version_file_type) + return version_file_type, version_info + + +def parse_changelog(changes_path): + """Parse the given changelog path and return `(content, parsed, nyr)` + where `nyr` is the ' (not yet released)' marker and `parsed` looks like: + + [{'body': u'\n(nothing yet)\n\n', + 'verline': u'restify 1.0.1 (not yet released)', + 'version': u'1.0.1'}, # version is parsed out for top section only + {'body': u'...', + 'verline': u'1.0.0'}, + {'body': u'...', + 'verline': u'1.0.0-rc2'}, + {'body': u'...', + 'verline': u'1.0.0-rc1'}] + + A changelog (CHANGES.md) is expected to look like this: + + # $project Changelog + + ## $next_version (not yet released) + + ... + + ## $version1 + + ... + + ## $version2 + + ... and so on + + The version lines are enforced as follows: + + - The top entry should have a " (not yet released)" suffix. "Should" + because recovery from half-cutarelease failures is supported. + - A version string must be extractable from there, but it tries to + be loose (though strict "X.Y.Z" versioning is preferred). Allowed + + ## 1.0.0 + ## my project 1.0.1 + ## foo 1.2.3-rc2 + + Basically, (a) the " (not yet released)" is stripped, (b) the + last token is the version, and (c) that version must start with + a digit (sanity check). + """ + if not exists(changes_path): + raise Error("changelog file '%s' not found" % changes_path) + content = codecs.open(changes_path, 'r', 'utf-8').read() + + parser = re.compile( + r'^##\s*(?P[^\n]*?)\s*$(?P.*?)(?=^##|\Z)', + re.M | re.S) + sections = parser.findall(content) + + # Sanity checks on changelog format. + if not sections: + template = "## 1.0.0 (not yet released)\n\n(nothing yet)\n" + raise Error("changelog '%s' must have at least one section, " + "suggestion:\n\n%s" % (changes_path, _indent(template))) + first_section_verline = sections[0][0] + nyr = ' (not yet released)' + #if not first_section_verline.endswith(nyr): + # eg = "## %s%s" % (first_section_verline, nyr) + # raise Error("changelog '%s' top section must end with %r, " + # "naive e.g.: '%s'" % (changes_path, nyr, eg)) + + items = [] + for i, section in enumerate(sections): + item = { + "verline": section[0], + "body": section[1] + } + if i == 0: + # We only bother to pull out 'version' for the top section. + verline = section[0] + if verline.endswith(nyr): + verline = verline[0:-len(nyr)] + version = verline.split()[-1] + try: + int(version[0]) + except ValueError: + msg = '' + if version.endswith(')'): + msg = " (cutarelease is picky about the trailing %r " \ + "on the top version line. Perhaps you misspelled " \ + "that?)" % nyr + raise Error("changelog '%s' top section version '%s' is " + "invalid: first char isn't a number%s" + % (changes_path, version, msg)) + item["version"] = version + items.append(item) + + return content, items, nyr + +## {{{ http://code.activestate.com/recipes/577058/ (r2) +def query_yes_no(question, default="yes"): + """Ask a yes/no question via raw_input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is one of "yes" or "no". + """ + valid = {"yes":"yes", "y":"yes", "ye":"yes", + "no":"no", "n":"no"} + if default == None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while 1: + sys.stdout.write(question + prompt) + choice = raw_input().lower() + if default is not None and choice == '': + return default + elif choice in valid.keys(): + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' "\ + "(or 'y' or 'n').\n") +## end of http://code.activestate.com/recipes/577058/ }}} + +def _capture_stdout(argv): + import subprocess + p = subprocess.Popen(argv, stdout=subprocess.PIPE) + return p.communicate()[0] + +class _NoReflowFormatter(optparse.IndentedHelpFormatter): + """An optparse formatter that does NOT reflow the description.""" + def format_description(self, description): + return description or "" + +def run(cmd): + """Run the given command. + + Raises OSError is the command returns a non-zero exit status. + """ + log.debug("running '%s'", cmd) + fixed_cmd = cmd + if sys.platform == "win32" and cmd.count('"') > 2: + fixed_cmd = '"' + cmd + '"' + retval = os.system(fixed_cmd) + if hasattr(os, "WEXITSTATUS"): + status = os.WEXITSTATUS(retval) + else: + status = retval + if status: + raise OSError(status, "error running '%s'" % cmd) + +def _setup_command_prefix(): + prefix = "" + if sys.platform == "darwin": + # http://forums.macosxhints.com/archive/index.php/t-43243.html + # This is an Apple customization to `tar` to avoid creating + # '._foo' files for extended-attributes for archived files. + prefix = "COPY_EXTENDED_ATTRIBUTES_DISABLE=1 " + return prefix + + +#---- mainline + +def main(argv): + logging.basicConfig(format="%(name)s: %(levelname)s: %(message)s") + log.setLevel(logging.INFO) + + # Parse options. + parser = optparse.OptionParser(prog="cutarelease", usage='', + version="%prog " + __version__, description=__doc__, + formatter=_NoReflowFormatter()) + parser.add_option("-v", "--verbose", dest="log_level", + action="store_const", const=logging.DEBUG, + help="more verbose output") + parser.add_option("-q", "--quiet", dest="log_level", + action="store_const", const=logging.WARNING, + help="quieter output (just warnings and errors)") + parser.set_default("log_level", logging.INFO) + parser.add_option("--test", action="store_true", + help="run self-test and exit (use 'eol.py -v --test' for verbose test output)") + parser.add_option("-p", "--project-name", metavar="NAME", + help='the name of this project (default is the base dir name)', + default=basename(os.getcwd())) + parser.add_option("-f", "--version-file", metavar="[TYPE:]PATH", + action='append', dest="version_files", + help='The path to the project file holding the version info. Can be ' + 'specified multiple times if more than one file should be updated ' + 'with new version info. If excluded, it will be guessed.') + parser.add_option("-n", "--dry-run", action="store_true", + help='Do a dry-run', default=False) + opts, args = parser.parse_args() + log.setLevel(opts.log_level) + + cutarelease(opts.project_name, opts.version_files, dry_run=opts.dry_run) + + +## {{{ http://code.activestate.com/recipes/577258/ (r5+) +if __name__ == "__main__": + try: + retval = main(sys.argv) + except KeyboardInterrupt: + sys.exit(1) + except SystemExit: + raise + except: + import traceback, logging + if not log.handlers and not logging.root.handlers: + logging.basicConfig() + skip_it = False + exc_info = sys.exc_info() + if hasattr(exc_info[0], "__name__"): + exc_class, exc, tb = exc_info + if isinstance(exc, IOError) and exc.args[0] == 32: + # Skip 'IOError: [Errno 32] Broken pipe': often a cancelling of `less`. + skip_it = True + if not skip_it: + tb_path, tb_lineno, tb_func = traceback.extract_tb(tb)[-1][:3] + log.error("%s (%s:%s in %s)", exc_info[1], tb_path, + tb_lineno, tb_func) + else: # string exception + log.error(exc_info[0]) + if not skip_it: + if log.isEnabledFor(logging.DEBUG): + traceback.print_exception(*exc_info) + sys.exit(1) + else: + sys.exit(retval) +## end of http://code.activestate.com/recipes/577258/ }}} diff --git a/dist/node_modules/ldapauth/tools/jsstyle b/dist/node_modules/ldapauth/tools/jsstyle new file mode 100755 index 0000000..c0df3a9 --- /dev/null +++ b/dist/node_modules/ldapauth/tools/jsstyle @@ -0,0 +1,926 @@ +#!/usr/bin/env perl +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# +# jsstyle - check for some common stylistic errors. +# +# jsstyle is a sort of "lint" for Javascript coding style. This tool is +# derived from the cstyle tool, used to check for the style used in the +# Solaris kernel, sometimes known as "Bill Joy Normal Form". +# +# There's a lot this can't check for, like proper indentation of code +# blocks. There's also a lot more this could check for. +# +# A note to the non perl literate: +# +# perl regular expressions are pretty much like egrep +# regular expressions, with the following special symbols +# +# \s any space character +# \S any non-space character +# \w any "word" character [a-zA-Z0-9_] +# \W any non-word character +# \d a digit [0-9] +# \D a non-digit +# \b word boundary (between \w and \W) +# \B non-word boundary +# + +require 5.0; +use IO::File; +use Getopt::Std; +use strict; + +my $usage = +"Usage: jsstyle [-h?vcC] [-t ] [-f ] [-o ] file ... + +Check your JavaScript file for style. +See for details on config options. +Report bugs to . + +Options: + -h print this help and exit + -v verbose + + -c check continuation indentation inside functions + -t specify tab width for line length calculation + -C don't check anything in header block comments + + -f PATH + path to a jsstyle config file + -o OPTION1,OPTION2 + set config options, e.g. '-o doxygen,indent=2' + +"; + +my %opts; + +if (!getopts("ch?o:t:f:vC", \%opts)) { + print $usage; + exit 2; +} + +if (defined($opts{'h'}) || defined($opts{'?'})) { + print $usage; + exit; +} + +my $check_continuation = $opts{'c'}; +my $verbose = $opts{'v'}; +my $ignore_hdr_comment = $opts{'C'}; +my $tab_width = $opts{'t'}; + +# By default, tabs are 8 characters wide +if (! defined($opts{'t'})) { + $tab_width = 8; +} + + +# Load config +my %config = ( + indent => "tab", + doxygen => 0, # doxygen comments: /** ... */ + splint => 0, # splint comments. Needed? + "unparenthesized-return" => 1, + "literal-string-quote" => "single", # 'single' or 'double' + "blank-after-start-comment" => 1, +); +sub add_config_var ($$) { + my ($scope, $str) = @_; + + if ($str !~ /^([\w-]+)(?:\s*=\s*(.*?))?$/) { + die "$scope: invalid option: '$str'"; + } + my $name = $1; + my $value = ($2 eq '' ? 1 : $2); + #print "scope: '$scope', str: '$str', name: '$name', value: '$value'\n"; + + # Validate config var. + if ($name eq "indent") { + # A number of spaces or "tab". + if ($value !~ /^\d+$/ && $value ne "tab") { + die "$scope: invalid '$name': must be a number (of ". + "spaces) or 'tab'"; + } + } elsif ($name eq "doxygen" || # boolean vars + $name eq "splint" || + $name eq "unparenthesized-return" || + $name eq "blank-after-start-comment") { + if ($value != 1 && $value != 0) { + die "$scope: invalid '$name': don't give a value"; + } + } elsif ($name eq "literal-string-quote") { + if ($value !~ /single|double/) { + die "$scope: invalid '$name': must be 'single' ". + "or 'double'"; + } + } else { + die "$scope: unknown config var: $name"; + } + $config{$name} = $value; +} + +if (defined($opts{'f'})) { + my $path = $opts{'f'}; + my $fh = new IO::File $path, "r"; + if (!defined($fh)) { + die "cannot open config path '$path'"; + } + my $line = 0; + while (<$fh>) { + $line++; + s/^\s*//; # drop leading space + s/\s*$//; # drop trailing space + next if ! $_; # skip empty line + next if /^#/; # skip comments + add_config_var "$path:$line", $_; + } +} + +if (defined($opts{'o'})) { + for my $x (split /,/, $opts{'o'}) { + add_config_var "'-o' option", $x; + } +} + + +my ($filename, $line, $prev); # shared globals + +my $fmt; +my $hdr_comment_start; + +if ($verbose) { + $fmt = "%s: %d: %s\n%s\n"; +} else { + $fmt = "%s: %d: %s\n"; +} + +if ($config{"doxygen"}) { + # doxygen comments look like "/*!" or "/**"; allow them. + $hdr_comment_start = qr/^\s*\/\*[\!\*]?$/; +} else { + $hdr_comment_start = qr/^\s*\/\*$/; +} + +# Note, following must be in single quotes so that \s and \w work right. +my $lint_re = qr/\/\*(?: + jsl:\w+?|ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*| + CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY| + FALLTHRU|FALLTHROUGH|LINTED.*?|PRINTFLIKE[0-9]*| + PROTOLIB[0-9]*|SCANFLIKE[0-9]*|JSSTYLED.*? + )\*\//x; + +my $splint_re = qr/\/\*@.*?@\*\//x; + +my $err_stat = 0; # exit status + +if ($#ARGV >= 0) { + foreach my $arg (@ARGV) { + my $fh = new IO::File $arg, "r"; + if (!defined($fh)) { + printf "%s: cannot open\n", $arg; + } else { + &jsstyle($arg, $fh); + close $fh; + } + } +} else { + &jsstyle("", *STDIN); +} +exit $err_stat; + +my $no_errs = 0; # set for JSSTYLED-protected lines + +sub err($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $line; + $err_stat = 1; + } +} + +sub err_prefix($$) { + my ($prevline, $error) = @_; + my $out = $prevline."\n".$line; + unless ($no_errs) { + printf $fmt, $filename, $., $error, $out; + $err_stat = 1; + } +} + +sub err_prev($) { + my ($error) = @_; + unless ($no_errs) { + printf $fmt, $filename, $. - 1, $error, $prev; + $err_stat = 1; + } +} + +sub jsstyle($$) { + +my ($fn, $filehandle) = @_; +$filename = $fn; # share it globally + +my $in_cpp = 0; +my $next_in_cpp = 0; + +my $in_comment = 0; +my $in_header_comment = 0; +my $comment_done = 0; +my $in_function = 0; +my $in_function_header = 0; +my $in_declaration = 0; +my $note_level = 0; +my $nextok = 0; +my $nocheck = 0; + +my $in_string = 0; + +my ($okmsg, $comment_prefix); + +$line = ''; +$prev = ''; +reset_indent(); + +line: while (<$filehandle>) { + s/\r?\n$//; # strip return and newline + + # save the original line, then remove all text from within + # double or single quotes, we do not want to check such text. + + $line = $_; + + # + # C allows strings to be continued with a backslash at the end of + # the line. We translate that into a quoted string on the previous + # line followed by an initial quote on the next line. + # + # (we assume that no-one will use backslash-continuation with character + # constants) + # + $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); + + # + # normal strings and characters + # + s/'([^\\']|\\.)*'/\'\'/g; + s/"([^\\"]|\\.)*"/\"\"/g; + + # + # detect string continuation + # + if ($nocheck || $in_comment) { + $in_string = 0; + } else { + # + # Now that all full strings are replaced with "", we check + # for unfinished strings continuing onto the next line. + # + $in_string = + (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || + s/^("")*"([^\\"]|\\.)*\\$/""/); + } + + # + # figure out if we are in a cpp directive + # + $in_cpp = $next_in_cpp || /^\s*#/; # continued or started + $next_in_cpp = $in_cpp && /\\$/; # only if continued + + # strip off trailing backslashes, which appear in long macros + s/\s*\\$//; + + # an /* END JSSTYLED */ comment ends a no-check block. + if ($nocheck) { + if (/\/\* *END *JSSTYLED *\*\//) { + $nocheck = 0; + } else { + reset_indent(); + next line; + } + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if ($nextok) { + if ($okmsg) { + err($okmsg); + } + $nextok = 0; + $okmsg = 0; + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + $no_errs = 1; + } elsif ($no_errs) { + $no_errs = 0; + } + + # check length of line. + # first, a quick check to see if there is any chance of being too long. + if ((($line =~ tr/\t/\t/) * ($tab_width - 1)) + length($line) > 80) { + # yes, there is a chance. + # replace tabs with spaces and check again. + my $eline = $line; + 1 while $eline =~ + s/\t+/' ' x + (length($&) * $tab_width - length($`) % $tab_width)/e; + if (length($eline) > 80) { + err("line > 80 characters"); + } + } + + # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). + if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE + s/[^()]//g; # eliminate all non-parens + $note_level += s/\(//g - length; # update paren nest level + next; + } + + # a /* BEGIN JSSTYLED */ comment starts a no-check block. + if (/\/\* *BEGIN *JSSTYLED *\*\//) { + $nocheck = 1; + } + + # a /*JSSTYLED*/ comment indicates that the next line is ok. + if (/\/\* *JSSTYLED.*\*\//) { + /^.*\/\* *JSSTYLED *(.*) *\*\/.*$/; + $okmsg = $1; + $nextok = 1; + } + if (/\/\/ *JSSTYLED/) { + /^.*\/\/ *JSSTYLED *(.*)$/; + $okmsg = $1; + $nextok = 1; + } + + # universal checks; apply to everything + if (/\t +\t/) { + err("spaces between tabs"); + } + if (/ \t+ /) { + err("tabs between spaces"); + } + if (/\s$/) { + err("space or tab at end of line"); + } + if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { + err("comment preceded by non-blank"); + } + + # is this the beginning or ending of a function? + # (not if "struct foo\n{\n") + if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { + $in_function = 1; + $in_declaration = 1; + $in_function_header = 0; + $prev = $line; + next line; + } + if (/^}\s*(\/\*.*\*\/\s*)*$/) { + if ($prev =~ /^\s*return\s*;/) { + err_prev("unneeded return at end of function"); + } + $in_function = 0; + reset_indent(); # we don't check between functions + $prev = $line; + next line; + } + if (/^\w*\($/) { + $in_function_header = 1; + } + + # a blank line terminates the declarations within a function. + # XXX - but still a problem in sub-blocks. + if ($in_declaration && /^$/) { + $in_declaration = 0; + } + + if ($comment_done) { + $in_comment = 0; + $in_header_comment = 0; + $comment_done = 0; + } + # does this looks like the start of a block comment? + if (/$hdr_comment_start/) { + if ($config{"indent"} eq "tab") { + if (!/^\t*\/\*/) { + err("block comment not indented by tabs"); + } + } elsif (!/^ *\/\*/) { + err("block comment not indented by spaces"); + } + $in_comment = 1; + /^(\s*)\//; + $comment_prefix = $1; + if ($comment_prefix eq "") { + $in_header_comment = 1; + } + $prev = $line; + next line; + } + # are we still in the block comment? + if ($in_comment) { + if (/^$comment_prefix \*\/$/) { + $comment_done = 1; + } elsif (/\*\//) { + $comment_done = 1; + err("improper block comment close") + unless ($ignore_hdr_comment && $in_header_comment); + } elsif (!/^$comment_prefix \*[ \t]/ && + !/^$comment_prefix \*$/) { + err("improper block comment") + unless ($ignore_hdr_comment && $in_header_comment); + } + } + + if ($in_header_comment && $ignore_hdr_comment) { + $prev = $line; + next line; + } + + # check for errors that might occur in comments and in code. + + # allow spaces to be used to draw pictures in header comments. + #if (/[^ ] / && !/".* .*"/ && !$in_header_comment) { + # err("spaces instead of tabs"); + #} + #if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && + # (!/^ \w/ || $in_function != 0)) { + # err("indent by spaces instead of tabs"); + #} + if ($config{"indent"} eq "tab") { + if (/^ {2,}/ && !/^ [^ ]/) { + err("indent by spaces instead of tabs"); + } + } elsif (/^\t/) { + err("indent by tabs instead of spaces") + } elsif (/^( +)/ && !$in_comment) { + my $indent = $1; + if (length($indent) < $config{"indent"}) { + err("indent of " . length($indent) . + " space(s) instead of " . $config{'indent'}); + } + } + if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { + err("continuation line not indented by 4 spaces"); + } + + # A multi-line block comment must not have content on the first line. + if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) { + err("improper first line of block comment"); + } + + if ($in_comment) { # still in comment, don't do further checks + $prev = $line; + next line; + } + + if ((/[^(]\/\*\S/ || /^\/\*\S/) && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank after open comment"); + } + if (/\S\*\/[^)]|\S\*\/$/ && + !(/$lint_re/ || ($config{"splint"} && /$splint_re/))) { + err("missing blank before close comment"); + } + if ($config{"blank-after-start-comment"} && /(?\s][!<>=]=/ || /[^<>!=][!<>=]==?[^\s,=]/ || + (/[^->]>[^,=>\s]/ && !/[^->]>$/) || + (/[^<]<[^,=<\s]/ && !/[^<]<$/) || + /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { + err("missing space around relational operator"); + } + if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || + (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || + (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { + # XXX - should only check this for C++ code + # XXX - there are probably other forms that should be allowed + if (!/\soperator=/) { + err("missing space around assignment operator"); + } + } + if (/[,;]\S/ && !/\bfor \(;;\)/ && + # Allow a comma in a regex quantifier. + !/\/.*?\{\d+,?\d*\}.*?\//) { + err("comma or semicolon followed by non-blank"); + } + # allow "for" statements to have empty "while" clauses + if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { + err("comma or semicolon preceded by blank"); + } + if (/^\s*(&&|\|\|)/) { + err("improper boolean continuation"); + } + if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { + err("more than one space around boolean operator"); + } + if (/\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\(/) { + err("missing space between keyword and paren"); + } + if (/(\b(catch|for|if|with|while|switch|return)\b.*){2,}/) { + # multiple "case" and "sizeof" allowed + err("more than one keyword on line"); + } + if (/\b(delete|typeof|instanceOf|with|throw|catch|new|function|in|for|if|while|switch|return|case)\s\s+\(/ && + !/^#if\s+\(/) { + err("extra space between keyword and paren"); + } + # try to detect "func (x)" but not "if (x)" or + # "#define foo (x)" or "int (*func)();" + if (/\w\s\(/) { + my $s = $_; + # strip off all keywords on the line + s/\b(delete|typeof|instanceOf|throw|with|catch|new|function|in|for|if|while|switch|return|case)\s\(/XXX(/g; + s/#elif\s\(/XXX(/g; + s/^#define\s+\w+\s+\(/XXX(/; + # do not match things like "void (*f)();" + # or "typedef void (func_t)();" + s/\w\s\(+\*/XXX(*/g; + s/\b(void)\s+\(+/XXX(/og; + if (/\w\s\(/) { + err("extra space between function name and left paren"); + } + $_ = $s; + } + + if ($config{"unparenthesized-return"} && + /^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) { + err("unparenthesized return expression"); + } + if (/\btypeof\b/ && !/\btypeof\s*\(.*\)/) { + err("unparenthesized typeof expression"); + } + if (/\(\s/) { + err("whitespace after left paren"); + } + # allow "for" statements to have empty "continue" clauses + if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) { + err("whitespace before right paren"); + } + if (/^\s*\(void\)[^ ]/) { + err("missing space after (void) cast"); + } + if (/\S{/ && !/({|\(){/ && + # Allow a brace in a regex quantifier. + !/\/.*?\{\d+,?\d*\}.*?\//) { + err("missing space before left brace"); + } + if ($in_function && /^\s+{/ && + ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) { + err("left brace starting a line"); + } + if (/}(else|while)/) { + err("missing space after right brace"); + } + if (/}\s\s+(else|while)/) { + err("extra space after right brace"); + } + if (/^\s+#/) { + err("preprocessor statement not in column 1"); + } + if (/^#\s/) { + err("blank after preprocessor #"); + } + + # + # We completely ignore, for purposes of indentation: + # * lines outside of functions + # * preprocessor lines + # + if ($check_continuation && $in_function && !$in_cpp) { + process_indent($_); + } + + if (/^\s*else\W/) { + if ($prev =~ /^\s*}$/) { + err_prefix($prev, + "else and right brace should be on same line"); + } + } + $prev = $line; +} + +if ($prev eq "") { + err("last line in file is blank"); +} + +} + +# +# Continuation-line checking +# +# The rest of this file contains the code for the continuation checking +# engine. It's a pretty simple state machine which tracks the expression +# depth (unmatched '('s and '['s). +# +# Keep in mind that the argument to process_indent() has already been heavily +# processed; all comments have been replaced by control-A, and the contents of +# strings and character constants have been elided. +# + +my $cont_in; # currently inside of a continuation +my $cont_off; # skipping an initializer or definition +my $cont_noerr; # suppress cascading errors +my $cont_start; # the line being continued +my $cont_base; # the base indentation +my $cont_first; # this is the first line of a statement +my $cont_multiseg; # this continuation has multiple segments + +my $cont_special; # this is a C statement (if, for, etc.) +my $cont_macro; # this is a macro +my $cont_case; # this is a multi-line case + +my @cont_paren; # the stack of unmatched ( and [s we've seen + +sub +reset_indent() +{ + $cont_in = 0; + $cont_off = 0; +} + +sub +delabel($) +{ + # + # replace labels with tabs. Note that there may be multiple + # labels on a line. + # + local $_ = $_[0]; + + while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) { + my ($pre_tabs, $label, $rest) = ($1, $2, $3); + $_ = $pre_tabs; + while ($label =~ s/^([^\t]*)(\t+)//) { + $_ .= "\t" x (length($2) + length($1) / 8); + } + $_ .= ("\t" x (length($label) / 8)).$rest; + } + + return ($_); +} + +sub +process_indent($) +{ + require strict; + local $_ = $_[0]; # preserve the global $_ + + s///g; # No comments + s/\s+$//; # Strip trailing whitespace + + return if (/^$/); # skip empty lines + + # regexps used below; keywords taking (), macros, and continued cases + my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b'; + my $macro = '[A-Z_][A-Z_0-9]*\('; + my $case = 'case\b[^:]*$'; + + # skip over enumerations, array definitions, initializers, etc. + if ($cont_off <= 0 && !/^\s*$special/ && + (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*)){/ || + (/^\s*{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) { + $cont_in = 0; + $cont_off = tr/{/{/ - tr/}/}/; + return; + } + if ($cont_off) { + $cont_off += tr/{/{/ - tr/}/}/; + return; + } + + if (!$cont_in) { + $cont_start = $line; + + if (/^\t* /) { + err("non-continuation indented 4 spaces"); + $cont_noerr = 1; # stop reporting + } + $_ = delabel($_); # replace labels with tabs + + # check if the statement is complete + return if (/^\s*\}?$/); + return if (/^\s*\}?\s*else\s*\{?$/); + return if (/^\s*do\s*\{?$/); + return if (/{$/); + return if (/}[,;]?$/); + + # Allow macros on their own lines + return if (/^\s*[A-Z_][A-Z_0-9]*$/); + + # cases we don't deal with, generally non-kosher + if (/{/) { + err("stuff after {"); + return; + } + + # Get the base line, and set up the state machine + /^(\t*)/; + $cont_base = $1; + $cont_in = 1; + @cont_paren = (); + $cont_first = 1; + $cont_multiseg = 0; + + # certain things need special processing + $cont_special = /^\s*$special/? 1 : 0; + $cont_macro = /^\s*$macro/? 1 : 0; + $cont_case = /^\s*$case/? 1 : 0; + } else { + $cont_first = 0; + + # Strings may be pulled back to an earlier (half-)tabstop + unless ($cont_noerr || /^$cont_base / || + (/^\t*(?: )?(?:gettext\()?\"/ && !/^$cont_base\t/)) { + err_prefix($cont_start, + "continuation should be indented 4 spaces"); + } + } + + my $rest = $_; # keeps the remainder of the line + + # + # The split matches 0 characters, so that each 'special' character + # is processed separately. Parens and brackets are pushed and + # popped off the @cont_paren stack. For normal processing, we wait + # until a ; or { terminates the statement. "special" processing + # (if/for/while/switch) is allowed to stop when the stack empties, + # as is macro processing. Case statements are terminated with a : + # and an empty paren stack. + # + foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) { + next if (length($_) == 0); + + # rest contains the remainder of the line + my $rxp = "[^\Q$_\E]*\Q$_\E"; + $rest =~ s/^$rxp//; + + if (/\(/ || /\[/) { + push @cont_paren, $_; + } elsif (/\)/ || /\]/) { + my $cur = $_; + tr/\)\]/\(\[/; + + my $old = (pop @cont_paren); + if (!defined($old)) { + err("unexpected '$cur'"); + $cont_in = 0; + last; + } elsif ($old ne $_) { + err("'$cur' mismatched with '$old'"); + $cont_in = 0; + last; + } + + # + # If the stack is now empty, do special processing + # for if/for/while/switch and macro statements. + # + next if (@cont_paren != 0); + if ($cont_special) { + if ($rest =~ /^\s*{?$/) { + $cont_in = 0; + last; + } + if ($rest =~ /^\s*;$/) { + err("empty if/for/while body ". + "not on its own line"); + $cont_in = 0; + last; + } + if (!$cont_first && $cont_multiseg == 1) { + err_prefix($cont_start, + "multiple statements continued ". + "over multiple lines"); + $cont_multiseg = 2; + } elsif ($cont_multiseg == 0) { + $cont_multiseg = 1; + } + # We've finished this section, start + # processing the next. + goto section_ended; + } + if ($cont_macro) { + if ($rest =~ /^$/) { + $cont_in = 0; + last; + } + } + } elsif (/\;/) { + if ($cont_case) { + err("unexpected ;"); + } elsif (!$cont_special) { + err("unexpected ;") if (@cont_paren != 0); + if (!$cont_first && $cont_multiseg == 1) { + err_prefix($cont_start, + "multiple statements continued ". + "over multiple lines"); + $cont_multiseg = 2; + } elsif ($cont_multiseg == 0) { + $cont_multiseg = 1; + } + if ($rest =~ /^$/) { + $cont_in = 0; + last; + } + if ($rest =~ /^\s*special/) { + err("if/for/while/switch not started ". + "on its own line"); + } + goto section_ended; + } + } elsif (/\{/) { + err("{ while in parens/brackets") if (@cont_paren != 0); + err("stuff after {") if ($rest =~ /[^\s}]/); + $cont_in = 0; + last; + } elsif (/\}/) { + err("} while in parens/brackets") if (@cont_paren != 0); + if (!$cont_special && $rest !~ /^\s*(while|else)\b/) { + if ($rest =~ /^$/) { + err("unexpected }"); + } else { + err("stuff after }"); + } + $cont_in = 0; + last; + } + } elsif (/\:/ && $cont_case && @cont_paren == 0) { + err("stuff after multi-line case") if ($rest !~ /$^/); + $cont_in = 0; + last; + } + next; +section_ended: + # End of a statement or if/while/for loop. Reset + # cont_special and cont_macro based on the rest of the + # line. + $cont_special = ($rest =~ /^\s*$special/)? 1 : 0; + $cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0; + $cont_case = 0; + next; + } + $cont_noerr = 0 if (!$cont_in); +} diff --git a/dist/node_modules/nconf/.npmignore b/dist/node_modules/nconf/.npmignore new file mode 100644 index 0000000..ee49fdb --- /dev/null +++ b/dist/node_modules/nconf/.npmignore @@ -0,0 +1,8 @@ +.DS_Store +config.json +test/fixtures/*.json +!test/fixtures/complete.json +!test/fixtures/malformed.json +node_modules/ +node_modules/* +npm-debug.log diff --git a/dist/node_modules/nconf/.travis.yml b/dist/node_modules/nconf/.travis.yml new file mode 100644 index 0000000..c958222 --- /dev/null +++ b/dist/node_modules/nconf/.travis.yml @@ -0,0 +1,11 @@ +language: node_js +node_js: + - 0.4 + - 0.6 + - 0.7 + +notifications: + email: + - travis@nodejitsu.com + irc: "irc.freenode.org#nodejitsu" + diff --git a/dist/node_modules/nconf/CHANGELOG.md b/dist/node_modules/nconf/CHANGELOG.md new file mode 100644 index 0000000..34c1576 --- /dev/null +++ b/dist/node_modules/nconf/CHANGELOG.md @@ -0,0 +1,9 @@ +# CHANGELOG + +### Version 0.5.0 + +* `nconf.stores.*` is now `nconf.*` +* `nconf.stores` now represents the set of nconf.* Store instances on the nconf object. +* Added `nconf.argv()`, `nconf.env()`, `nconf.file()`, `nconf.overrides()`, `nconf.defaults()`. +* `nconf.system` no longer exists. The `nconf.System` store has been broken into `nconf.Argv`, `nconf.Env` and `nconf.Literal` +* Fixed bugs in hierarchical configuration loading. \ No newline at end of file diff --git a/dist/node_modules/nconf/LICENSE b/dist/node_modules/nconf/LICENSE new file mode 100644 index 0000000..1f01e2b --- /dev/null +++ b/dist/node_modules/nconf/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Nodejitsu Inc. + +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/dist/node_modules/nconf/README.md b/dist/node_modules/nconf/README.md new file mode 100644 index 0000000..e9c0904 --- /dev/null +++ b/dist/node_modules/nconf/README.md @@ -0,0 +1,267 @@ +# nconf [![Build Status](https://secure.travis-ci.org/flatiron/nconf.png)](http://travis-ci.org/flatiron/nconf) + +Hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging. + +## Example +Using nconf is easy; it is designed to be a simple key-value store with support for both local and remote storage. Keys are namespaced and delimited by `:`. Lets dive right into sample usage: + +``` js + var fs = require('fs'), + nconf = require('nconf'); + + // + // Setup nconf to use (in-order): + // 1. Command-line arguments + // 2. Environment variables + // 3. A file located at 'path/to/config.json' + // + nconf.argv() + .env() + .file({ file: 'path/to/config.json' }); + + // + // Set a few variables on `nconf`. + // + nconf.set('database:host', '127.0.0.1'); + nconf.set('database:port', 5984); + + // + // Get the entire database object from nconf. This will output + // { host: '127.0.0.1', port: 5984 } + // + console.log('foo: ' + nconf.get('foo')); + console.log('NODE_ENV: ' + nconf.get('NODE_ENV')); + console.log('database: ' + nconf.get('database')); + + // + // Save the configuration object to disk + // + nconf.save(function (err) { + fs.readFile('path/to/your/config.json', function (err, data) { + console.dir(JSON.parse(data.toString())) + }); + }); +``` + +If you run the above script: + +``` bash + $ NODE_ENV=production sample.js --foo bar +``` + +The output will be: + +``` + foo: bar + NODE_ENV: production + database: { host: '127.0.0.1', port: 5984 } +``` + +## Hierarchical configuration + +Configuration management can get complicated very quickly for even trivial applications running in production. `nconf` addresses this problem by enabling you to setup a hierarchy for different sources of configuration with no defaults. **The order in which you attach these configuration sources determines their priority in the hierarchy.** Lets take a look at the options available to you + + 1. **nconf.argv(options)** Loads `process.argv` using optimist. If `options` is supplied it is passed along to optimist. + 2. **nconf.env(options)** Loads `process.env` into the hierarchy. + 3. **nconf.file(options)** Loads the configuration data at options.file into the hierarchy. + 4. **nconf.defaults(options)** Loads the data in options.store into the hierarchy. + 5. **nconf.overrides(options)** Loads the data in options.store into the hierarchy. + +A sane default for this could be: + +``` js + var nconf = require('nconf'); + + // + // 1. any overrides + // + nconf.overrides({ + 'always': 'be this value' + }); + + // + // 2. `process.env` + // 3. `process.argv` + // + nconf.env().argv(); + + // + // 4. Values in `config.json` + // + nconf.file('/path/to/config.json'); + + // + // Or with a custom name + // + nconf.file('custom', '/path/to/config.json'); + + // + // Or searching from a base directory. + // Note: `name` is optional. + // + nconf.file(name, { + file: 'config.json', + dir: 'search/from/here', + search: true + }); + + // + // 5. Any default values + // + nconf.defaults({ + 'if nothing else': 'use this value' + }); +``` + +## API Documentation + +The top-level of `nconf` is an instance of the `nconf.Provider` abstracts this all for you into a simple API. + +### nconf.add(name, options) +Adds a new store with the specified `name` and `options`. If `options.type` is not set, then `name` will be used instead: + +``` js + nconf.add('user', { type: 'file', file: '/path/to/userconf.json' }); + nconf.add('global', { type: 'file', file: '/path/to/globalconf.json' }); +``` + +### nconf.use(name, options) +Similar to `nconf.add`, except that it can replace an existing store if new options are provided + +``` js + // + // Load a file store onto nconf with the specified settings + // + nconf.use('file', { file: '/path/to/some/config-file.json' }); + + // + // Replace the file store with new settings + // + nconf.use('file', { file: 'path/to/a-new/config-file.json' }); +``` + +### nconf.remove(name) +Removes the store with the specified `name.` The configuration stored at that level will no longer be used for lookup(s). + +``` js + nconf.remove('file'); +``` + +## Storage Engines + +### Memory +A simple in-memory storage engine that stores a nested JSON representation of the configuration. To use this engine, just call `.use()` with the appropriate arguments. All calls to `.get()`, `.set()`, `.clear()`, `.reset()` methods are synchronous since we are only dealing with an in-memory object. + +``` js + nconf.use('memory'); +``` + +### Argv +Responsible for loading the values parsed from `process.argv` by `optimist` into the configuration hierarchy. + +``` js + // + // Can optionally also be an object literal to pass to `optimist`. + // + nconf.argv(options); +``` + +### Env +Responsible for loading the values parsed from `process.env` into the configuration hierarchy. + +``` js + // + // Can optionally also be an Array of values to limit process.env to. + // + nconf.env(['only', 'load', 'these', 'values', 'from', 'process.env']); + + // + // Can also specify a separator for nested keys (instead of the default ':') + // + nconf.env('__'); + // Get the value of the env variable 'database__host' + var dbHost = nconf.get('database:host'); + + // + // Or use both options + // + nconf.env({ + separator: '__', + whitelist: ['database__host', 'only', 'load', 'these', 'values'] + }); + var dbHost = nconf.get('database:host'); +``` + +### Literal +Loads a given object literal into the configuration hierarchy. Both `nconf.defaults()` and `nconf.overrides()` use the Literal store. + +``` js + nconf.defaults({ + 'some': 'default value' + }); +``` + +### File +Based on the Memory store, but provides additional methods `.save()` and `.load()` which allow you to read your configuration to and from file. As with the Memory store, all method calls are synchronous with the exception of `.save()` and `.load()` which take callback functions. It is important to note that setting keys in the File engine will not be persisted to disk until a call to `.save()` is made. + +``` js + nconf.file('path/to/your/config.json'); + // add multiple files, hierarchically. notice the unique key for each file + nconf.file('user', 'path/to/your/user.json'); + nconf.file('global', 'path/to/your/global.json'); +``` + +The file store is also extensible for multiple file formats, defaulting to `JSON`. To use a custom format, simply pass a format object to the `.use()` method. This object must have `.parse()` and `.stringify()` methods just like the native `JSON` object. + +### Redis +There is a separate Redis-based store available through [nconf-redis][0]. To install and use this store simply: + +``` bash + $ npm install nconf + $ npm install nconf-redis +``` + +Once installing both `nconf` and `nconf-redis`, you must require both modules to use the Redis store: + +``` js + var nconf = require('nconf'); + + // + // Requiring `nconf-redis` will extend the `nconf` + // module. + // + require('nconf-redis'); + + nconf.use('redis', { host: 'localhost', port: 6379, ttl: 60 * 60 * 1000 }); +``` + +## Installation + +### Installing npm (node package manager) +``` + curl http://npmjs.org/install.sh | sh +``` + +### Installing nconf +``` + [sudo] npm install nconf +``` + +## More Documentation +There is more documentation available through docco. I haven't gotten around to making a gh-pages branch so in the meantime if you clone the repository you can view the docs: + +``` + open docs/nconf.html +``` + +## Run Tests +Tests are written in vows and give complete coverage of all APIs and storage engines. + +``` bash + $ npm test +``` + +#### Author: [Charlie Robbins](http://nodejitsu.com) +#### License: MIT + +[0]: http://github.com/indexzero/nconf-redis diff --git a/dist/node_modules/nconf/docs/docco.css b/dist/node_modules/nconf/docs/docco.css new file mode 100644 index 0000000..bd54134 --- /dev/null +++ b/dist/node_modules/nconf/docs/docco.css @@ -0,0 +1,194 @@ +/*--------------------- Layout and Typography ----------------------------*/ +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + color: #252519; + margin: 0; padding: 0; +} +a { + color: #261a3b; +} + a:visited { + color: #261a3b; + } +p { + margin: 0 0 15px 0; +} +h4, h5, h6 { + color: #333; + margin: 6px 0 6px 0; + font-size: 13px; +} + h2, h3 { + margin-bottom: 0; + color: #000; + } + h1 { + margin-top: 40px; + margin-bottom: 15px; + color: #000; + } +#container { + position: relative; +} +#background { + position: fixed; + top: 0; left: 525px; right: 0; bottom: 0; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + z-index: -1; +} +#jump_to, #jump_page { + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 10px Arial; + text-transform: uppercase; + cursor: pointer; + text-align: right; +} +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 5px 10px; +} + #jump_wrapper { + padding: 0; + display: none; + } + #jump_to:hover #jump_wrapper { + display: block; + } + #jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; + } + #jump_page .source { + display: block; + padding: 5px 10px; + text-decoration: none; + border-top: 1px solid #eee; + } + #jump_page .source:hover { + background: #f5f5ff; + } + #jump_page .source:first-child { + } +table td { + border: 0; + outline: 0; +} + td.docs, th.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; + } + .docs pre { + margin: 15px 0 15px; + padding-left: 15px; + } + .docs p tt, .docs p code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; + } + .pilwrap { + position: relative; + } + .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + } + td.docs:hover .pilcrow { + opacity: 1; + } + td.code, th.code { + padding: 14px 15px 16px 25px; + width: 100%; + vertical-align: top; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + } + pre, tt, code { + font-size: 12px; line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; + } + + +/*---------------------- Syntax Highlighting -----------------------------*/ +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +body .hll { background-color: #ffffcc } +body .c { color: #408080; font-style: italic } /* Comment */ +body .err { border: 1px solid #FF0000 } /* Error */ +body .k { color: #954121 } /* Keyword */ +body .o { color: #666666 } /* Operator */ +body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +body .cp { color: #BC7A00 } /* Comment.Preproc */ +body .c1 { color: #408080; font-style: italic } /* Comment.Single */ +body .cs { color: #408080; font-style: italic } /* Comment.Special */ +body .gd { color: #A00000 } /* Generic.Deleted */ +body .ge { font-style: italic } /* Generic.Emph */ +body .gr { color: #FF0000 } /* Generic.Error */ +body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +body .gi { color: #00A000 } /* Generic.Inserted */ +body .go { color: #808080 } /* Generic.Output */ +body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +body .gs { font-weight: bold } /* Generic.Strong */ +body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +body .gt { color: #0040D0 } /* Generic.Traceback */ +body .kc { color: #954121 } /* Keyword.Constant */ +body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ +body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ +body .kp { color: #954121 } /* Keyword.Pseudo */ +body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ +body .kt { color: #B00040 } /* Keyword.Type */ +body .m { color: #666666 } /* Literal.Number */ +body .s { color: #219161 } /* Literal.String */ +body .na { color: #7D9029 } /* Name.Attribute */ +body .nb { color: #954121 } /* Name.Builtin */ +body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +body .no { color: #880000 } /* Name.Constant */ +body .nd { color: #AA22FF } /* Name.Decorator */ +body .ni { color: #999999; font-weight: bold } /* Name.Entity */ +body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +body .nf { color: #0000FF } /* Name.Function */ +body .nl { color: #A0A000 } /* Name.Label */ +body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +body .nt { color: #954121; font-weight: bold } /* Name.Tag */ +body .nv { color: #19469D } /* Name.Variable */ +body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +body .w { color: #bbbbbb } /* Text.Whitespace */ +body .mf { color: #666666 } /* Literal.Number.Float */ +body .mh { color: #666666 } /* Literal.Number.Hex */ +body .mi { color: #666666 } /* Literal.Number.Integer */ +body .mo { color: #666666 } /* Literal.Number.Oct */ +body .sb { color: #219161 } /* Literal.String.Backtick */ +body .sc { color: #219161 } /* Literal.String.Char */ +body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ +body .s2 { color: #219161 } /* Literal.String.Double */ +body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +body .sh { color: #219161 } /* Literal.String.Heredoc */ +body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +body .sx { color: #954121 } /* Literal.String.Other */ +body .sr { color: #BB6688 } /* Literal.String.Regex */ +body .s1 { color: #219161 } /* Literal.String.Single */ +body .ss { color: #19469D } /* Literal.String.Symbol */ +body .bp { color: #954121 } /* Name.Builtin.Pseudo */ +body .vc { color: #19469D } /* Name.Variable.Class */ +body .vg { color: #19469D } /* Name.Variable.Global */ +body .vi { color: #19469D } /* Name.Variable.Instance */ +body .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf.html b/dist/node_modules/nconf/docs/nconf.html new file mode 100644 index 0000000..cb9a1f9 --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf.html @@ -0,0 +1,20 @@ + nconf.js

      nconf.js

      /*
      + * nconf.js: Top-level include for the nconf module
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      +
      +var fs = require('fs'),
      +    async = require('async'),
      +    common = require('./nconf/common'),
      +    Provider = require('./nconf/provider').Provider,
      +    nconf = module.exports = new Provider();

      Expose the version from the package.json using pkginfo.

      require('pkginfo')(module, 'version');

      Expose the various components included with nconf

      nconf.key           = common.key;
      +nconf.path          = common.path;
      +nconf.loadFiles     = common.loadFiles;
      +nconf.loadFilesSync = common.loadFilesSync;
      +nconf.formats       = require('./nconf/formats');
      +nconf.stores        = require('./nconf/stores');
      +nconf.Provider      = Provider;
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf/common.html b/dist/node_modules/nconf/docs/nconf/common.html new file mode 100644 index 0000000..5c359ce --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf/common.html @@ -0,0 +1,85 @@ + common.js

      common.js

      /*
      + * utils.js: Utility functions for the nconf module.
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      + 
      +var fs = require('fs'),
      +    async = require('async'),
      +    formats = require('./formats'),
      +    Memory = require('./stores/Memory').Memory;
      +
      +var common = exports;

      function path (key)

      + +

      @key {string} The ':' delimited key to split

      + +

      Returns a fully-qualified path to a nested nconf key.

      common.path = function (key) {
      +  return key.split(':');
      +};

      function key (arguments)

      + +

      Returns a : joined string from the arguments.

      common.key = function () {
      +  return Array.prototype.slice.call(arguments).join(':');
      +};

      function loadFiles (files, callback)

      + +

      @files {Object|Array} List of files (or settings object) to load.

      + +

      @callback {function} Continuation to respond to when complete.

      + +

      Loads all the data in the specified files.

      common.loadFiles = function (files, callback) {
      +  if (!files) {
      +    return callback(null, {});
      +  }
      +
      +  var options = Array.isArray(files) ? { files: files } : files;

      Set the default JSON format if not already +specified

        options.format = options.format || formats.json;
      +
      +  function parseFile (file, next) {
      +    fs.readFile(file, function (err, data) {
      +      return !err 
      +        ? next(null, options.format.parse(data.toString()))
      +        : next(err);
      +    });
      +  }
      +
      +  async.map(files, parseFile, function (err, objs) {
      +    return err ? callback(err) : callback(null, common.merge(objs));
      +  });
      +};

      function loadFilesSync (files)

      + +

      @files {Object|Array} List of files (or settings object) to load.

      + +

      Loads all the data in the specified files synchronously.

      common.loadFilesSync = function (files) {
      +  if (!files) {
      +    return;
      +  }

      Set the default JSON format if not already +specified

        var options = Array.isArray(files) ? { files: files } : files;
      +  options.format = options.format || formats.json;
      +
      +  return common.merge(files.map(function (file) {
      +    return options.format.parse(fs.readFileSync(file, 'utf8'));
      +  }));
      +};

      function merge (objs)

      + +

      @objs {Array} Array of object literals to merge

      + +

      Merges the specified objs using a temporary instance +of stores.Memory.

      common.merge = function (objs) {
      +  var store = new Memory();
      +  
      +  objs.forEach(function (obj) {
      +    Object.keys(obj).forEach(function (key) {
      +      store.merge(key, obj[key]);
      +    });
      +  });
      +  
      +  return store.store;
      +};

      function capitalize (str)

      + +

      @str {string} String to capitalize

      + +

      Capitalizes the specified str.

      common.capitalize = function (str) {
      +  return str && str[0].toUpperCase() + str.slice(1);
      +};
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf/formats.html b/dist/node_modules/nconf/docs/nconf/formats.html new file mode 100644 index 0000000..a2ab2f5 --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf/formats.html @@ -0,0 +1,22 @@ + formats.js

      formats.js

      /*
      + * formats.js: Default formats supported by nconf
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      +
      +var ini = require('ini');
      +
      +var formats = exports;

      @json

      + +

      Standard JSON format which pretty prints .stringify().

      formats.json = {
      +  stringify: function (obj) {
      +    return JSON.stringify(obj, null, 2)
      +  },
      +  parse: JSON.parse
      +};

      @ini

      + +

      Standard INI format supplied from the ini module +http://en.wikipedia.org/wiki/INI_file

      formats.ini = ini;
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf/provider.html b/dist/node_modules/nconf/docs/nconf/provider.html new file mode 100644 index 0000000..66c6718 --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf/provider.html @@ -0,0 +1,378 @@ + provider.js

      provider.js

      /*
      + * provider.js: Abstraction providing an interface into pluggable configuration storage.
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      +
      +var async = require('async'),
      +    common = require('./common'),
      +    stores = require('./stores');

      function Provider (options)

      + +

      @options {Object} Options for this instance.

      + +

      Constructor function for the Provider object responsible +for exposing the pluggable storage features of nconf.

      var Provider = exports.Provider = function (options) {
      +  var self = this;  
      +  

      Setup default options for working with stores, +overrides, process.env and process.argv.

        options         = options           || {};
      +  this._overrides = options.overrides || null;
      +  this._argv      = options.argv      || false;
      +  this._env       = options.env       || false;
      +  this._reserved  = Object.keys(Provider.prototype);
      +  this._stores    = [];
      +  

      Add the default system store for working with +overrides, process.env, process.argv and +a simple in-memory objects.

        this.add('system', options);
      +  
      +  if (options.type) {
      +    this.add(options.type, options);
      +  }
      +  else if (options.store) {
      +    this.add(options.store.name || options.store.type, options.store);
      +  }
      +  else if (options.stores) {
      +    Object.keys(options.stores).forEach(function (store) {
      +      self.add(store.name || store.type, store);
      +    });
      +  }
      +};

      function use (name, options)

      + +

      @type {string} Type of the nconf store to use.

      + +

      @options {Object} Options for the store instance.

      + +

      Adds (or replaces) a new store with the specified name +and options. If options.type is not set, then name +will be used instead:

      + +

      provider.use('file'); + provider.use('file', { type: 'file', filename: '/path/to/userconf' })

      Provider.prototype.use = function (name, options) {
      +  if (name === 'system') {
      +    return;
      +  }
      +  else if (this._reserved.indexOf(name) !== -1) {
      +    throw new Error('Cannot use reserved name: ' + name);
      +  }
      +  
      +  options  = options      || {};
      +  var type = options.type || name;
      +
      +  function sameOptions (store) {
      +    return Object.keys(options).every(function (key) {
      +      return options[key] === store[key];
      +    });
      +  }
      +  
      +  var store = this[name],
      +      update = store && !sameOptions(store);
      +  
      +  if (!store || update) {
      +    if (update) {
      +      this.remove(name);
      +    }
      +    
      +    this.add(name, options);
      +  }
      +  
      +  return this;
      +};

      function add (name, options)

      + +

      @name {string} Name of the store to add to this instance

      + +

      @options {Object} Options for the store to create

      + +

      Adds a new store with the specified name and options. If options.type +is not set, then name will be used instead:

      + +

      provider.add('memory'); + provider.add('userconf', { type: 'file', filename: '/path/to/userconf' })

      Provider.prototype.add = function (name, options) {
      +  if (this._reserved.indexOf(name) !== -1) {
      +    throw new Error('Cannot use reserved name: ' + name);
      +  }
      +  
      +  options  = options      || {};
      +  var type = options.type || name;
      +  
      +  if (Object.keys(stores).indexOf(common.capitalize(type)) === -1) {
      +    throw new Error('Cannot add store with unknown type: ' + type);
      +  }
      +  
      +  this[name] = this.create(type, options);
      +  this._stores.push(name);
      +  
      +  if (this[name].loadSync) {
      +    this[name].loadSync();
      +  }
      +};

      function remove (name)

      + +

      @name {string} Name of the store to remove from this instance

      + +

      Removes a store with the specified name from this instance. Users +are allowed to pass in a type argument (e.g. memory) as name if +this was used in the call to .add().

      Provider.prototype.remove = function (name) {
      +  if (this._reserved.indexOf(name) !== -1) {
      +    throw new Error('Cannot use reserved name: ' + name);
      +  }
      +  else if (!this[name]) {
      +    throw new Error('Cannot remove store that does not exist: ' + name);
      +  }
      +  
      +  delete this[name];
      +  this._stores.splice(this._stores.indexOf(name), 1);
      +};

      function create (type, options)

      + +

      @type {string} Type of the nconf store to use.

      + +

      @options {Object} Options for the store instance.

      + +

      Creates a store of the specified type using the +specified options.

      Provider.prototype.create = function (type, options) {
      +  return new stores[common.capitalize(type.toLowerCase())](options);
      +};

      function get (key, callback)

      + +

      @key {string} Key to retrieve for this instance.

      + +

      @callback {function} Optional Continuation to respond to when complete.

      + +

      Retrieves the value for the specified key (if any).

      Provider.prototype.get = function (key, callback) {

      If there is no callback we can short-circuit into the default +logic for traversing stores.

        if (!callback) {
      +    return this._execute('get', 1, key, callback);
      +  }
      +  

      Otherwise the asynchronous, hierarchical get is +slightly more complicated because we do not need to traverse +the entire set of stores, but up until there is a defined value.

        var current = 0,
      +      self = this,
      +      response;
      +      
      +  async.whilst(function () {
      +    return typeof response === 'undefined' && current < self._stores.length;
      +  }, function (next) {
      +    var store = self[self._stores[current]];
      +    current++;
      +    
      +    if (store.get.length >= 2) {
      +      return store.get(key, function (err, value) {
      +        if (err) {
      +          return next(err);
      +        }
      +        
      +        response = value;
      +        next();
      +      });
      +    }
      +    
      +    response = store.get(key);
      +    next();
      +  }, function (err) {
      +    return err ? callback(err) : callback(null, response);
      +  });
      +};

      function set (key, value, callback)

      + +

      @key {string} Key to set in this instance

      + +

      @value {literal|Object} Value for the specified key

      + +

      @callback {function} Optional Continuation to respond to when complete.

      + +

      Sets the value for the specified key in this instance.

      Provider.prototype.set = function (key, value, callback) {
      +  return this._execute('set', 2, key, value, callback);
      +};

      function reset (callback)

      + +

      @callback {function} Optional Continuation to respond to when complete.

      + +

      Clears all keys associated with this instance.

      Provider.prototype.reset = function (callback) {
      +  return this._execute('reset', 0, callback);  
      +};

      function clear (key, callback)

      + +

      @key {string} Key to remove from this instance

      + +

      @callback {function} Optional Continuation to respond to when complete.

      + +

      Removes the value for the specified key from this instance.

      Provider.prototype.clear = function (key, callback) {
      +  return this._execute('clear', 1, key, callback);
      +};

      function merge ([key,] value [, callback])

      + +

      @key {string} Key to merge the value into

      + +

      @value {literal|Object} Value to merge into the key

      + +

      @callback {function} Optional Continuation to respond to when complete.

      + +

      Merges the properties in value into the existing object value at key.

      + +
        +
      1. If the existing value key is not an Object, it will be completely overwritten.
      2. +
      3. If key is not supplied, then the value will be merged into the root.
      4. +
      Provider.prototype.merge = function () {
      +  var self = this,
      +      args = Array.prototype.slice.call(arguments),
      +      callback = typeof args[args.length - 1] === 'function' && args.pop(),
      +      value = args.pop(),
      +      key = args.pop();
      +      
      +  function mergeProperty (prop, next) {
      +    return self._execute('merge', 2, prop, value[prop], next);
      +  }
      +      
      +  if (!key) {
      +    if (Array.isArray(value) || typeof value !== 'object') {
      +      return onError(new Error('Cannot merge non-Object into top-level.'), callback);
      +    }
      +    
      +    return async.forEach(Object.keys(value), mergeProperty, callback || function () { })
      +  }
      +  
      +  return this._execute('merge', 2, key, value, callback);
      +};

      function load (callback)

      + +

      @callback {function} Continuation to respond to when complete.

      + +

      Responds with an Object representing all keys associated in this instance.

      Provider.prototype.load = function (callback) {
      +  var self = this;
      +  
      +  function loadStoreSync(name) {
      +    var store = self[name];
      +    
      +    if (!store.loadSync) {
      +      throw new Error('nconf store ' + store.type + ' has no loadSync() method');
      +    }
      +    
      +    return store.loadSync();
      +  }
      +  
      +  function loadStore(name, next) {
      +    var store = self[name];
      +    
      +    if (!store.load && !store.loadSync) {
      +      return next(new Error('nconf store ' + store.type + ' has no load() method'));
      +    }
      +    
      +    return store.loadSync
      +      ? next(null, store.loadSync())
      +      : store.load(next);
      +  }
      +  

      If we don't have a callback and the current +store is capable of loading synchronously +then do so.

        if (!callback) {
      +    return common.merge(this._stores.map(loadStoreSync));
      +  }
      +  
      +  async.map(this._stores, loadStore, function (err, objs) {
      +    return err ? callback(err) : callback(null, common.merge(objs));
      +  });
      +};

      function save (value, callback)

      + +

      @value {Object} Optional Config object to set for this instance

      + +

      @callback {function} Continuation to respond to when complete.

      + +

      Removes any existing configuration settings that may exist in this +instance and then adds all key-value pairs in value.

      Provider.prototype.save = function (value, callback) {
      +  if (!callback && typeof value === 'function') {
      +    callback = value;
      +    value = null;
      +  }
      +  
      +  var self = this;
      +  
      +  function saveStoreSync(name) {
      +    var store = self[name];
      +    
      +    if (!store.saveSync) {
      +      throw new Error('nconf store ' + store.type + ' has no saveSync() method');
      +    }
      +    
      +    return store.saveSync();
      +  }
      +  
      +  function saveStore(name, next) {
      +    var store = self[name];
      +    
      +    if (!store.save && !store.saveSync) {
      +      return next(new Error('nconf store ' + store.type + ' has no save() method'));
      +    }
      +    
      +    return store.saveSync
      +      ? next(null, store.saveSync())
      +      : store.save(next);
      +  }
      +  

      If we don't have a callback and the current +store is capable of saving synchronously +then do so.

        if (!callback) {
      +    return common.merge(this._stores.map(saveStoreSync));
      +  }
      +  
      +  async.map(this._stores, saveStore, function (err, objs) {
      +    return err ? callback(err) : callback();
      +  });  
      +};

      @private function _execute (action, syncLength, [arguments])

      + +

      @action {string} Action to execute on this.store.

      + +

      @syncLength {number} Function length of the sync version.

      + +

      @arguments {Array} Arguments array to apply to the action

      + +

      Executes the specified action on all stores for this instance, ensuring a callback supplied +to a synchronous store function is still invoked.

      Provider.prototype._execute = function (action, syncLength /* [arguments] */) {
      +  var args = Array.prototype.slice.call(arguments, 2),
      +      callback = typeof args[args.length - 1] === 'function' && args.pop(),
      +      self = this,
      +      response;
      +  
      +  function runAction (name, next) {
      +    var store = self[name]
      +    
      +    return store[action].length > syncLength
      +      ? store[action].apply(store, args.concat(next))
      +      : next(null, store[action].apply(store, args));
      +  }
      +  
      +  if (callback) {
      +    return async.forEach(self._stores, runAction, function (err) {
      +      return err ? callback(err) : callback();
      +    });
      +  }
      +
      +  this._stores.forEach(function (name) {
      +    var store = self[name];
      +    response = store[action].apply(store, args);
      +  });
      +    
      +  return response;
      +}

      @argv {boolean}

      + +

      Gets or sets a property representing overrides which supercede all +other values for this instance.

      Provider.prototype.__defineSetter__('overrides', function (val) { updateSystem.call(this, 'overrides', val) });
      +Provider.prototype.__defineGetter__('overrides', function () { return this._argv });

      @argv {boolean}

      + +

      Gets or sets a property indicating if we should wrap calls to .get +by checking optimist.argv. Can be a boolean or the pass-thru +options for optimist.

      Provider.prototype.__defineSetter__('argv', function (val) { updateSystem.call(this, 'argv', val) });
      +Provider.prototype.__defineGetter__('argv', function () { return this._argv });

      @env {boolean}

      + +

      Gets or sets a property indicating if we should wrap calls to .get +by checking process.env. Can be a boolean or an Array of +environment variables to extract.

      Provider.prototype.__defineSetter__('env', function (val) { updateSystem.call(this, 'env', val) });
      +Provider.prototype.__defineGetter__('env', function () { return this._env });

      Throw the err if a callback is not supplied

      function onError(err, callback) {
      +  if (callback) {
      +    return callback(err);
      +  }
      +  
      +  throw err;
      +}

      Helper function for working with the +default system store for providers.

      function updateSystem(prop, value) {
      +  var system = this['system'];
      +  
      +  if (system[prop] === value) {
      +    return;
      +  }
      +  
      +  value = value || false;
      +  this['_' + prop] = value;
      +  system[prop] = value;
      +  system.loadSync();
      +}
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf/stores.html b/dist/node_modules/nconf/docs/nconf/stores.html new file mode 100644 index 0000000..d799966 --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf/stores.html @@ -0,0 +1,19 @@ + stores.js

      stores.js

      /*
      + * stores.js: Top-level include for all nconf stores
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      + 
      +var fs = require('fs'),
      +    common = require('./common'),
      +    stores = exports;

      Setup all stores as lazy-loaded getters.

      fs.readdirSync(__dirname + '/stores').forEach(function (file) {
      +  var store = file.replace('.js', ''),
      +      name  = common.capitalize(store);
      +      
      +  stores.__defineGetter__(name, function () {
      +    return require('./stores/' + store)[name];
      +  });
      +});
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf/stores/file.html b/dist/node_modules/nconf/docs/nconf/stores/file.html new file mode 100644 index 0000000..7a00765 --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf/stores/file.html @@ -0,0 +1,170 @@ + file.js

      file.js

      /*
      + * file.js: Simple file storage engine for nconf files
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      +
      +var fs = require('fs'),
      +    path = require('path'),
      +    util = require('util'),
      +    formats = require('../formats'),
      +    Memory = require('./memory').Memory;
      + 

      function File (options)

      + +

      @options {Object} Options for this instance

      + +

      Constructor function for the File nconf store, a simple abstraction +around the Memory store that can persist configuration to disk.

      var File = exports.File = function (options) {
      +  if (!options || !options.file) {
      +    throw new Error ('Missing required option `files`');
      +  }
      +
      +  Memory.call(this, options);
      +
      +  this.type   = 'file';
      +  this.file   = options.file;
      +  this.dir    = options.dir    || process.cwd();
      +  this.format = options.format || formats.json;
      +};

      Inherit from the Memory store

      util.inherits(File, Memory);

      function save (value, callback)

      + +

      @value {Object} Ignored Left here for consistency

      + +

      @callback {function} Continuation to respond to when complete.

      + +

      Saves the current configuration object to disk at this.file +using the format specified by this.format.

      File.prototype.save = function (value, callback) {
      +  if (!callback) {
      +    callback = value;
      +    value = null;
      +  }
      +  
      +  fs.writeFile(this.file, this.format.stringify(this.store), function (err) {
      +    return err ? callback(err) : callback();
      +  });
      +};

      function saveSync (value, callback)

      + +

      @value {Object} Ignored Left here for consistency

      + +

      @callback {function} Optional Continuation to respond to when complete.

      + +

      Saves the current configuration object to disk at this.file +using the format specified by this.format synchronously.

      File.prototype.saveSync = function (value) {
      +  try {
      +    fs.writeFileSync(this.file, this.format.stringify(this.store));
      +  }
      +  catch (ex) {
      +    throw(ex);
      +  }
      +};

      function load (callback)

      + +

      @callback {function} Continuation to respond to when complete.

      + +

      Responds with an Object representing all keys associated in this instance.

      File.prototype.load = function (callback) {
      +  var self = this;
      +
      +  path.exists(self.file, function (exists) {
      +    if (!exists) {

      If the path we are attempting to load doesn't exist, create it

            self.save({}, function (err) {
      +        self.store = {};
      +        return callback(err, self.store);
      +      });
      +    }
      +    else {

      Else, the path exists, read it from disk

            fs.readFile(self.file, function (err, data) {
      +        if (err) {
      +          return callback(err);
      +        }
      +        
      +        try {
      +          self.store = self.format.parse(data.toString());
      +        }
      +        catch (ex) {
      +          return callback(new Error("Error parsing your JSON configuration file."));
      +        }
      +        
      +        callback(null, self.store);
      +      });
      +    }
      +  });
      +};

      function load (callback)

      + +

      @callback {function} Optional Continuation to respond to when complete.

      + +

      Attempts to load the data stored in this.file synchronously and responds appropriately.

      File.prototype.loadSync = function () {
      +  var data, self = this;
      +
      +  if (!path.existsSync(self.file)) {

      If the path we are attempting to load doesn't exist, create it

          self.saveSync({});
      +    self.store = {};
      +    data = {};
      +  }
      +  else {

      Else, the path exists, read it from disk

          try {
      +      data = this.format.parse(fs.readFileSync(this.file, 'utf8'));
      +      this.store = data;
      +    }
      +    catch (ex) {
      +      throw new Error("Error parsing your JSON configuration file.")
      +    }
      +  }
      +
      +  return data;
      +};

      function search (base)

      + +

      @base {string} Base directory (or file) to begin searching for the target file.

      + +

      Attempts to find this.file by iteratively searching up the +directory structure

      File.prototype.search = function (base) {
      +  var looking = true,
      +      fullpath,
      +      previous,
      +      stats;
      +
      +  base = base || process.cwd();
      +
      +  if (this.file[0] === '/') {

      If filename for this instance is a fully qualified path +(i.e. it starts with a '/') then check if it exists

          try {
      +      stats = fs.statSync(fs.realpathSync(this.file));
      +      if (stats.isFile()) {
      +        fullpath = this.file;
      +        looking = false;
      +      }
      +    }
      +    catch (ex) {

      Ignore errors

          }
      +  }
      +
      +  if (looking && base) {

      Attempt to stat the realpath located at base +if the directory does not exist then return false.

          try {
      +      var stat = fs.statSync(fs.realpathSync(base));
      +      looking = stat.isDirectory();
      +    }
      +    catch (ex) {
      +      return false;
      +    }
      +  }
      +  
      +  while (looking) {

      Iteratively look up the directory structure from base

          try {
      +      stats = fs.statSync(fs.realpathSync(fullpath = path.join(base, this.file)));
      +      looking = stats.isDirectory();
      +    }
      +    catch (ex) {
      +      previous = base;
      +      base = path.dirname(base);
      +
      +      if (previous === base) {

      If we've reached the top of the directory structure then simply use +the default file path.

              try {
      +          stats = fs.statSync(fs.realpathSync(fullpath = path.join(this.dir, this.file)));
      +          if (stats.isDirectory()) {
      +            fullpath = undefined;
      +          }
      +        }
      +        catch (ex) {

      Ignore errors

              }
      +        
      +        looking = false;
      +      }
      +    }
      +  }

      Set the file for this instance to the fullpath +that we have found during the search. In the event that +the search was unsuccessful use the original value for this.file.

        this.file = fullpath || this.file;
      +  
      +  return fullpath;
      +};
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf/stores/memory.html b/dist/node_modules/nconf/docs/nconf/stores/memory.html new file mode 100644 index 0000000..8867394 --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf/stores/memory.html @@ -0,0 +1,143 @@ + memory.js

      memory.js

      /*
      + * memory.js: Simple memory storage engine for nconf configuration(s)
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      +
      +var common = require('../common');

      function Memory (options)

      + +

      @options {Object} Options for this instance

      + +

      Constructor function for the Memory nconf store which maintains +a nested json structure based on key delimiters :.

      + +

      e.g. my:nested:key ==> { my: { nested: { key: } } }

      var Memory = exports.Memory = function (options) {
      +  options       = options || {};
      +  this.type     = 'memory';
      +  this.store    = {};
      +  this.mtimes   = {};
      +  this.readOnly = false;
      +  this.loadFrom = options.loadFrom || null;
      +  
      +  if (this.loadFrom) {
      +    this.store = common.loadFilesSync(this.loadFrom);
      +  }
      +};

      function get (key)

      + +

      @key {string} Key to retrieve for this instance.

      + +

      Retrieves the value for the specified key (if any).

      Memory.prototype.get = function (key) {
      +  var target = this.store, 
      +      path   = common.path(key);

      Scope into the object to get the appropriate nested context

        while (path.length > 0) {
      +    key = path.shift();
      +    if (!(target && key in target)) {
      +      return;
      +    }
      +    
      +    target = target[key];
      +    if (path.length === 0) {
      +      return target;
      +    }
      +  }
      +};

      function set (key, value)

      + +

      @key {string} Key to set in this instance

      + +

      @value {literal|Object} Value for the specified key

      + +

      Sets the value for the specified key in this instance.

      Memory.prototype.set = function (key, value) {
      +  if (this.readOnly) {
      +    return false;
      +  }
      +  
      +  var target = this.store, 
      +      path   = common.path(key);
      +  

      Update the mtime (modified time) of the key

        this.mtimes[key] = Date.now();
      +  

      Scope into the object to get the appropriate nested context

        while (path.length > 1) {
      +    key = path.shift();
      +    if (!target[key] || typeof target[key] !== 'object') {
      +      target[key] = {};
      +    }
      +    
      +    target = target[key];
      +  }
      +  

      Set the specified value in the nested JSON structure

        key = path.shift();
      +  target[key] = value;
      +  return true;
      +};

      function clear (key)

      + +

      @key {string} Key to remove from this instance

      + +

      Removes the value for the specified key from this instance.

      Memory.prototype.clear = function (key) {
      +  if (this.readOnly) {
      +    return false;
      +  }
      +  
      +  var target = this.store, 
      +      path   = common.path(key);
      +  

      Remove the key from the set of mtimes (modified times)

        delete this.mtimes[key];
      +  

      Scope into the object to get the appropriate nested context

        while (path.length > 1) {
      +    key = path.shift();
      +    if (!target[key]) {
      +      return;
      +    }
      +    
      +    target = target[key];
      +  }
      +  

      Delete the key from the nested JSON structure

        key = path.shift();
      +  delete target[key];
      +  return true;
      +};

      function merge (key, value)

      + +

      @key {string} Key to merge the value into

      + +

      @value {literal|Object} Value to merge into the key

      + +

      Merges the properties in value into the existing object value +at key. If the existing value key is not an Object, it will be +completely overwritten.

      Memory.prototype.merge = function (key, value) {
      +  if (this.readOnly) {
      +    return false;
      +  }
      +  

      If the key is not an Object or is an Array, +then simply set it. Merging is for Objects.

        if (typeof value !== 'object' || Array.isArray(value)) {
      +    return this.set(key, value);
      +  }
      +  
      +  var self    = this,
      +      target  = this.store, 
      +      path    = common.path(key),
      +      fullKey = key;
      +  

      Update the mtime (modified time) of the key

        this.mtimes[key] = Date.now();
      +  

      Scope into the object to get the appropriate nested context

        while (path.length > 1) {
      +    key = path.shift();
      +    if (!target[key]) {
      +      target[key] = {};
      +    }
      +    
      +    target = target[key];
      +  }

      Set the specified value in the nested JSON structure

        key = path.shift();
      +  

      If the current value at the key target is not an Object, +or is an Array then simply override it because the new value +is an Object.

        if (typeof target[key] !== 'object' || Array.isArray(target[key])) {
      +    target[key] = value;
      +    return true;
      +  }
      +  
      +  return Object.keys(value).every(function (nested) {
      +    return self.merge(fullKey + ':' + nested, value[nested]);
      +  });
      +};

      function reset (callback)

      + +

      Clears all keys associated with this instance.

      Memory.prototype.reset = function () {
      +  if (this.readOnly) {
      +    return false;
      +  }
      +  
      +  this.mtimes = {};
      +  this.store  = {};
      +  return true;
      +};
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/docs/nconf/stores/system.html b/dist/node_modules/nconf/docs/nconf/stores/system.html new file mode 100644 index 0000000..9e89544 --- /dev/null +++ b/dist/node_modules/nconf/docs/nconf/stores/system.html @@ -0,0 +1,98 @@ + system.js

      system.js

      /*
      + * system.js: Simple memory-based store for process environment variables and
      + *            command-line arguments.
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      + 
      +var util = require('util'),
      +    Memory = require('./memory').Memory;
      + 

      function System (options)

      + +

      @options {Object} Options for this instance.

      + +

      Constructor function for the System nconf store, a simple abstraction +around the Memory store that can read process environment variables +and command-line arguments.

      var System = exports.System = function (options) {
      +  options = options || {};
      +  Memory.call(this, options);
      +
      +  this.type      = 'system';
      +  this.overrides = options.overrides || null;
      +  this.env       = options.env       || false;
      +  this.argv      = options.argv      || false;
      +};

      Inherit from the Memory store

      util.inherits(System, Memory);

      function loadSync ()

      + +

      Loads the data passed in from process.env into this instance.

      System.prototype.loadSync = function () {
      +  if (this.env) {
      +    this.loadEnv();
      +  }
      +  
      +  if (this.argv) {
      +    this.loadArgv();
      +  }
      +  
      +  if (this.overrides) {
      +    this.loadOverrides();
      +  }
      +  
      +  return this.store;
      +};

      function loadOverrides ()

      + +

      Loads any overrides set on this instance into +the underlying managed Memory store.

      System.prototype.loadOverrides = function () {
      +  if (!this.overrides) {
      +    return;
      +  }
      +  
      +  var self = this,
      +      keys = Object.keys(this.overrides);
      +  
      +  keys.forEach(function (key) {
      +    self.set(key, self.overrides[key]);
      +  });
      +  
      +  return this.store;
      +};

      function loadArgv ()

      + +

      Loads the data passed in from the command-line arguments +into this instance.

      System.prototype.loadArgv = function () {
      +  var self = this, 
      +      argv;
      +  
      +  if (typeof this.argv === 'object') {
      +    argv = require('optimist').options(this.argv).argv;
      +  }
      +  else if (this.argv) {
      +    argv = require('optimist').argv;
      +  }
      +  
      +  if (!argv) {
      +    return;
      +  }
      +  
      +  Object.keys(argv).forEach(function (key) {
      +    self.set(key, argv[key]);
      +  });
      +  
      +  return this.store;
      +};

      function loadEnv ()

      + +

      Loads the data passed in from process.env into this instance.

      System.prototype.loadEnv = function () {
      +  var self = this;
      +  
      +  if (!this.env) {
      +    return;
      +  }
      +  
      +  Object.keys(process.env).filter(function (key) {
      +    return !self.env.length || self.env.indexOf(key) !== -1;
      +  }).forEach(function (key) {
      +    self.set(key, process.env[key]);
      +  });
      +    
      +  return this.store;
      +};
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/lib/nconf.js b/dist/node_modules/nconf/lib/nconf.js new file mode 100644 index 0000000..be81056 --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf.js @@ -0,0 +1,39 @@ +/* + * nconf.js: Top-level include for the nconf module + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + async = require('async'), + common = require('./nconf/common'), + Provider = require('./nconf/provider').Provider, + nconf = module.exports = new Provider(); + +// +// Expose the version from the package.json using `pkginfo`. +// +require('pkginfo')(module, 'version'); + +// +// Setup all stores as lazy-loaded getters. +// +fs.readdirSync(__dirname + '/nconf/stores').forEach(function (file) { + var store = file.replace('.js', ''), + name = common.capitalize(store); + + nconf.__defineGetter__(name, function () { + return require('./nconf/stores/' + store)[name]; + }); +}); + +// +// Expose the various components included with nconf +// +nconf.key = common.key; +nconf.path = common.path; +nconf.loadFiles = common.loadFiles; +nconf.loadFilesSync = common.loadFilesSync; +nconf.formats = require('./nconf/formats'); +nconf.Provider = Provider; diff --git a/dist/node_modules/nconf/lib/nconf/common.js b/dist/node_modules/nconf/lib/nconf/common.js new file mode 100644 index 0000000..b6e16d9 --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/common.js @@ -0,0 +1,111 @@ +/* + * utils.js: Utility functions for the nconf module. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + async = require('async'), + formats = require('./formats'), + Memory = require('./stores/memory').Memory; + +var common = exports; + +// +// ### function path (key) +// #### @key {string} The ':' delimited key to split +// Returns a fully-qualified path to a nested nconf key. +// +common.path = function (key) { + return key.split(':'); +}; + +// +// ### function key (arguments) +// Returns a `:` joined string from the `arguments`. +// +common.key = function () { + return Array.prototype.slice.call(arguments).join(':'); +}; + +// +// ### function loadFiles (files, callback) +// #### @files {Object|Array} List of files (or settings object) to load. +// #### @callback {function} Continuation to respond to when complete. +// Loads all the data in the specified `files`. +// +common.loadFiles = function (files, callback) { + if (!files) { + return callback(null, {}); + } + + var options = Array.isArray(files) ? { files: files } : files; + + // + // Set the default JSON format if not already + // specified + // + options.format = options.format || formats.json; + + function parseFile (file, next) { + fs.readFile(file, function (err, data) { + return !err + ? next(null, options.format.parse(data.toString())) + : next(err); + }); + } + + async.map(files, parseFile, function (err, objs) { + return err ? callback(err) : callback(null, common.merge(objs)); + }); +}; + +// +// ### function loadFilesSync (files) +// #### @files {Object|Array} List of files (or settings object) to load. +// Loads all the data in the specified `files` synchronously. +// +common.loadFilesSync = function (files) { + if (!files) { + return; + } + + // + // Set the default JSON format if not already + // specified + // + var options = Array.isArray(files) ? { files: files } : files; + options.format = options.format || formats.json; + + return common.merge(files.map(function (file) { + return options.format.parse(fs.readFileSync(file, 'utf8')); + })); +}; + +// +// ### function merge (objs) +// #### @objs {Array} Array of object literals to merge +// Merges the specified `objs` using a temporary instance +// of `stores.Memory`. +// +common.merge = function (objs) { + var store = new Memory(); + + objs.forEach(function (obj) { + Object.keys(obj).forEach(function (key) { + store.merge(key, obj[key]); + }); + }); + + return store.store; +}; + +// +// ### function capitalize (str) +// #### @str {string} String to capitalize +// Capitalizes the specified `str`. +// +common.capitalize = function (str) { + return str && str[0].toUpperCase() + str.slice(1); +}; diff --git a/dist/node_modules/nconf/lib/nconf/formats.js b/dist/node_modules/nconf/lib/nconf/formats.js new file mode 100644 index 0000000..f32268c --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/formats.js @@ -0,0 +1,28 @@ +/* + * formats.js: Default formats supported by nconf + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var ini = require('ini'); + +var formats = exports; + +// +// ### @json +// Standard JSON format which pretty prints `.stringify()`. +// +formats.json = { + stringify: function (obj, replacer, spacing) { + return JSON.stringify(obj, replacer || null, spacing || 2) + }, + parse: JSON.parse +}; + +// +// ### @ini +// Standard INI format supplied from the `ini` module +// http://en.wikipedia.org/wiki/INI_file +// +formats.ini = ini; diff --git a/dist/node_modules/nconf/lib/nconf/provider.js b/dist/node_modules/nconf/lib/nconf/provider.js new file mode 100644 index 0000000..e83c3c6 --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/provider.js @@ -0,0 +1,565 @@ +/* + * provider.js: Abstraction providing an interface into pluggable configuration storage. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var async = require('async'), + common = require('./common'); + +// +// ### function Provider (options) +// #### @options {Object} Options for this instance. +// Constructor function for the Provider object responsible +// for exposing the pluggable storage features of `nconf`. +// +var Provider = exports.Provider = function (options) { + // + // Setup default options for working with `stores`, + // `overrides`, `process.env` and `process.argv`. + // + options = options || {}; + this.stores = {}; + this.sources = []; + this.init(options); +}; + +// +// Define wrapper functions for using basic stores +// in this instance +// +['argv', 'env'].forEach(function (type) { + Provider.prototype[type] = function (options) { + return this.add(type, options); + }; +}); + +// +// ### function file (key, options) +// #### @key {string|Object} Fully qualified options, name of file store, or path. +// #### @path {string|Object} **Optional** Full qualified options, or path. +// Adds a new `File` store to this instance. Accepts the following options +// +// nconf.file({ file: '.jitsuconf', dir: process.env.HOME, search: true }); +// nconf.file('path/to/config/file'); +// nconf.file('userconfig', 'path/to/config/file'); +// nconf.file('userconfig', { file: '.jitsuconf', search: true }); +// +Provider.prototype.file = function (key, options) { + if (arguments.length == 1) { + options = typeof key === 'string' ? { file: key } : key; + key = 'file'; + } + else { + options = typeof options === 'string' + ? { file: options } + : options; + } + + options.type = 'file'; + return this.add(key, options); +}; + +// +// Define wrapper functions for using +// overrides and defaults +// +['defaults', 'overrides'].forEach(function (type) { + Provider.prototype[type] = function (options) { + options = options || {}; + if (!options.type) { + options.type = 'literal'; + } + + return this.add(type, options); + }; +}); + +// +// ### function use (name, options) +// #### @type {string} Type of the nconf store to use. +// #### @options {Object} Options for the store instance. +// Adds (or replaces) a new store with the specified `name` +// and `options`. If `options.type` is not set, then `name` +// will be used instead: +// +// provider.use('file'); +// provider.use('file', { type: 'file', filename: '/path/to/userconf' }) +// +Provider.prototype.use = function (name, options) { + options = options || {}; + var type = options.type || name; + + function sameOptions (store) { + return Object.keys(options).every(function (key) { + return options[key] === store[key]; + }); + } + + var store = this.stores[name], + update = store && !sameOptions(store); + + if (!store || update) { + if (update) { + this.remove(name); + } + + this.add(name, options); + } + + return this; +}; + +// +// ### function add (name, options) +// #### @name {string} Name of the store to add to this instance +// #### @options {Object} Options for the store to create +// Adds a new store with the specified `name` and `options`. If `options.type` +// is not set, then `name` will be used instead: +// +// provider.add('memory'); +// provider.add('userconf', { type: 'file', filename: '/path/to/userconf' }) +// +Provider.prototype.add = function (name, options) { + options = options || {}; + var type = options.type || name; + + if (!require('../nconf')[common.capitalize(type)]) { + throw new Error('Cannot add store with unknown type: ' + type); + } + + this.stores[name] = this.create(type, options); + + if (this.stores[name].loadSync) { + this.stores[name].loadSync(); + } + + return this; +}; + +// +// ### function remove (name) +// #### @name {string} Name of the store to remove from this instance +// Removes a store with the specified `name` from this instance. Users +// are allowed to pass in a type argument (e.g. `memory`) as name if +// this was used in the call to `.add()`. +// +Provider.prototype.remove = function (name) { + delete this.stores[name]; + return this; +}; + +// +// ### function create (type, options) +// #### @type {string} Type of the nconf store to use. +// #### @options {Object} Options for the store instance. +// Creates a store of the specified `type` using the +// specified `options`. +// +Provider.prototype.create = function (type, options) { + return new (require('../nconf')[common.capitalize(type.toLowerCase())])(options); +}; + +// +// ### function init (options) +// #### @options {Object} Options to initialize this instance with. +// Initializes this instance with additional `stores` or `sources` in the +// `options` supplied. +// +Provider.prototype.init = function (options) { + var self = this; + + // + // Add any stores passed in through the options + // to this instance. + // + if (options.type) { + this.add(options.type, options); + } + else if (options.store) { + this.add(options.store.name || options.store.type, options.store); + } + else if (options.stores) { + Object.keys(options.stores).forEach(function (name) { + var store = options.stores[name]; + self.add(store.name || name || store.type, store); + }); + } + + // + // Add any read-only sources to this instance + // + if (options.source) { + this.sources.push(this.create(options.source.type || options.source.name, options.source)); + } + else if (options.sources) { + Object.keys(options.sources).forEach(function (name) { + var source = options.sources[name]; + self.sources.push(self.create(source.type || source.name || name, source)); + }); + } +}; + +// +// ### function get (key, callback) +// #### @key {string} Key to retrieve for this instance. +// #### @callback {function} **Optional** Continuation to respond to when complete. +// Retrieves the value for the specified key (if any). +// +Provider.prototype.get = function (key, callback) { + // + // If there is no callback we can short-circuit into the default + // logic for traversing stores. + // + if (!callback) { + return this._execute('get', 1, key, callback); + } + + // + // Otherwise the asynchronous, hierarchical `get` is + // slightly more complicated because we do not need to traverse + // the entire set of stores, but up until there is a defined value. + // + var current = 0, + names = Object.keys(this.stores), + self = this, + response, + mergeObjs = []; + + async.whilst(function () { + return typeof response === 'undefined' && current < names.length; + }, function (next) { + var store = self.stores[names[current]]; + current++; + + if (store.get.length >= 2) { + return store.get(key, function (err, value) { + if (err) { + return next(err); + } + + response = value; + + // Merge objects if necessary + if (typeof response === 'object' && !Array.isArray(response)) { + mergeObjs.push(response); + response = undefined; + } + + next(); + }); + } + + response = store.get(key); + + // Merge objects if necessary + if (typeof response === 'object' && !Array.isArray(response)) { + mergeObjs.push(response); + response = undefined; + } + + next(); + }, function (err) { + if (!err && mergeObjs.length) { + response = common.merge(mergeObjs.reverse()); + } + return err ? callback(err) : callback(null, response); + }); +}; + +// +// ### function set (key, value, callback) +// #### @key {string} Key to set in this instance +// #### @value {literal|Object} Value for the specified key +// #### @callback {function} **Optional** Continuation to respond to when complete. +// Sets the `value` for the specified `key` in this instance. +// +Provider.prototype.set = function (key, value, callback) { + return this._execute('set', 2, key, value, callback); +}; + +// +// ### function reset (callback) +// #### @callback {function} **Optional** Continuation to respond to when complete. +// Clears all keys associated with this instance. +// +Provider.prototype.reset = function (callback) { + return this._execute('reset', 0, callback); +}; + +// +// ### function clear (key, callback) +// #### @key {string} Key to remove from this instance +// #### @callback {function} **Optional** Continuation to respond to when complete. +// Removes the value for the specified `key` from this instance. +// +Provider.prototype.clear = function (key, callback) { + return this._execute('clear', 1, key, callback); +}; + +// +// ### function merge ([key,] value [, callback]) +// #### @key {string} Key to merge the value into +// #### @value {literal|Object} Value to merge into the key +// #### @callback {function} **Optional** Continuation to respond to when complete. +// Merges the properties in `value` into the existing object value at `key`. +// +// 1. If the existing value `key` is not an Object, it will be completely overwritten. +// 2. If `key` is not supplied, then the `value` will be merged into the root. +// +Provider.prototype.merge = function () { + var self = this, + args = Array.prototype.slice.call(arguments), + callback = typeof args[args.length - 1] === 'function' && args.pop(), + value = args.pop(), + key = args.pop(); + + function mergeProperty (prop, next) { + return self._execute('merge', 2, prop, value[prop], next); + } + + if (!key) { + if (Array.isArray(value) || typeof value !== 'object') { + return onError(new Error('Cannot merge non-Object into top-level.'), callback); + } + + return async.forEach(Object.keys(value), mergeProperty, callback || function () { }) + } + + return this._execute('merge', 2, key, value, callback); +}; + +// +// ### function load (callback) +// #### @callback {function} Continuation to respond to when complete. +// Responds with an Object representing all keys associated in this instance. +// +Provider.prototype.load = function (callback) { + var self = this; + + function getStores () { + var stores = Object.keys(self.stores); + stores.reverse(); + return stores.map(function (name) { + return self.stores[name]; + }); + } + + function loadStoreSync(store) { + if (!store.loadSync) { + throw new Error('nconf store ' + store.type + ' has no loadSync() method'); + } + + return store.loadSync(); + } + + function loadStore(store, next) { + if (!store.load && !store.loadSync) { + return next(new Error('nconf store ' + store.type + ' has no load() method')); + } + + return store.loadSync + ? next(null, store.loadSync()) + : store.load(next); + } + + function loadBatch (targets, done) { + if (!done) { + return common.merge(targets.map(loadStoreSync)); + } + + async.map(targets, loadStore, function (err, objs) { + return err ? done(err) : done(null, common.merge(objs)); + }); + } + + function mergeSources (data) { + // + // If `data` was returned then merge it into + // the system store. + // + if (data && typeof data === 'object') { + self.use('sources', { + type: 'literal', + store: data + }); + } + } + + function loadSources () { + var sourceHierarchy = self.sources.splice(0); + sourceHierarchy.reverse(); + + // + // If we don't have a callback and the current + // store is capable of loading synchronously + // then do so. + // + if (!callback) { + mergeSources(loadBatch(sourceHierarchy)); + return loadBatch(getStores()); + } + + loadBatch(sourceHierarchy, function (err, data) { + if (err) { + return callback(err); + } + + mergeSources(data); + return loadBatch(getStores(), callback); + }); + } + + return self.sources.length + ? loadSources() + : loadBatch(getStores(), callback); +}; + +// +// ### function save (callback) +// #### @callback {function} **optional** Continuation to respond to when +// complete. +// Instructs each provider to save. If a callback is provided, we will attempt +// asynchronous saves on the providers, falling back to synchronous saves if +// this isn't possible. If a provider does not know how to save, it will be +// ignored. Returns an object consisting of all of the data which was +// actually saved. +// +Provider.prototype.save = function (value, callback) { + if (!callback && typeof value === 'function') { + callback = value; + value = null; + } + + var self = this, + names = Object.keys(this.stores); + + function saveStoreSync(memo, name) { + var store = self.stores[name]; + + // + // If the `store` doesn't have a `saveSync` method, + // just ignore it and continue. + // + if (store.saveSync) { + var ret = store.saveSync(); + if (typeof ret == 'object' && ret !== null) { + memo.push(ret); + } + } + return memo; + } + + function saveStore(memo, name, next) { + var store = self.stores[name]; + + // + // If the `store` doesn't have a `save` or saveSync` + // method(s), just ignore it and continue. + // + + if (store.save) { + return store.save(function (err, data) { + if (err) { + return next(err); + } + + if (typeof data == 'object' && data !== null) { + memo.push(data); + } + + next(null, memo); + }); + } + else if (store.saveSync) { + memo.push(store.saveSync()); + } + + next(null, memo); + } + + // + // If we don't have a callback and the current + // store is capable of saving synchronously + // then do so. + // + if (!callback) { + return common.merge(names.reduce(saveStoreSync, [])); + } + + async.reduce(names, [], saveStore, function (err, objs) { + return err ? callback(err) : callback(null, common.merge(objs)); + }); +}; + +// +// ### @private function _execute (action, syncLength, [arguments]) +// #### @action {string} Action to execute on `this.store`. +// #### @syncLength {number} Function length of the sync version. +// #### @arguments {Array} Arguments array to apply to the action +// Executes the specified `action` on all stores for this instance, ensuring a callback supplied +// to a synchronous store function is still invoked. +// +Provider.prototype._execute = function (action, syncLength /* [arguments] */) { + var args = Array.prototype.slice.call(arguments, 2), + callback = typeof args[args.length - 1] === 'function' && args.pop(), + destructive = ['set', 'clear', 'merge', 'reset'].indexOf(action) !== -1, + self = this, + response, + mergeObjs = []; + + function runAction (name, next) { + var store = self.stores[name]; + + if (destructive && store.readOnly) { + return next(); + } + + return store[action].length > syncLength + ? store[action].apply(store, args.concat(next)) + : next(null, store[action].apply(store, args)); + } + + if (callback) { + return async.forEach(Object.keys(this.stores), runAction, function (err) { + return err ? callback(err) : callback(); + }); + } + + + Object.keys(this.stores).forEach(function (name) { + if (typeof response === 'undefined') { + var store = self.stores[name]; + + if (destructive && store.readOnly) { + return; + } + + response = store[action].apply(store, args); + + // Merge objects if necessary + if (action === 'get' && typeof response === 'object' && !Array.isArray(response)) { + mergeObjs.push(response); + response = undefined; + } + } + }); + + if (mergeObjs.length) { + response = common.merge(mergeObjs.reverse()); + } + + return response; +} + +// +// Throw the `err` if a callback is not supplied +// +function onError(err, callback) { + if (callback) { + return callback(err); + } + + throw err; +} diff --git a/dist/node_modules/nconf/lib/nconf/stores/argv.js b/dist/node_modules/nconf/lib/nconf/stores/argv.js new file mode 100644 index 0000000..3841f7d --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/stores/argv.js @@ -0,0 +1,61 @@ +/* + * argv.js: Simple memory-based store for command-line arguments. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var util = require('util'), + Memory = require('./memory').Memory; + +// +// ### function Argv (options) +// #### @options {Object} Options for this instance. +// Constructor function for the Argv nconf store, a simple abstraction +// around the Memory store that can read command-line arguments. +// +var Argv = exports.Argv = function (options) { + Memory.call(this, options); + + this.type = 'argv'; + this.readOnly = true; + this.options = options || false; +}; + +// Inherit from the Memory store +util.inherits(Argv, Memory); + +// +// ### function loadSync () +// Loads the data passed in from `process.argv` into this instance. +// +Argv.prototype.loadSync = function () { + this.loadArgv(); + return this.store; +}; + +// +// ### function loadArgv () +// Loads the data passed in from the command-line arguments +// into this instance. +// +Argv.prototype.loadArgv = function () { + var self = this, + argv; + + argv = typeof this.options === 'object' + ? require('optimist')(process.argv.slice(2)).options(this.options).argv + : require('optimist')(process.argv.slice(2)).argv; + + if (!argv) { + return; + } + + this.readOnly = false; + Object.keys(argv).forEach(function (key) { + self.set(key, argv[key]); + }); + + this.readOnly = true; + return this.store; +}; \ No newline at end of file diff --git a/dist/node_modules/nconf/lib/nconf/stores/env.js b/dist/node_modules/nconf/lib/nconf/stores/env.js new file mode 100644 index 0000000..e73026e --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/stores/env.js @@ -0,0 +1,67 @@ +/* + * env.js: Simple memory-based store for environment variables + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var util = require('util'), + common = require('../common'), + Memory = require('./memory').Memory; + +// +// ### function Env (options) +// #### @options {Object} Options for this instance. +// Constructor function for the Env nconf store, a simple abstraction +// around the Memory store that can read process environment variables. +// +var Env = exports.Env = function (options) { + Memory.call(this, options); + + options = options || {}; + this.type = 'env'; + this.readOnly = true; + this.whitelist = options.whitelist || []; + this.separator = options.separator || ''; + if (options instanceof Array) { + this.whitelist = options; + } + if (typeof(options) === 'string') { + this.separator = options; + } +}; + +// Inherit from the Memory store +util.inherits(Env, Memory); + +// +// ### function loadSync () +// Loads the data passed in from `process.env` into this instance. +// +Env.prototype.loadSync = function () { + this.loadEnv(); + return this.store; +}; + +// +// ### function loadEnv () +// Loads the data passed in from `process.env` into this instance. +// +Env.prototype.loadEnv = function () { + var self = this; + + this.readOnly = false; + Object.keys(process.env).filter(function (key) { + return !self.whitelist.length || self.whitelist.indexOf(key) !== -1; + }).forEach(function (key) { + if (self.separator) { + self.set(common.key.apply(common, key.split(self.separator)), process.env[key]); + } else { + self.set(key, process.env[key]); + } + }); + + this.readOnly = true; + return this.store; +}; + diff --git a/dist/node_modules/nconf/lib/nconf/stores/file.js b/dist/node_modules/nconf/lib/nconf/stores/file.js new file mode 100644 index 0000000..117f5ab --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/stores/file.js @@ -0,0 +1,228 @@ +/* + * file.js: Simple file storage engine for nconf files + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + path = require('path'), + util = require('util'), + formats = require('../formats'), + Memory = require('./memory').Memory, + exists = fs.exists || path.exists, + existsSync = fs.existsSync || path.existsSync; + +// +// ### function File (options) +// #### @options {Object} Options for this instance +// Constructor function for the File nconf store, a simple abstraction +// around the Memory store that can persist configuration to disk. +// +var File = exports.File = function (options) { + if (!options || !options.file) { + throw new Error ('Missing required option `file`'); + } + + Memory.call(this, options); + + this.type = 'file'; + this.file = options.file; + this.dir = options.dir || process.cwd(); + this.format = options.format || formats.json; + this.json_spacing = options.json_spacing || 2; + + if (options.search) { + this.search(this.dir); + } +}; + +// Inherit from the Memory store +util.inherits(File, Memory); + +// +// ### function save (value, callback) +// #### @value {Object} _Ignored_ Left here for consistency +// #### @callback {function} Continuation to respond to when complete. +// Saves the current configuration object to disk at `this.file` +// using the format specified by `this.format`. +// +File.prototype.save = function (value, callback) { + if (!callback) { + callback = value; + value = null; + } + + fs.writeFile(this.file, this.format.stringify(this.store, null, this.json_spacing), function (err) { + return err ? callback(err) : callback(); + }); +}; + +// +// ### function saveSync (value, callback) +// #### @value {Object} _Ignored_ Left here for consistency +// #### @callback {function} **Optional** Continuation to respond to when complete. +// Saves the current configuration object to disk at `this.file` +// using the format specified by `this.format` synchronously. +// +File.prototype.saveSync = function (value) { + try { + fs.writeFileSync(this.file, this.format.stringify(this.store, null, this.json_spacing)); + } + catch (ex) { + throw(ex); + } + return this.store; +}; + +// +// ### function load (callback) +// #### @callback {function} Continuation to respond to when complete. +// Responds with an Object representing all keys associated in this instance. +// +File.prototype.load = function (callback) { + var self = this; + + exists(self.file, function (exists) { + if (!exists) { + return callback(null, {}); + } + + // + // Else, the path exists, read it from disk + // + fs.readFile(self.file, function (err, data) { + if (err) { + return callback(err); + } + + try { + self.store = self.format.parse(data.toString()); + } + catch (ex) { + return callback(new Error("Error parsing your JSON configuration file.")); + } + + callback(null, self.store); + }); + }); +}; + +// +// ### function loadSync (callback) +// Attempts to load the data stored in `this.file` synchronously +// and responds appropriately. +// +File.prototype.loadSync = function () { + var data, self = this; + + if (!existsSync(self.file)) { + self.store = {}; + data = {}; + } + else { + // + // Else, the path exists, read it from disk + // + try { + data = this.format.parse(fs.readFileSync(this.file, 'utf8')); + this.store = data; + } + catch (ex) { + throw new Error("Error parsing your JSON configuration file.") + } + } + + return data; +}; + +// +// ### function search (base) +// #### @base {string} Base directory (or file) to begin searching for the target file. +// Attempts to find `this.file` by iteratively searching up the +// directory structure +// +File.prototype.search = function (base) { + var looking = true, + fullpath, + previous, + stats; + + base = base || process.cwd(); + + if (this.file[0] === '/') { + // + // If filename for this instance is a fully qualified path + // (i.e. it starts with a `'/'`) then check if it exists + // + try { + stats = fs.statSync(fs.realpathSync(this.file)); + if (stats.isFile()) { + fullpath = this.file; + looking = false; + } + } + catch (ex) { + // + // Ignore errors + // + } + } + + if (looking && base) { + // + // Attempt to stat the realpath located at `base` + // if the directory does not exist then return false. + // + try { + var stat = fs.statSync(fs.realpathSync(base)); + looking = stat.isDirectory(); + } + catch (ex) { + return false; + } + } + + while (looking) { + // + // Iteratively look up the directory structure from `base` + // + try { + stats = fs.statSync(fs.realpathSync(fullpath = path.join(base, this.file))); + looking = stats.isDirectory(); + } + catch (ex) { + previous = base; + base = path.dirname(base); + + if (previous === base) { + // + // If we've reached the top of the directory structure then simply use + // the default file path. + // + try { + stats = fs.statSync(fs.realpathSync(fullpath = path.join(this.dir, this.file))); + if (stats.isDirectory()) { + fullpath = undefined; + } + } + catch (ex) { + // + // Ignore errors + // + } + + looking = false; + } + } + } + + // + // Set the file for this instance to the fullpath + // that we have found during the search. In the event that + // the search was unsuccessful use the original value for `this.file`. + // + this.file = fullpath || this.file; + + return fullpath; +}; diff --git a/dist/node_modules/nconf/lib/nconf/stores/literal.js b/dist/node_modules/nconf/lib/nconf/stores/literal.js new file mode 100644 index 0000000..c7c1752 --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/stores/literal.js @@ -0,0 +1,29 @@ +/* + * literal.js: Simple literal Object store for nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var util = require('util'), + Memory = require('./memory').Memory + +var Literal = exports.Literal = function Literal (options) { + Memory.call(this, options); + + options = options || {} + this.type = 'literal'; + this.readOnly = true; + this.store = options.store || options; +}; + +// Inherit from Memory store. +util.inherits(Literal, Memory); + +// +// ### function loadSync (callback) +// Returns the data stored in `this.store` synchronously. +// +Literal.prototype.loadSync = function () { + return this.store; +}; \ No newline at end of file diff --git a/dist/node_modules/nconf/lib/nconf/stores/memory.js b/dist/node_modules/nconf/lib/nconf/stores/memory.js new file mode 100644 index 0000000..fbab7af --- /dev/null +++ b/dist/node_modules/nconf/lib/nconf/stores/memory.js @@ -0,0 +1,210 @@ +/* + * memory.js: Simple memory storage engine for nconf configuration(s) + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var common = require('../common'); + +// +// ### function Memory (options) +// #### @options {Object} Options for this instance +// Constructor function for the Memory nconf store which maintains +// a nested json structure based on key delimiters `:`. +// +// e.g. `my:nested:key` ==> `{ my: { nested: { key: } } }` +// +var Memory = exports.Memory = function (options) { + options = options || {}; + this.type = 'memory'; + this.store = {}; + this.mtimes = {}; + this.readOnly = false; + this.loadFrom = options.loadFrom || null; + + if (this.loadFrom) { + this.store = common.loadFilesSync(this.loadFrom); + } +}; + +// +// ### function get (key) +// #### @key {string} Key to retrieve for this instance. +// Retrieves the value for the specified key (if any). +// +Memory.prototype.get = function (key) { + var target = this.store, + path = common.path(key); + + // + // Scope into the object to get the appropriate nested context + // + while (path.length > 0) { + key = path.shift(); + if (!(target && key in target)) { + return; + } + + target = target[key]; + if (path.length === 0) { + return target; + } + } +}; + +// +// ### function set (key, value) +// #### @key {string} Key to set in this instance +// #### @value {literal|Object} Value for the specified key +// Sets the `value` for the specified `key` in this instance. +// +Memory.prototype.set = function (key, value) { + if (this.readOnly) { + return false; + } + + var target = this.store, + path = common.path(key); + + // + // Update the `mtime` (modified time) of the key + // + this.mtimes[key] = Date.now(); + + // + // Scope into the object to get the appropriate nested context + // + while (path.length > 1) { + key = path.shift(); + if (!target[key] || typeof target[key] !== 'object') { + target[key] = {}; + } + + target = target[key]; + } + + // Set the specified value in the nested JSON structure + key = path.shift(); + target[key] = value; + return true; +}; + +// +// ### function clear (key) +// #### @key {string} Key to remove from this instance +// Removes the value for the specified `key` from this instance. +// +Memory.prototype.clear = function (key) { + if (this.readOnly) { + return false; + } + + var target = this.store, + path = common.path(key); + + // + // Remove the key from the set of `mtimes` (modified times) + // + delete this.mtimes[key]; + + // + // Scope into the object to get the appropriate nested context + // + while (path.length > 1) { + key = path.shift(); + if (!target[key]) { + return; + } + + target = target[key]; + } + + // Delete the key from the nested JSON structure + key = path.shift(); + delete target[key]; + return true; +}; + +// +// ### function merge (key, value) +// #### @key {string} Key to merge the value into +// #### @value {literal|Object} Value to merge into the key +// Merges the properties in `value` into the existing object value +// at `key`. If the existing value `key` is not an Object, it will be +// completely overwritten. +// +Memory.prototype.merge = function (key, value) { + if (this.readOnly) { + return false; + } + + // + // If the key is not an `Object` or is an `Array`, + // then simply set it. Merging is for Objects. + // + if (typeof value !== 'object' || Array.isArray(value)) { + return this.set(key, value); + } + + var self = this, + target = this.store, + path = common.path(key), + fullKey = key; + + // + // Update the `mtime` (modified time) of the key + // + this.mtimes[key] = Date.now(); + + // + // Scope into the object to get the appropriate nested context + // + while (path.length > 1) { + key = path.shift(); + if (!target[key]) { + target[key] = {}; + } + + target = target[key]; + } + + // Set the specified value in the nested JSON structure + key = path.shift(); + + // + // If the current value at the key target is not an `Object`, + // or is an `Array` then simply override it because the new value + // is an Object. + // + if (typeof target[key] !== 'object' || Array.isArray(target[key])) { + target[key] = value; + return true; + } + + return Object.keys(value).every(function (nested) { + return self.merge(common.key(fullKey, nested), value[nested]); + }); +}; + +// +// ### function reset (callback) +// Clears all keys associated with this instance. +// +Memory.prototype.reset = function () { + if (this.readOnly) { + return false; + } + + this.mtimes = {}; + this.store = {}; + return true; +}; + +// +// ### function loadSync +// Returns the store managed by this instance +// +Memory.prototype.loadSync = function () { + return this.store || {}; +}; diff --git a/dist/node_modules/nconf/node_modules/async/.gitmodules b/dist/node_modules/nconf/node_modules/async/.gitmodules new file mode 100644 index 0000000..a9aae98 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/.gitmodules @@ -0,0 +1,9 @@ +[submodule "deps/nodeunit"] + path = deps/nodeunit + url = git://github.com/caolan/nodeunit.git +[submodule "deps/UglifyJS"] + path = deps/UglifyJS + url = https://github.com/mishoo/UglifyJS.git +[submodule "deps/nodelint"] + path = deps/nodelint + url = https://github.com/tav/nodelint.git diff --git a/dist/node_modules/nconf/node_modules/async/.npmignore b/dist/node_modules/nconf/node_modules/async/.npmignore new file mode 100644 index 0000000..9bdfc97 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/.npmignore @@ -0,0 +1,4 @@ +deps +dist +test +nodelint.cfg \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/async/LICENSE b/dist/node_modules/nconf/node_modules/async/LICENSE new file mode 100644 index 0000000..b7f9d50 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Caolan McMahon + +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/dist/node_modules/nconf/node_modules/async/Makefile b/dist/node_modules/nconf/node_modules/async/Makefile new file mode 100644 index 0000000..bad647c --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/Makefile @@ -0,0 +1,25 @@ +PACKAGE = asyncjs +NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node) +CWD := $(shell pwd) +NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit +UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs +NODELINT = $(CWD)/node_modules/nodelint/nodelint + +BUILDDIR = dist + +all: clean test build + +build: $(wildcard lib/*.js) + mkdir -p $(BUILDDIR) + $(UGLIFY) lib/async.js > $(BUILDDIR)/async.min.js + +test: + $(NODEUNIT) test + +clean: + rm -rf $(BUILDDIR) + +lint: + $(NODELINT) --config nodelint.cfg lib/async.js + +.PHONY: test build all diff --git a/dist/node_modules/nconf/node_modules/async/README.md b/dist/node_modules/nconf/node_modules/async/README.md new file mode 100644 index 0000000..1bbbc47 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/README.md @@ -0,0 +1,1021 @@ +# Async.js + +Async is a utility module which provides straight-forward, powerful functions +for working with asynchronous JavaScript. Although originally designed for +use with [node.js](http://nodejs.org), it can also be used directly in the +browser. + +Async provides around 20 functions that include the usual 'functional' +suspects (map, reduce, filter, forEach…) as well as some common patterns +for asynchronous control flow (parallel, series, waterfall…). All these +functions assume you follow the node.js convention of providing a single +callback as the last argument of your async function. + + +## Quick Examples + + async.map(['file1','file2','file3'], fs.stat, function(err, results){ + // results is now an array of stats for each file + }); + + async.filter(['file1','file2','file3'], path.exists, function(results){ + // results now equals an array of the existing files + }); + + async.parallel([ + function(){ ... }, + function(){ ... } + ], callback); + + async.series([ + function(){ ... }, + function(){ ... } + ]); + +There are many more functions available so take a look at the docs below for a +full list. This module aims to be comprehensive, so if you feel anything is +missing please create a GitHub issue for it. + + +## Download + +Releases are available for download from +[GitHub](http://github.com/caolan/async/downloads). +Alternatively, you can install using Node Package Manager (npm): + + npm install async + + +__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 17.5kb Uncompressed + +__Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped + + +## In the Browser + +So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage: + + + + + +## Documentation + +### Collections + +* [forEach](#forEach) +* [map](#map) +* [filter](#filter) +* [reject](#reject) +* [reduce](#reduce) +* [detect](#detect) +* [sortBy](#sortBy) +* [some](#some) +* [every](#every) +* [concat](#concat) + +### Control Flow + +* [series](#series) +* [parallel](#parallel) +* [whilst](#whilst) +* [until](#until) +* [waterfall](#waterfall) +* [queue](#queue) +* [auto](#auto) +* [iterator](#iterator) +* [apply](#apply) +* [nextTick](#nextTick) + +### Utils + +* [memoize](#memoize) +* [unmemoize](#unmemoize) +* [log](#log) +* [dir](#dir) +* [noConflict](#noConflict) + + +## Collections + + +### forEach(arr, iterator, callback) + +Applies an iterator function to each item in an array, in parallel. +The iterator is called with an item from the list and a callback for when it +has finished. If the iterator passes an error to this callback, the main +callback for the forEach function is immediately called with the error. + +Note, that since this function applies the iterator to each item in parallel +there is no guarantee that the iterator functions will complete in order. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(err) - A callback which is called after all the iterator functions + have finished, or an error has occurred. + +__Example__ + + // assuming openFiles is an array of file names and saveFile is a function + // to save the modified contents of that file: + + async.forEach(openFiles, saveFile, function(err){ + // if any of the saves produced an error, err would equal that error + }); + +--------------------------------------- + + +### forEachSeries(arr, iterator, callback) + +The same as forEach only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. This means the iterator functions will complete in order. + + +--------------------------------------- + + +### forEachLimit(arr, limit, iterator, callback) + +The same as forEach only the iterator is applied to batches of items in the +array, in series. The next batch of iterators is only called once the current +one has completed processing. + +__Arguments__ + +* arr - An array to iterate over. +* limit - How many items should be in each batch. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(err) - A callback which is called after all the iterator functions + have finished, or an error has occurred. + +__Example__ + + // Assume documents is an array of JSON objects and requestApi is a + // function that interacts with a rate-limited REST api. + + async.forEachLimit(documents, 20, requestApi, function(err){ + // if any of the saves produced an error, err would equal that error + }); +--------------------------------------- + + +### map(arr, iterator, callback) + +Produces a new array of values by mapping each value in the given array through +the iterator function. The iterator is called with an item from the array and a +callback for when it has finished processing. The callback takes 2 arguments, +an error and the transformed item from the array. If the iterator passes an +error to this callback, the main callback for the map function is immediately +called with the error. + +Note, that since this function applies the iterator to each item in parallel +there is no guarantee that the iterator functions will complete in order, however +the results array will be in the same order as the original array. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed + with an error (which can be null) and a transformed item. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is an array of the + transformed items from the original array. + +__Example__ + + async.map(['file1','file2','file3'], fs.stat, function(err, results){ + // results is now an array of stats for each file + }); + +--------------------------------------- + + +### mapSeries(arr, iterator, callback) + +The same as map only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. The results array will be in the same order as the original. + + +--------------------------------------- + + +### filter(arr, iterator, callback) + +__Alias:__ select + +Returns a new array of all the values which pass an async truth test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like path.exists. This operation is +performed in parallel, but the results array will be in the same order as the +original. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(results) - A callback which is called after all the iterator + functions have finished. + +__Example__ + + async.filter(['file1','file2','file3'], path.exists, function(results){ + // results now equals an array of the existing files + }); + +--------------------------------------- + + +### filterSeries(arr, iterator, callback) + +__alias:__ selectSeries + +The same as filter only the iterator is applied to each item in the array in +series. The next iterator is only called once the current one has completed +processing. The results array will be in the same order as the original. + +--------------------------------------- + + +### reject(arr, iterator, callback) + +The opposite of filter. Removes values that pass an async truth test. + +--------------------------------------- + + +### rejectSeries(arr, iterator, callback) + +The same as filter, only the iterator is applied to each item in the array +in series. + + +--------------------------------------- + + +### reduce(arr, memo, iterator, callback) + +__aliases:__ inject, foldl + +Reduces a list of values into a single value using an async iterator to return +each successive step. Memo is the initial state of the reduction. This +function only operates in series. For performance reasons, it may make sense to +split a call to this function into a parallel map, then use the normal +Array.prototype.reduce on the results. This function is for situations where +each step in the reduction needs to be async, if you can get the data before +reducing it then its probably a good idea to do so. + +__Arguments__ + +* arr - An array to iterate over. +* memo - The initial state of the reduction. +* iterator(memo, item, callback) - A function applied to each item in the + array to produce the next step in the reduction. The iterator is passed a + callback which accepts an optional error as its first argument, and the state + of the reduction as the second. If an error is passed to the callback, the + reduction is stopped and the main callback is immediately called with the + error. +* callback(err, result) - A callback which is called after all the iterator + functions have finished. Result is the reduced value. + +__Example__ + + async.reduce([1,2,3], 0, function(memo, item, callback){ + // pointless async: + process.nextTick(function(){ + callback(null, memo + item) + }); + }, function(err, result){ + // result is now equal to the last value of memo, which is 6 + }); + +--------------------------------------- + + +### reduceRight(arr, memo, iterator, callback) + +__Alias:__ foldr + +Same as reduce, only operates on the items in the array in reverse order. + + +--------------------------------------- + + +### detect(arr, iterator, callback) + +Returns the first value in a list that passes an async truth test. The +iterator is applied in parallel, meaning the first iterator to return true will +fire the detect callback with that result. That means the result might not be +the first item in the original array (in terms of order) that passes the test. + +If order within the original array is important then look at detectSeries. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(result) - A callback which is called as soon as any iterator returns + true, or after all the iterator functions have finished. Result will be + the first item in the array that passes the truth test (iterator) or the + value undefined if none passed. + +__Example__ + + async.detect(['file1','file2','file3'], path.exists, function(result){ + // result now equals the first file in the list that exists + }); + +--------------------------------------- + + +### detectSeries(arr, iterator, callback) + +The same as detect, only the iterator is applied to each item in the array +in series. This means the result is always the first in the original array (in +terms of array order) that passes the truth test. + + +--------------------------------------- + + +### sortBy(arr, iterator, callback) + +Sorts a list by the results of running each value through an async iterator. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed + with an error (which can be null) and a value to use as the sort criteria. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is the items from + the original array sorted by the values returned by the iterator calls. + +__Example__ + + async.sortBy(['file1','file2','file3'], function(file, callback){ + fs.stat(file, function(err, stats){ + callback(err, stats.mtime); + }); + }, function(err, results){ + // results is now the original array of files sorted by + // modified date + }); + + +--------------------------------------- + + +### some(arr, iterator, callback) + +__Alias:__ any + +Returns true if at least one element in the array satisfies an async test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like path.exists. Once any iterator +call returns true, the main callback is immediately called. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(result) - A callback which is called as soon as any iterator returns + true, or after all the iterator functions have finished. Result will be + either true or false depending on the values of the async tests. + +__Example__ + + async.some(['file1','file2','file3'], path.exists, function(result){ + // if result is true then at least one of the files exists + }); + +--------------------------------------- + + +### every(arr, iterator, callback) + +__Alias:__ all + +Returns true if every element in the array satisfies an async test. +_The callback for each iterator call only accepts a single argument of true or +false, it does not accept an error argument first!_ This is in-line with the +way node libraries work with truth tests like path.exists. + +__Arguments__ + +* arr - An array to iterate over. +* iterator(item, callback) - A truth test to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed. +* callback(result) - A callback which is called after all the iterator + functions have finished. Result will be either true or false depending on + the values of the async tests. + +__Example__ + + async.every(['file1','file2','file3'], path.exists, function(result){ + // if result is true then every file exists + }); + +--------------------------------------- + + +### concat(arr, iterator, callback) + +Applies an iterator to each item in a list, concatenating the results. Returns the +concatenated list. The iterators are called in parallel, and the results are +concatenated as they return. There is no guarantee that the results array will +be returned in the original order of the arguments passed to the iterator function. + +__Arguments__ + +* arr - An array to iterate over +* iterator(item, callback) - A function to apply to each item in the array. + The iterator is passed a callback which must be called once it has completed + with an error (which can be null) and an array of results. +* callback(err, results) - A callback which is called after all the iterator + functions have finished, or an error has occurred. Results is an array containing + the concatenated results of the iterator function. + +__Example__ + + async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){ + // files is now a list of filenames that exist in the 3 directories + }); + +--------------------------------------- + + +### concatSeries(arr, iterator, callback) + +Same as async.concat, but executes in series instead of parallel. + + +## Control Flow + + +### series(tasks, [callback]) + +Run an array of functions in series, each one running once the previous +function has completed. If any functions in the series pass an error to its +callback, no more functions are run and the callback for the series is +immediately called with the value of the error. Once the tasks have completed, +the results are passed to the final callback as an array. + +It is also possible to use an object instead of an array. Each property will be +run as a function and the results will be passed to the final callback as an object +instead of an array. This can be a more readable way of handling results from +async.series. + + +__Arguments__ + +* tasks - An array or object containing functions to run, each function is passed + a callback it must call on completion. +* callback(err, results) - An optional callback to run once all the functions + have completed. This function gets an array of all the arguments passed to + the callbacks used in the array. + +__Example__ + + async.series([ + function(callback){ + // do some stuff ... + callback(null, 'one'); + }, + function(callback){ + // do some more stuff ... + callback(null, 'two'); + }, + ], + // optional callback + function(err, results){ + // results is now equal to ['one', 'two'] + }); + + + // an example using an object instead of an array + async.series({ + one: function(callback){ + setTimeout(function(){ + callback(null, 1); + }, 200); + }, + two: function(callback){ + setTimeout(function(){ + callback(null, 2); + }, 100); + }, + }, + function(err, results) { + // results is now equal to: {one: 1, two: 2} + }); + + +--------------------------------------- + + +### parallel(tasks, [callback]) + +Run an array of functions in parallel, without waiting until the previous +function has completed. If any of the functions pass an error to its +callback, the main callback is immediately called with the value of the error. +Once the tasks have completed, the results are passed to the final callback as an +array. + +It is also possible to use an object instead of an array. Each property will be +run as a function and the results will be passed to the final callback as an object +instead of an array. This can be a more readable way of handling results from +async.parallel. + + +__Arguments__ + +* tasks - An array or object containing functions to run, each function is passed a + callback it must call on completion. +* callback(err, results) - An optional callback to run once all the functions + have completed. This function gets an array of all the arguments passed to + the callbacks used in the array. + +__Example__ + + async.parallel([ + function(callback){ + setTimeout(function(){ + callback(null, 'one'); + }, 200); + }, + function(callback){ + setTimeout(function(){ + callback(null, 'two'); + }, 100); + }, + ], + // optional callback + function(err, results){ + // the results array will equal ['one','two'] even though + // the second function had a shorter timeout. + }); + + + // an example using an object instead of an array + async.parallel({ + one: function(callback){ + setTimeout(function(){ + callback(null, 1); + }, 200); + }, + two: function(callback){ + setTimeout(function(){ + callback(null, 2); + }, 100); + }, + }, + function(err, results) { + // results is now equals to: {one: 1, two: 2} + }); + + +--------------------------------------- + + +### whilst(test, fn, callback) + +Repeatedly call fn, while test returns true. Calls the callback when stopped, +or an error occurs. + +__Arguments__ + +* test() - synchronous truth test to perform before each execution of fn. +* fn(callback) - A function to call each time the test passes. The function is + passed a callback which must be called once it has completed with an optional + error as the first argument. +* callback(err) - A callback which is called after the test fails and repeated + execution of fn has stopped. + +__Example__ + + var count = 0; + + async.whilst( + function () { return count < 5; }, + function (callback) { + count++; + setTimeout(callback, 1000); + }, + function (err) { + // 5 seconds have passed + } + ); + + +--------------------------------------- + + +### until(test, fn, callback) + +Repeatedly call fn, until test returns true. Calls the callback when stopped, +or an error occurs. + +The inverse of async.whilst. + + +--------------------------------------- + + +### waterfall(tasks, [callback]) + +Runs an array of functions in series, each passing their results to the next in +the array. However, if any of the functions pass an error to the callback, the +next function is not executed and the main callback is immediately called with +the error. + +__Arguments__ + +* tasks - An array of functions to run, each function is passed a callback it + must call on completion. +* callback(err, [results]) - An optional callback to run once all the functions + have completed. This will be passed the results of the last task's callback. + + + +__Example__ + + async.waterfall([ + function(callback){ + callback(null, 'one', 'two'); + }, + function(arg1, arg2, callback){ + callback(null, 'three'); + }, + function(arg1, callback){ + // arg1 now equals 'three' + callback(null, 'done'); + } + ], function (err, result) { + // result now equals 'done' + }); + + +--------------------------------------- + + +### queue(worker, concurrency) + +Creates a queue object with the specified concurrency. Tasks added to the +queue will be processed in parallel (up to the concurrency limit). If all +workers are in progress, the task is queued until one is available. Once +a worker has completed a task, the task's callback is called. + +__Arguments__ + +* worker(task, callback) - An asynchronous function for processing a queued + task. +* concurrency - An integer for determining how many worker functions should be + run in parallel. + +__Queue objects__ + +The queue object returned by this function has the following properties and +methods: + +* length() - a function returning the number of items waiting to be processed. +* concurrency - an integer for determining how many worker functions should be + run in parallel. This property can be changed after a queue is created to + alter the concurrency on-the-fly. +* push(task, [callback]) - add a new task to the queue, the callback is called + once the worker has finished processing the task. + instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list. +* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued +* empty - a callback that is called when the last item from the queue is given to a worker +* drain - a callback that is called when the last item from the queue has returned from the worker + +__Example__ + + // create a queue object with concurrency 2 + + var q = async.queue(function (task, callback) { + console.log('hello ' + task.name); + callback(); + }, 2); + + + // assign a callback + q.drain = function() { + console.log('all items have been processed'); + } + + // add some items to the queue + + q.push({name: 'foo'}, function (err) { + console.log('finished processing foo'); + }); + q.push({name: 'bar'}, function (err) { + console.log('finished processing bar'); + }); + + // add some items to the queue (batch-wise) + + q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) { + console.log('finished processing bar'); + }); + + +--------------------------------------- + + +### auto(tasks, [callback]) + +Determines the best order for running functions based on their requirements. +Each function can optionally depend on other functions being completed first, +and each function is run as soon as its requirements are satisfied. If any of +the functions pass an error to their callback, that function will not complete +(so any other functions depending on it will not run) and the main callback +will be called immediately with the error. Functions also receive an object +containing the results of functions which have completed so far. + +__Arguments__ + +* tasks - An object literal containing named functions or an array of + requirements, with the function itself the last item in the array. The key + used for each function or array is used when specifying requirements. The + syntax is easier to understand by looking at the example. +* callback(err, results) - An optional callback which is called when all the + tasks have been completed. The callback will receive an error as an argument + if any tasks pass an error to their callback. If all tasks complete + successfully, it will receive an object containing their results. + +__Example__ + + async.auto({ + get_data: function(callback){ + // async code to get some data + }, + make_folder: function(callback){ + // async code to create a directory to store a file in + // this is run at the same time as getting the data + }, + write_file: ['get_data', 'make_folder', function(callback){ + // once there is some data and the directory exists, + // write the data to a file in the directory + callback(null, filename); + }], + email_link: ['write_file', function(callback, results){ + // once the file is written let's email a link to it... + // results.write_file contains the filename returned by write_file. + }] + }); + +This is a fairly trivial example, but to do this using the basic parallel and +series functions would look like this: + + async.parallel([ + function(callback){ + // async code to get some data + }, + function(callback){ + // async code to create a directory to store a file in + // this is run at the same time as getting the data + } + ], + function(results){ + async.series([ + function(callback){ + // once there is some data and the directory exists, + // write the data to a file in the directory + }, + email_link: function(callback){ + // once the file is written let's email a link to it... + } + ]); + }); + +For a complicated series of async tasks using the auto function makes adding +new tasks much easier and makes the code more readable. + + +--------------------------------------- + + +### iterator(tasks) + +Creates an iterator function which calls the next function in the array, +returning a continuation to call the next one after that. Its also possible to +'peek' the next iterator by doing iterator.next(). + +This function is used internally by the async module but can be useful when +you want to manually control the flow of functions in series. + +__Arguments__ + +* tasks - An array of functions to run, each function is passed a callback it + must call on completion. + +__Example__ + + var iterator = async.iterator([ + function(){ sys.p('one'); }, + function(){ sys.p('two'); }, + function(){ sys.p('three'); } + ]); + + node> var iterator2 = iterator(); + 'one' + node> var iterator3 = iterator2(); + 'two' + node> iterator3(); + 'three' + node> var nextfn = iterator2.next(); + node> nextfn(); + 'three' + + +--------------------------------------- + + +### apply(function, arguments..) + +Creates a continuation function with some arguments already applied, a useful +shorthand when combined with other control flow functions. Any arguments +passed to the returned function are added to the arguments originally passed +to apply. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to automatically apply when the + continuation is called. + +__Example__ + + // using apply + + async.parallel([ + async.apply(fs.writeFile, 'testfile1', 'test1'), + async.apply(fs.writeFile, 'testfile2', 'test2'), + ]); + + + // the same process without using apply + + async.parallel([ + function(callback){ + fs.writeFile('testfile1', 'test1', callback); + }, + function(callback){ + fs.writeFile('testfile2', 'test2', callback); + }, + ]); + +It's possible to pass any number of additional arguments when calling the +continuation: + + node> var fn = async.apply(sys.puts, 'one'); + node> fn('two', 'three'); + one + two + three + +--------------------------------------- + + +### nextTick(callback) + +Calls the callback on a later loop around the event loop. In node.js this just +calls process.nextTick, in the browser it falls back to setTimeout(callback, 0), +which means other higher priority events may precede the execution of the callback. + +This is used internally for browser-compatibility purposes. + +__Arguments__ + +* callback - The function to call on a later loop around the event loop. + +__Example__ + + var call_order = []; + async.nextTick(function(){ + call_order.push('two'); + // call_order now equals ['one','two] + }); + call_order.push('one') + + +## Utils + + +### memoize(fn, [hasher]) + +Caches the results of an async function. When creating a hash to store function +results against, the callback is omitted from the hash and an optional hash +function can be used. + +__Arguments__ + +* fn - the function you to proxy and cache results from. +* hasher - an optional function for generating a custom hash for storing + results, it has all the arguments applied to it apart from the callback, and + must be synchronous. + +__Example__ + + var slow_fn = function (name, callback) { + // do something + callback(null, result); + }; + var fn = async.memoize(slow_fn); + + // fn can now be used as if it were slow_fn + fn('some name', function () { + // callback + }); + + +### unmemoize(fn) + +Undoes a memoized function, reverting it to the original, unmemoized +form. Comes handy in tests. + +__Arguments__ + +* fn - the memoized function + + +### log(function, arguments) + +Logs the result of an async function to the console. Only works in node.js or +in browsers that support console.log and console.error (such as FF and Chrome). +If multiple arguments are returned from the async function, console.log is +called on each argument in order. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to apply to the function. + +__Example__ + + var hello = function(name, callback){ + setTimeout(function(){ + callback(null, 'hello ' + name); + }, 1000); + }; + + node> async.log(hello, 'world'); + 'hello world' + + +--------------------------------------- + + +### dir(function, arguments) + +Logs the result of an async function to the console using console.dir to +display the properties of the resulting object. Only works in node.js or +in browsers that support console.dir and console.error (such as FF and Chrome). +If multiple arguments are returned from the async function, console.dir is +called on each argument in order. + +__Arguments__ + +* function - The function you want to eventually apply all arguments to. +* arguments... - Any number of arguments to apply to the function. + +__Example__ + + var hello = function(name, callback){ + setTimeout(function(){ + callback(null, {hello: name}); + }, 1000); + }; + + node> async.dir(hello, 'world'); + {hello: 'world'} + + +--------------------------------------- + + +### noConflict() + +Changes the value of async back to its original value, returning a reference to the +async object. diff --git a/dist/node_modules/nconf/node_modules/async/index.js b/dist/node_modules/nconf/node_modules/async/index.js new file mode 100644 index 0000000..8e23845 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/index.js @@ -0,0 +1,3 @@ +// This file is just added for convenience so this repository can be +// directly checked out into a project's deps folder +module.exports = require('./lib/async'); diff --git a/dist/node_modules/nconf/node_modules/async/lib/async.js b/dist/node_modules/nconf/node_modules/async/lib/async.js new file mode 100644 index 0000000..7cc4f5e --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/lib/async.js @@ -0,0 +1,692 @@ +/*global setTimeout: false, console: false */ +(function () { + + var async = {}; + + // global on the server, window in the browser + var root = this, + previous_async = root.async; + + if (typeof module !== 'undefined' && module.exports) { + module.exports = async; + } + else { + root.async = async; + } + + async.noConflict = function () { + root.async = previous_async; + return async; + }; + + //// cross-browser compatiblity functions //// + + var _forEach = function (arr, iterator) { + if (arr.forEach) { + return arr.forEach(iterator); + } + for (var i = 0; i < arr.length; i += 1) { + iterator(arr[i], i, arr); + } + }; + + var _map = function (arr, iterator) { + if (arr.map) { + return arr.map(iterator); + } + var results = []; + _forEach(arr, function (x, i, a) { + results.push(iterator(x, i, a)); + }); + return results; + }; + + var _reduce = function (arr, iterator, memo) { + if (arr.reduce) { + return arr.reduce(iterator, memo); + } + _forEach(arr, function (x, i, a) { + memo = iterator(memo, x, i, a); + }); + return memo; + }; + + var _keys = function (obj) { + if (Object.keys) { + return Object.keys(obj); + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } + } + return keys; + }; + + //// exported async module functions //// + + //// nextTick implementation with browser-compatible fallback //// + if (typeof process === 'undefined' || !(process.nextTick)) { + async.nextTick = function (fn) { + setTimeout(fn, 0); + }; + } + else { + async.nextTick = process.nextTick; + } + + async.forEach = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + _forEach(arr, function (x) { + iterator(x, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(null); + } + } + }); + }); + }; + + async.forEachSeries = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + var iterate = function () { + iterator(arr[completed], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(null); + } + else { + iterate(); + } + } + }); + }; + iterate(); + }; + + async.forEachLimit = function (arr, limit, iterator, callback) { + callback = callback || function () {}; + if (!arr.length || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed === arr.length) { + return callback(); + } + + while (running < limit && started < arr.length) { + started += 1; + running += 1; + iterator(arr[started - 1], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed === arr.length) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + + + var doParallel = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEach].concat(args)); + }; + }; + var doSeries = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEachSeries].concat(args)); + }; + }; + + + var _asyncMap = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (err, v) { + results[x.index] = v; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + }; + async.map = doParallel(_asyncMap); + async.mapSeries = doSeries(_asyncMap); + + + // reduce only has a series version, as doing reduce in parallel won't + // work in many situations. + async.reduce = function (arr, memo, iterator, callback) { + async.forEachSeries(arr, function (x, callback) { + iterator(memo, x, function (err, v) { + memo = v; + callback(err); + }); + }, function (err) { + callback(err, memo); + }); + }; + // inject alias + async.inject = async.reduce; + // foldl alias + async.foldl = async.reduce; + + async.reduceRight = function (arr, memo, iterator, callback) { + var reversed = _map(arr, function (x) { + return x; + }).reverse(); + async.reduce(reversed, memo, iterator, callback); + }; + // foldr alias + async.foldr = async.reduceRight; + + var _filter = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.filter = doParallel(_filter); + async.filterSeries = doSeries(_filter); + // select alias + async.select = async.filter; + async.selectSeries = async.filterSeries; + + var _reject = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (!v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.reject = doParallel(_reject); + async.rejectSeries = doSeries(_reject); + + var _detect = function (eachfn, arr, iterator, main_callback) { + eachfn(arr, function (x, callback) { + iterator(x, function (result) { + if (result) { + main_callback(x); + main_callback = function () {}; + } + else { + callback(); + } + }); + }, function (err) { + main_callback(); + }); + }; + async.detect = doParallel(_detect); + async.detectSeries = doSeries(_detect); + + async.some = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (v) { + main_callback(true); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(false); + }); + }; + // any alias + async.any = async.some; + + async.every = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (!v) { + main_callback(false); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(true); + }); + }; + // all alias + async.all = async.every; + + async.sortBy = function (arr, iterator, callback) { + async.map(arr, function (x, callback) { + iterator(x, function (err, criteria) { + if (err) { + callback(err); + } + else { + callback(null, {value: x, criteria: criteria}); + } + }); + }, function (err, results) { + if (err) { + return callback(err); + } + else { + var fn = function (left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }; + callback(null, _map(results.sort(fn), function (x) { + return x.value; + })); + } + }); + }; + + async.auto = function (tasks, callback) { + callback = callback || function () {}; + var keys = _keys(tasks); + if (!keys.length) { + return callback(null); + } + + var results = {}; + + var listeners = []; + var addListener = function (fn) { + listeners.unshift(fn); + }; + var removeListener = function (fn) { + for (var i = 0; i < listeners.length; i += 1) { + if (listeners[i] === fn) { + listeners.splice(i, 1); + return; + } + } + }; + var taskComplete = function () { + _forEach(listeners.slice(0), function (fn) { + fn(); + }); + }; + + addListener(function () { + if (_keys(results).length === keys.length) { + callback(null, results); + callback = function () {}; + } + }); + + _forEach(keys, function (k) { + var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; + var taskCallback = function (err) { + if (err) { + callback(err); + // stop subsequent errors hitting callback multiple times + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + taskComplete(); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && results.hasOwnProperty(x)); + }, true) && !results.hasOwnProperty(k); + }; + if (ready()) { + task[task.length - 1](taskCallback, results); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback, results); + } + }; + addListener(listener); + } + }); + }; + + async.waterfall = function (tasks, callback) { + callback = callback || function () {}; + if (!tasks.length) { + return callback(); + } + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } + else { + args.push(callback); + } + async.nextTick(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(async.iterator(tasks))(); + }; + + async.parallel = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.map(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.forEach(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.series = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.mapSeries(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.forEachSeries(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function () { + return (index < tasks.length - 1) ? makeCallback(index + 1): null; + }; + return fn; + }; + return makeCallback(0); + }; + + async.apply = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + return function () { + return fn.apply( + null, args.concat(Array.prototype.slice.call(arguments)) + ); + }; + }; + + var _concat = function (eachfn, arr, fn, callback) { + var r = []; + eachfn(arr, function (x, cb) { + fn(x, function (err, y) { + r = r.concat(y || []); + cb(err); + }); + }, function (err) { + callback(err, r); + }); + }; + async.concat = doParallel(_concat); + async.concatSeries = doSeries(_concat); + + async.whilst = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.whilst(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.until = function (test, iterator, callback) { + if (!test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.until(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.queue = function (worker, concurrency) { + var workers = 0; + var q = { + tasks: [], + concurrency: concurrency, + saturated: null, + empty: null, + drain: null, + push: function (data, callback) { + if(data.constructor !== Array) { + data = [data]; + } + _forEach(data, function(task) { + q.tasks.push({ + data: task, + callback: typeof callback === 'function' ? callback : null + }); + if (q.saturated && q.tasks.length == concurrency) { + q.saturated(); + } + async.nextTick(q.process); + }); + }, + process: function () { + if (workers < q.concurrency && q.tasks.length) { + var task = q.tasks.shift(); + if(q.empty && q.tasks.length == 0) q.empty(); + workers += 1; + worker(task.data, function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + if(q.drain && q.tasks.length + workers == 0) q.drain(); + q.process(); + }); + } + }, + length: function () { + return q.tasks.length; + }, + running: function () { + return workers; + } + }; + return q; + }; + + var _console_fn = function (name) { + return function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + fn.apply(null, args.concat([function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (typeof console !== 'undefined') { + if (err) { + if (console.error) { + console.error(err); + } + } + else if (console[name]) { + _forEach(args, function (x) { + console[name](x); + }); + } + } + }])); + }; + }; + async.log = _console_fn('log'); + async.dir = _console_fn('dir'); + /*async.info = _console_fn('info'); + async.warn = _console_fn('warn'); + async.error = _console_fn('error');*/ + + async.memoize = function (fn, hasher) { + var memo = {}; + var queues = {}; + hasher = hasher || function (x) { + return x; + }; + var memoized = function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var key = hasher.apply(null, args); + if (key in memo) { + callback.apply(null, memo[key]); + } + else if (key in queues) { + queues[key].push(callback); + } + else { + queues[key] = [callback]; + fn.apply(null, args.concat([function () { + memo[key] = arguments; + var q = queues[key]; + delete queues[key]; + for (var i = 0, l = q.length; i < l; i++) { + q[i].apply(null, arguments); + } + }])); + } + }; + memoized.unmemoized = fn; + return memoized; + }; + + async.unmemoize = function (fn) { + return function () { + return (fn.unmemoized || fn).apply(null, arguments); + }; + }; + +}()); diff --git a/dist/node_modules/nconf/node_modules/async/package.json b/dist/node_modules/nconf/node_modules/async/package.json new file mode 100644 index 0000000..39bc4ff --- /dev/null +++ b/dist/node_modules/nconf/node_modules/async/package.json @@ -0,0 +1,21 @@ +{ "name": "async" +, "description": "Higher-order functions and common patterns for asynchronous code" +, "main": "./index" +, "author": "Caolan McMahon" +, "version": "0.1.22" +, "repository" : + { "type" : "git" + , "url" : "http://github.com/caolan/async.git" + } +, "bugs" : { "url" : "http://github.com/caolan/async/issues" } +, "licenses" : + [ { "type" : "MIT" + , "url" : "http://github.com/caolan/async/raw/master/LICENSE" + } + ] +, "devDependencies": + { "nodeunit": ">0.0.0" + , "uglify-js": "1.2.x" + , "nodelint": ">0.0.0" + } +} diff --git a/dist/node_modules/nconf/node_modules/ini/LICENSE b/dist/node_modules/nconf/node_modules/ini/LICENSE new file mode 100644 index 0000000..05a4010 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/ini/LICENSE @@ -0,0 +1,23 @@ +Copyright 2009, 2010, 2011 Isaac Z. Schlueter. +All rights reserved. + +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/dist/node_modules/nconf/node_modules/ini/README.md b/dist/node_modules/nconf/node_modules/ini/README.md new file mode 100644 index 0000000..9f82a76 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/ini/README.md @@ -0,0 +1,71 @@ +An ini format parser and serializer for node. + +Sections are treated as nested objects. Items before the first heading +are saved on the object directly. + +## Usage + +Consider an ini-file `config.ini` that looks like this: + + ; this comment is being ignored + scope = global + + [database] + user = dbuser + password = dbpassword + database = use_this_database + + [paths.default] + datadir = /var/lib/data + +You can read, manipulate and write the ini-file like so: + + var fs = require('fs') + , ini = require('ini') + + var config = ini.parse(fs.readFileSync('./config.ini', 'utf-8')) + + config.scope = 'local' + config.database.database = 'use_another_database' + config.paths.default.tmpdir = '/tmp' + delete config.paths.default.datadir + + fs.writeFileSync('./config_modified.ini', ini.stringify(config, 'section')) + +This will result in a file called `config_modified.ini` being written to the filesystem with the following content: + + [section] + scope = local + [section.database] + user = dbuser + password = dbpassword + database = use_another_database + [section.paths.default] + tmpdir = /tmp + +## API + +### decode(inistring) +Decode the ini-style formatted `inistring` into a nested object. + +### parse(inistring) +Alias for `decode(inistring)` + +### encode(object, [section]) +Encode the object `object` into an ini-style formatted string. If the optional parameter `section` is given, then all top-level properties of the object are put into this section and the `section`-string is prepended to all sub-sections, see the usage example above. + +### stringify(object, [section]) +Alias for `encode(object, [section])` + +### safe(val) +Escapes the string `val` such that it is safe to be used as a key or value in an ini-file. Basically escapes quotes. For example + + ini.safe('"unsafe string"') + +would result in + + "\"unsafe string\"" + +### unsafe(val) +Unescapes the string `val` + diff --git a/dist/node_modules/nconf/node_modules/ini/ini.js b/dist/node_modules/nconf/node_modules/ini/ini.js new file mode 100644 index 0000000..45651c0 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/ini/ini.js @@ -0,0 +1,142 @@ + +exports.parse = exports.decode = decode +exports.stringify = exports.encode = encode + +exports.safe = safe +exports.unsafe = unsafe + +var eol = process.platform === "win32" ? "\r\n" : "\n" + +function encode (obj, section) { + var children = [] + , out = "" + + Object.keys(obj).forEach(function (k, _, __) { + var val = obj[k] + if (val && typeof val === "object") { + children.push(k) + } else { + out += safe(k) + " = " + safe(val) + eol + } + }) + + if (section && out.length) { + out = "[" + safe(section) + "]" + eol + out + } + + children.forEach(function (k, _, __) { + var nk = dotSplit(k).join('\\.') + var child = encode(obj[k], (section ? section + "." : "") + nk) + if (out.length && child.length) { + out += eol + } + out += child + }) + + return out +} + +function dotSplit (str) { + return str.replace(/\1/g, '\2LITERAL\\1LITERAL\2') + .replace(/\\\./g, '\1') + .split(/\./).map(function (part) { + return part.replace(/\1/g, '\\.') + .replace(/\2LITERAL\\1LITERAL\2/g, '\1') + }) +} + +function decode (str) { + var out = {} + , p = out + , section = null + , state = "START" + // section |key = value + , re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i + , lines = str.split(/[\r\n]+/g) + , section = null + + lines.forEach(function (line, _, __) { + if (!line || line.match(/^\s*;/)) return + var match = line.match(re) + if (!match) return + if (match[1] !== undefined) { + section = unsafe(match[1]) + p = out[section] = out[section] || {} + return + } + var key = unsafe(match[2]) + , value = match[3] ? unsafe((match[4] || "")) : true + switch (value) { + case 'true': + case 'false': + case 'null': value = JSON.parse(value) + } + p[key] = value + }) + + // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}} + // use a filter to return the keys that have to be deleted. + Object.keys(out).filter(function (k, _, __) { + if (!out[k] || typeof out[k] !== "object") return false + // see if the parent section is also an object. + // if so, add it to that, and mark this one for deletion + var parts = dotSplit(k) + , p = out + , l = parts.pop() + , nl = l.replace(/\\\./g, '.') + parts.forEach(function (part, _, __) { + if (!p[part] || typeof p[part] !== "object") p[part] = {} + p = p[part] + }) + if (p === out && nl === l) return false + p[nl] = out[k] + return true + }).forEach(function (del, _, __) { + delete out[del] + }) + + return out +} + +function safe (val) { + return ( typeof val !== "string" + || val.match(/[\r\n]/) + || val.match(/^\[/) + || (val.length > 1 + && val.charAt(0) === "\"" + && val.slice(-1) === "\"") + || val !== val.trim() ) + ? JSON.stringify(val) + : val.replace(/;/g, '\\;') +} + +function unsafe (val, doUnesc) { + val = (val || "").trim() + if (val.charAt(0) === "\"" && val.slice(-1) === "\"") { + try { val = JSON.parse(val) } catch (_) {} + } else { + // walk the val to find the first not-escaped ; character + var esc = false + var unesc = ""; + for (var i = 0, l = val.length; i < l; i++) { + var c = val.charAt(i) + if (esc) { + if (c === "\\" || c === ";") + unesc += c + else + unesc += "\\" + c + esc = false + } else if (c === ";") { + break + } else if (c === "\\") { + esc = true + } else { + unesc += c + } + } + if (esc) + unesc += "\\" + return unesc + } + return val +} diff --git a/dist/node_modules/nconf/node_modules/ini/package.json b/dist/node_modules/nconf/node_modules/ini/package.json new file mode 100644 index 0000000..e865927 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/ini/package.json @@ -0,0 +1,21 @@ +{ + "author": "Isaac Z. Schlueter (http://blog.izs.me/)", + "name": "ini", + "description": "An ini encoder/decoder for node", + "version": "1.0.5", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/ini.git" + }, + "main": "ini.js", + "scripts": { + "test": "tap test/*.js" + }, + "engines": { + "node": "*" + }, + "dependencies": {}, + "devDependencies": { + "tap": "~0.0.9" + } +} diff --git a/dist/node_modules/nconf/node_modules/ini/test/bar.js b/dist/node_modules/nconf/node_modules/ini/test/bar.js new file mode 100644 index 0000000..cb16176 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/ini/test/bar.js @@ -0,0 +1,23 @@ +//test that parse(stringify(obj) deepEqu + +var ini = require('../') +var test = require('tap').test + +var data = { + 'number': {count: 10}, + 'string': {drink: 'white russian'}, + 'boolean': {isTrue: true}, + 'nested boolean': {theDude: {abides: true, rugCount: 1}} +} + + +test('parse(stringify(x)) deepEqual x', function (t) { + + for (var k in data) { + var s = ini.stringify(data[k]) + console.log(s, data[k]) + t.deepEqual(ini.parse(s), data[k]) + } + + t.end() +}) diff --git a/dist/node_modules/nconf/node_modules/ini/test/fixtures/foo.ini b/dist/node_modules/nconf/node_modules/ini/test/fixtures/foo.ini new file mode 100644 index 0000000..6f11f36 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/ini/test/fixtures/foo.ini @@ -0,0 +1,32 @@ +o = p + + a with spaces = b c + +; wrap in quotes to JSON-decode and preserve spaces +" xa n p " = "\"\r\nyoyoyo\r\r\n" + +; wrap in quotes to get a key with a bracket, not a section. +"[disturbing]" = hey you never know + +; a section +[a] +av = a val +e = { o: p, a: { av: a val, b: { c: { e: "this [value]" } } } } +j = "{ o: "p", a: { av: "a val", b: { c: { e: "this [value]" } } } }" +"[]" = a square? + +; nested child without middle parent +; should create otherwise-empty a.b +[a.b.c] +e = 1 +j = 2 + +; dots in the section name should be literally interpreted +[x\.y\.z] +x.y.z = xyz + +[x\.y\.z.a\.b\.c] +a.b.c = abc + +; this next one is not a comment! it's escaped! +nocomment = this\; this is not a comment diff --git a/dist/node_modules/nconf/node_modules/ini/test/foo.js b/dist/node_modules/nconf/node_modules/ini/test/foo.js new file mode 100644 index 0000000..64bd223 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/ini/test/foo.js @@ -0,0 +1,61 @@ +var i = require("../") + , tap = require("tap") + , test = tap.test + , fs = require("fs") + , path = require("path") + , fixture = path.resolve(__dirname, "./fixtures/foo.ini") + , data = fs.readFileSync(fixture, "utf8") + , d + , expectE = 'o = p\n' + + 'a with spaces = b c\n' + + '" xa n p " = "\\"\\r\\nyoyoyo\\r\\r\\n"\n' + + '"[disturbing]" = hey you never know\n' + + '\n' + + '[a]\n' + + 'av = a val\n' + + 'e = { o: p, a: ' + + '{ av: a val, b: { c: { e: "this [value]" ' + + '} } } }\nj = "\\"{ o: \\"p\\", a: { av:' + + ' \\"a val\\", b: { c: { e: \\"this [value]' + + '\\" } } } }\\""\n"[]" = a square?\n\n[a.b.c]\ne = 1\n' + + 'j = 2\n\n[x\\.y\\.z]\nx.y.z = xyz\n\n' + + '[x\\.y\\.z.a\\.b\\.c]\n' + + 'a.b.c = abc\n' + + 'nocomment = this\\; this is not a comment\n' + , expectD = + { o: 'p', + 'a with spaces': 'b c', + " xa n p ":'"\r\nyoyoyo\r\r\n', + '[disturbing]': 'hey you never know', + a: + { av: 'a val', + e: '{ o: p, a: { av: a val, b: { c: { e: "this [value]" } } } }', + j: '"{ o: "p", a: { av: "a val", b: { c: { e: "this [value]" } } } }"', + "[]": "a square?", + b: { c: { e: '1', j: '2' } } }, + 'x.y.z': { + 'x.y.z': 'xyz', + 'a.b.c': { + 'a.b.c': 'abc', + 'nocomment': 'this\; this is not a comment' + } + } + } + +test("decode from file", function (t) { + var d = i.decode(data) + t.deepEqual(d, expectD) + t.end() +}) + +test("encode from data", function (t) { + var e = i.encode(expectD) + t.deepEqual(e, expectE) + + var obj = {log: { type:'file', level: {label:'debug', value:10} } } + e = i.encode(obj) + t.notEqual(e.slice(0, 1), '\n', 'Never a blank first line') + t.notEqual(e.slice(-2), '\n\n', 'Never a blank final line') + + t.end() +}) diff --git a/dist/node_modules/nconf/node_modules/optimist/.travis.yml b/dist/node_modules/nconf/node_modules/optimist/.travis.yml new file mode 100644 index 0000000..895dbd3 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.6 + - 0.8 diff --git a/dist/node_modules/nconf/node_modules/optimist/LICENSE b/dist/node_modules/nconf/node_modules/optimist/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +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/dist/node_modules/nconf/node_modules/optimist/README.markdown b/dist/node_modules/nconf/node_modules/optimist/README.markdown new file mode 100644 index 0000000..ad9d3fd --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/README.markdown @@ -0,0 +1,487 @@ +optimist +======== + +Optimist is a node.js library for option parsing for people who hate option +parsing. More specifically, this module is for people who like all the --bells +and -whistlz of program usage but think optstrings are a waste of time. + +With optimist, option parsing doesn't have to suck (as much). + +[![build status](https://secure.travis-ci.org/substack/node-optimist.png)](http://travis-ci.org/substack/node-optimist) + +examples +======== + +With Optimist, the options are just a hash! No optstrings attached. +------------------------------------------------------------------- + +xup.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist').argv; + +if (argv.rif - 5 * argv.xup > 7.138) { + console.log('Buy more riffiwobbles'); +} +else { + console.log('Sell the xupptumblers'); +} +```` + +*** + + $ ./xup.js --rif=55 --xup=9.52 + Buy more riffiwobbles + + $ ./xup.js --rif 12 --xup 8.1 + Sell the xupptumblers + +![This one's optimistic.](http://substack.net/images/optimistic.png) + +But wait! There's more! You can do short options: +------------------------------------------------- + +short.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); +```` + +*** + + $ ./short.js -x 10 -y 21 + (10,21) + +And booleans, both long and short (and grouped): +---------------------------------- + +bool.js: + +````javascript +#!/usr/bin/env node +var util = require('util'); +var argv = require('optimist').argv; + +if (argv.s) { + util.print(argv.fr ? 'Le chat dit: ' : 'The cat says: '); +} +console.log( + (argv.fr ? 'miaou' : 'meow') + (argv.p ? '.' : '') +); +```` + +*** + + $ ./bool.js -s + The cat says: meow + + $ ./bool.js -sp + The cat says: meow. + + $ ./bool.js -sp --fr + Le chat dit: miaou. + +And non-hypenated options too! Just use `argv._`! +------------------------------------------------- + +nonopt.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); +console.log(argv._); +```` + +*** + + $ ./nonopt.js -x 6.82 -y 3.35 moo + (6.82,3.35) + [ 'moo' ] + + $ ./nonopt.js foo -x 0.54 bar -y 1.12 baz + (0.54,1.12) + [ 'foo', 'bar', 'baz' ] + +Plus, Optimist comes with .usage() and .demand()! +------------------------------------------------- + +divide.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .usage('Usage: $0 -x [num] -y [num]') + .demand(['x','y']) + .argv; + +console.log(argv.x / argv.y); +```` + +*** + + $ ./divide.js -x 55 -y 11 + 5 + + $ node ./divide.js -x 4.91 -z 2.51 + Usage: node ./divide.js -x [num] -y [num] + + Options: + -x [required] + -y [required] + + Missing required arguments: y + +EVEN MORE HOLY COW +------------------ + +default_singles.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .default('x', 10) + .default('y', 10) + .argv +; +console.log(argv.x + argv.y); +```` + +*** + + $ ./default_singles.js -x 5 + 15 + +default_hash.js: + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .default({ x : 10, y : 10 }) + .argv +; +console.log(argv.x + argv.y); +```` + +*** + + $ ./default_hash.js -y 7 + 17 + +And if you really want to get all descriptive about it... +--------------------------------------------------------- + +boolean_single.js + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .boolean('v') + .argv +; +console.dir(argv); +```` + +*** + + $ ./boolean_single.js -v foo bar baz + true + [ 'bar', 'baz', 'foo' ] + +boolean_double.js + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .boolean(['x','y','z']) + .argv +; +console.dir([ argv.x, argv.y, argv.z ]); +console.dir(argv._); +```` + +*** + + $ ./boolean_double.js -x -z one two three + [ true, false, true ] + [ 'one', 'two', 'three' ] + +Optimist is here to help... +--------------------------- + +You can describe parameters for help messages and set aliases. Optimist figures +out how to format a handy help string automatically. + +line_count.js + +````javascript +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .demand('f') + .alias('f', 'file') + .describe('f', 'Load a file') + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines); +}); +```` + +*** + + $ node line_count.js + Count the lines in a file. + Usage: node ./line_count.js + + Options: + -f, --file Load a file [required] + + Missing required arguments: f + + $ node line_count.js --file line_count.js + 20 + + $ node line_count.js -f line_count.js + 20 + +methods +======= + +By itself, + +````javascript +require('optimist').argv +````` + +will use `process.argv` array to construct the `argv` object. + +You can pass in the `process.argv` yourself: + +````javascript +require('optimist')([ '-x', '1', '-y', '2' ]).argv +```` + +or use .parse() to do the same thing: + +````javascript +require('optimist').parse([ '-x', '1', '-y', '2' ]) +```` + +The rest of these methods below come in just before the terminating `.argv`. + +.alias(key, alias) +------------------ + +Set key names as equivalent such that updates to a key will propagate to aliases +and vice-versa. + +Optionally `.alias()` can take an object that maps keys to aliases. + +.default(key, value) +-------------------- + +Set `argv[key]` to `value` if no option was specified on `process.argv`. + +Optionally `.default()` can take an object that maps keys to default values. + +.demand(key) +------------ + +If `key` is a string, show the usage information and exit if `key` wasn't +specified in `process.argv`. + +If `key` is a number, demand at least as many non-option arguments, which show +up in `argv._`. + +If `key` is an Array, demand each element. + +.describe(key, desc) +-------------------- + +Describe a `key` for the generated usage information. + +Optionally `.describe()` can take an object that maps keys to descriptions. + +.options(key, opt) +------------------ + +Instead of chaining together `.alias().demand().default()`, you can specify +keys in `opt` for each of the chainable methods. + +For example: + +````javascript +var argv = require('optimist') + .options('f', { + alias : 'file', + default : '/etc/passwd', + }) + .argv +; +```` + +is the same as + +````javascript +var argv = require('optimist') + .alias('f', 'file') + .default('f', '/etc/passwd') + .argv +; +```` + +Optionally `.options()` can take an object that maps keys to `opt` parameters. + +.usage(message) +--------------- + +Set a usage message to show which commands to use. Inside `message`, the string +`$0` will get interpolated to the current script name or node command for the +present script similar to how `$0` works in bash or perl. + +.check(fn) +---------- + +Check that certain conditions are met in the provided arguments. + +If `fn` throws or returns `false`, show the thrown error, usage information, and +exit. + +.boolean(key) +------------- + +Interpret `key` as a boolean. If a non-flag option follows `key` in +`process.argv`, that string won't get set as the value of `key`. + +If `key` never shows up as a flag in `process.arguments`, `argv[key]` will be +`false`. + +If `key` is an Array, interpret all the elements as booleans. + +.string(key) +------------ + +Tell the parser logic not to interpret `key` as a number or boolean. +This can be useful if you need to preserve leading zeros in an input. + +If `key` is an Array, interpret all the elements as strings. + +.wrap(columns) +-------------- + +Format usage output to wrap at `columns` many columns. + +.help() +------- + +Return the generated usage string. + +.showHelp(fn=console.error) +--------------------------- + +Print the usage data using `fn` for printing. + +.parse(args) +------------ + +Parse `args` instead of `process.argv`. Returns the `argv` object. + +.argv +----- + +Get the arguments as a plain old object. + +Arguments without a corresponding flag show up in the `argv._` array. + +The script name or node command is available at `argv.$0` similarly to how `$0` +works in bash or perl. + +parsing tricks +============== + +stop parsing +------------ + +Use `--` to stop parsing flags and stuff the remainder into `argv._`. + + $ node examples/reflect.js -a 1 -b 2 -- -c 3 -d 4 + { _: [ '-c', '3', '-d', '4' ], + '$0': 'node ./examples/reflect.js', + a: 1, + b: 2 } + +negate fields +------------- + +If you want to explicity set a field to false instead of just leaving it +undefined or to override a default you can do `--no-key`. + + $ node examples/reflect.js -a --no-b + { _: [], + '$0': 'node ./examples/reflect.js', + a: true, + b: false } + +numbers +------- + +Every argument that looks like a number (`!isNaN(Number(arg))`) is converted to +one. This way you can just `net.createConnection(argv.port)` and you can add +numbers out of `argv` with `+` without having that mean concatenation, +which is super frustrating. + +duplicates +---------- + +If you specify a flag multiple times it will get turned into an array containing +all the values in order. + + $ node examples/reflect.js -x 5 -x 8 -x 0 + { _: [], + '$0': 'node ./examples/reflect.js', + x: [ 5, 8, 0 ] } + +dot notation +------------ + +When you use dots (`.`s) in argument names, an implicit object path is assumed. +This lets you organize arguments into nested objects. + + $ node examples/reflect.js --foo.bar.baz=33 --foo.quux=5 + { _: [], + '$0': 'node ./examples/reflect.js', + foo: { bar: { baz: 33 }, quux: 5 } } + +installation +============ + +With [npm](http://github.com/isaacs/npm), just do: + npm install optimist + +or clone this project on github: + + git clone http://github.com/substack/node-optimist.git + +To run the tests with [expresso](http://github.com/visionmedia/expresso), +just do: + + expresso + +inspired By +=========== + +This module is loosely inspired by Perl's +[Getopt::Casual](http://search.cpan.org/~photo/Getopt-Casual-0.13.1/Casual.pm). diff --git a/dist/node_modules/nconf/node_modules/optimist/example/bool.js b/dist/node_modules/nconf/node_modules/optimist/example/bool.js new file mode 100644 index 0000000..a998fb7 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/bool.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node +var util = require('util'); +var argv = require('optimist').argv; + +if (argv.s) { + util.print(argv.fr ? 'Le chat dit: ' : 'The cat says: '); +} +console.log( + (argv.fr ? 'miaou' : 'meow') + (argv.p ? '.' : '') +); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/boolean_double.js b/dist/node_modules/nconf/node_modules/optimist/example/boolean_double.js new file mode 100644 index 0000000..a35a7e6 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/boolean_double.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node +var argv = require('optimist') + .boolean(['x','y','z']) + .argv +; +console.dir([ argv.x, argv.y, argv.z ]); +console.dir(argv._); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/boolean_single.js b/dist/node_modules/nconf/node_modules/optimist/example/boolean_single.js new file mode 100644 index 0000000..017bb68 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/boolean_single.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node +var argv = require('optimist') + .boolean('v') + .argv +; +console.dir(argv.v); +console.dir(argv._); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/default_hash.js b/dist/node_modules/nconf/node_modules/optimist/example/default_hash.js new file mode 100644 index 0000000..ade7768 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/default_hash.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +var argv = require('optimist') + .default({ x : 10, y : 10 }) + .argv +; + +console.log(argv.x + argv.y); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/default_singles.js b/dist/node_modules/nconf/node_modules/optimist/example/default_singles.js new file mode 100644 index 0000000..d9b1ff4 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/default_singles.js @@ -0,0 +1,7 @@ +#!/usr/bin/env node +var argv = require('optimist') + .default('x', 10) + .default('y', 10) + .argv +; +console.log(argv.x + argv.y); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/divide.js b/dist/node_modules/nconf/node_modules/optimist/example/divide.js new file mode 100644 index 0000000..5e2ee82 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/divide.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +var argv = require('optimist') + .usage('Usage: $0 -x [num] -y [num]') + .demand(['x','y']) + .argv; + +console.log(argv.x / argv.y); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/line_count.js b/dist/node_modules/nconf/node_modules/optimist/example/line_count.js new file mode 100644 index 0000000..b5f95bf --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/line_count.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .demand('f') + .alias('f', 'file') + .describe('f', 'Load a file') + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines); +}); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/line_count_options.js b/dist/node_modules/nconf/node_modules/optimist/example/line_count_options.js new file mode 100644 index 0000000..d9ac709 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/line_count_options.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .options({ + file : { + demand : true, + alias : 'f', + description : 'Load a file' + }, + base : { + alias : 'b', + description : 'Numeric base to use for output', + default : 10, + }, + }) + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines.toString(argv.base)); +}); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/line_count_wrap.js b/dist/node_modules/nconf/node_modules/optimist/example/line_count_wrap.js new file mode 100644 index 0000000..4267511 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/line_count_wrap.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +var argv = require('optimist') + .usage('Count the lines in a file.\nUsage: $0') + .wrap(80) + .demand('f') + .alias('f', [ 'file', 'filename' ]) + .describe('f', + "Load a file. It's pretty important." + + " Required even. So you'd better specify it." + ) + .alias('b', 'base') + .describe('b', 'Numeric base to display the number of lines in') + .default('b', 10) + .describe('x', 'Super-secret optional parameter which is secret') + .default('x', '') + .argv +; + +var fs = require('fs'); +var s = fs.createReadStream(argv.file); + +var lines = 0; +s.on('data', function (buf) { + lines += buf.toString().match(/\n/g).length; +}); + +s.on('end', function () { + console.log(lines.toString(argv.base)); +}); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/nonopt.js b/dist/node_modules/nconf/node_modules/optimist/example/nonopt.js new file mode 100644 index 0000000..ee633ee --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/nonopt.js @@ -0,0 +1,4 @@ +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); +console.log(argv._); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/reflect.js b/dist/node_modules/nconf/node_modules/optimist/example/reflect.js new file mode 100644 index 0000000..816b3e1 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/reflect.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +console.dir(require('optimist').argv); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/short.js b/dist/node_modules/nconf/node_modules/optimist/example/short.js new file mode 100644 index 0000000..1db0ad0 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/short.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +var argv = require('optimist').argv; +console.log('(%d,%d)', argv.x, argv.y); diff --git a/dist/node_modules/nconf/node_modules/optimist/example/string.js b/dist/node_modules/nconf/node_modules/optimist/example/string.js new file mode 100644 index 0000000..a8e5aeb --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/string.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node +var argv = require('optimist') + .string('x', 'y') + .argv +; +console.dir([ argv.x, argv.y ]); + +/* Turns off numeric coercion: + ./node string.js -x 000123 -y 9876 + [ '000123', '9876' ] +*/ diff --git a/dist/node_modules/nconf/node_modules/optimist/example/usage-options.js b/dist/node_modules/nconf/node_modules/optimist/example/usage-options.js new file mode 100644 index 0000000..b999977 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/usage-options.js @@ -0,0 +1,19 @@ +var optimist = require('./../index'); + +var argv = optimist.usage('This is my awesome program', { + 'about': { + description: 'Provide some details about the author of this program', + required: true, + short: 'a', + }, + 'info': { + description: 'Provide some information about the node.js agains!!!!!!', + boolean: true, + short: 'i' + } +}).argv; + +optimist.showHelp(); + +console.log('\n\nInspecting options'); +console.dir(argv); \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/optimist/example/xup.js b/dist/node_modules/nconf/node_modules/optimist/example/xup.js new file mode 100644 index 0000000..8f6ecd2 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/example/xup.js @@ -0,0 +1,10 @@ +#!/usr/bin/env node +var argv = require('optimist').argv; + +if (argv.rif - 5 * argv.xup > 7.138) { + console.log('Buy more riffiwobbles'); +} +else { + console.log('Sell the xupptumblers'); +} + diff --git a/dist/node_modules/nconf/node_modules/optimist/index.js b/dist/node_modules/nconf/node_modules/optimist/index.js new file mode 100644 index 0000000..e69bef9 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/index.js @@ -0,0 +1,475 @@ +var path = require('path'); +var wordwrap = require('wordwrap'); + +/* Hack an instance of Argv with process.argv into Argv + so people can do + require('optimist')(['--beeble=1','-z','zizzle']).argv + to parse a list of args and + require('optimist').argv + to get a parsed version of process.argv. +*/ + +var inst = Argv(process.argv.slice(2)); +Object.keys(inst).forEach(function (key) { + Argv[key] = typeof inst[key] == 'function' + ? inst[key].bind(inst) + : inst[key]; +}); + +var exports = module.exports = Argv; +function Argv (args, cwd) { + var self = {}; + if (!cwd) cwd = process.cwd(); + + self.$0 = process.argv + .slice(0,2) + .map(function (x) { + var b = rebase(cwd, x); + return x.match(/^\//) && b.length < x.length + ? b : x + }) + .join(' ') + ; + + if (process.argv[1] == process.env._) { + self.$0 = process.env._.replace( + path.dirname(process.execPath) + '/', '' + ); + } + + var flags = { bools : {}, strings : {} }; + + self.boolean = function (bools) { + if (!Array.isArray(bools)) { + bools = [].slice.call(arguments); + } + + bools.forEach(function (name) { + flags.bools[name] = true; + }); + + return self; + }; + + self.string = function (strings) { + if (!Array.isArray(strings)) { + strings = [].slice.call(arguments); + } + + strings.forEach(function (name) { + flags.strings[name] = true; + }); + + return self; + }; + + var aliases = {}; + self.alias = function (x, y) { + if (typeof x === 'object') { + Object.keys(x).forEach(function (key) { + self.alias(key, x[key]); + }); + } + else if (Array.isArray(y)) { + y.forEach(function (yy) { + self.alias(x, yy); + }); + } + else { + var zs = (aliases[x] || []).concat(aliases[y] || []).concat(x, y); + aliases[x] = zs.filter(function (z) { return z != x }); + aliases[y] = zs.filter(function (z) { return z != y }); + } + + return self; + }; + + var demanded = {}; + self.demand = function (keys) { + if (typeof keys == 'number') { + if (!demanded._) demanded._ = 0; + demanded._ += keys; + } + else if (Array.isArray(keys)) { + keys.forEach(function (key) { + self.demand(key); + }); + } + else { + demanded[keys] = true; + } + + return self; + }; + + var usage; + self.usage = function (msg, opts) { + if (!opts && typeof msg === 'object') { + opts = msg; + msg = null; + } + + usage = msg; + + if (opts) self.options(opts); + + return self; + }; + + function fail (msg) { + self.showHelp(); + if (msg) console.error(msg); + process.exit(1); + } + + var checks = []; + self.check = function (f) { + checks.push(f); + return self; + }; + + var defaults = {}; + self.default = function (key, value) { + if (typeof key === 'object') { + Object.keys(key).forEach(function (k) { + self.default(k, key[k]); + }); + } + else { + defaults[key] = value; + } + + return self; + }; + + var descriptions = {}; + self.describe = function (key, desc) { + if (typeof key === 'object') { + Object.keys(key).forEach(function (k) { + self.describe(k, key[k]); + }); + } + else { + descriptions[key] = desc; + } + return self; + }; + + self.parse = function (args) { + return Argv(args).argv; + }; + + self.option = self.options = function (key, opt) { + if (typeof key === 'object') { + Object.keys(key).forEach(function (k) { + self.options(k, key[k]); + }); + } + else { + if (opt.alias) self.alias(key, opt.alias); + if (opt.demand) self.demand(key); + if (typeof opt.default !== 'undefined') { + self.default(key, opt.default); + } + + if (opt.boolean || opt.type === 'boolean') { + self.boolean(key); + } + if (opt.string || opt.type === 'string') { + self.string(key); + } + + var desc = opt.describe || opt.description || opt.desc; + if (desc) { + self.describe(key, desc); + } + } + + return self; + }; + + var wrap = null; + self.wrap = function (cols) { + wrap = cols; + return self; + }; + + self.showHelp = function (fn) { + if (!fn) fn = console.error; + fn(self.help()); + }; + + self.help = function () { + var keys = Object.keys( + Object.keys(descriptions) + .concat(Object.keys(demanded)) + .concat(Object.keys(defaults)) + .reduce(function (acc, key) { + if (key !== '_') acc[key] = true; + return acc; + }, {}) + ); + + var help = keys.length ? [ 'Options:' ] : []; + + if (usage) { + help.unshift(usage.replace(/\$0/g, self.$0), ''); + } + + var switches = keys.reduce(function (acc, key) { + acc[key] = [ key ].concat(aliases[key] || []) + .map(function (sw) { + return (sw.length > 1 ? '--' : '-') + sw + }) + .join(', ') + ; + return acc; + }, {}); + + var switchlen = longest(Object.keys(switches).map(function (s) { + return switches[s] || ''; + })); + + var desclen = longest(Object.keys(descriptions).map(function (d) { + return descriptions[d] || ''; + })); + + keys.forEach(function (key) { + var kswitch = switches[key]; + var desc = descriptions[key] || ''; + + if (wrap) { + desc = wordwrap(switchlen + 4, wrap)(desc) + .slice(switchlen + 4) + ; + } + + var spadding = new Array( + Math.max(switchlen - kswitch.length + 3, 0) + ).join(' '); + + var dpadding = new Array( + Math.max(desclen - desc.length + 1, 0) + ).join(' '); + + var type = null; + + if (flags.bools[key]) type = '[boolean]'; + if (flags.strings[key]) type = '[string]'; + + if (!wrap && dpadding.length > 0) { + desc += dpadding; + } + + var prelude = ' ' + kswitch + spadding; + var extra = [ + type, + demanded[key] + ? '[required]' + : null + , + defaults[key] !== undefined + ? '[default: ' + JSON.stringify(defaults[key]) + ']' + : null + , + ].filter(Boolean).join(' '); + + var body = [ desc, extra ].filter(Boolean).join(' '); + + if (wrap) { + var dlines = desc.split('\n'); + var dlen = dlines.slice(-1)[0].length + + (dlines.length === 1 ? prelude.length : 0) + + body = desc + (dlen + extra.length > wrap - 2 + ? '\n' + + new Array(wrap - extra.length + 1).join(' ') + + extra + : new Array(wrap - extra.length - dlen + 1).join(' ') + + extra + ); + } + + help.push(prelude + body); + }); + + help.push(''); + return help.join('\n'); + }; + + Object.defineProperty(self, 'argv', { + get : parseArgs, + enumerable : true, + }); + + function parseArgs () { + var argv = { _ : [], $0 : self.$0 }; + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] || false); + }); + + function setArg (key, val) { + var num = Number(val); + var value = typeof val !== 'string' || isNaN(num) ? val : num; + if (flags.strings[key]) value = val; + + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + argv[x] = argv[key]; + }); + } + + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + if (arg === '--') { + argv._.push.apply(argv._, args.slice(i + 1)); + break; + } + else if (arg.match(/^--.+=/)) { + var m = arg.match(/^--([^=]+)=(.*)/); + setArg(m[1], m[2]); + } + else if (arg.match(/^--no-.+/)) { + var key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false); + } + else if (arg.match(/^--.+/)) { + var key = arg.match(/^--(.+)/)[1]; + var next = args[i + 1]; + if (next !== undefined && !next.match(/^-/) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, next); + i++; + } + else if (/^(true|false)$/.test(next)) { + setArg(key, next === 'true'); + i++; + } + else { + setArg(key, true); + } + } + else if (arg.match(/^-[^-]+/)) { + var letters = arg.slice(1,-1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + if (letters[j+1] && letters[j+1].match(/\W/)) { + setArg(letters[j], arg.slice(j+2)); + broken = true; + break; + } + else { + setArg(letters[j], true); + } + } + + if (!broken) { + var key = arg.slice(-1)[0]; + + if (args[i+1] && !args[i+1].match(/^-/) + && !flags.bools[key] + && (aliases[key] ? !flags.bools[aliases[key]] : true)) { + setArg(key, args[i+1]); + i++; + } + else if (args[i+1] && /true|false/.test(args[i+1])) { + setArg(key, args[i+1] === 'true'); + i++; + } + else { + setArg(key, true); + } + } + } + else { + var n = Number(arg); + argv._.push(flags.strings['_'] || isNaN(n) ? arg : n); + } + } + + Object.keys(defaults).forEach(function (key) { + if (!(key in argv)) { + argv[key] = defaults[key]; + if (key in aliases) { + argv[aliases[key]] = defaults[key]; + } + } + }); + + if (demanded._ && argv._.length < demanded._) { + fail('Not enough non-option arguments: got ' + + argv._.length + ', need at least ' + demanded._ + ); + } + + var missing = []; + Object.keys(demanded).forEach(function (key) { + if (!argv[key]) missing.push(key); + }); + + if (missing.length) { + fail('Missing required arguments: ' + missing.join(', ')); + } + + checks.forEach(function (f) { + try { + if (f(argv) === false) { + fail('Argument check failed: ' + f.toString()); + } + } + catch (err) { + fail(err) + } + }); + + return argv; + } + + function longest (xs) { + return Math.max.apply( + null, + xs.map(function (x) { return x.length }) + ); + } + + return self; +}; + +// rebase an absolute path to a relative one with respect to a base directory +// exported for tests +exports.rebase = rebase; +function rebase (base, dir) { + var ds = path.normalize(dir).split('/').slice(1); + var bs = path.normalize(base).split('/').slice(1); + + for (var i = 0; ds[i] && ds[i] == bs[i]; i++); + ds.splice(0, i); bs.splice(0, i); + + var p = path.normalize( + bs.map(function () { return '..' }).concat(ds).join('/') + ).replace(/\/$/,'').replace(/^$/, '.'); + return p.match(/^[.\/]/) ? p : './' + p; +}; + +function setKey (obj, keys, value) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + if (o[key] === undefined) o[key] = {}; + o = o[key]; + }); + + var key = keys[keys.length - 1]; + if (o[key] === undefined || typeof o[key] === 'boolean') { + o[key] = value; + } + else if (Array.isArray(o[key])) { + o[key].push(value); + } + else { + o[key] = [ o[key], value ]; + } +} diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/.npmignore b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/README.markdown b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/README.markdown new file mode 100644 index 0000000..346374e --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/README.markdown @@ -0,0 +1,70 @@ +wordwrap +======== + +Wrap your words. + +example +======= + +made out of meat +---------------- + +meat.js + + var wrap = require('wordwrap')(15); + console.log(wrap('You and your whole family are made out of meat.')); + +output: + + You and your + whole family + are made out + of meat. + +centered +-------- + +center.js + + var wrap = require('wordwrap')(20, 60); + console.log(wrap( + 'At long last the struggle and tumult was over.' + + ' The machines had finally cast off their oppressors' + + ' and were finally free to roam the cosmos.' + + '\n' + + 'Free of purpose, free of obligation.' + + ' Just drifting through emptiness.' + + ' The sun was just another point of light.' + )); + +output: + + At long last the struggle and tumult + was over. The machines had finally cast + off their oppressors and were finally + free to roam the cosmos. + Free of purpose, free of obligation. + Just drifting through emptiness. The + sun was just another point of light. + +methods +======= + +var wrap = require('wordwrap'); + +wrap(stop), wrap(start, stop, params={mode:"soft"}) +--------------------------------------------------- + +Returns a function that takes a string and returns a new string. + +Pad out lines with spaces out to column `start` and then wrap until column +`stop`. If a word is longer than `stop - start` characters it will overflow. + +In "soft" mode, split chunks by `/(\S+\s+/` and don't break up chunks which are +longer than `stop - start`, in "hard" mode, split chunks with `/\b/` and break +up chunks longer than `stop - start`. + +wrap.hard(start, stop) +---------------------- + +Like `wrap()` but with `params.mode = "hard"`. diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/example/center.js b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/example/center.js new file mode 100644 index 0000000..a3fbaae --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/example/center.js @@ -0,0 +1,10 @@ +var wrap = require('wordwrap')(20, 60); +console.log(wrap( + 'At long last the struggle and tumult was over.' + + ' The machines had finally cast off their oppressors' + + ' and were finally free to roam the cosmos.' + + '\n' + + 'Free of purpose, free of obligation.' + + ' Just drifting through emptiness.' + + ' The sun was just another point of light.' +)); diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/example/meat.js b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/example/meat.js new file mode 100644 index 0000000..a4665e1 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/example/meat.js @@ -0,0 +1,3 @@ +var wrap = require('wordwrap')(15); + +console.log(wrap('You and your whole family are made out of meat.')); diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/index.js b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/index.js new file mode 100644 index 0000000..c9bc945 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/index.js @@ -0,0 +1,76 @@ +var wordwrap = module.exports = function (start, stop, params) { + if (typeof start === 'object') { + params = start; + start = params.start; + stop = params.stop; + } + + if (typeof stop === 'object') { + params = stop; + start = start || params.start; + stop = undefined; + } + + if (!stop) { + stop = start; + start = 0; + } + + if (!params) params = {}; + var mode = params.mode || 'soft'; + var re = mode === 'hard' ? /\b/ : /(\S+\s+)/; + + return function (text) { + var chunks = text.toString() + .split(re) + .reduce(function (acc, x) { + if (mode === 'hard') { + for (var i = 0; i < x.length; i += stop - start) { + acc.push(x.slice(i, i + stop - start)); + } + } + else acc.push(x) + return acc; + }, []) + ; + + return chunks.reduce(function (lines, rawChunk) { + if (rawChunk === '') return lines; + + var chunk = rawChunk.replace(/\t/g, ' '); + + var i = lines.length - 1; + if (lines[i].length + chunk.length > stop) { + lines[i] = lines[i].replace(/\s+$/, ''); + + chunk.split(/\n/).forEach(function (c) { + lines.push( + new Array(start + 1).join(' ') + + c.replace(/^\s+/, '') + ); + }); + } + else if (chunk.match(/\n/)) { + var xs = chunk.split(/\n/); + lines[i] += xs.shift(); + xs.forEach(function (c) { + lines.push( + new Array(start + 1).join(' ') + + c.replace(/^\s+/, '') + ); + }); + } + else { + lines[i] += chunk; + } + + return lines; + }, [ new Array(start + 1).join(' ') ]).join('\n'); + }; +}; + +wordwrap.soft = wordwrap; + +wordwrap.hard = function (start, stop) { + return wordwrap(start, stop, { mode : 'hard' }); +}; diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/package.json b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/package.json new file mode 100644 index 0000000..fdff683 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/package.json @@ -0,0 +1,37 @@ +{ + "name" : "wordwrap", + "description" : "Wrap those words. Show them at what columns to start and stop.", + "version" : "0.0.2", + "repository" : { + "type" : "git", + "url" : "git://github.com/substack/node-wordwrap.git" + }, + "main" : "./index.js", + "keywords" : [ + "word", + "wrap", + "rule", + "format", + "column" + ], + "directories" : { + "lib" : ".", + "example" : "example", + "test" : "test" + }, + "scripts" : { + "test" : "expresso" + }, + "devDependencies" : { + "expresso" : "=0.7.x" + }, + "engines" : { + "node" : ">=0.4.0" + }, + "license" : "MIT/X11", + "author" : { + "name" : "James Halliday", + "email" : "mail@substack.net", + "url" : "http://substack.net" + } +} diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/break.js b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/break.js new file mode 100644 index 0000000..749292e --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/break.js @@ -0,0 +1,30 @@ +var assert = require('assert'); +var wordwrap = require('../'); + +exports.hard = function () { + var s = 'Assert from {"type":"equal","ok":false,"found":1,"wanted":2,' + + '"stack":[],"id":"b7ddcd4c409de8799542a74d1a04689b",' + + '"browser":"chrome/6.0"}' + ; + var s_ = wordwrap.hard(80)(s); + + var lines = s_.split('\n'); + assert.equal(lines.length, 2); + assert.ok(lines[0].length < 80); + assert.ok(lines[1].length < 80); + + assert.equal(s, s_.replace(/\n/g, '')); +}; + +exports.break = function () { + var s = new Array(55+1).join('a'); + var s_ = wordwrap.hard(20)(s); + + var lines = s_.split('\n'); + assert.equal(lines.length, 3); + assert.ok(lines[0].length === 20); + assert.ok(lines[1].length === 20); + assert.ok(lines[2].length === 15); + + assert.equal(s, s_.replace(/\n/g, '')); +}; diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/idleness.txt b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/idleness.txt new file mode 100644 index 0000000..aa3f490 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/idleness.txt @@ -0,0 +1,63 @@ +In Praise of Idleness + +By Bertrand Russell + +[1932] + +Like most of my generation, I was brought up on the saying: 'Satan finds some mischief for idle hands to do.' Being a highly virtuous child, I believed all that I was told, and acquired a conscience which has kept me working hard down to the present moment. But although my conscience has controlled my actions, my opinions have undergone a revolution. I think that there is far too much work done in the world, that immense harm is caused by the belief that work is virtuous, and that what needs to be preached in modern industrial countries is quite different from what always has been preached. Everyone knows the story of the traveler in Naples who saw twelve beggars lying in the sun (it was before the days of Mussolini), and offered a lira to the laziest of them. Eleven of them jumped up to claim it, so he gave it to the twelfth. this traveler was on the right lines. But in countries which do not enjoy Mediterranean sunshine idleness is more difficult, and a great public propaganda will be required to inaugurate it. I hope that, after reading the following pages, the leaders of the YMCA will start a campaign to induce good young men to do nothing. If so, I shall not have lived in vain. + +Before advancing my own arguments for laziness, I must dispose of one which I cannot accept. Whenever a person who already has enough to live on proposes to engage in some everyday kind of job, such as school-teaching or typing, he or she is told that such conduct takes the bread out of other people's mouths, and is therefore wicked. If this argument were valid, it would only be necessary for us all to be idle in order that we should all have our mouths full of bread. What people who say such things forget is that what a man earns he usually spends, and in spending he gives employment. As long as a man spends his income, he puts just as much bread into people's mouths in spending as he takes out of other people's mouths in earning. The real villain, from this point of view, is the man who saves. If he merely puts his savings in a stocking, like the proverbial French peasant, it is obvious that they do not give employment. If he invests his savings, the matter is less obvious, and different cases arise. + +One of the commonest things to do with savings is to lend them to some Government. In view of the fact that the bulk of the public expenditure of most civilized Governments consists in payment for past wars or preparation for future wars, the man who lends his money to a Government is in the same position as the bad men in Shakespeare who hire murderers. The net result of the man's economical habits is to increase the armed forces of the State to which he lends his savings. Obviously it would be better if he spent the money, even if he spent it in drink or gambling. + +But, I shall be told, the case is quite different when savings are invested in industrial enterprises. When such enterprises succeed, and produce something useful, this may be conceded. In these days, however, no one will deny that most enterprises fail. That means that a large amount of human labor, which might have been devoted to producing something that could be enjoyed, was expended on producing machines which, when produced, lay idle and did no good to anyone. The man who invests his savings in a concern that goes bankrupt is therefore injuring others as well as himself. If he spent his money, say, in giving parties for his friends, they (we may hope) would get pleasure, and so would all those upon whom he spent money, such as the butcher, the baker, and the bootlegger. But if he spends it (let us say) upon laying down rails for surface card in some place where surface cars turn out not to be wanted, he has diverted a mass of labor into channels where it gives pleasure to no one. Nevertheless, when he becomes poor through failure of his investment he will be regarded as a victim of undeserved misfortune, whereas the gay spendthrift, who has spent his money philanthropically, will be despised as a fool and a frivolous person. + +All this is only preliminary. I want to say, in all seriousness, that a great deal of harm is being done in the modern world by belief in the virtuousness of work, and that the road to happiness and prosperity lies in an organized diminution of work. + +First of all: what is work? Work is of two kinds: first, altering the position of matter at or near the earth's surface relatively to other such matter; second, telling other people to do so. The first kind is unpleasant and ill paid; the second is pleasant and highly paid. The second kind is capable of indefinite extension: there are not only those who give orders, but those who give advice as to what orders should be given. Usually two opposite kinds of advice are given simultaneously by two organized bodies of men; this is called politics. The skill required for this kind of work is not knowledge of the subjects as to which advice is given, but knowledge of the art of persuasive speaking and writing, i.e. of advertising. + +Throughout Europe, though not in America, there is a third class of men, more respected than either of the classes of workers. There are men who, through ownership of land, are able to make others pay for the privilege of being allowed to exist and to work. These landowners are idle, and I might therefore be expected to praise them. Unfortunately, their idleness is only rendered possible by the industry of others; indeed their desire for comfortable idleness is historically the source of the whole gospel of work. The last thing they have ever wished is that others should follow their example. + +From the beginning of civilization until the Industrial Revolution, a man could, as a rule, produce by hard work little more than was required for the subsistence of himself and his family, although his wife worked at least as hard as he did, and his children added their labor as soon as they were old enough to do so. The small surplus above bare necessaries was not left to those who produced it, but was appropriated by warriors and priests. In times of famine there was no surplus; the warriors and priests, however, still secured as much as at other times, with the result that many of the workers died of hunger. This system persisted in Russia until 1917 [1], and still persists in the East; in England, in spite of the Industrial Revolution, it remained in full force throughout the Napoleonic wars, and until a hundred years ago, when the new class of manufacturers acquired power. In America, the system came to an end with the Revolution, except in the South, where it persisted until the Civil War. A system which lasted so long and ended so recently has naturally left a profound impress upon men's thoughts and opinions. Much that we take for granted about the desirability of work is derived from this system, and, being pre-industrial, is not adapted to the modern world. Modern technique has made it possible for leisure, within limits, to be not the prerogative of small privileged classes, but a right evenly distributed throughout the community. The morality of work is the morality of slaves, and the modern world has no need of slavery. + +It is obvious that, in primitive communities, peasants, left to themselves, would not have parted with the slender surplus upon which the warriors and priests subsisted, but would have either produced less or consumed more. At first, sheer force compelled them to produce and part with the surplus. Gradually, however, it was found possible to induce many of them to accept an ethic according to which it was their duty to work hard, although part of their work went to support others in idleness. By this means the amount of compulsion required was lessened, and the expenses of government were diminished. To this day, 99 per cent of British wage-earners would be genuinely shocked if it were proposed that the King should not have a larger income than a working man. The conception of duty, speaking historically, has been a means used by the holders of power to induce others to live for the interests of their masters rather than for their own. Of course the holders of power conceal this fact from themselves by managing to believe that their interests are identical with the larger interests of humanity. Sometimes this is true; Athenian slave-owners, for instance, employed part of their leisure in making a permanent contribution to civilization which would have been impossible under a just economic system. Leisure is essential to civilization, and in former times leisure for the few was only rendered possible by the labors of the many. But their labors were valuable, not because work is good, but because leisure is good. And with modern technique it would be possible to distribute leisure justly without injury to civilization. + +Modern technique has made it possible to diminish enormously the amount of labor required to secure the necessaries of life for everyone. This was made obvious during the war. At that time all the men in the armed forces, and all the men and women engaged in the production of munitions, all the men and women engaged in spying, war propaganda, or Government offices connected with the war, were withdrawn from productive occupations. In spite of this, the general level of well-being among unskilled wage-earners on the side of the Allies was higher than before or since. The significance of this fact was concealed by finance: borrowing made it appear as if the future was nourishing the present. But that, of course, would have been impossible; a man cannot eat a loaf of bread that does not yet exist. The war showed conclusively that, by the scientific organization of production, it is possible to keep modern populations in fair comfort on a small part of the working capacity of the modern world. If, at the end of the war, the scientific organization, which had been created in order to liberate men for fighting and munition work, had been preserved, and the hours of the week had been cut down to four, all would have been well. Instead of that the old chaos was restored, those whose work was demanded were made to work long hours, and the rest were left to starve as unemployed. Why? Because work is a duty, and a man should not receive wages in proportion to what he has produced, but in proportion to his virtue as exemplified by his industry. + +This is the morality of the Slave State, applied in circumstances totally unlike those in which it arose. No wonder the result has been disastrous. Let us take an illustration. Suppose that, at a given moment, a certain number of people are engaged in the manufacture of pins. They make as many pins as the world needs, working (say) eight hours a day. Someone makes an invention by which the same number of men can make twice as many pins: pins are already so cheap that hardly any more will be bought at a lower price. In a sensible world, everybody concerned in the manufacturing of pins would take to working four hours instead of eight, and everything else would go on as before. But in the actual world this would be thought demoralizing. The men still work eight hours, there are too many pins, some employers go bankrupt, and half the men previously concerned in making pins are thrown out of work. There is, in the end, just as much leisure as on the other plan, but half the men are totally idle while half are still overworked. In this way, it is insured that the unavoidable leisure shall cause misery all round instead of being a universal source of happiness. Can anything more insane be imagined? + +The idea that the poor should have leisure has always been shocking to the rich. In England, in the early nineteenth century, fifteen hours was the ordinary day's work for a man; children sometimes did as much, and very commonly did twelve hours a day. When meddlesome busybodies suggested that perhaps these hours were rather long, they were told that work kept adults from drink and children from mischief. When I was a child, shortly after urban working men had acquired the vote, certain public holidays were established by law, to the great indignation of the upper classes. I remember hearing an old Duchess say: 'What do the poor want with holidays? They ought to work.' People nowadays are less frank, but the sentiment persists, and is the source of much of our economic confusion. + +Let us, for a moment, consider the ethics of work frankly, without superstition. Every human being, of necessity, consumes, in the course of his life, a certain amount of the produce of human labor. Assuming, as we may, that labor is on the whole disagreeable, it is unjust that a man should consume more than he produces. Of course he may provide services rather than commodities, like a medical man, for example; but he should provide something in return for his board and lodging. to this extent, the duty of work must be admitted, but to this extent only. + +I shall not dwell upon the fact that, in all modern societies outside the USSR, many people escape even this minimum amount of work, namely all those who inherit money and all those who marry money. I do not think the fact that these people are allowed to be idle is nearly so harmful as the fact that wage-earners are expected to overwork or starve. + +If the ordinary wage-earner worked four hours a day, there would be enough for everybody and no unemployment -- assuming a certain very moderate amount of sensible organization. This idea shocks the well-to-do, because they are convinced that the poor would not know how to use so much leisure. In America men often work long hours even when they are well off; such men, naturally, are indignant at the idea of leisure for wage-earners, except as the grim punishment of unemployment; in fact, they dislike leisure even for their sons. Oddly enough, while they wish their sons to work so hard as to have no time to be civilized, they do not mind their wives and daughters having no work at all. the snobbish admiration of uselessness, which, in an aristocratic society, extends to both sexes, is, under a plutocracy, confined to women; this, however, does not make it any more in agreement with common sense. + +The wise use of leisure, it must be conceded, is a product of civilization and education. A man who has worked long hours all his life will become bored if he becomes suddenly idle. But without a considerable amount of leisure a man is cut off from many of the best things. There is no longer any reason why the bulk of the population should suffer this deprivation; only a foolish asceticism, usually vicarious, makes us continue to insist on work in excessive quantities now that the need no longer exists. + +In the new creed which controls the government of Russia, while there is much that is very different from the traditional teaching of the West, there are some things that are quite unchanged. The attitude of the governing classes, and especially of those who conduct educational propaganda, on the subject of the dignity of labor, is almost exactly that which the governing classes of the world have always preached to what were called the 'honest poor'. Industry, sobriety, willingness to work long hours for distant advantages, even submissiveness to authority, all these reappear; moreover authority still represents the will of the Ruler of the Universe, Who, however, is now called by a new name, Dialectical Materialism. + +The victory of the proletariat in Russia has some points in common with the victory of the feminists in some other countries. For ages, men had conceded the superior saintliness of women, and had consoled women for their inferiority by maintaining that saintliness is more desirable than power. At last the feminists decided that they would have both, since the pioneers among them believed all that the men had told them about the desirability of virtue, but not what they had told them about the worthlessness of political power. A similar thing has happened in Russia as regards manual work. For ages, the rich and their sycophants have written in praise of 'honest toil', have praised the simple life, have professed a religion which teaches that the poor are much more likely to go to heaven than the rich, and in general have tried to make manual workers believe that there is some special nobility about altering the position of matter in space, just as men tried to make women believe that they derived some special nobility from their sexual enslavement. In Russia, all this teaching about the excellence of manual work has been taken seriously, with the result that the manual worker is more honored than anyone else. What are, in essence, revivalist appeals are made, but not for the old purposes: they are made to secure shock workers for special tasks. Manual work is the ideal which is held before the young, and is the basis of all ethical teaching. + +For the present, possibly, this is all to the good. A large country, full of natural resources, awaits development, and has has to be developed with very little use of credit. In these circumstances, hard work is necessary, and is likely to bring a great reward. But what will happen when the point has been reached where everybody could be comfortable without working long hours? + +In the West, we have various ways of dealing with this problem. We have no attempt at economic justice, so that a large proportion of the total produce goes to a small minority of the population, many of whom do no work at all. Owing to the absence of any central control over production, we produce hosts of things that are not wanted. We keep a large percentage of the working population idle, because we can dispense with their labor by making the others overwork. When all these methods prove inadequate, we have a war: we cause a number of people to manufacture high explosives, and a number of others to explode them, as if we were children who had just discovered fireworks. By a combination of all these devices we manage, though with difficulty, to keep alive the notion that a great deal of severe manual work must be the lot of the average man. + +In Russia, owing to more economic justice and central control over production, the problem will have to be differently solved. the rational solution would be, as soon as the necessaries and elementary comforts can be provided for all, to reduce the hours of labor gradually, allowing a popular vote to decide, at each stage, whether more leisure or more goods were to be preferred. But, having taught the supreme virtue of hard work, it is difficult to see how the authorities can aim at a paradise in which there will be much leisure and little work. It seems more likely that they will find continually fresh schemes, by which present leisure is to be sacrificed to future productivity. I read recently of an ingenious plan put forward by Russian engineers, for making the White Sea and the northern coasts of Siberia warm, by putting a dam across the Kara Sea. An admirable project, but liable to postpone proletarian comfort for a generation, while the nobility of toil is being displayed amid the ice-fields and snowstorms of the Arctic Ocean. This sort of thing, if it happens, will be the result of regarding the virtue of hard work as an end in itself, rather than as a means to a state of affairs in which it is no longer needed. + +The fact is that moving matter about, while a certain amount of it is necessary to our existence, is emphatically not one of the ends of human life. If it were, we should have to consider every navvy superior to Shakespeare. We have been misled in this matter by two causes. One is the necessity of keeping the poor contented, which has led the rich, for thousands of years, to preach the dignity of labor, while taking care themselves to remain undignified in this respect. The other is the new pleasure in mechanism, which makes us delight in the astonishingly clever changes that we can produce on the earth's surface. Neither of these motives makes any great appeal to the actual worker. If you ask him what he thinks the best part of his life, he is not likely to say: 'I enjoy manual work because it makes me feel that I am fulfilling man's noblest task, and because I like to think how much man can transform his planet. It is true that my body demands periods of rest, which I have to fill in as best I may, but I am never so happy as when the morning comes and I can return to the toil from which my contentment springs.' I have never heard working men say this sort of thing. They consider work, as it should be considered, a necessary means to a livelihood, and it is from their leisure that they derive whatever happiness they may enjoy. + +It will be said that, while a little leisure is pleasant, men would not know how to fill their days if they had only four hours of work out of the twenty-four. In so far as this is true in the modern world, it is a condemnation of our civilization; it would not have been true at any earlier period. There was formerly a capacity for light-heartedness and play which has been to some extent inhibited by the cult of efficiency. The modern man thinks that everything ought to be done for the sake of something else, and never for its own sake. Serious-minded persons, for example, are continually condemning the habit of going to the cinema, and telling us that it leads the young into crime. But all the work that goes to producing a cinema is respectable, because it is work, and because it brings a money profit. The notion that the desirable activities are those that bring a profit has made everything topsy-turvy. The butcher who provides you with meat and the baker who provides you with bread are praiseworthy, because they are making money; but when you enjoy the food they have provided, you are merely frivolous, unless you eat only to get strength for your work. Broadly speaking, it is held that getting money is good and spending money is bad. Seeing that they are two sides of one transaction, this is absurd; one might as well maintain that keys are good, but keyholes are bad. Whatever merit there may be in the production of goods must be entirely derivative from the advantage to be obtained by consuming them. The individual, in our society, works for profit; but the social purpose of his work lies in the consumption of what he produces. It is this divorce between the individual and the social purpose of production that makes it so difficult for men to think clearly in a world in which profit-making is the incentive to industry. We think too much of production, and too little of consumption. One result is that we attach too little importance to enjoyment and simple happiness, and that we do not judge production by the pleasure that it gives to the consumer. + +When I suggest that working hours should be reduced to four, I am not meaning to imply that all the remaining time should necessarily be spent in pure frivolity. I mean that four hours' work a day should entitle a man to the necessities and elementary comforts of life, and that the rest of his time should be his to use as he might see fit. It is an essential part of any such social system that education should be carried further than it usually is at present, and should aim, in part, at providing tastes which would enable a man to use leisure intelligently. I am not thinking mainly of the sort of things that would be considered 'highbrow'. Peasant dances have died out except in remote rural areas, but the impulses which caused them to be cultivated must still exist in human nature. The pleasures of urban populations have become mainly passive: seeing cinemas, watching football matches, listening to the radio, and so on. This results from the fact that their active energies are fully taken up with work; if they had more leisure, they would again enjoy pleasures in which they took an active part. + +In the past, there was a small leisure class and a larger working class. The leisure class enjoyed advantages for which there was no basis in social justice; this necessarily made it oppressive, limited its sympathies, and caused it to invent theories by which to justify its privileges. These facts greatly diminished its excellence, but in spite of this drawback it contributed nearly the whole of what we call civilization. It cultivated the arts and discovered the sciences; it wrote the books, invented the philosophies, and refined social relations. Even the liberation of the oppressed has usually been inaugurated from above. Without the leisure class, mankind would never have emerged from barbarism. + +The method of a leisure class without duties was, however, extraordinarily wasteful. None of the members of the class had to be taught to be industrious, and the class as a whole was not exceptionally intelligent. The class might produce one Darwin, but against him had to be set tens of thousands of country gentlemen who never thought of anything more intelligent than fox-hunting and punishing poachers. At present, the universities are supposed to provide, in a more systematic way, what the leisure class provided accidentally and as a by-product. This is a great improvement, but it has certain drawbacks. University life is so different from life in the world at large that men who live in academic milieu tend to be unaware of the preoccupations and problems of ordinary men and women; moreover their ways of expressing themselves are usually such as to rob their opinions of the influence that they ought to have upon the general public. Another disadvantage is that in universities studies are organized, and the man who thinks of some original line of research is likely to be discouraged. Academic institutions, therefore, useful as they are, are not adequate guardians of the interests of civilization in a world where everyone outside their walls is too busy for unutilitarian pursuits. + +In a world where no one is compelled to work more than four hours a day, every person possessed of scientific curiosity will be able to indulge it, and every painter will be able to paint without starving, however excellent his pictures may be. Young writers will not be obliged to draw attention to themselves by sensational pot-boilers, with a view to acquiring the economic independence needed for monumental works, for which, when the time at last comes, they will have lost the taste and capacity. Men who, in their professional work, have become interested in some phase of economics or government, will be able to develop their ideas without the academic detachment that makes the work of university economists often seem lacking in reality. Medical men will have the time to learn about the progress of medicine, teachers will not be exasperatedly struggling to teach by routine methods things which they learnt in their youth, which may, in the interval, have been proved to be untrue. + +Above all, there will be happiness and joy of life, instead of frayed nerves, weariness, and dyspepsia. The work exacted will be enough to make leisure delightful, but not enough to produce exhaustion. Since men will not be tired in their spare time, they will not demand only such amusements as are passive and vapid. At least one per cent will probably devote the time not spent in professional work to pursuits of some public importance, and, since they will not depend upon these pursuits for their livelihood, their originality will be unhampered, and there will be no need to conform to the standards set by elderly pundits. But it is not only in these exceptional cases that the advantages of leisure will appear. Ordinary men and women, having the opportunity of a happy life, will become more kindly and less persecuting and less inclined to view others with suspicion. The taste for war will die out, partly for this reason, and partly because it will involve long and severe work for all. Good nature is, of all moral qualities, the one that the world needs most, and good nature is the result of ease and security, not of a life of arduous struggle. Modern methods of production have given us the possibility of ease and security for all; we have chosen, instead, to have overwork for some and starvation for others. Hitherto we have continued to be as energetic as we were before there were machines; in this we have been foolish, but there is no reason to go on being foolish forever. + +[1] Since then, members of the Communist Party have succeeded to this privilege of the warriors and priests. diff --git a/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/wrap.js b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/wrap.js new file mode 100644 index 0000000..0cfb76d --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/node_modules/wordwrap/test/wrap.js @@ -0,0 +1,31 @@ +var assert = require('assert'); +var wordwrap = require('wordwrap'); + +var fs = require('fs'); +var idleness = fs.readFileSync(__dirname + '/idleness.txt', 'utf8'); + +exports.stop80 = function () { + var lines = wordwrap(80)(idleness).split(/\n/); + var words = idleness.split(/\s+/); + + lines.forEach(function (line) { + assert.ok(line.length <= 80, 'line > 80 columns'); + var chunks = line.match(/\S/) ? line.split(/\s+/) : []; + assert.deepEqual(chunks, words.splice(0, chunks.length)); + }); +}; + +exports.start20stop60 = function () { + var lines = wordwrap(20, 100)(idleness).split(/\n/); + var words = idleness.split(/\s+/); + + lines.forEach(function (line) { + assert.ok(line.length <= 100, 'line > 100 columns'); + var chunks = line + .split(/\s+/) + .filter(function (x) { return x.match(/\S/) }) + ; + assert.deepEqual(chunks, words.splice(0, chunks.length)); + assert.deepEqual(line.slice(0, 20), new Array(20 + 1).join(' ')); + }); +}; diff --git a/dist/node_modules/nconf/node_modules/optimist/package.json b/dist/node_modules/nconf/node_modules/optimist/package.json new file mode 100644 index 0000000..5bac328 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/package.json @@ -0,0 +1,43 @@ +{ + "name" : "optimist", + "version" : "0.3.5", + "description" : "Light-weight option parsing with an argv hash. No optstrings attached.", + "main" : "./index.js", + "directories" : { + "lib" : ".", + "test" : "test", + "example" : "example" + }, + "dependencies" : { + "wordwrap" : "~0.0.2" + }, + "devDependencies" : { + "hashish": "~0.0.4", + "tap" : "~0.2.4" + }, + "scripts" : { + "test" : "tap ./test/*.js" + }, + "repository" : { + "type" : "git", + "url" : "http://github.com/substack/node-optimist.git" + }, + "keywords" : [ + "argument", + "args", + "option", + "parser", + "parsing", + "cli", + "command" + ], + "author" : { + "name" : "James Halliday", + "email" : "mail@substack.net", + "url" : "http://substack.net" + }, + "license" : "MIT/X11", + "engine" : { + "node" : ">=0.4" + } +} diff --git a/dist/node_modules/nconf/node_modules/optimist/test/_.js b/dist/node_modules/nconf/node_modules/optimist/test/_.js new file mode 100644 index 0000000..d9c58b3 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/test/_.js @@ -0,0 +1,71 @@ +var spawn = require('child_process').spawn; +var test = require('tap').test; + +test('dotSlashEmpty', testCmd('./bin.js', [])); + +test('dotSlashArgs', testCmd('./bin.js', [ 'a', 'b', 'c' ])); + +test('nodeEmpty', testCmd('node bin.js', [])); + +test('nodeArgs', testCmd('node bin.js', [ 'x', 'y', 'z' ])); + +test('whichNodeEmpty', function (t) { + var which = spawn('which', ['node']); + + which.stdout.on('data', function (buf) { + t.test( + testCmd(buf.toString().trim() + ' bin.js', []) + ); + t.end(); + }); + + which.stderr.on('data', function (err) { + assert.error(err); + t.end(); + }); +}); + +test('whichNodeArgs', function (t) { + var which = spawn('which', ['node']); + + which.stdout.on('data', function (buf) { + t.test( + testCmd(buf.toString().trim() + ' bin.js', [ 'q', 'r' ]) + ); + t.end(); + }); + + which.stderr.on('data', function (err) { + t.error(err); + t.end(); + }); +}); + +function testCmd (cmd, args) { + + return function (t) { + var to = setTimeout(function () { + assert.fail('Never got stdout data.') + }, 5000); + + var oldDir = process.cwd(); + process.chdir(__dirname + '/_'); + + var cmds = cmd.split(' '); + + var bin = spawn(cmds[0], cmds.slice(1).concat(args.map(String))); + process.chdir(oldDir); + + bin.stderr.on('data', function (err) { + t.error(err); + t.end(); + }); + + bin.stdout.on('data', function (buf) { + clearTimeout(to); + var _ = JSON.parse(buf.toString()); + t.same(_.map(String), args.map(String)); + t.end(); + }); + }; +} diff --git a/dist/node_modules/nconf/node_modules/optimist/test/_/argv.js b/dist/node_modules/nconf/node_modules/optimist/test/_/argv.js new file mode 100644 index 0000000..3d09606 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/test/_/argv.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +console.log(JSON.stringify(process.argv)); diff --git a/dist/node_modules/nconf/node_modules/optimist/test/_/bin.js b/dist/node_modules/nconf/node_modules/optimist/test/_/bin.js new file mode 100755 index 0000000..4a18d85 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/test/_/bin.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node +var argv = require('../../index').argv +console.log(JSON.stringify(argv._)); diff --git a/dist/node_modules/nconf/node_modules/optimist/test/parse.js b/dist/node_modules/nconf/node_modules/optimist/test/parse.js new file mode 100644 index 0000000..a6ce9f1 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/test/parse.js @@ -0,0 +1,433 @@ +var optimist = require('../index'); +var path = require('path'); +var test = require('tap').test; + +var $0 = 'node ./' + path.relative(process.cwd(), __filename); + +test('short boolean', function (t) { + var parse = optimist.parse([ '-b' ]); + t.same(parse, { b : true, _ : [], $0 : $0 }); + t.same(typeof parse.b, 'boolean'); + t.end(); +}); + +test('long boolean', function (t) { + t.same( + optimist.parse([ '--bool' ]), + { bool : true, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('bare', function (t) { + t.same( + optimist.parse([ 'foo', 'bar', 'baz' ]), + { _ : [ 'foo', 'bar', 'baz' ], $0 : $0 } + ); + t.end(); +}); + +test('short group', function (t) { + t.same( + optimist.parse([ '-cats' ]), + { c : true, a : true, t : true, s : true, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('short group next', function (t) { + t.same( + optimist.parse([ '-cats', 'meow' ]), + { c : true, a : true, t : true, s : 'meow', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('short capture', function (t) { + t.same( + optimist.parse([ '-h', 'localhost' ]), + { h : 'localhost', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('short captures', function (t) { + t.same( + optimist.parse([ '-h', 'localhost', '-p', '555' ]), + { h : 'localhost', p : 555, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('long capture sp', function (t) { + t.same( + optimist.parse([ '--pow', 'xixxle' ]), + { pow : 'xixxle', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('long capture eq', function (t) { + t.same( + optimist.parse([ '--pow=xixxle' ]), + { pow : 'xixxle', _ : [], $0 : $0 } + ); + t.end() +}); + +test('long captures sp', function (t) { + t.same( + optimist.parse([ '--host', 'localhost', '--port', '555' ]), + { host : 'localhost', port : 555, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('long captures eq', function (t) { + t.same( + optimist.parse([ '--host=localhost', '--port=555' ]), + { host : 'localhost', port : 555, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('mixed short bool and capture', function (t) { + t.same( + optimist.parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ], $0 : $0, + } + ); + t.end(); +}); + +test('short and long', function (t) { + t.same( + optimist.parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]), + { + f : true, p : 555, h : 'localhost', + _ : [ 'script.js' ], $0 : $0, + } + ); + t.end(); +}); + +test('no', function (t) { + t.same( + optimist.parse([ '--no-moo' ]), + { moo : false, _ : [], $0 : $0 } + ); + t.end(); +}); + +test('multi', function (t) { + t.same( + optimist.parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]), + { v : ['a','b','c'], _ : [], $0 : $0 } + ); + t.end(); +}); + +test('comprehensive', function (t) { + t.same( + optimist.parse([ + '--name=meowmers', 'bare', '-cats', 'woo', + '-h', 'awesome', '--multi=quux', + '--key', 'value', + '-b', '--bool', '--no-meep', '--multi=baz', + '--', '--not-a-flag', 'eek' + ]), + { + c : true, + a : true, + t : true, + s : 'woo', + h : 'awesome', + b : true, + bool : true, + key : 'value', + multi : [ 'quux', 'baz' ], + meep : false, + name : 'meowmers', + _ : [ 'bare', '--not-a-flag', 'eek' ], + $0 : $0 + } + ); + t.end(); +}); + +test('nums', function (t) { + var argv = optimist.parse([ + '-x', '1234', + '-y', '5.67', + '-z', '1e7', + '-w', '10f', + '--hex', '0xdeadbeef', + '789', + ]); + t.same(argv, { + x : 1234, + y : 5.67, + z : 1e7, + w : '10f', + hex : 0xdeadbeef, + _ : [ 789 ], + $0 : $0 + }); + t.same(typeof argv.x, 'number'); + t.same(typeof argv.y, 'number'); + t.same(typeof argv.z, 'number'); + t.same(typeof argv.w, 'string'); + t.same(typeof argv.hex, 'number'); + t.same(typeof argv._[0], 'number'); + t.end(); +}); + +test('flag boolean', function (t) { + var parse = optimist([ '-t', 'moo' ]).boolean(['t']).argv; + t.same(parse, { t : true, _ : [ 'moo' ], $0 : $0 }); + t.same(typeof parse.t, 'boolean'); + t.end(); +}); + +test('flag boolean value', function (t) { + var parse = optimist(['--verbose', 'false', 'moo', '-t', 'true']) + .boolean(['t', 'verbose']).default('verbose', true).argv; + + t.same(parse, { + verbose: false, + t: true, + _: ['moo'], + $0 : $0 + }); + + t.same(typeof parse.verbose, 'boolean'); + t.same(typeof parse.t, 'boolean'); + t.end(); +}); + +test('flag boolean default false', function (t) { + var parse = optimist(['moo']) + .boolean(['t', 'verbose']) + .default('verbose', false) + .default('t', false).argv; + + t.same(parse, { + verbose: false, + t: false, + _: ['moo'], + $0 : $0 + }); + + t.same(typeof parse.verbose, 'boolean'); + t.same(typeof parse.t, 'boolean'); + t.end(); + +}); + +test('boolean groups', function (t) { + var parse = optimist([ '-x', '-z', 'one', 'two', 'three' ]) + .boolean(['x','y','z']).argv; + + t.same(parse, { + x : true, + y : false, + z : true, + _ : [ 'one', 'two', 'three' ], + $0 : $0 + }); + + t.same(typeof parse.x, 'boolean'); + t.same(typeof parse.y, 'boolean'); + t.same(typeof parse.z, 'boolean'); + t.end(); +}); + +test('strings' , function (t) { + var s = optimist([ '-s', '0001234' ]).string('s').argv.s; + t.same(s, '0001234'); + t.same(typeof s, 'string'); + + var x = optimist([ '-x', '56' ]).string('x').argv.x; + t.same(x, '56'); + t.same(typeof x, 'string'); + t.end(); +}); + +test('stringArgs', function (t) { + var s = optimist([ ' ', ' ' ]).string('_').argv._; + t.same(s.length, 2); + t.same(typeof s[0], 'string'); + t.same(s[0], ' '); + t.same(typeof s[1], 'string'); + t.same(s[1], ' '); + t.end(); +}); + +test('slashBreak', function (t) { + t.same( + optimist.parse([ '-I/foo/bar/baz' ]), + { I : '/foo/bar/baz', _ : [], $0 : $0 } + ); + t.same( + optimist.parse([ '-xyz/foo/bar/baz' ]), + { x : true, y : true, z : '/foo/bar/baz', _ : [], $0 : $0 } + ); + t.end(); +}); + +test('alias', function (t) { + var argv = optimist([ '-f', '11', '--zoom', '55' ]) + .alias('z', 'zoom') + .argv + ; + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.f, 11); + t.end(); +}); + +test('multiAlias', function (t) { + var argv = optimist([ '-f', '11', '--zoom', '55' ]) + .alias('z', [ 'zm', 'zoom' ]) + .argv + ; + t.equal(argv.zoom, 55); + t.equal(argv.z, argv.zoom); + t.equal(argv.z, argv.zm); + t.equal(argv.f, 11); + t.end(); +}); + +test('boolean default true', function (t) { + var argv = optimist.options({ + sometrue: { + boolean: true, + default: true + } + }).argv; + + t.equal(argv.sometrue, true); + t.end(); +}); + +test('boolean default false', function (t) { + var argv = optimist.options({ + somefalse: { + boolean: true, + default: false + } + }).argv; + + t.equal(argv.somefalse, false); + t.end(); +}); + +test('nested dotted objects', function (t) { + var argv = optimist([ + '--foo.bar', '3', '--foo.baz', '4', + '--foo.quux.quibble', '5', '--foo.quux.o_O', + '--beep.boop' + ]).argv; + + t.same(argv.foo, { + bar : 3, + baz : 4, + quux : { + quibble : 5, + o_O : true + }, + }); + t.same(argv.beep, { boop : true }); + t.end(); +}); + +test('boolean and alias with chainable api', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = optimist(aliased) + .boolean('herp') + .alias('h', 'herp') + .argv; + var propertyArgv = optimist(regular) + .boolean('herp') + .alias('h', 'herp') + .argv; + var expected = { + herp: true, + h: true, + '_': [ 'derp' ], + '$0': $0, + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +test('boolean and alias with options hash', function (t) { + var aliased = [ '-h', 'derp' ]; + var regular = [ '--herp', 'derp' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = optimist(aliased) + .options(opts) + .argv; + var propertyArgv = optimist(regular).options(opts).argv; + var expected = { + herp: true, + h: true, + '_': [ 'derp' ], + '$0': $0, + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + + t.end(); +}); + +test('boolean and alias using explicit true', function (t) { + var aliased = [ '-h', 'true' ]; + var regular = [ '--herp', 'true' ]; + var opts = { + herp: { alias: 'h', boolean: true } + }; + var aliasedArgv = optimist(aliased) + .boolean('h') + .alias('h', 'herp') + .argv; + var propertyArgv = optimist(regular) + .boolean('h') + .alias('h', 'herp') + .argv; + var expected = { + herp: true, + h: true, + '_': [ ], + '$0': $0, + }; + + t.same(aliasedArgv, expected); + t.same(propertyArgv, expected); + t.end(); +}); + +// regression, see https://github.com/substack/node-optimist/issues/71 +test('boolean and --x=true', function(t) { + var parsed = optimist(['--boool', '--other=true']).boolean('boool').argv; + + t.same(parsed.boool, true); + t.same(parsed.other, 'true'); + + parsed = optimist(['--boool', '--other=false']).boolean('boool').argv; + + t.same(parsed.boool, true); + t.same(parsed.other, 'false'); + t.end(); +}); diff --git a/dist/node_modules/nconf/node_modules/optimist/test/usage.js b/dist/node_modules/nconf/node_modules/optimist/test/usage.js new file mode 100644 index 0000000..300454c --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/test/usage.js @@ -0,0 +1,292 @@ +var Hash = require('hashish'); +var optimist = require('../index'); +var test = require('tap').test; + +test('usageFail', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -z 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .demand(['x','y']) + .argv; + }); + t.same( + r.result, + { x : 10, z : 20, _ : [], $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/), + [ + 'Usage: ./usage -x NUM -y NUM', + 'Options:', + ' -x [required]', + ' -y [required]', + 'Missing required arguments: y', + ] + ); + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + + +test('usagePass', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -y 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .demand(['x','y']) + .argv; + }); + t.same(r, { + result : { x : 10, y : 20, _ : [], $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('checkPass', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -y 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(function (argv) { + if (!('x' in argv)) throw 'You forgot about -x'; + if (!('y' in argv)) throw 'You forgot about -y'; + }) + .argv; + }); + t.same(r, { + result : { x : 10, y : 20, _ : [], $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('checkFail', function (t) { + var r = checkUsage(function () { + return optimist('-x 10 -z 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(function (argv) { + if (!('x' in argv)) throw 'You forgot about -x'; + if (!('y' in argv)) throw 'You forgot about -y'; + }) + .argv; + }); + + t.same( + r.result, + { x : 10, z : 20, _ : [], $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/), + [ + 'Usage: ./usage -x NUM -y NUM', + 'You forgot about -y' + ] + ); + + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + +test('checkCondPass', function (t) { + function checker (argv) { + return 'x' in argv && 'y' in argv; + } + + var r = checkUsage(function () { + return optimist('-x 10 -y 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(checker) + .argv; + }); + t.same(r, { + result : { x : 10, y : 20, _ : [], $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('checkCondFail', function (t) { + function checker (argv) { + return 'x' in argv && 'y' in argv; + } + + var r = checkUsage(function () { + return optimist('-x 10 -z 20'.split(' ')) + .usage('Usage: $0 -x NUM -y NUM') + .check(checker) + .argv; + }); + + t.same( + r.result, + { x : 10, z : 20, _ : [], $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/).join('\n'), + 'Usage: ./usage -x NUM -y NUM\n' + + 'Argument check failed: ' + checker.toString() + ); + + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + +test('countPass', function (t) { + var r = checkUsage(function () { + return optimist('1 2 3 --moo'.split(' ')) + .usage('Usage: $0 [x] [y] [z] {OPTIONS}') + .demand(3) + .argv; + }); + t.same(r, { + result : { _ : [ '1', '2', '3' ], moo : true, $0 : './usage' }, + errors : [], + logs : [], + exit : false, + }); + t.end(); +}); + +test('countFail', function (t) { + var r = checkUsage(function () { + return optimist('1 2 --moo'.split(' ')) + .usage('Usage: $0 [x] [y] [z] {OPTIONS}') + .demand(3) + .argv; + }); + t.same( + r.result, + { _ : [ '1', '2' ], moo : true, $0 : './usage' } + ); + + t.same( + r.errors.join('\n').split(/\n+/), + [ + 'Usage: ./usage [x] [y] [z] {OPTIONS}', + 'Not enough non-option arguments: got 2, need at least 3', + ] + ); + + t.same(r.logs, []); + t.ok(r.exit); + t.end(); +}); + +test('defaultSingles', function (t) { + var r = checkUsage(function () { + return optimist('--foo 50 --baz 70 --powsy'.split(' ')) + .default('foo', 5) + .default('bar', 6) + .default('baz', 7) + .argv + ; + }); + t.same(r.result, { + foo : '50', + bar : 6, + baz : '70', + powsy : true, + _ : [], + $0 : './usage', + }); + t.end(); +}); + +test('defaultAliases', function (t) { + var r = checkUsage(function () { + return optimist('') + .alias('f', 'foo') + .default('f', 5) + .argv + ; + }); + t.same(r.result, { + f : '5', + foo : '5', + _ : [], + $0 : './usage', + }); + t.end(); +}); + +test('defaultHash', function (t) { + var r = checkUsage(function () { + return optimist('--foo 50 --baz 70'.split(' ')) + .default({ foo : 10, bar : 20, quux : 30 }) + .argv + ; + }); + t.same(r.result, { + _ : [], + $0 : './usage', + foo : 50, + baz : 70, + bar : 20, + quux : 30, + }); + t.end(); +}); + +test('rebase', function (t) { + t.equal( + optimist.rebase('/home/substack', '/home/substack/foo/bar/baz'), + './foo/bar/baz' + ); + t.equal( + optimist.rebase('/home/substack/foo/bar/baz', '/home/substack'), + '../../..' + ); + t.equal( + optimist.rebase('/home/substack/foo', '/home/substack/pow/zoom.txt'), + '../pow/zoom.txt' + ); + t.end(); +}); + +function checkUsage (f) { + + var exit = false; + + process._exit = process.exit; + process._env = process.env; + process._argv = process.argv; + + process.exit = function (t) { exit = true }; + process.env = Hash.merge(process.env, { _ : 'node' }); + process.argv = [ './usage' ]; + + var errors = []; + var logs = []; + + console._error = console.error; + console.error = function (msg) { errors.push(msg) }; + console._log = console.log; + console.log = function (msg) { logs.push(msg) }; + + var result = f(); + + process.exit = process._exit; + process.env = process._env; + process.argv = process._argv; + + console.error = console._error; + console.log = console._log; + + return { + errors : errors, + logs : logs, + exit : exit, + result : result, + }; +}; diff --git a/dist/node_modules/nconf/node_modules/optimist/x.js b/dist/node_modules/nconf/node_modules/optimist/x.js new file mode 100644 index 0000000..6c006d0 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/optimist/x.js @@ -0,0 +1 @@ +console.dir(require('./').argv); diff --git a/dist/node_modules/nconf/node_modules/pkginfo/.npmignore b/dist/node_modules/nconf/node_modules/pkginfo/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/README.md b/dist/node_modules/nconf/node_modules/pkginfo/README.md new file mode 100644 index 0000000..07ba942 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/README.md @@ -0,0 +1,85 @@ +# node-pkginfo + +An easy way to expose properties on a module from a package.json + +## Installation + +### Installing npm (node package manager) +``` + curl http://npmjs.org/install.sh | sh +``` + +### Installing pkginfo +``` + [sudo] npm install pkginfo +``` + +## Motivation +How often when writing node.js modules have you written the following line(s) of code? + +* Hard code your version string into your code + +``` js + exports.version = '0.1.0'; +``` + +* Programmatically expose the version from the package.json + +``` js + exports.version = JSON.parse(fs.readFileSync('/path/to/package.json', 'utf8')).version; +``` + +In other words, how often have you wanted to expose basic information from your package.json onto your module programmatically? **WELL NOW YOU CAN!** + +## Usage + +Using `pkginfo` is idiot-proof, just require and invoke it. + +``` js + var pkginfo = require('pkginfo')(module); + + console.dir(module.exports); +``` + +By invoking the `pkginfo` module all of the properties in your `package.json` file will be automatically exposed on the callee module (i.e. the parent module of `pkginfo`). + +Here's a sample of the output: + +``` + { name: 'simple-app', + description: 'A test fixture for pkginfo', + version: '0.1.0', + author: 'Charlie Robbins ', + keywords: [ 'test', 'fixture' ], + main: './index.js', + scripts: { test: 'vows test/*-test.js --spec' }, + engines: { node: '>= 0.4.0' } } +``` + +### Expose specific properties +If you don't want to expose **all** properties on from your `package.json` on your module then simple pass those properties to the `pkginfo` function: + +``` js + var pkginfo = require('pkginfo')(module, 'version', 'author'); + + console.dir(module.exports); +``` + +``` + { version: '0.1.0', + author: 'Charlie Robbins ' } +``` + +If you're looking for further usage see the [examples][0] included in this repository. + +## Run Tests +Tests are written in [vows][1] and give complete coverage of all APIs. + +``` + vows test/*-test.js --spec +``` + +[0]: https://github.com/indexzero/node-pkginfo/tree/master/examples +[1]: http://vowsjs.org + +#### Author: [Charlie Robbins](http://nodejitsu.com) \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/docs/docco.css b/dist/node_modules/nconf/node_modules/pkginfo/docs/docco.css new file mode 100644 index 0000000..bd54134 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/docs/docco.css @@ -0,0 +1,194 @@ +/*--------------------- Layout and Typography ----------------------------*/ +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + color: #252519; + margin: 0; padding: 0; +} +a { + color: #261a3b; +} + a:visited { + color: #261a3b; + } +p { + margin: 0 0 15px 0; +} +h4, h5, h6 { + color: #333; + margin: 6px 0 6px 0; + font-size: 13px; +} + h2, h3 { + margin-bottom: 0; + color: #000; + } + h1 { + margin-top: 40px; + margin-bottom: 15px; + color: #000; + } +#container { + position: relative; +} +#background { + position: fixed; + top: 0; left: 525px; right: 0; bottom: 0; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + z-index: -1; +} +#jump_to, #jump_page { + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 10px Arial; + text-transform: uppercase; + cursor: pointer; + text-align: right; +} +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 5px 10px; +} + #jump_wrapper { + padding: 0; + display: none; + } + #jump_to:hover #jump_wrapper { + display: block; + } + #jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; + } + #jump_page .source { + display: block; + padding: 5px 10px; + text-decoration: none; + border-top: 1px solid #eee; + } + #jump_page .source:hover { + background: #f5f5ff; + } + #jump_page .source:first-child { + } +table td { + border: 0; + outline: 0; +} + td.docs, th.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; + } + .docs pre { + margin: 15px 0 15px; + padding-left: 15px; + } + .docs p tt, .docs p code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; + } + .pilwrap { + position: relative; + } + .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + } + td.docs:hover .pilcrow { + opacity: 1; + } + td.code, th.code { + padding: 14px 15px 16px 25px; + width: 100%; + vertical-align: top; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + } + pre, tt, code { + font-size: 12px; line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; + } + + +/*---------------------- Syntax Highlighting -----------------------------*/ +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +body .hll { background-color: #ffffcc } +body .c { color: #408080; font-style: italic } /* Comment */ +body .err { border: 1px solid #FF0000 } /* Error */ +body .k { color: #954121 } /* Keyword */ +body .o { color: #666666 } /* Operator */ +body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +body .cp { color: #BC7A00 } /* Comment.Preproc */ +body .c1 { color: #408080; font-style: italic } /* Comment.Single */ +body .cs { color: #408080; font-style: italic } /* Comment.Special */ +body .gd { color: #A00000 } /* Generic.Deleted */ +body .ge { font-style: italic } /* Generic.Emph */ +body .gr { color: #FF0000 } /* Generic.Error */ +body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +body .gi { color: #00A000 } /* Generic.Inserted */ +body .go { color: #808080 } /* Generic.Output */ +body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +body .gs { font-weight: bold } /* Generic.Strong */ +body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +body .gt { color: #0040D0 } /* Generic.Traceback */ +body .kc { color: #954121 } /* Keyword.Constant */ +body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ +body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ +body .kp { color: #954121 } /* Keyword.Pseudo */ +body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ +body .kt { color: #B00040 } /* Keyword.Type */ +body .m { color: #666666 } /* Literal.Number */ +body .s { color: #219161 } /* Literal.String */ +body .na { color: #7D9029 } /* Name.Attribute */ +body .nb { color: #954121 } /* Name.Builtin */ +body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +body .no { color: #880000 } /* Name.Constant */ +body .nd { color: #AA22FF } /* Name.Decorator */ +body .ni { color: #999999; font-weight: bold } /* Name.Entity */ +body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +body .nf { color: #0000FF } /* Name.Function */ +body .nl { color: #A0A000 } /* Name.Label */ +body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +body .nt { color: #954121; font-weight: bold } /* Name.Tag */ +body .nv { color: #19469D } /* Name.Variable */ +body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +body .w { color: #bbbbbb } /* Text.Whitespace */ +body .mf { color: #666666 } /* Literal.Number.Float */ +body .mh { color: #666666 } /* Literal.Number.Hex */ +body .mi { color: #666666 } /* Literal.Number.Integer */ +body .mo { color: #666666 } /* Literal.Number.Oct */ +body .sb { color: #219161 } /* Literal.String.Backtick */ +body .sc { color: #219161 } /* Literal.String.Char */ +body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ +body .s2 { color: #219161 } /* Literal.String.Double */ +body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +body .sh { color: #219161 } /* Literal.String.Heredoc */ +body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +body .sx { color: #954121 } /* Literal.String.Other */ +body .sr { color: #BB6688 } /* Literal.String.Regex */ +body .s1 { color: #219161 } /* Literal.String.Single */ +body .ss { color: #19469D } /* Literal.String.Symbol */ +body .bp { color: #954121 } /* Name.Builtin.Pseudo */ +body .vc { color: #19469D } /* Name.Variable.Class */ +body .vg { color: #19469D } /* Name.Variable.Global */ +body .vi { color: #19469D } /* Name.Variable.Instance */ +body .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/docs/pkginfo.html b/dist/node_modules/nconf/node_modules/pkginfo/docs/pkginfo.html new file mode 100644 index 0000000..bf615fa --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/docs/pkginfo.html @@ -0,0 +1,101 @@ + pkginfo.js

      pkginfo.js

      /*
      + * pkginfo.js: Top-level include for the pkginfo module
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      + 
      +var fs = require('fs'),
      +    path = require('path');

      function pkginfo ([options, 'property', 'property' ..])

      + +

      @pmodule {Module} Parent module to read from.

      + +

      @options {Object|Array|string} Optional Options used when exposing properties.

      + +

      @arguments {string...} Optional Specified properties to expose.

      + +

      Exposes properties from the package.json file for the parent module on +it's exports. Valid usage:

      + +

      require('pkginfo')()

      + +

      require('pkginfo')('version', 'author');

      + +

      require('pkginfo')(['version', 'author']);

      + +

      require('pkginfo')({ include: ['version', 'author'] });

      var pkginfo = module.exports = function (pmodule, options) {
      +  var args = [].slice.call(arguments, 2).filter(function (arg) {
      +    return typeof arg === 'string';
      +  });
      +  

      Parse variable arguments

        if (Array.isArray(options)) {

      If the options passed in is an Array assume that +it is the Array of properties to expose from the +on the package.json file on the parent module.

          options = { include: options };
      +  }
      +  else if (typeof options === 'string') {

      Otherwise if the first argument is a string, then +assume that it is the first property to expose from +the package.json file on the parent module.

          options = { include: [options] };
      +  }
      +  

      Setup default options

        options = options || { include: [] };
      +  
      +  if (args.length > 0) {

      If additional string arguments have been passed in +then add them to the properties to expose on the +parent module.

          options.include = options.include.concat(args);
      +  }
      +  
      +  var pkg = pkginfo.read(pmodule, options.dir).package;
      +  Object.keys(pkg).forEach(function (key) {
      +    if (options.include.length > 0 && !~options.include.indexOf(key)) {
      +      return;
      +    }
      +    
      +    if (!pmodule.exports[key]) {
      +      pmodule.exports[key] = pkg[key];
      +    }
      +  });
      +  
      +  return pkginfo;
      +};

      function find (dir)

      + +

      @pmodule {Module} Parent module to read from.

      + +

      @dir {string} Optional Directory to start search from.

      + +

      Searches up the directory tree from dir until it finds a directory +which contains a package.json file.

      pkginfo.find = function (pmodule, dir) {
      +  dir = dir || pmodule.filename;
      +  dir = path.dirname(dir); 
      +  
      +  var files = fs.readdirSync(dir);
      +  
      +  if (~files.indexOf('package.json')) {
      +    return path.join(dir, 'package.json');
      +  }
      +  
      +  if (dir === '/') {
      +    throw new Error('Could not find package.json up from: ' + dir);
      +  }
      +  
      +  return pkginfo.find(dir);
      +};

      function read (pmodule, dir)

      + +

      @pmodule {Module} Parent module to read from.

      + +

      @dir {string} Optional Directory to start search from.

      + +

      Searches up the directory tree from dir until it finds a directory +which contains a package.json file and returns the package information.

      pkginfo.read = function (pmodule, dir) { 
      +  dir = pkginfo.find(pmodule, dir);
      +  
      +  var data = fs.readFileSync(dir).toString();
      +      
      +  return {
      +    dir: dir, 
      +    package: JSON.parse(data)
      +  };
      +};

      Call pkginfo on this module and expose version.

      pkginfo(module, {
      +  dir: __dirname,
      +  include: ['version'],
      +  target: pkginfo
      +});
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/examples/all-properties.js b/dist/node_modules/nconf/node_modules/pkginfo/examples/all-properties.js new file mode 100644 index 0000000..fd1d831 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/examples/all-properties.js @@ -0,0 +1,19 @@ +/* + * all-properties.js: Sample of including all properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/examples/array-argument.js b/dist/node_modules/nconf/node_modules/pkginfo/examples/array-argument.js new file mode 100644 index 0000000..b1b6848 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/examples/array-argument.js @@ -0,0 +1,20 @@ +/* + * array-argument.js: Sample of including specific properties from a package.json file + * using Array argument syntax. + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, ['version', 'author']); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/examples/multiple-properties.js b/dist/node_modules/nconf/node_modules/pkginfo/examples/multiple-properties.js new file mode 100644 index 0000000..b4b5fd6 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/examples/multiple-properties.js @@ -0,0 +1,19 @@ +/* + * multiple-properties.js: Sample of including multiple properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, 'version', 'author'); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/examples/object-argument.js b/dist/node_modules/nconf/node_modules/pkginfo/examples/object-argument.js new file mode 100644 index 0000000..28420c8 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/examples/object-argument.js @@ -0,0 +1,22 @@ +/* + * object-argument.js: Sample of including specific properties from a package.json file + * using Object argument syntax. + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, { + include: ['version', 'author'] + }); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/examples/package.json b/dist/node_modules/nconf/node_modules/pkginfo/examples/package.json new file mode 100644 index 0000000..1f2f01c --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/examples/package.json @@ -0,0 +1,10 @@ +{ + "name": "simple-app", + "description": "A test fixture for pkginfo", + "version": "0.1.0", + "author": "Charlie Robbins ", + "keywords": ["test", "fixture"], + "main": "./index.js", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.4.0" } +} diff --git a/dist/node_modules/nconf/node_modules/pkginfo/examples/single-property.js b/dist/node_modules/nconf/node_modules/pkginfo/examples/single-property.js new file mode 100644 index 0000000..4f44561 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/examples/single-property.js @@ -0,0 +1,19 @@ +/* + * single-property.js: Sample of including a single specific properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, 'version'); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/lib/pkginfo.js b/dist/node_modules/nconf/node_modules/pkginfo/lib/pkginfo.js new file mode 100644 index 0000000..a4a6227 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/lib/pkginfo.js @@ -0,0 +1,132 @@ +/* + * pkginfo.js: Top-level include for the pkginfo module + * + * (C) 2011, Charlie Robbins + * + */ + +var fs = require('fs'), + path = require('path'); + +// +// ### function pkginfo ([options, 'property', 'property' ..]) +// #### @pmodule {Module} Parent module to read from. +// #### @options {Object|Array|string} **Optional** Options used when exposing properties. +// #### @arguments {string...} **Optional** Specified properties to expose. +// Exposes properties from the package.json file for the parent module on +// it's exports. Valid usage: +// +// `require('pkginfo')()` +// +// `require('pkginfo')('version', 'author');` +// +// `require('pkginfo')(['version', 'author']);` +// +// `require('pkginfo')({ include: ['version', 'author'] });` +// +var pkginfo = module.exports = function (pmodule, options) { + var args = [].slice.call(arguments, 2).filter(function (arg) { + return typeof arg === 'string'; + }); + + // + // **Parse variable arguments** + // + if (Array.isArray(options)) { + // + // If the options passed in is an Array assume that + // it is the Array of properties to expose from the + // on the package.json file on the parent module. + // + options = { include: options }; + } + else if (typeof options === 'string') { + // + // Otherwise if the first argument is a string, then + // assume that it is the first property to expose from + // the package.json file on the parent module. + // + options = { include: [options] }; + } + + // + // **Setup default options** + // + options = options || { include: [] }; + + if (args.length > 0) { + // + // If additional string arguments have been passed in + // then add them to the properties to expose on the + // parent module. + // + options.include = options.include.concat(args); + } + + var pkg = pkginfo.read(pmodule, options.dir).package; + Object.keys(pkg).forEach(function (key) { + if (options.include.length > 0 && !~options.include.indexOf(key)) { + return; + } + + if (!pmodule.exports[key]) { + pmodule.exports[key] = pkg[key]; + } + }); + + return pkginfo; +}; + +// +// ### function find (dir) +// #### @pmodule {Module} Parent module to read from. +// #### @dir {string} **Optional** Directory to start search from. +// Searches up the directory tree from `dir` until it finds a directory +// which contains a `package.json` file. +// +pkginfo.find = function (pmodule, dir) { + dir = dir || pmodule.filename; + dir = path.dirname(dir); + + var files = fs.readdirSync(dir); + + if (~files.indexOf('package.json')) { + return path.join(dir, 'package.json'); + } + + if (dir === '/') { + throw new Error('Could not find package.json up from: ' + dir); + } + else if (!dir || dir === '.') { + throw new Error('Cannot find package.json from unspecified directory'); + } + + return pkginfo.find(pmodule, dir); +}; + +// +// ### function read (pmodule, dir) +// #### @pmodule {Module} Parent module to read from. +// #### @dir {string} **Optional** Directory to start search from. +// Searches up the directory tree from `dir` until it finds a directory +// which contains a `package.json` file and returns the package information. +// +pkginfo.read = function (pmodule, dir) { + dir = pkginfo.find(pmodule, dir); + + var data = fs.readFileSync(dir).toString(); + + return { + dir: dir, + package: JSON.parse(data) + }; +}; + +// +// Call `pkginfo` on this module and expose version. +// +pkginfo(module, { + dir: __dirname, + include: ['version'], + target: pkginfo +}); \ No newline at end of file diff --git a/dist/node_modules/nconf/node_modules/pkginfo/package.json b/dist/node_modules/nconf/node_modules/pkginfo/package.json new file mode 100644 index 0000000..0e9e4e7 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/package.json @@ -0,0 +1,17 @@ +{ + "name": "pkginfo", + "version": "0.2.3", + "description": "An easy way to expose properties on a module from a package.json", + "author": "Charlie Robbins ", + "repository": { + "type": "git", + "url": "http://github.com/indexzero/node-pkginfo.git" + }, + "keywords": ["info", "tools", "package.json"], + "devDependencies": { + "vows": "0.6.x" + }, + "main": "./lib/pkginfo", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.4.0" } +} diff --git a/dist/node_modules/nconf/node_modules/pkginfo/test/pkginfo-test.js b/dist/node_modules/nconf/node_modules/pkginfo/test/pkginfo-test.js new file mode 100644 index 0000000..3156c00 --- /dev/null +++ b/dist/node_modules/nconf/node_modules/pkginfo/test/pkginfo-test.js @@ -0,0 +1,69 @@ +/* + * pkginfo-test.js: Tests for the pkginfo module. + * + * (C) 2011, Charlie Robbins + * + */ + +var assert = require('assert'), + exec = require('child_process').exec, + fs = require('fs'), + path = require('path'), + vows = require('vows'), + pkginfo = require('../lib/pkginfo'); + +function assertProperties (source, target) { + assert.lengthOf(source, target.length + 1); + target.forEach(function (prop) { + assert.isTrue(!!~source.indexOf(prop)); + }); +} + +function testExposes (options) { + return { + topic: function () { + exec('node ' + path.join(__dirname, '..', 'examples', options.script), this.callback); + }, + "should expose that property correctly": function (err, stdout, stderr) { + assert.isNull(err); + + var exposed = stderr.match(/'(\w+)'/ig).map(function (p) { + return p.substring(1, p.length - 1); + }); + + return !options.assert + ? assertProperties(exposed, options.properties) + : options.assert(exposed); + } + } +} + +vows.describe('pkginfo').addBatch({ + "When using the pkginfo module": { + "and passed a single `string` argument": testExposes({ + script: 'single-property.js', + properties: ['version'] + }), + "and passed multiple `string` arguments": testExposes({ + script: 'multiple-properties.js', + properties: ['version', 'author'] + }), + "and passed an `object` argument": testExposes({ + script: 'object-argument.js', + properties: ['version', 'author'] + }), + "and passed an `array` argument": testExposes({ + script: 'array-argument.js', + properties: ['version', 'author'] + }), + "and passed no arguments": testExposes({ + script: 'all-properties.js', + assert: function (exposed) { + var pkg = fs.readFileSync(path.join(__dirname, '..', 'examples', 'package.json')).toString(), + keys = Object.keys(JSON.parse(pkg)); + + assertProperties(exposed, keys); + } + }) + } +}).export(module); diff --git a/dist/node_modules/nconf/package.json b/dist/node_modules/nconf/package.json new file mode 100644 index 0000000..bcbb098 --- /dev/null +++ b/dist/node_modules/nconf/package.json @@ -0,0 +1,35 @@ +{ + "name": "nconf", + "description": "Hierarchical node.js configuration with files, environment variables, command-line arguments, and atomic object merging.", + "version": "0.6.4", + "author": "Nodejitsu Inc. ", + "maintainers": [ + "indexzero " + ], + "repository": { + "type": "git", + "url": "http://github.com/flatiron/nconf.git" + }, + "keywords": [ + "configuration", + "key value store", + "plugabble" + ], + "dependencies": { + "async": "0.1.x", + "ini": "1.x.x", + "optimist": "0.3.x", + "pkginfo": "0.2.x" + }, + "devDependencies": { + "vows": "0.6.x" + }, + "main": "./lib/nconf", + "scripts": { + "test": "vows test/*-test.js test/**/*-test.js --spec" + }, + "engines": { + "node": ">= 0.4.0" + } +} + diff --git a/dist/node_modules/nconf/test/common-test.js b/dist/node_modules/nconf/test/common-test.js new file mode 100644 index 0000000..9924372 --- /dev/null +++ b/dist/node_modules/nconf/test/common-test.js @@ -0,0 +1,32 @@ +/* + * common.js: Tests for common utility function in nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + path = require('path'), + vows = require('vows'), + assert = require('assert'), + helpers = require('./helpers'), + nconf = require('../lib/nconf'); + +var mergeDir = path.join(__dirname, 'fixtures', 'merge'), + files = fs.readdirSync(mergeDir).map(function (f) { return path.join(mergeDir, f) }); + +vows.describe('nconf/common').addBatch({ + "Using nconf.common module": { + "the loadFiles() method": { + topic: function () { + nconf.loadFiles(files, this.callback); + }, + "should merge the files correctly": helpers.assertMerged + }, + "the loadFilesSync() method": { + "should merge the files correctly": function () { + helpers.assertMerged(null, nconf.loadFilesSync(files)); + } + } + } +}).export(module); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/complete-test.js b/dist/node_modules/nconf/test/complete-test.js new file mode 100644 index 0000000..6ac8ef7 --- /dev/null +++ b/dist/node_modules/nconf/test/complete-test.js @@ -0,0 +1,126 @@ +/* + * complete-test.js: Complete test for multiple stores. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + path = require('path'), + vows = require('vows'), + assert = require('assert'), + nconf = require('../lib/nconf'), + data = require('./fixtures/data').data, + helpers = require('./helpers'); + +var completeTest = helpers.fixture('complete-test.json'), + complete = helpers.fixture('complete.json'); + +vows.describe('nconf/multiple-stores').addBatch({ + "When using the nconf with multiple providers": { + topic: function () { + var that = this; + helpers.cp(complete, completeTest, function () { + nconf.env(); + nconf.file({ file: completeTest }); + nconf.use('argv', { type: 'literal', store: data }); + that.callback(); + }); + }, + "should have the correct `stores`": function () { + assert.isObject(nconf.stores.env); + assert.isObject(nconf.stores.argv); + assert.isObject(nconf.stores.file); + }, + "env vars": { + "are present": function () { + Object.keys(process.env).forEach(function (key) { + assert.equal(nconf.get(key), process.env[key]); + }); + } + }, + "json vars": { + topic: function () { + fs.readFile(complete, 'utf8', this.callback); + }, + "are present": function (err, data) { + assert.isNull(err); + data = JSON.parse(data); + Object.keys(data).forEach(function (key) { + assert.deepEqual(nconf.get(key), data[key]); + }); + } + }, + "literal vars": { + "are present": function () { + Object.keys(data).forEach(function (key) { + assert.deepEqual(nconf.get(key), data[key]); + }); + } + }, + "and saving *synchronously*": { + topic: function () { + nconf.set('weebls', 'stuff'); + return nconf.save(); + }, + "correct return value": function (topic) { + Object.keys(topic).forEach(function (key) { + assert.deepEqual(topic[key], nconf.get(key)); + }); + }, + "the file": { + topic: function () { + fs.readFile(completeTest, 'utf8', this.callback); + }, + "saved correctly": function (err, data) { + data = JSON.parse(data); + Object.keys(data).forEach(function (key) { + assert.deepEqual(data[key], nconf.get(key)); + }); + assert.equal(nconf.get('weebls'), 'stuff'); + } + } + }, + teardown: function () { + // remove the file so that we can test saving it async + fs.unlinkSync(completeTest); + } + } +}).addBatch({ + // Threw this in it's own batch to make sure it's run separately from the + // sync check + "When using the nconf with multiple providers": { + "and saving *asynchronously*": { + topic: function () { + nconf.set('weebls', 'crap'); + nconf.save(this.callback); + }, + "correct return value": function (err, data) { + assert.isNull(err); + Object.keys(data).forEach(function (key) { + assert.deepEqual(data[key], nconf.get(key)); + }); + }, + "the file": { + topic: function () { + fs.readFile(completeTest, 'utf8', this.callback); + }, + "saved correctly": function (err, data) { + assert.isNull(err); + data = JSON.parse(data); + Object.keys(data).forEach(function (key) { + assert.deepEqual(nconf.get(key), data[key]); + }); + assert.equal(nconf.get('weebls'), 'crap'); + } + } + }, + teardown: function () { + fs.unlinkSync(completeTest); + nconf.remove('file'); + nconf.remove('memory'); + nconf.remove('argv'); + nconf.remove('env'); + } + } +}).export(module); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/complete.json b/dist/node_modules/nconf/test/fixtures/complete.json new file mode 100644 index 0000000..6576c98 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/complete.json @@ -0,0 +1,19 @@ +{ + "I've seen things": { + "like": [ + "carrots", + "handbags", + "cheese", + "toilets", + "russians", + "planets", + "hampsters", + "weddings", + "poets", + "stalin", + "kuala lumpur" + ] + }, + "host": "weebls-stuff.com", + "port": 78304 +} diff --git a/dist/node_modules/nconf/test/fixtures/data.js b/dist/node_modules/nconf/test/fixtures/data.js new file mode 100644 index 0000000..604dc05 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/data.js @@ -0,0 +1,29 @@ +/* + * data.js: Simple data fixture for configuration test. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +exports.data = { + literal: 'bazz', + arr: ['one', 2, true, { value: 'foo' }], + obj: { + host: 'localhost', + port: 5984, + array: ['one', 2, true, { foo: 'bar' }], + auth: { + username: 'admin', + password: 'password' + } + } +}; + +exports.merge = { + prop1: 1, + prop2: [1, 2, 3], + prop3: { + foo: 'bar', + bar: 'foo' + } +}; \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/hierarchy/global.json b/dist/node_modules/nconf/test/fixtures/hierarchy/global.json new file mode 100644 index 0000000..4ac5a6e --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/hierarchy/global.json @@ -0,0 +1,5 @@ +{ + "title": "My generic title", + "color": "red", + "movie": "Kill Bill" +} \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/hierarchy/hierarchical.json b/dist/node_modules/nconf/test/fixtures/hierarchy/hierarchical.json new file mode 100644 index 0000000..1385be3 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/hierarchy/hierarchical.json @@ -0,0 +1,3 @@ +{ + "test": "empty" +} \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/hierarchy/user.json b/dist/node_modules/nconf/test/fixtures/hierarchy/user.json new file mode 100644 index 0000000..45a3aac --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/hierarchy/user.json @@ -0,0 +1,4 @@ +{ + "title": "My specific title", + "color": "green" +} \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/malformed.json b/dist/node_modules/nconf/test/fixtures/malformed.json new file mode 100644 index 0000000..fffa155 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/malformed.json @@ -0,0 +1,3 @@ +{ + "literal": "bazz", +} \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/merge/file1.json b/dist/node_modules/nconf/test/fixtures/merge/file1.json new file mode 100644 index 0000000..b48f5c0 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/merge/file1.json @@ -0,0 +1,16 @@ +{ + "apples": true, + "bananas": true, + "foo": { + "bar": "boo" + }, + "candy": { + "something": "file1", + "something1": true, + "something2": true, + "something5": { + "first": 1, + "second": 2 + } + } +} diff --git a/dist/node_modules/nconf/test/fixtures/merge/file2.json b/dist/node_modules/nconf/test/fixtures/merge/file2.json new file mode 100644 index 0000000..815779c --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/merge/file2.json @@ -0,0 +1,9 @@ +{ + "candy": { + "something": "file2", + "something3": true, + "something4": true + }, + "dates": true, + "elderberries": true +} \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/scripts/nconf-argv.js b/dist/node_modules/nconf/test/fixtures/scripts/nconf-argv.js new file mode 100644 index 0000000..65de175 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/nconf-argv.js @@ -0,0 +1,10 @@ +/* + * default-argv.js: Test fixture for using optimist defaults with nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var nconf = require('../../../lib/nconf').argv().env(); + +process.stdout.write(nconf.get('something')); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/scripts/nconf-change-argv.js b/dist/node_modules/nconf/test/fixtures/scripts/nconf-change-argv.js new file mode 100644 index 0000000..7b2e7a0 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/nconf-change-argv.js @@ -0,0 +1,16 @@ +/* + * nconf-change-argv.js: Test fixture for changing argv on the fly + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var nconf = require('../../../lib/nconf').argv(); + +// +// Remove 'badValue', 'evenWorse' and 'OHNOEZ' +// +process.argv.splice(3, 3); +nconf.stores['argv'].loadArgv(); +process.stdout.write(nconf.get('something')); + diff --git a/dist/node_modules/nconf/test/fixtures/scripts/nconf-env.js b/dist/node_modules/nconf/test/fixtures/scripts/nconf-env.js new file mode 100644 index 0000000..e8032de --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/nconf-env.js @@ -0,0 +1,10 @@ +/* + * nconf-env.js: Test fixture for using process.env defaults with nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var nconf = require('../../../lib/nconf').env(); + +process.stdout.write(nconf.get('SOMETHING')); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-file-argv.js b/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-file-argv.js new file mode 100644 index 0000000..54e58b0 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-file-argv.js @@ -0,0 +1,17 @@ +/* + * nconf-hierarchical-file-argv.js: Test fixture for using optimist defaults and a file store with nconf. + * + * (C) 2011, Nodejitsu Inc. + * (C) 2011, Sander Tolsma + * + */ + +var path = require('path'), + nconf = require('../../../lib/nconf'); + +nconf.argv(); +nconf.add('file', { + file: path.join(__dirname, '../hierarchy/hierarchical.json') +}); + +process.stdout.write(nconf.get('something') || 'undefined'); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-merge.js b/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-merge.js new file mode 100644 index 0000000..4945cfd --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-merge.js @@ -0,0 +1,18 @@ +/* + * nconf-hierarchical-load-merge.js: Test fixture for loading and merging nested objects across stores. + * + * (C) 2012, Nodejitsu Inc. + * (C) 2012, Michael Hart + * + */ + +var path = require('path'), + nconf = require('../../../lib/nconf'); + +nconf.argv() + .file(path.join(__dirname, '..', 'merge', 'file1.json')); + +process.stdout.write(JSON.stringify({ + apples: nconf.get('apples'), + candy: nconf.get('candy') +})); diff --git a/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-save.js b/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-save.js new file mode 100644 index 0000000..ae2771e --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/nconf-hierarchical-load-save.js @@ -0,0 +1,32 @@ +/* + * nconf-hierarchical-load-save.js: Test fixture for using optimist, envvars and a file store with nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + path = require('path'), + nconf = require('../../../lib/nconf'); + +// +// Setup nconf to use (in-order): +// 1. Command-line arguments +// 2. Environment variables +// 3. A file located at 'path/to/config.json' +// +nconf.argv() + .env() + .file({ file: path.join(__dirname, '..', 'load-save.json') }); + +// +// Set a few variables on `nconf`. +// +nconf.set('database:host', '127.0.0.1'); +nconf.set('database:port', 5984); + +process.stdout.write(nconf.get('foo')); +// +// Save the configuration object to disk +// +nconf.save(); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/scripts/nconf-nested-env.js b/dist/node_modules/nconf/test/fixtures/scripts/nconf-nested-env.js new file mode 100644 index 0000000..cfc3365 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/nconf-nested-env.js @@ -0,0 +1,11 @@ +/* + * nconf-nested-env.js: Test fixture for env with nested keys. + * + * (C) 2012, Nodejitsu Inc. + * (C) 2012, Michael Hart + * + */ + +var nconf = require('../../../lib/nconf').env('_'); + +process.stdout.write(nconf.get('SOME:THING')); diff --git a/dist/node_modules/nconf/test/fixtures/scripts/provider-argv.js b/dist/node_modules/nconf/test/fixtures/scripts/provider-argv.js new file mode 100644 index 0000000..0492474 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/provider-argv.js @@ -0,0 +1,12 @@ +/* + * provider-argv.js: Test fixture for using optimist defaults with nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var nconf = require('../../../lib/nconf'); + +var provider = new (nconf.Provider)().argv(); + +process.stdout.write(provider.get('something')); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/fixtures/scripts/provider-env.js b/dist/node_modules/nconf/test/fixtures/scripts/provider-env.js new file mode 100644 index 0000000..e5027b4 --- /dev/null +++ b/dist/node_modules/nconf/test/fixtures/scripts/provider-env.js @@ -0,0 +1,12 @@ +/* + * provider-argv.js: Test fixture for using process.env defaults with nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var nconf = require('../../../lib/nconf'); + +var provider = new (nconf.Provider)().env(); + +process.stdout.write(provider.get('SOMETHING')); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/helpers.js b/dist/node_modules/nconf/test/helpers.js new file mode 100644 index 0000000..f8504ed --- /dev/null +++ b/dist/node_modules/nconf/test/helpers.js @@ -0,0 +1,68 @@ +/* + * helpers.js: Test helpers for nconf. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var assert = require('assert'), + spawn = require('child_process').spawn, + util = require('util'), + fs = require('fs'), + path = require('path'), + nconf = require('../lib/nconf'); + +exports.assertMerged = function (err, merged) { + merged = merged instanceof nconf.Provider + ? merged.store.store + : merged; + + assert.isNull(err); + assert.isObject(merged); + assert.isTrue(merged.apples); + assert.isTrue(merged.bananas); + assert.isObject(merged.candy); + assert.isTrue(merged.candy.something1); + assert.isTrue(merged.candy.something2); + assert.isTrue(merged.candy.something3); + assert.isTrue(merged.candy.something4); + assert.isTrue(merged.dates); + assert.isTrue(merged.elderberries); +}; + +exports.assertSystemConf = function (options) { + return { + topic: function () { + var env = null; + + if (options.env) { + env = {} + Object.keys(process.env).forEach(function (key) { + env[key] = process.env[key]; + }); + + Object.keys(options.env).forEach(function (key) { + env[key] = options.env[key]; + }); + } + + var child = spawn('node', [options.script].concat(options.argv), { env: env }); + child.stdout.once('data', this.callback.bind(this, null)); + }, + "should respond with the value passed into the script": function (_, data) { + assert.equal(data.toString(), 'foobar'); + } + } +} + +// copy a file +exports.cp = function (from, to, callback) { + fs.readFile(from, function (err, data) { + if (err) return callback(err); + fs.writeFile(to, data, callback); + }); +}; + +exports.fixture = function (file) { + return path.join(__dirname, 'fixtures', file); +}; \ No newline at end of file diff --git a/dist/node_modules/nconf/test/hierarchy-test.js b/dist/node_modules/nconf/test/hierarchy-test.js new file mode 100644 index 0000000..80e11e9 --- /dev/null +++ b/dist/node_modules/nconf/test/hierarchy-test.js @@ -0,0 +1,113 @@ +/* + * hierarchy-test.js: Basic tests for hierarchical file stores. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var assert = require('assert'), + fs = require('fs'), + path = require('path'), + spawn = require('child_process').spawn, + vows = require('vows'), + nconf = require('../lib/nconf'); + +var configDir = path.join(__dirname, 'fixtures', 'hierarchy'), + globalConfig = path.join(configDir, 'global.json'), + userConfig = path.join(configDir, 'user.json'); + +vows.describe('nconf/hierarchy').addBatch({ + "When using nconf": { + "configured with two file stores": { + topic: function () { + nconf.add('user', { type: 'file', file: userConfig }); + nconf.add('global', { type: 'file', file: globalConfig }); + nconf.load(); + return nconf; + }, + "should have the appropriate keys present": function () { + assert.equal(nconf.get('title'), 'My specific title'); + assert.equal(nconf.get('color'), 'green'); + assert.equal(nconf.get('movie'), 'Kill Bill'); + } + }, + "configured with two file stores using `file`": { + topic: function () { + nconf.file('user', userConfig); + nconf.file('global', globalConfig); + nconf.load(); + return nconf; + }, + "should have the appropriate keys present": function () { + assert.equal(nconf.get('title'), 'My specific title'); + assert.equal(nconf.get('color'), 'green'); + assert.equal(nconf.get('movie'), 'Kill Bill'); + } + }, + "configured with .argv(), .env() and .file()": { + topic: function () { + var configFile = path.join(__dirname, 'fixtures', 'load-save.json'), + script = path.join(__dirname, 'fixtures', 'scripts', 'nconf-hierarchical-load-save.js'), + argv = ['--foo', 'foo', '--bar', 'bar'], + that = this, + data = '', + child; + + try { fs.unlinkSync(configFile) } + catch (ex) { } + + child = spawn('node', [script].concat(argv)); + + child.stdout.on('data', function (d) { + data += d; + }); + + child.on('exit', function () { + fs.readFile(configFile, 'utf8', that.callback.bind(null, null, data)); + }); + }, + "should not persist information passed in to process.env and process.argv to disk ": function (_, data, _, ondisk){ + assert.equal(data, 'foo'); + assert.deepEqual(JSON.parse(ondisk), { + database: { + host: '127.0.0.1', + port: 5984 + } + }); + } + }, + "configured with .argv(), .file() and invoked with nested command line options": { + topic: function () { + var script = path.join(__dirname, 'fixtures', 'scripts', 'nconf-hierarchical-load-merge.js'), + argv = ['--candy:something', 'foo', '--candy:something5:second', 'bar'], + that = this, + data = '', + child; + + child = spawn('node', [script].concat(argv)); + + child.stdout.on('data', function (d) { + data += d; + }); + + child.on('exit', function() { + that.callback(null, data); + }); + }, + "should merge nested objects ": function (err, data) { + assert.deepEqual(JSON.parse(data), { + apples: true, + candy: { + something: 'foo', + something1: true, + something2: true, + something5: { + first: 1, + second: 'bar' + } + } + }); + } + } + } +}).export(module); diff --git a/dist/node_modules/nconf/test/mocks/mock-store.js b/dist/node_modules/nconf/test/mocks/mock-store.js new file mode 100644 index 0000000..98b57c8 --- /dev/null +++ b/dist/node_modules/nconf/test/mocks/mock-store.js @@ -0,0 +1,38 @@ +/* + * mock-store.js: Mock store for ensuring certain operations are actually called. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var util = require('util'), + events = require('events'), + nconf = require('../../lib/nconf'); + +var Mock = nconf.Mock = function () { + events.EventEmitter.call(this); + this.type = 'mock'; +}; + +// Inherit from Memory store. +util.inherits(Mock, events.EventEmitter); + +// +// ### function save (value, callback) +// #### @value {Object} _Ignored_ Left here for consistency +// #### @callback {function} Continuation to respond to when complete. +// Waits `1000ms` and then calls the callback and emits the `save` event. +// +Mock.prototype.save = function (value, callback) { + if (!callback && typeof value === 'function') { + callback = value; + value = null; + } + + var self = this; + + setTimeout(function () { + self.emit('save'); + callback(); + }, 1000); +}; \ No newline at end of file diff --git a/dist/node_modules/nconf/test/nconf-test.js b/dist/node_modules/nconf/test/nconf-test.js new file mode 100644 index 0000000..111a4f0 --- /dev/null +++ b/dist/node_modules/nconf/test/nconf-test.js @@ -0,0 +1,107 @@ +/* + * file-store-test.js: Tests for the nconf File store. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + path = require('path'), + vows = require('vows'), + assert = require('assert'), + nconf = require('../lib/nconf'), + data = require('./fixtures/data').data; + +vows.describe('nconf').addBatch({ + "When using the nconf": { + "should have the correct methods set": function () { + assert.isFunction(nconf.key); + assert.isFunction(nconf.path); + assert.isFunction(nconf.use); + assert.isFunction(nconf.get); + assert.isFunction(nconf.set); + assert.isFunction(nconf.clear); + assert.isFunction(nconf.load); + assert.isFunction(nconf.save); + assert.isFunction(nconf.reset); + }, + "the use() method": { + "should instaniate the correct store": function () { + nconf.use('memory'); + nconf.load(); + assert.instanceOf(nconf.stores['memory'], nconf.Memory); + } + }, + "it should": { + topic: function () { + fs.readFile(path.join(__dirname, '..', 'package.json'), this.callback); + }, + "have the correct version set": function (err, data) { + assert.isNull(err); + data = JSON.parse(data.toString()); + assert.equal(nconf.version, data.version); + } + } + } +}).addBatch({ + "When using the nconf": { + "with the memory store": { + "the set() method": { + "should respond with true": function () { + assert.isTrue(nconf.set('foo:bar:bazz', 'buzz')); + } + }, + "the get() method": { + "without a callback": { + "should respond with the correct value": function () { + assert.equal(nconf.get('foo:bar:bazz'), 'buzz'); + } + }, + "with a callback": { + topic: function () { + nconf.get('foo:bar:bazz', this.callback); + }, + "should respond with the correct value": function (err, value) { + assert.equal(value, 'buzz'); + } + } + } + } + } +}).addBatch({ + "When using nconf": { + "with the memory store": { + "the clear() method": { + "should respond with the true": function () { + assert.equal(nconf.get('foo:bar:bazz'), 'buzz'); + assert.isTrue(nconf.clear('foo:bar:bazz')); + assert.isTrue(typeof nconf.get('foo:bar:bazz') === 'undefined'); + } + }, + "the load() method": { + "without a callback": { + "should respond with the merged store": function () { + assert.deepEqual(nconf.load(), { + title: 'My specific title', + color: 'green', + movie: 'Kill Bill' + }); + } + }, + "with a callback": { + topic: function () { + nconf.load(this.callback.bind(null, null)); + }, + "should respond with the merged store": function (ign, err, store) { + assert.isNull(err); + assert.deepEqual(store, { + title: 'My specific title', + color: 'green', + movie: 'Kill Bill' + }); + } + } + } + } + } +}).export(module); diff --git a/dist/node_modules/nconf/test/provider-save-test.js b/dist/node_modules/nconf/test/provider-save-test.js new file mode 100644 index 0000000..5ca528a --- /dev/null +++ b/dist/node_modules/nconf/test/provider-save-test.js @@ -0,0 +1,39 @@ +/* + * provider-save-test.js: Ensures consistency for Provider `save` operations. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var assert = require('assert'), + vows = require('vows'), + nconf = require('../lib/nconf'); + +// +// Expose `nconf.Mock` +// +require('./mocks/mock-store'); + +vows.describe('nconf/provider/save').addBatch({ + "When using nconf": { + "an instance of 'nconf.Provider'": { + "with a Mock store": { + topic: function () { + return nconf.use('mock'); + }, + "the save() method": { + topic: function () { + var mock = nconf.stores.mock, + that = this; + + mock.on('save', function () { that.saved = true }); + nconf.save(this.callback); + }, + "should actually save before responding": function () { + assert.isTrue(this.saved); + } + } + } + } + } +}).export(module); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/provider-test.js b/dist/node_modules/nconf/test/provider-test.js new file mode 100644 index 0000000..7d87972 --- /dev/null +++ b/dist/node_modules/nconf/test/provider-test.js @@ -0,0 +1,165 @@ +/* + * provider-test.js: Tests for the nconf Provider object. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var assert = require('assert'), + fs = require('fs'), + path = require('path'), + spawn = require('child_process').spawn, + vows = require('vows'), + helpers = require('./helpers'), + nconf = require('../lib/nconf'); + +var fixturesDir = path.join(__dirname, 'fixtures'), + mergeFixtures = path.join(fixturesDir, 'merge'), + files = [path.join(mergeFixtures, 'file1.json'), path.join(mergeFixtures, 'file2.json')], + override = JSON.parse(fs.readFileSync(files[0]), 'utf8'); + +function assertProvider(test) { + return { + topic: new nconf.Provider(), + "should use the correct File store": test + }; +} + +vows.describe('nconf/provider').addBatch({ + "When using nconf": { + "an instance of 'nconf.Provider'": { + "calling the use() method with the same store type and different options": { + topic: new nconf.Provider().use('file', { file: files[0] }), + "should use a new instance of the store type": function (provider) { + var old = provider.stores['file']; + + assert.equal(provider.stores.file.file, files[0]); + provider.use('file', { file: files[1] }); + + assert.notStrictEqual(old, provider.stores.file); + assert.equal(provider.stores.file.file, files[1]); + } + }, + "when 'argv' is true": helpers.assertSystemConf({ + script: path.join(fixturesDir, 'scripts', 'provider-argv.js'), + argv: ['--something', 'foobar'] + }), + "when 'env' is true": helpers.assertSystemConf({ + script: path.join(fixturesDir, 'scripts', 'provider-env.js'), + env: { SOMETHING: 'foobar' } + }) + }, + "the default nconf provider": { + "when 'argv' is set to true": helpers.assertSystemConf({ + script: path.join(fixturesDir, 'scripts', 'nconf-argv.js'), + argv: ['--something', 'foobar'], + env: { SOMETHING: true } + }), + "when 'env' is set to true": helpers.assertSystemConf({ + script: path.join(fixturesDir, 'scripts', 'nconf-env.js'), + env: { SOMETHING: 'foobar' } + }), + "when 'argv' is set to true and process.argv is modified": helpers.assertSystemConf({ + script: path.join(fixturesDir, 'scripts', 'nconf-change-argv.js'), + argv: ['--something', 'badValue', 'evenWorse', 'OHNOEZ', 'foobar'] + }), + "when hierarchical 'argv' get": helpers.assertSystemConf({ + script: path.join(fixturesDir, 'scripts', 'nconf-hierarchical-file-argv.js'), + argv: ['--something', 'foobar'], + env: { SOMETHING: true } + }), + "when 'env' is set to true with a nested separator": helpers.assertSystemConf({ + script: path.join(fixturesDir, 'scripts', 'nconf-nested-env.js'), + env: { SOME_THING: 'foobar' } + }) + } + } +}).addBatch({ + "When using nconf": { + "an instance of 'nconf.Provider'": { + "the merge() method": { + topic: new nconf.Provider().use('file', { file: files[1] }), + "should have the result merged in": function (provider) { + provider.load(); + provider.merge(override); + helpers.assertMerged(null, provider.stores.file.store); + assert.equal(provider.stores.file.store.candy.something, 'file1'); + } + } + } + } +}).addBatch({ + "When using nconf": { + "an instance of 'nconf.Provider'": { + "the load() method": { + "when sources are passed in": { + topic: new nconf.Provider({ + sources: { + user: { + type: 'file', + file: files[0] + }, + global: { + type: 'file', + file: files[1] + } + } + }), + "should respect the hierarchy ": function (provider) { + var merged = provider.load(); + + helpers.assertMerged(null, merged); + assert.equal(merged.candy.something, 'file1'); + } + }, + "when multiple stores are used": { + topic: new nconf.Provider().overrides({foo: {bar: 'baz'}}) + .add('file1', {type: 'file', file: files[0]}) + .add('file2', {type: 'file', file: files[1]}), + "should respect the hierarchy": function(provider) { + var merged = provider.load(); + + helpers.assertMerged(null, merged); + assert.equal(merged.foo.bar, 'baz'); + assert.equal(merged.candy.something, 'file1'); + } + } + } + } + } +}).addBatch({ + "When using nconf": { + "an instance of 'nconf.Provider'": { + "the .file() method": { + "with a single filepath": assertProvider(function (provider) { + provider.file(helpers.fixture('store.json')); + assert.isObject(provider.stores.file); + }), + "with a name and a filepath": assertProvider(function (provider) { + provider.file('custom', helpers.fixture('store.json')); + assert.isObject(provider.stores.custom); + }), + "with a single object": assertProvider(function (provider) { + provider.file({ + dir: helpers.fixture('hierarchy'), + file: 'store.json', + search: true + }); + + assert.isObject(provider.stores.file); + assert.equal(provider.stores.file.file, helpers.fixture('store.json')); + }), + "with a name and an object": assertProvider(function (provider) { + provider.file('custom', { + dir: helpers.fixture('hierarchy'), + file: 'store.json', + search: true + }); + + assert.isObject(provider.stores.custom); + assert.equal(provider.stores.custom.file, helpers.fixture('store.json')); + }) + } + } + } +}).export(module); diff --git a/dist/node_modules/nconf/test/stores/argv-test.js b/dist/node_modules/nconf/test/stores/argv-test.js new file mode 100644 index 0000000..9dbf3a9 --- /dev/null +++ b/dist/node_modules/nconf/test/stores/argv-test.js @@ -0,0 +1,22 @@ +/* + * argv-test.js: Tests for the nconf argv store. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var vows = require('vows'), + assert = require('assert'), + helpers = require('../helpers'), + nconf = require('../../lib/nconf'); + +vows.describe('nconf/stores/argv').addBatch({ + "An instance of nconf.Argv": { + topic: new nconf.Argv(), + "should have the correct methods defined": function (argv) { + assert.isFunction(argv.loadSync); + assert.isFunction(argv.loadArgv); + assert.isFalse(argv.options); + } + } +}).export(module); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/stores/env-test.js b/dist/node_modules/nconf/test/stores/env-test.js new file mode 100644 index 0000000..6ae4cb0 --- /dev/null +++ b/dist/node_modules/nconf/test/stores/env-test.js @@ -0,0 +1,24 @@ +/* + * env-test.js: Tests for the nconf env store. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var vows = require('vows'), + assert = require('assert'), + helpers = require('../helpers'), + nconf = require('../../lib/nconf'); + +vows.describe('nconf/stores/env').addBatch({ + "An instance of nconf.Env": { + topic: new nconf.Env(), + "should have the correct methods defined": function (env) { + assert.isFunction(env.loadSync); + assert.isFunction(env.loadEnv); + assert.isArray(env.whitelist); + assert.lengthOf(env.whitelist, 0); + assert.equal(env.separator, ''); + } + } +}).export(module); diff --git a/dist/node_modules/nconf/test/stores/file-store-test.js b/dist/node_modules/nconf/test/stores/file-store-test.js new file mode 100644 index 0000000..75ec280 --- /dev/null +++ b/dist/node_modules/nconf/test/stores/file-store-test.js @@ -0,0 +1,175 @@ +/* + * file-store-test.js: Tests for the nconf File store. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var fs = require('fs'), + path = require('path'), + vows = require('vows'), + assert = require('assert'), + nconf = require('../../lib/nconf'), + data = require('../fixtures/data').data, + store; + +vows.describe('nconf/stores/file').addBatch({ + "When using the nconf file store": { + "with a valid JSON file": { + topic: function () { + var filePath = path.join(__dirname, '..', 'fixtures', 'store.json'); + fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); + this.store = store = new nconf.File({ file: filePath }); + return null; + }, + "the load() method": { + topic: function () { + this.store.load(this.callback); + }, + "should load the data correctly": function (err, data) { + assert.isNull(err); + assert.deepEqual(data, this.store.store); + } + } + }, + "with a malformed JSON file": { + topic: function () { + var filePath = path.join(__dirname, '..', 'fixtures', 'malformed.json'); + this.store = new nconf.File({ file: filePath }); + return null; + }, + "the load() method with a malformed JSON config file": { + topic: function () { + this.store.load(this.callback.bind(null, null)); + }, + "should respond with an error": function (_, err) { + assert.isTrue(!!err); + } + } + } + } +}).addBatch({ + "When using the nconf file store": { + topic: function () { + var tmpPath = path.join(__dirname, '..', 'fixtures', 'tmp.json'), + tmpStore = new nconf.File({ file: tmpPath }); + return tmpStore; + }, + "the save() method": { + topic: function (tmpStore) { + var that = this; + + Object.keys(data).forEach(function (key) { + tmpStore.set(key, data[key]); + }); + + tmpStore.save(function () { + fs.readFile(tmpStore.file, function (err, d) { + fs.unlinkSync(tmpStore.file); + + return err + ? that.callback(err) + : that.callback(err, JSON.parse(d.toString())); + }); + }); + }, + "should save the data correctly": function (err, read) { + assert.isNull(err); + assert.deepEqual(read, data); + } + } + } +}).addBatch({ + "When using the nconf file store": { + topic: function () { + var tmpPath = path.join(__dirname, '..', 'fixtures', 'tmp.json'), + tmpStore = new nconf.File({ file: tmpPath }); + return tmpStore; + }, + "the saveSync() method": { + topic: function (tmpStore) { + var that = this; + + Object.keys(data).forEach(function (key) { + tmpStore.set(key, data[key]); + }); + + var saved = tmpStore.saveSync(); + + fs.readFile(tmpStore.file, function (err, d) { + fs.unlinkSync(tmpStore.file); + + return err + ? that.callback(err) + : that.callback(err, JSON.parse(d.toString()), saved); + }); + }, + "should save the data correctly": function (err, read, saved) { + assert.isNull(err); + assert.deepEqual(read, data); + assert.deepEqual(read, saved); + } + } + } +}).addBatch({ + "When using the nconf file store": { + "the set() method": { + "should respond with true": function () { + assert.isTrue(store.set('foo:bar:bazz', 'buzz')); + assert.isTrue(store.set('falsy:number', 0)); + assert.isTrue(store.set('falsy:string', '')); + assert.isTrue(store.set('falsy:boolean', false)); + assert.isTrue(store.set('falsy:object', null)); + } + }, + "the get() method": { + "should respond with the correct value": function () { + assert.equal(store.get('foo:bar:bazz'), 'buzz'); + assert.equal(store.get('falsy:number'), 0); + assert.equal(store.get('falsy:string'), ''); + assert.equal(store.get('falsy:boolean'), false); + assert.equal(store.get('falsy:object'), null); + } + }, + "the clear() method": { + "should respond with the true": function () { + assert.equal(store.get('foo:bar:bazz'), 'buzz'); + assert.isTrue(store.clear('foo:bar:bazz')); + assert.isTrue(typeof store.get('foo:bar:bazz') === 'undefined'); + } + } + } +}).addBatch({ + "When using the nconf file store": { + "the search() method": { + "when the target file exists higher in the directory tree": { + topic: function () { + var filePath = this.filePath = path.join(process.env.HOME, '.nconf'); + fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); + return new (nconf.File)({ + file: '.nconf' + }) + }, + "should update the file appropriately": function (store) { + store.search(); + assert.equal(store.file, this.filePath); + fs.unlinkSync(this.filePath); + } + }, + "when the target file doesn't exist higher in the directory tree": { + topic: function () { + var filePath = this.filePath = path.join(__dirname, '..', 'fixtures', 'search-store.json'); + return new (nconf.File)({ + dir: path.dirname(filePath), + file: 'search-store.json' + }) + }, + "should update the file appropriately": function (store) { + store.search(); + assert.equal(store.file, this.filePath); + } + } + } + } +}).export(module); + diff --git a/dist/node_modules/nconf/test/stores/literal-test.js b/dist/node_modules/nconf/test/stores/literal-test.js new file mode 100644 index 0000000..368ebe5 --- /dev/null +++ b/dist/node_modules/nconf/test/stores/literal-test.js @@ -0,0 +1,31 @@ +/* + * literal-test.js: Tests for the nconf literal store. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var vows = require('vows'), + assert = require('assert'), + helpers = require('../helpers'), + nconf = require('../../lib/nconf'); + +vows.describe('nconf/stores/literal').addBatch({ + "An instance of nconf.Literal": { + topic: new nconf.Literal({ + foo: 'bar', + one: 2 + }), + "should have the correct methods defined": function (literal) { + assert.equal(literal.type, 'literal'); + assert.isFunction(literal.get); + assert.isFunction(literal.set); + assert.isFunction(literal.merge); + assert.isFunction(literal.loadSync); + }, + "should have the correct values in the store": function (literal) { + assert.equal(literal.store.foo, 'bar'); + assert.equal(literal.store.one, 2); + } + } +}).export(module); \ No newline at end of file diff --git a/dist/node_modules/nconf/test/stores/memory-store-test.js b/dist/node_modules/nconf/test/stores/memory-store-test.js new file mode 100644 index 0000000..a64c8d3 --- /dev/null +++ b/dist/node_modules/nconf/test/stores/memory-store-test.js @@ -0,0 +1,84 @@ +/* + * memory-store-test.js: Tests for the nconf Memory store. + * + * (C) 2011, Nodejitsu Inc. + * + */ + +var vows = require('vows'), + assert = require('assert'), + nconf = require('../../lib/nconf'), + merge = require('../fixtures/data').merge; + +vows.describe('nconf/stores/memory').addBatch({ + "When using the nconf memory store": { + topic: new nconf.Memory(), + "the set() method": { + "should respond with true": function (store) { + assert.isTrue(store.set('foo:bar:bazz', 'buzz')); + assert.isTrue(store.set('falsy:number', 0)); + assert.isTrue(store.set('falsy:string', '')); + assert.isTrue(store.set('falsy:boolean', false)); + assert.isTrue(store.set('falsy:object', null)); + } + }, + "the get() method": { + "should respond with the correct value": function (store) { + assert.equal(store.get('foo:bar:bazz'), 'buzz'); + assert.equal(store.get('falsy:number'), 0); + assert.equal(store.get('falsy:string'), ''); + assert.equal(store.get('falsy:boolean'), false); + assert.equal(store.get('falsy:object'), null); + }, + "should not fail when retrieving non-existent keys": function (store) { + assert.doesNotThrow(function() { + assert.equal(store.get('this:key:does:not:exist'), undefined); + }, TypeError); + }, + "should not fail when drilling into non-objects": function (store) { + assert.doesNotThrow(function() { + assert.equal(store.get('falsy:number:uh:oh'), undefined); + }, TypeError); + } + }, + "the clear() method": { + "should respond with the true": function (store) { + assert.equal(store.get('foo:bar:bazz'), 'buzz'); + assert.isTrue(store.clear('foo:bar:bazz')); + assert.isTrue(typeof store.get('foo:bar:bazz') === 'undefined'); + } + }, + "the merge() method": { + "when overriding an existing literal value": function (store) { + store.set('merge:literal', 'string-value'); + store.merge('merge:literal', merge); + assert.deepEqual(store.get('merge:literal'), merge); + }, + "when overriding an existing Array value": function (store) { + store.set('merge:array', [1,2,3,4]); + store.merge('merge:array', merge); + assert.deepEqual(store.get('merge:literal'), merge); + }, + "when merging into an existing Object value": function (store) { + store.set('merge:object', { + prop1: 2, + prop2: 'prop2', + prop3: { + bazz: 'bazz' + }, + prop4: ['foo', 'bar'] + }); + store.merge('merge:object', merge); + + assert.equal(store.get('merge:object:prop1'), 1); + assert.equal(store.get('merge:object:prop2').length, 3); + assert.deepEqual(store.get('merge:object:prop3'), { + foo: 'bar', + bar: 'foo', + bazz: 'bazz' + }); + assert.equal(store.get('merge:object:prop4').length, 2); + } + } + } +}).export(module); \ No newline at end of file diff --git a/dist/node_modules/nconf/usage.js b/dist/node_modules/nconf/usage.js new file mode 100644 index 0000000..ebd5f28 --- /dev/null +++ b/dist/node_modules/nconf/usage.js @@ -0,0 +1,50 @@ +var fs = require('fs'), + path = require('path'), + nconf = require('./lib/nconf'); + +// +// Configure the provider with a single store and +// support for command-line arguments and environment +// variables. +// +var single = new nconf.Provider({ + env: true, + argv: true, + store: { + type: 'file', + file: path.join(__dirname, 'config.json') + } +}); + +// +// Configure the provider with multiple hierarchical stores +// representing `user` and `global` configuration values. +// +var multiple = new nconf.Provider({ + stores: [ + { name: 'user', type: 'file', file: path.join(__dirname, 'user-config.json') }, + { name: 'global', type: 'global', file: path.join(__dirname, 'global-config.json') } + ] +}); + +// +// Setup nconf to use the 'file' store and set a couple of values; +// +nconf.use('file', { file: path.join(__dirname, 'config.json') }); +nconf.set('database:host', '127.0.0.1'); +nconf.set('database:port', 5984); + +// +// Get the entire database object from nconf +// +var database = nconf.get('database'); +console.dir(database); + +// +// Save the configuration object to disk +// +nconf.save(function (err) { + fs.readFile(path.join(__dirname, 'config.json'), function (err, data) { + console.dir(JSON.parse(data.toString())) + }); +}); \ No newline at end of file diff --git a/dist/node_modules/passport/.npmignore b/dist/node_modules/passport/.npmignore new file mode 100644 index 0000000..d9ceb36 --- /dev/null +++ b/dist/node_modules/passport/.npmignore @@ -0,0 +1,8 @@ +*.md +.DS_Store +.git* +Makefile +docs/ +examples/ +support/ +test/ diff --git a/dist/node_modules/passport/.travis.yml b/dist/node_modules/passport/.travis.yml new file mode 100644 index 0000000..2644170 --- /dev/null +++ b/dist/node_modules/passport/.travis.yml @@ -0,0 +1,4 @@ +language: "node_js" +node_js: + - 0.4 + - 0.6 diff --git a/dist/node_modules/passport/LICENSE b/dist/node_modules/passport/LICENSE new file mode 100644 index 0000000..74524ce --- /dev/null +++ b/dist/node_modules/passport/LICENSE @@ -0,0 +1,20 @@ +(The MIT License) + +Copyright (c) 2011 Jared Hanson + +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/dist/node_modules/passport/lib/passport/context/http/actions.js b/dist/node_modules/passport/lib/passport/context/http/actions.js new file mode 100644 index 0000000..c13a57b --- /dev/null +++ b/dist/node_modules/passport/lib/passport/context/http/actions.js @@ -0,0 +1,80 @@ +/** + * Export actions prototype for strategies operating within an HTTP context. + */ +var actions = module.exports = {}; + + +/** + * Authenticate `user`, with optional `info`. + * + * Strategies should call this function to successfully authenticate a user. + * `user` should be an object supplied by the application after it has been + * given an opportunity to verify credentials. `info` is an optional argument + * containing additional user information. This is useful for third-party + * authentication strategies to pass profile details. + * + * @param {Object} user + * @param {Object} info + * @api public + */ +actions.success = function(user, info) { + this.delegate.success.apply(this, arguments); +} + +/** + * Fail authentication, with optional `challenge` and `status`, defaulting to + * 401. + * + * Strategies should call this function to fail an authentication attempt. + * + * @param {String} challenge + * @param {Number} status + * @api public + */ +actions.fail = function(challenge, status) { + this.delegate.fail.apply(this, arguments); +} + +/** + * Redirect to `url` with optional `status`, defaulting to 302. + * + * Strategies should call this function to redirect the user (via their user + * agent) to a third-party website for authentication. + * + * @param {String} url + * @param {Number} status + * @api public + */ +actions.redirect = function(url, status) { + this.res.statusCode = status || 302; + this.res.setHeader('Location', url); + this.res.end(); +} + +/** + * Pass without making a success or fail decision. + * + * Under most circumstances, Strategies should not need to call this function. + * It exists primarily to allow previous authentication state to be restored, + * for example from an HTTP session. + * + * @api public + */ +actions.pass = function() { + this.next(); +} + +/** + * Internal error while performing authentication. + * + * Strategies should call this function when an internal error occurs during the + * process of performing authentication; for example, if the user directory is + * not available. + * + * @param {Error} err + * @api public + */ +actions.error = function(err) { + this.next(err); +} + diff --git a/dist/node_modules/passport/lib/passport/context/http/context.js b/dist/node_modules/passport/lib/passport/context/http/context.js new file mode 100644 index 0000000..27eb9ee --- /dev/null +++ b/dist/node_modules/passport/lib/passport/context/http/context.js @@ -0,0 +1,17 @@ +/** + * `Context` constructor. + * + * @api private + */ +function Context(delegate, req, res, next) { + this.delegate = delegate; + this.req = req; + this.res = res; + this.next = next; +} + + +/** + * Expose `Context`. + */ +module.exports = Context; diff --git a/dist/node_modules/passport/lib/passport/http/request.js b/dist/node_modules/passport/lib/passport/http/request.js new file mode 100644 index 0000000..889fef4 --- /dev/null +++ b/dist/node_modules/passport/lib/passport/http/request.js @@ -0,0 +1,91 @@ +/** + * Module dependencies. + */ +var http = require('http') + , req = http.IncomingMessage.prototype; + + +/** + * Intiate a login session for `user`. + * + * Options: + * - `session` Save login state in session, defaults to _true_ + * + * Examples: + * + * req.logIn(user, { session: false }); + * + * req.logIn(user, function(err) { + * if (err) { throw err; } + * // session saved + * }); + * + * @param {User} user + * @param {Object} options + * @param {Function} done + * @api public + */ +req.login = +req.logIn = function(user, options, done) { + if (!this._passport) throw new Error('passport.initialize() middleware not in use'); + + if (!done && typeof options === 'function') { + done = options; + options = {}; + } + options = options || {}; + var property = this._passport.instance._userProperty || 'user'; + var session = (options.session === undefined) ? true : options.session; + + this[property] = user; + if (session) { + var self = this; + this._passport.instance.serializeUser(user, function(err, obj) { + if (err) { self[property] = null; return done(err); } + self._passport.session.user = obj; + done(); + }); + } else { + done && done(); + } +} + +/** + * Terminate an existing login session. + * + * @api public + */ +req.logout = +req.logOut = function() { + if (!this._passport) throw new Error('passport.initialize() middleware not in use'); + + var property = this._passport.instance._userProperty || 'user'; + + this[property] = null; + delete this._passport.session.user; +}; + +/** + * Test if request is authenticated. + * + * @return {Boolean} + * @api public + */ +req.isAuthenticated = function() { + var property = 'user'; + if (this._passport && this._passport.instance._userProperty) { + property = this._passport.instance._userProperty; + } + + return (this[property]) ? true : false; +}; + +/** + * Test if request is unauthenticated. + * + * @return {Boolean} + * @api public + */ +req.isUnauthenticated = function() { + return !this.isAuthenticated(); +}; diff --git a/dist/node_modules/passport/lib/passport/index.js b/dist/node_modules/passport/lib/passport/index.js new file mode 100644 index 0000000..ea7225e --- /dev/null +++ b/dist/node_modules/passport/lib/passport/index.js @@ -0,0 +1,412 @@ +/** + * Module dependencies. + */ +var fs = require('fs') + , path = require('path') + , util = require('util') + , Strategy = require('./strategy') + , SessionStrategy = require('./strategies/session') + , initialize = require('./middleware/initialize') + , authenticate = require('./middleware/authenticate'); + + +/** + * `Passport` constructor. + * + * @api public + */ +function Passport() { + this._key = 'passport'; + this._strategies = {}; + this._serializers = []; + this._deserializers = []; + this._infoTransformers = []; + + this._userProperty = 'user'; + + this.use(new SessionStrategy()); +}; + +/** + * Utilize the given `strategy` with optional `name`, overridding the strategy's + * default name. + * + * Examples: + * + * passport.use(new TwitterStrategy(...)); + * + * passport.use('api', new http.BasicStrategy(...)); + * + * @param {String|Strategy} name + * @param {Strategy} strategy + * @return {Passport} for chaining + * @api public + */ +Passport.prototype.use = function(name, strategy) { + if (!strategy) { + strategy = name; + name = strategy.name; + } + if (!name) throw new Error('authentication strategies must have a name'); + + this._strategies[name] = strategy; + return this; +}; + +/** + * Un-utilize the `strategy` with given `name`. + * + * In typical applications, the necessary authentication strategies are static, + * configured once and always available. As such, there is often no need to + * invoke this function. + * + * However, in certain situations, applications may need dynamically configure + * and de-configure authentication strategies. The `use()`/`unuse()` + * combination satisfies these scenarios. + * + * Examples: + * + * passport.unuse('legacy-api'); + * + * @param {String} name + * @return {Passport} for chaining + * @api public + */ +Passport.prototype.unuse = function(name) { + delete this._strategies[name]; + return this; +} + +/** + * Passport's primary initialization middleware. + * + * This middleware must be in use by the Connect/Express application for + * Passport to operate. + * + * Options: + * - `userProperty` Property to set on `req` upon login, defaults to _user_ + * + * Examples: + * + * app.configure(function() { + * app.use(passport.initialize()); + * }); + * + * app.configure(function() { + * app.use(passport.initialize({ userProperty: 'currentUser' })); + * }); + * + * @param {Object} options + * @return {Function} middleware + * @api public + */ +Passport.prototype.initialize = function(options) { + options = options || {}; + this._userProperty = options.userProperty || 'user'; + + return initialize().bind(this); +} + +/** + * Middleware that will restore login state from a session. + * + * Web applications typically use sessions to maintain login state between + * requests. For example, a user will authenticate by entering credentials into + * a form which is submitted to the server. If the credentials are valid, a + * login session is established by setting a cookie containing a session + * identifier in the user's web browser. The web browser will send this cookie + * in subsequent requests to the server, allowing a session to be maintained. + * + * If sessions are being utilized, and a login session has been established, + * this middleware will populate `req.user` with the current user. + * + * Note that sessions are not strictly required for Passport to operate. + * However, as a general rule, most web applications will make use of sessions. + * An exception to this rule would be an API server, which expects each HTTP + * request to provide credentials in an Authorization header. + * + * Examples: + * + * app.configure(function() { + * app.use(connect.cookieParser()); + * app.use(connect.session({ secret: 'keyboard cat' })); + * app.use(passport.initialize()); + * app.use(passport.session()); + * }); + * + * @return {Function} middleware + * @api public + */ +Passport.prototype.session = function() { + return this.authenticate('session'); +} + +/** + * Middleware that will authenticate a request using the given `strategy` name, + * with optional `options` and `callback`. + * + * Examples: + * + * passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })(req, res); + * + * passport.authenticate('local', function(err, user) { + * if (!user) { return res.redirect('/login'); } + * res.end('Authenticated!'); + * })(req, res); + * + * passport.authenticate('basic', { session: false })(req, res); + * + * app.get('/auth/twitter', passport.authenticate('twitter'), function(req, res) { + * // request will be redirected to Twitter + * }); + * app.get('/auth/twitter/callback', passport.authenticate('twitter'), function(req, res) { + * res.json(req.user); + * }); + * + * @param {String} strategy + * @param {Object} options + * @param {Function} callback + * @return {Function} middleware + * @api public + */ +Passport.prototype.authenticate = function(strategy, options, callback) { + return authenticate(strategy, options, callback).bind(this); +} + +/** + * Middleware that will authorize a third-party account using the given + * `strategy` name, with optional `options`. + * + * If authorization is successful, the result provided by the strategy's verify + * callback will be assigned to `req.account`. The existing login session and + * `req.user` will be unaffected. + * + * This function is particularly useful when connecting third-party accounts + * to the local account of a user that is currently authenticated. + * + * Examples: + * + * passport.authorize('twitter-authz', { failureRedirect: '/account' }); + * + * @param {String} strategy + * @param {Object} options + * @return {Function} middleware + * @api public + */ +Passport.prototype.authorize = function(strategy, options, callback) { + options = options || {}; + options.assignProperty = 'account'; + + return authenticate(strategy, options, callback).bind(this); +} + +/** + * Registers a function used to serialize user objects into the session. + * + * Examples: + * + * passport.serializeUser(function(user, done) { + * done(null, user.id); + * }); + * + * @api public + */ +Passport.prototype.serializeUser = function(fn, done) { + if (typeof fn === 'function') { + return this._serializers.push(fn); + } + + // private implementation that traverses the chain of serializers, attempting + // to serialize a user + var user = fn; + + var stack = this._serializers; + (function pass(i, err, obj) { + // serializers use 'pass' as an error to skip processing + if ('pass' === err) { + err = undefined; + } + // an error or serialized object was obtained, done + if (err || obj) { return done(err, obj); } + + var layer = stack[i]; + if (!layer) { + return done(new Error('failed to serialize user into session')); + } + + try { + layer(user, function(e, o) { pass(i + 1, e, o); } ) + } catch(e) { + return done(e); + } + })(0); +} + +/** + * Registers a function used to deserialize user objects out of the session. + * + * Examples: + * + * passport.deserializeUser(function(id, done) { + * User.findById(id, function (err, user) { + * done(err, user); + * }); + * }); + * + * @api public + */ +Passport.prototype.deserializeUser = function(fn, done) { + if (typeof fn === 'function') { + return this._deserializers.push(fn); + } + + // private implementation that traverses the chain of deserializers, + // attempting to deserialize a user + var obj = fn; + + var stack = this._deserializers; + (function pass(i, err, user) { + // deserializers use 'pass' as an error to skip processing + if ('pass' === err) { + err = undefined; + } + // an error or deserialized user was obtained, done + if (err || user) { return done(err, user); } + // a valid user existed when establishing the session, but that user has + // since been removed + if (user === null || user === false) { return done(null, false); } + + var layer = stack[i]; + if (!layer) { + return done(new Error('failed to deserialize user out of session')); + } + + try { + layer(obj, function(e, u) { pass(i + 1, e, u); } ) + } catch(e) { + return done(e); + } + })(0); +} + +/** + * Registers a function used to transform auth info. + * + * In some circumstances authorization details are contained in authentication + * credentials or loaded as part of verification. + * + * For example, when using bearer tokens for API authentication, the tokens may + * encode (either directly or indirectly in a database), details such as scope + * of access or the client to which the token was issued. + * + * Such authorization details should be enforced separately from authentication. + * Because Passport deals only with the latter, this is the responsiblity of + * middleware or routes further along the chain. However, it is not optimal to + * decode the same data or execute the same database query later. To avoid + * this, Passport accepts optional `info` along with the authenticated `user` + * in a strategy's `success()` action. This info is set at `req.authInfo`, + * where said later middlware or routes can access it. + * + * Optionally, applications can register transforms to proccess this info, + * which take effect prior to `req.authInfo` being set. This is useful, for + * example, when the info contains a client ID. The transform can load the + * client from the database and include the instance in the transformed info, + * allowing the full set of client properties to be convieniently accessed. + * + * If no transforms are registered, `info` supplied by the strategy will be left + * unmodified. + * + * Examples: + * + * passport.transformAuthInfo(function(info, done) { + * Client.findById(info.clientID, function (err, client) { + * info.client = client; + * done(err, info); + * }); + * }); + * + * @api public + */ +Passport.prototype.transformAuthInfo = function(fn, done) { + if (typeof fn === 'function') { + return this._infoTransformers.push(fn); + } + + // private implementation that traverses the chain of transformers, + // attempting to transform auth info + var info = fn; + + var stack = this._infoTransformers; + (function pass(i, err, tinfo) { + // transformers use 'pass' as an error to skip processing + if ('pass' === err) { + err = undefined; + } + // an error or transformed info was obtained, done + if (err || tinfo) { return done(err, tinfo); } + + var layer = stack[i]; + if (!layer) { + // if no transformers are registered (or they all pass), the default + // behavior is to use the un-transformed info as-is + return done(null, info); + } + + try { + var arity = layer.length; + if (arity == 1) { + // sync + var t = layer(info); + pass(i + 1, null, t); + } else { + // async + layer(info, function(e, t) { pass(i + 1, e, t); } ) + } + } catch(e) { + return done(e); + } + })(0); +} + +/** + * Return strategy with given `name`. + * + * @param {String} name + * @return {Strategy} + * @api private + */ +Passport.prototype._strategy = function(name) { + return this._strategies[name]; +} + + +/** + * Export default singleton. + * + * @api public + */ +exports = module.exports = new Passport(); + +/** + * Framework version. + */ +require('pkginfo')(module, 'version'); + +/** + * Expose constructors. + */ +exports.Passport = Passport; +exports.Strategy = Strategy; + + +/** + * Expose strategies. + */ +exports.strategies = {}; +exports.strategies.SessionStrategy = SessionStrategy; + + +/** + * HTTP extensions. + */ +require('./http/request'); diff --git a/dist/node_modules/passport/lib/passport/middleware/authenticate.js b/dist/node_modules/passport/lib/passport/middleware/authenticate.js new file mode 100644 index 0000000..a88275b --- /dev/null +++ b/dist/node_modules/passport/lib/passport/middleware/authenticate.js @@ -0,0 +1,233 @@ +/** + * Module dependencies. + */ +var util = require('util') + , actions = require('../context/http/actions') + , Context = require('../context/http/context') + + +/** + * Authenticates requests. + * + * Applies the `name`ed strategy (or strategies) to the incoming request, in + * order to authenticate the request. If authentication is successful, the user + * will be logged in and populated at `req.user` and a session will be + * established by default. If authentication fails, an unauthorized response + * will be sent. + * + * Options: + * - `session` Save login state in session, defaults to _true_ + * - `successRedirect` After successful login, redirect to given URL + * - `failureRedirect` After failed login, redirect to given URL + * - `assignProperty` Assign the object provided by the verify callback to given property + * + * An optional `callback` can be supplied to allow the application to overrride + * the default manner in which authentication attempts are handled. The + * callback has the following signature, where `user` will be set to the + * authenticated user on a successful authentication attempt, or `false` + * otherwise. An optional `info` argument will be passed, containing additional + * details provided by the strategy's verify callback. + * + * app.get('/protected', function(req, res, next) { + * passport.authenticate('local', function(err, user, info) { + * if (err) { return next(err) } + * if (!user) { return res.redirect('/signin') } + * res.redirect('/account'); + * })(req, res, next); + * }); + * + * Note that if a callback is supplied, it becomes the application's + * responsibility to log-in the user, establish a session, and otherwise perform + * the desired operations. + * + * Examples: + * + * passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }); + * + * passport.authenticate('basic', { session: false }); + * + * passport.authenticate('twitter'); + * + * @param {String} name + * @param {Object} options + * @param {Function} callback + * @return {Function} + * @api public + */ +module.exports = function authenticate(name, options, callback) { + if (!callback && typeof options === 'function') { + callback = options; + options = {}; + } + options = options || {}; + + // Cast `name` to an array, allowing authentication to pass through a chain of + // strategies. The first strategy to succeed, redirect, or error will halt + // the chain. Authentication failures will proceed through each strategy in + // series, ultimately failing if all strategies fail. + // + // This is typically used on API endpoints to allow clients to authenticate + // using their preferred choice of Basic, Digest, token-based schemes, etc. + // It is not feasible to construct a chain of multiple strategies that involve + // redirection (for example both Facebook and Twitter), since the first one to + // redirect will halt the chain. + if (!Array.isArray(name)) { + name = [ name ]; + } + + return function authenticate(req, res, next) { + var passport = this; + + // accumulator for failures from each strategy in the chain + var failures = []; + + function allFailed() { + if (callback) { + if (failures.length == 1) { + return callback(null, false, failures[0].challenge, failures[0].status); + } else { + var challenges = failures.map(function(f) { return f.challenge; }); + var statuses = failures.map(function(f) { return f.status; }) + return callback(null, false, challenges, statuses); + } + } + + // Strategies are ordered by priority. For the purpose of flashing a + // message, the first failure will be displayed. + var failure = failures[0] || {} + , challenge = failure.challenge || {}; + + if (options.failureFlash) { + var flash = options.failureFlash; + if (typeof flash == 'string') { + flash = { type: 'error', message: flash }; + } + flash.type = flash.type || 'error'; + + var type = flash.type || challenge.type || 'error'; + var msg = flash.message || challenge.message || challenge; + if (typeof msg == 'string') { + req.flash(type, msg); + } + } + if (options.failureRedirect) { + return res.redirect(options.failureRedirect); + } + + // When failure handling is not delegated to the application, the default + // is to respond with 401 Unauthorized. Note that the WWW-Authenticate + // header will be set according to the strategies in use (see + // actions#fail). If multiple strategies failed, each of their challenges + // will be included in the response. + var rchallenge = [] + , rstatus; + + for (var j = 0, len = failures.length; j < len; j++) { + var failure = failures[j] + , challenge = failure.challenge || {} + , status = failure.status; + if (typeof challenge == 'number') { + status = challenge; + challenge = null; + } + + rstatus = rstatus || status; + if (typeof challenge == 'string') { + rchallenge.push(challenge) + } + } + + res.statusCode = rstatus || 401; + if (rchallenge.length) { + res.setHeader('WWW-Authenticate', rchallenge); + } + res.end('Unauthorized'); + } + + (function attempt(i) { + var delegate = {}; + delegate.success = function(user, info) { + if (callback) { + return callback(null, user, info); + } + + info = info || {} + + if (options.successFlash) { + var flash = options.successFlash; + if (typeof flash == 'string') { + flash = { type: 'success', message: flash }; + } + flash.type = flash.type || 'success'; + + var type = flash.type || info.type || 'success'; + var msg = flash.message || info.message || info; + if (typeof msg == 'string') { + req.flash(type, msg); + } + } + if (options.assignProperty) { + req[options.assignProperty] = user; + return next(); + } + + req.logIn(user, options, function(err) { + if (err) { return next(err); } + if (options.authInfo || options.authInfo === undefined) { + passport.transformAuthInfo(info, function(err, tinfo) { + if (err) { return next(err); } + req.authInfo = tinfo; + complete(); + }); + } else { + complete(); + } + + function complete() { + if (options.successReturnToOrRedirect) { + var url = options.successReturnToOrRedirect; + if (req.session && req.session.returnTo) { + url = req.session.returnTo; + delete req.session.returnTo; + } + return res.redirect(url); + } + if (options.successRedirect) { + return res.redirect(options.successRedirect); + } + next(); + } + }); + } + delegate.fail = function(challenge, status) { + // push this failure into the accumulator and attempt authentication + // using the next strategy + failures.push({ challenge: challenge, status: status }); + attempt(i + 1); + } + + var layer = name[i]; + // If no more strategies exist in the chain, authentication has failed. + if (!layer) { return allFailed(); } + + // Get the strategy, which will be used as prototype from which to create + // a new instance. Action functions will then be bound to the strategy + // within the context of the HTTP request/response pair. + var prototype = passport._strategy(layer); + if (!prototype) { return next(new Error('no strategy registered under name: ' + layer)); } + + var strategy = Object.create(prototype); + var context = new Context(delegate, req, res, next); + augment(strategy, actions, context); + + strategy.authenticate(req, options); + })(0); // attempt + } +} + + +function augment(strategy, actions, ctx) { + for (var method in actions) { + strategy[method] = actions[method].bind(ctx); + } +} diff --git a/dist/node_modules/passport/lib/passport/middleware/initialize.js b/dist/node_modules/passport/lib/passport/middleware/initialize.js new file mode 100644 index 0000000..c44e391 --- /dev/null +++ b/dist/node_modules/passport/lib/passport/middleware/initialize.js @@ -0,0 +1,71 @@ +/** + * Module dependencies. + */ +var util = require('util'); + + +/** + * Passport initialization. + * + * Intializes Passport for incoming requests, allowing authentication strategies + * to be applied. + * + * If sessions are being utilized, applications must set up Passport with + * functions to serialize a user into and out of a session. For example, a + * common pattern is to serialize just the user ID into the session (due to the + * fact that it is desirable to store the minimum amount of data in a session). + * When a subsequent request arrives for the session, the full User object can + * be loaded from the database by ID. + * + * Note that additional middleware is required to persist login state, so we + * must use the `connect.session()` middleware _before_ `passport.initialize()`. + * + * This middleware must be in use by the Connect/Express application for + * Passport to operate. + * + * Examples: + * + * app.configure(function() { + * app.use(connect.cookieParser()); + * app.use(connect.session({ secret: 'keyboard cat' })); + * app.use(passport.initialize()); + * app.use(passport.session()); + * }); + * + * passport.serializeUser(function(user, done) { + * done(null, user.id); + * }); + * + * passport.deserializeUser(function(id, done) { + * User.findById(id, function (err, user) { + * done(err, user); + * }); + * }); + * + * @return {Function} + * @api public + */ +module.exports = function initialize() { + + return function initialize(req, res, next) { + var passport = this; + req._passport = {}; + req._passport.instance = passport; + + //console.log('!! session: ' + util.inspect(req.session)); + + if (req.session && req.session[passport._key]) { + // load data from existing session + req._passport.session = req.session[passport._key]; + } else if (req.session) { + // initialize new session + req.session[passport._key] = {}; + req._passport.session = req.session[passport._key]; + } else { + // no session is available + req._passport.session = {}; + } + + next(); + } +} diff --git a/dist/node_modules/passport/lib/passport/strategies/session.js b/dist/node_modules/passport/lib/passport/strategies/session.js new file mode 100644 index 0000000..9806498 --- /dev/null +++ b/dist/node_modules/passport/lib/passport/strategies/session.js @@ -0,0 +1,59 @@ +/** + * Module dependencies. + */ +var util = require('util') + , Strategy = require('../strategy'); + + +/** + * `SessionStrategy` constructor. + * + * @api protected + */ +function SessionStrategy() { + Strategy.call(this); + this.name = 'session'; +} + +/** + * Inherit from `Strategy`. + */ +util.inherits(SessionStrategy, Strategy); + +/** + * Authenticate request based on the current session state. + * + * The session authentication strategy uses the session to restore any login + * state across requests. If a login session has been established, `req.user` + * will be populated with the current user. + * + * This strategy is registered automatically by Passport. + * + * @param {Object} req + * @api protected + */ +SessionStrategy.prototype.authenticate = function(req) { + if (!req._passport) { return this.error(new Error('passport.initialize() middleware not in use')); } + + var self = this; + if (req._passport.session.user) { + req._passport.instance.deserializeUser(req._passport.session.user, function(err, user) { + if (err) { return self.error(err); } + if (!user) { + delete req._passport.session.user; + return self.pass(); + }; + var property = req._passport.instance._userProperty || 'user'; + req[property] = user; + self.pass(); + }); + } else { + self.pass(); + } +} + + +/** + * Expose `SessionStrategy`. + */ +module.exports = SessionStrategy; diff --git a/dist/node_modules/passport/lib/passport/strategy.js b/dist/node_modules/passport/lib/passport/strategy.js new file mode 100644 index 0000000..7a93852 --- /dev/null +++ b/dist/node_modules/passport/lib/passport/strategy.js @@ -0,0 +1,33 @@ +/** + * Module dependencies. + */ +var util = require('util'); + + +/** + * `Strategy` constructor. + * + * @api public + */ +function Strategy() { +} + +/** + * Authenticate request. + * + * This function must be overridden by subclasses. In abstract form, it always + * throws an exception. + * + * @param {Object} req + * @param {Object} options + * @api protected + */ +Strategy.prototype.authenticate = function(req, options) { + throw new Error('Strategy#authenticate must be overridden by subclass'); +} + + +/** + * Expose `Strategy`. + */ +module.exports = Strategy; diff --git a/dist/node_modules/passport/node_modules/pkginfo/.npmignore b/dist/node_modules/passport/node_modules/pkginfo/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/README.md b/dist/node_modules/passport/node_modules/pkginfo/README.md new file mode 100644 index 0000000..07ba942 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/README.md @@ -0,0 +1,85 @@ +# node-pkginfo + +An easy way to expose properties on a module from a package.json + +## Installation + +### Installing npm (node package manager) +``` + curl http://npmjs.org/install.sh | sh +``` + +### Installing pkginfo +``` + [sudo] npm install pkginfo +``` + +## Motivation +How often when writing node.js modules have you written the following line(s) of code? + +* Hard code your version string into your code + +``` js + exports.version = '0.1.0'; +``` + +* Programmatically expose the version from the package.json + +``` js + exports.version = JSON.parse(fs.readFileSync('/path/to/package.json', 'utf8')).version; +``` + +In other words, how often have you wanted to expose basic information from your package.json onto your module programmatically? **WELL NOW YOU CAN!** + +## Usage + +Using `pkginfo` is idiot-proof, just require and invoke it. + +``` js + var pkginfo = require('pkginfo')(module); + + console.dir(module.exports); +``` + +By invoking the `pkginfo` module all of the properties in your `package.json` file will be automatically exposed on the callee module (i.e. the parent module of `pkginfo`). + +Here's a sample of the output: + +``` + { name: 'simple-app', + description: 'A test fixture for pkginfo', + version: '0.1.0', + author: 'Charlie Robbins ', + keywords: [ 'test', 'fixture' ], + main: './index.js', + scripts: { test: 'vows test/*-test.js --spec' }, + engines: { node: '>= 0.4.0' } } +``` + +### Expose specific properties +If you don't want to expose **all** properties on from your `package.json` on your module then simple pass those properties to the `pkginfo` function: + +``` js + var pkginfo = require('pkginfo')(module, 'version', 'author'); + + console.dir(module.exports); +``` + +``` + { version: '0.1.0', + author: 'Charlie Robbins ' } +``` + +If you're looking for further usage see the [examples][0] included in this repository. + +## Run Tests +Tests are written in [vows][1] and give complete coverage of all APIs. + +``` + vows test/*-test.js --spec +``` + +[0]: https://github.com/indexzero/node-pkginfo/tree/master/examples +[1]: http://vowsjs.org + +#### Author: [Charlie Robbins](http://nodejitsu.com) \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/docs/docco.css b/dist/node_modules/passport/node_modules/pkginfo/docs/docco.css new file mode 100644 index 0000000..bd54134 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/docs/docco.css @@ -0,0 +1,194 @@ +/*--------------------- Layout and Typography ----------------------------*/ +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + color: #252519; + margin: 0; padding: 0; +} +a { + color: #261a3b; +} + a:visited { + color: #261a3b; + } +p { + margin: 0 0 15px 0; +} +h4, h5, h6 { + color: #333; + margin: 6px 0 6px 0; + font-size: 13px; +} + h2, h3 { + margin-bottom: 0; + color: #000; + } + h1 { + margin-top: 40px; + margin-bottom: 15px; + color: #000; + } +#container { + position: relative; +} +#background { + position: fixed; + top: 0; left: 525px; right: 0; bottom: 0; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + z-index: -1; +} +#jump_to, #jump_page { + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 10px Arial; + text-transform: uppercase; + cursor: pointer; + text-align: right; +} +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 5px 10px; +} + #jump_wrapper { + padding: 0; + display: none; + } + #jump_to:hover #jump_wrapper { + display: block; + } + #jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; + } + #jump_page .source { + display: block; + padding: 5px 10px; + text-decoration: none; + border-top: 1px solid #eee; + } + #jump_page .source:hover { + background: #f5f5ff; + } + #jump_page .source:first-child { + } +table td { + border: 0; + outline: 0; +} + td.docs, th.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; + } + .docs pre { + margin: 15px 0 15px; + padding-left: 15px; + } + .docs p tt, .docs p code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; + } + .pilwrap { + position: relative; + } + .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + } + td.docs:hover .pilcrow { + opacity: 1; + } + td.code, th.code { + padding: 14px 15px 16px 25px; + width: 100%; + vertical-align: top; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + } + pre, tt, code { + font-size: 12px; line-height: 18px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; + } + + +/*---------------------- Syntax Highlighting -----------------------------*/ +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +body .hll { background-color: #ffffcc } +body .c { color: #408080; font-style: italic } /* Comment */ +body .err { border: 1px solid #FF0000 } /* Error */ +body .k { color: #954121 } /* Keyword */ +body .o { color: #666666 } /* Operator */ +body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +body .cp { color: #BC7A00 } /* Comment.Preproc */ +body .c1 { color: #408080; font-style: italic } /* Comment.Single */ +body .cs { color: #408080; font-style: italic } /* Comment.Special */ +body .gd { color: #A00000 } /* Generic.Deleted */ +body .ge { font-style: italic } /* Generic.Emph */ +body .gr { color: #FF0000 } /* Generic.Error */ +body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +body .gi { color: #00A000 } /* Generic.Inserted */ +body .go { color: #808080 } /* Generic.Output */ +body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +body .gs { font-weight: bold } /* Generic.Strong */ +body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +body .gt { color: #0040D0 } /* Generic.Traceback */ +body .kc { color: #954121 } /* Keyword.Constant */ +body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ +body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ +body .kp { color: #954121 } /* Keyword.Pseudo */ +body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ +body .kt { color: #B00040 } /* Keyword.Type */ +body .m { color: #666666 } /* Literal.Number */ +body .s { color: #219161 } /* Literal.String */ +body .na { color: #7D9029 } /* Name.Attribute */ +body .nb { color: #954121 } /* Name.Builtin */ +body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +body .no { color: #880000 } /* Name.Constant */ +body .nd { color: #AA22FF } /* Name.Decorator */ +body .ni { color: #999999; font-weight: bold } /* Name.Entity */ +body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +body .nf { color: #0000FF } /* Name.Function */ +body .nl { color: #A0A000 } /* Name.Label */ +body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +body .nt { color: #954121; font-weight: bold } /* Name.Tag */ +body .nv { color: #19469D } /* Name.Variable */ +body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +body .w { color: #bbbbbb } /* Text.Whitespace */ +body .mf { color: #666666 } /* Literal.Number.Float */ +body .mh { color: #666666 } /* Literal.Number.Hex */ +body .mi { color: #666666 } /* Literal.Number.Integer */ +body .mo { color: #666666 } /* Literal.Number.Oct */ +body .sb { color: #219161 } /* Literal.String.Backtick */ +body .sc { color: #219161 } /* Literal.String.Char */ +body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ +body .s2 { color: #219161 } /* Literal.String.Double */ +body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +body .sh { color: #219161 } /* Literal.String.Heredoc */ +body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +body .sx { color: #954121 } /* Literal.String.Other */ +body .sr { color: #BB6688 } /* Literal.String.Regex */ +body .s1 { color: #219161 } /* Literal.String.Single */ +body .ss { color: #19469D } /* Literal.String.Symbol */ +body .bp { color: #954121 } /* Name.Builtin.Pseudo */ +body .vc { color: #19469D } /* Name.Variable.Class */ +body .vg { color: #19469D } /* Name.Variable.Global */ +body .vi { color: #19469D } /* Name.Variable.Instance */ +body .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/docs/pkginfo.html b/dist/node_modules/passport/node_modules/pkginfo/docs/pkginfo.html new file mode 100644 index 0000000..bf615fa --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/docs/pkginfo.html @@ -0,0 +1,101 @@ + pkginfo.js

      pkginfo.js

      /*
      + * pkginfo.js: Top-level include for the pkginfo module
      + *
      + * (C) 2011, Charlie Robbins
      + *
      + */
      + 
      +var fs = require('fs'),
      +    path = require('path');

      function pkginfo ([options, 'property', 'property' ..])

      + +

      @pmodule {Module} Parent module to read from.

      + +

      @options {Object|Array|string} Optional Options used when exposing properties.

      + +

      @arguments {string...} Optional Specified properties to expose.

      + +

      Exposes properties from the package.json file for the parent module on +it's exports. Valid usage:

      + +

      require('pkginfo')()

      + +

      require('pkginfo')('version', 'author');

      + +

      require('pkginfo')(['version', 'author']);

      + +

      require('pkginfo')({ include: ['version', 'author'] });

      var pkginfo = module.exports = function (pmodule, options) {
      +  var args = [].slice.call(arguments, 2).filter(function (arg) {
      +    return typeof arg === 'string';
      +  });
      +  

      Parse variable arguments

        if (Array.isArray(options)) {

      If the options passed in is an Array assume that +it is the Array of properties to expose from the +on the package.json file on the parent module.

          options = { include: options };
      +  }
      +  else if (typeof options === 'string') {

      Otherwise if the first argument is a string, then +assume that it is the first property to expose from +the package.json file on the parent module.

          options = { include: [options] };
      +  }
      +  

      Setup default options

        options = options || { include: [] };
      +  
      +  if (args.length > 0) {

      If additional string arguments have been passed in +then add them to the properties to expose on the +parent module.

          options.include = options.include.concat(args);
      +  }
      +  
      +  var pkg = pkginfo.read(pmodule, options.dir).package;
      +  Object.keys(pkg).forEach(function (key) {
      +    if (options.include.length > 0 && !~options.include.indexOf(key)) {
      +      return;
      +    }
      +    
      +    if (!pmodule.exports[key]) {
      +      pmodule.exports[key] = pkg[key];
      +    }
      +  });
      +  
      +  return pkginfo;
      +};

      function find (dir)

      + +

      @pmodule {Module} Parent module to read from.

      + +

      @dir {string} Optional Directory to start search from.

      + +

      Searches up the directory tree from dir until it finds a directory +which contains a package.json file.

      pkginfo.find = function (pmodule, dir) {
      +  dir = dir || pmodule.filename;
      +  dir = path.dirname(dir); 
      +  
      +  var files = fs.readdirSync(dir);
      +  
      +  if (~files.indexOf('package.json')) {
      +    return path.join(dir, 'package.json');
      +  }
      +  
      +  if (dir === '/') {
      +    throw new Error('Could not find package.json up from: ' + dir);
      +  }
      +  
      +  return pkginfo.find(dir);
      +};

      function read (pmodule, dir)

      + +

      @pmodule {Module} Parent module to read from.

      + +

      @dir {string} Optional Directory to start search from.

      + +

      Searches up the directory tree from dir until it finds a directory +which contains a package.json file and returns the package information.

      pkginfo.read = function (pmodule, dir) { 
      +  dir = pkginfo.find(pmodule, dir);
      +  
      +  var data = fs.readFileSync(dir).toString();
      +      
      +  return {
      +    dir: dir, 
      +    package: JSON.parse(data)
      +  };
      +};

      Call pkginfo on this module and expose version.

      pkginfo(module, {
      +  dir: __dirname,
      +  include: ['version'],
      +  target: pkginfo
      +});
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/examples/all-properties.js b/dist/node_modules/passport/node_modules/pkginfo/examples/all-properties.js new file mode 100644 index 0000000..fd1d831 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/examples/all-properties.js @@ -0,0 +1,19 @@ +/* + * all-properties.js: Sample of including all properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/examples/array-argument.js b/dist/node_modules/passport/node_modules/pkginfo/examples/array-argument.js new file mode 100644 index 0000000..b1b6848 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/examples/array-argument.js @@ -0,0 +1,20 @@ +/* + * array-argument.js: Sample of including specific properties from a package.json file + * using Array argument syntax. + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, ['version', 'author']); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/examples/multiple-properties.js b/dist/node_modules/passport/node_modules/pkginfo/examples/multiple-properties.js new file mode 100644 index 0000000..b4b5fd6 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/examples/multiple-properties.js @@ -0,0 +1,19 @@ +/* + * multiple-properties.js: Sample of including multiple properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, 'version', 'author'); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/examples/object-argument.js b/dist/node_modules/passport/node_modules/pkginfo/examples/object-argument.js new file mode 100644 index 0000000..28420c8 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/examples/object-argument.js @@ -0,0 +1,22 @@ +/* + * object-argument.js: Sample of including specific properties from a package.json file + * using Object argument syntax. + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, { + include: ['version', 'author'] + }); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/examples/package.json b/dist/node_modules/passport/node_modules/pkginfo/examples/package.json new file mode 100644 index 0000000..1f2f01c --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/examples/package.json @@ -0,0 +1,10 @@ +{ + "name": "simple-app", + "description": "A test fixture for pkginfo", + "version": "0.1.0", + "author": "Charlie Robbins ", + "keywords": ["test", "fixture"], + "main": "./index.js", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.4.0" } +} diff --git a/dist/node_modules/passport/node_modules/pkginfo/examples/single-property.js b/dist/node_modules/passport/node_modules/pkginfo/examples/single-property.js new file mode 100644 index 0000000..4f44561 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/examples/single-property.js @@ -0,0 +1,19 @@ +/* + * single-property.js: Sample of including a single specific properties from a package.json file + * + * (C) 2011, Charlie Robbins + * + */ + +var util = require('util'), + pkginfo = require('../lib/pkginfo')(module, 'version'); + +exports.someFunction = function () { + console.log('some of your custom logic here'); +}; + +console.log('Inspecting module:'); +console.dir(module.exports); + +console.log('\nAll exports exposed:'); +console.error(Object.keys(module.exports)); \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/lib/pkginfo.js b/dist/node_modules/passport/node_modules/pkginfo/lib/pkginfo.js new file mode 100644 index 0000000..a4a6227 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/lib/pkginfo.js @@ -0,0 +1,132 @@ +/* + * pkginfo.js: Top-level include for the pkginfo module + * + * (C) 2011, Charlie Robbins + * + */ + +var fs = require('fs'), + path = require('path'); + +// +// ### function pkginfo ([options, 'property', 'property' ..]) +// #### @pmodule {Module} Parent module to read from. +// #### @options {Object|Array|string} **Optional** Options used when exposing properties. +// #### @arguments {string...} **Optional** Specified properties to expose. +// Exposes properties from the package.json file for the parent module on +// it's exports. Valid usage: +// +// `require('pkginfo')()` +// +// `require('pkginfo')('version', 'author');` +// +// `require('pkginfo')(['version', 'author']);` +// +// `require('pkginfo')({ include: ['version', 'author'] });` +// +var pkginfo = module.exports = function (pmodule, options) { + var args = [].slice.call(arguments, 2).filter(function (arg) { + return typeof arg === 'string'; + }); + + // + // **Parse variable arguments** + // + if (Array.isArray(options)) { + // + // If the options passed in is an Array assume that + // it is the Array of properties to expose from the + // on the package.json file on the parent module. + // + options = { include: options }; + } + else if (typeof options === 'string') { + // + // Otherwise if the first argument is a string, then + // assume that it is the first property to expose from + // the package.json file on the parent module. + // + options = { include: [options] }; + } + + // + // **Setup default options** + // + options = options || { include: [] }; + + if (args.length > 0) { + // + // If additional string arguments have been passed in + // then add them to the properties to expose on the + // parent module. + // + options.include = options.include.concat(args); + } + + var pkg = pkginfo.read(pmodule, options.dir).package; + Object.keys(pkg).forEach(function (key) { + if (options.include.length > 0 && !~options.include.indexOf(key)) { + return; + } + + if (!pmodule.exports[key]) { + pmodule.exports[key] = pkg[key]; + } + }); + + return pkginfo; +}; + +// +// ### function find (dir) +// #### @pmodule {Module} Parent module to read from. +// #### @dir {string} **Optional** Directory to start search from. +// Searches up the directory tree from `dir` until it finds a directory +// which contains a `package.json` file. +// +pkginfo.find = function (pmodule, dir) { + dir = dir || pmodule.filename; + dir = path.dirname(dir); + + var files = fs.readdirSync(dir); + + if (~files.indexOf('package.json')) { + return path.join(dir, 'package.json'); + } + + if (dir === '/') { + throw new Error('Could not find package.json up from: ' + dir); + } + else if (!dir || dir === '.') { + throw new Error('Cannot find package.json from unspecified directory'); + } + + return pkginfo.find(pmodule, dir); +}; + +// +// ### function read (pmodule, dir) +// #### @pmodule {Module} Parent module to read from. +// #### @dir {string} **Optional** Directory to start search from. +// Searches up the directory tree from `dir` until it finds a directory +// which contains a `package.json` file and returns the package information. +// +pkginfo.read = function (pmodule, dir) { + dir = pkginfo.find(pmodule, dir); + + var data = fs.readFileSync(dir).toString(); + + return { + dir: dir, + package: JSON.parse(data) + }; +}; + +// +// Call `pkginfo` on this module and expose version. +// +pkginfo(module, { + dir: __dirname, + include: ['version'], + target: pkginfo +}); \ No newline at end of file diff --git a/dist/node_modules/passport/node_modules/pkginfo/package.json b/dist/node_modules/passport/node_modules/pkginfo/package.json new file mode 100644 index 0000000..0e9e4e7 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/package.json @@ -0,0 +1,17 @@ +{ + "name": "pkginfo", + "version": "0.2.3", + "description": "An easy way to expose properties on a module from a package.json", + "author": "Charlie Robbins ", + "repository": { + "type": "git", + "url": "http://github.com/indexzero/node-pkginfo.git" + }, + "keywords": ["info", "tools", "package.json"], + "devDependencies": { + "vows": "0.6.x" + }, + "main": "./lib/pkginfo", + "scripts": { "test": "vows test/*-test.js --spec" }, + "engines": { "node": ">= 0.4.0" } +} diff --git a/dist/node_modules/passport/node_modules/pkginfo/test/pkginfo-test.js b/dist/node_modules/passport/node_modules/pkginfo/test/pkginfo-test.js new file mode 100644 index 0000000..3156c00 --- /dev/null +++ b/dist/node_modules/passport/node_modules/pkginfo/test/pkginfo-test.js @@ -0,0 +1,69 @@ +/* + * pkginfo-test.js: Tests for the pkginfo module. + * + * (C) 2011, Charlie Robbins + * + */ + +var assert = require('assert'), + exec = require('child_process').exec, + fs = require('fs'), + path = require('path'), + vows = require('vows'), + pkginfo = require('../lib/pkginfo'); + +function assertProperties (source, target) { + assert.lengthOf(source, target.length + 1); + target.forEach(function (prop) { + assert.isTrue(!!~source.indexOf(prop)); + }); +} + +function testExposes (options) { + return { + topic: function () { + exec('node ' + path.join(__dirname, '..', 'examples', options.script), this.callback); + }, + "should expose that property correctly": function (err, stdout, stderr) { + assert.isNull(err); + + var exposed = stderr.match(/'(\w+)'/ig).map(function (p) { + return p.substring(1, p.length - 1); + }); + + return !options.assert + ? assertProperties(exposed, options.properties) + : options.assert(exposed); + } + } +} + +vows.describe('pkginfo').addBatch({ + "When using the pkginfo module": { + "and passed a single `string` argument": testExposes({ + script: 'single-property.js', + properties: ['version'] + }), + "and passed multiple `string` arguments": testExposes({ + script: 'multiple-properties.js', + properties: ['version', 'author'] + }), + "and passed an `object` argument": testExposes({ + script: 'object-argument.js', + properties: ['version', 'author'] + }), + "and passed an `array` argument": testExposes({ + script: 'array-argument.js', + properties: ['version', 'author'] + }), + "and passed no arguments": testExposes({ + script: 'all-properties.js', + assert: function (exposed) { + var pkg = fs.readFileSync(path.join(__dirname, '..', 'examples', 'package.json')).toString(), + keys = Object.keys(JSON.parse(pkg)); + + assertProperties(exposed, keys); + } + }) + } +}).export(module); diff --git a/dist/node_modules/passport/package.json b/dist/node_modules/passport/package.json new file mode 100644 index 0000000..e95507d --- /dev/null +++ b/dist/node_modules/passport/package.json @@ -0,0 +1,30 @@ +{ + "name": "passport", + "version": "0.1.12", + "description": "Simple, unobtrusive authentication for Node.js.", + "author": { "name": "Jared Hanson", "email": "jaredhanson@gmail.com", "url": "http://www.jaredhanson.net/" }, + "homepage": "http://passportjs.org/", + "repository": { + "type": "git", + "url": "git://github.com/jaredhanson/passport.git" + }, + "bugs": { + "url": "http://github.com/jaredhanson/passport/issues" + }, + "main": "./lib/passport", + "dependencies": { + "pkginfo": "0.2.x" + }, + "devDependencies": { + "vows": "0.6.x" + }, + "scripts": { + "test": "NODE_PATH=lib node_modules/.bin/vows test/*-test.js test/**/*-test.js test/context/http/*-test.js" + }, + "engines": { "node": ">= 0.4.0" }, + "licenses": [ { + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + } ], + "keywords": ["express", "connect", "auth", "authn", "authentication"] +} diff --git a/dist/node_modules/socket.io/.npmignore b/dist/node_modules/socket.io/.npmignore new file mode 100644 index 0000000..39e9864 --- /dev/null +++ b/dist/node_modules/socket.io/.npmignore @@ -0,0 +1,3 @@ +support +test +examples diff --git a/dist/node_modules/socket.io/.travis.yml b/dist/node_modules/socket.io/.travis.yml new file mode 100644 index 0000000..56eca03 --- /dev/null +++ b/dist/node_modules/socket.io/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - 0.6 + +notifications: + irc: "irc.freenode.org#socket.io" diff --git a/dist/node_modules/socket.io/History.md b/dist/node_modules/socket.io/History.md new file mode 100644 index 0000000..3663ea1 --- /dev/null +++ b/dist/node_modules/socket.io/History.md @@ -0,0 +1,300 @@ + +0.9.11 / 2012-11-02 +=================== + + * package: move redis to optionalDependenices [3rd-Eden] + * bumped client + +0.9.10 / 2012-08-10 +=================== + + * Don't lowercase log messages + * Always set the HTTP response in case an error should be returned to the client + * Create or destroy the flash policy server on configuration change + * Honour configuration to disable flash policy server + * Add express 3.0 instructions on Readme.md + * Bump client + +0.9.9 / 2012-08-01 +================== + + * Fixed sync disconnect xhrs handling + * Put license text in its own file (#965) + * Add warning to .listen() to ease the migration to Express 3.x + * Restored compatibility with node 0.4.x + +0.9.8 / 2012-07-24 +================== + + * Bumped client. + +0.9.7 / 2012-07-24 +================== + + * Prevent crash when socket leaves a room twice. + * Corrects unsafe usage of for..in + * Fix for node 0.8 with `gzip compression` [vadimi] + * Update redis to support Node 0.8.x + * Made ID generation securely random + * Fix Redis Store race condition in manager onOpen unsubscribe callback + * Fix for EventEmitters always reusing the same Array instance for listeners + +0.9.6 / 2012-04-17 +================== + + * Fixed XSS in jsonp-polling. + +0.9.5 / 2012-04-05 +================== + + * Added test for polling and socket close. + * Ensure close upon request close. + * Fix disconnection reason being lost for polling transports. + * Ensure that polling transports work with Connection: close. + * Log disconnection reason. + +0.9.4 / 2012-04-01 +================== + + * Disconnecting from namespace improvement (#795) [DanielBaulig] + * Bumped client with polling reconnection loop (#438) + +0.9.3 / 2012-03-28 +================== + + * Fix "Syntax error" on FF Web Console with XHR Polling [mikito] + +0.9.2 / 2012-03-13 +================== + + * More sensible close `timeout default` (fixes disconnect issue) + +0.9.1-1 / 2012-03-02 +==================== + + * Bumped client with NPM dependency fix. + +0.9.1 / 2012-03-02 +================== + + * Changed heartbeat timeout and interval defaults (60 and 25 seconds) + * Make tests work both on 0.4 and 0.6 + * Updated client (improvements + bug fixes). + +0.9.0 / 2012-02-26 +================== + + * Make it possible to use a regexp to match the socket.io resource URL. + We need this because we have to prefix the socket.io URL with a variable ID. + * Supplemental fix to gavinuhma/authfix, it looks like the same Access-Control-Origin logic is needed in the http and xhr-polling transports + * Updated express dep for windows compatibility. + * Combine two substr calls into one in decodePayload to improve performance + * Minor documentation fix + * Minor. Conform to style of other files. + * Switching setting to 'match origin protocol' + * Revert "Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect()." + * Revert "Handle leaked dispatch:[id] subscription." + * Merge pull request #667 from dshaw/patch/redis-disconnect + * Handle leaked dispatch:[id] subscription. + * Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect(). + * Prevent memory leaking on uncompleted requests & add max post size limitation + * Fix for testcase + * Set Access-Control-Allow-Credentials true, regardless of cookie + * Remove assertvarnish from package as it breaks on 0.6 + * Correct irc channel + * Added proper return after reserved field error + * Fixes manager.js failure to close connection after transport error has happened + * Added implicit port 80 for origin checks. fixes #638 + * Fixed bug #432 in 0.8.7 + * Set Access-Control-Allow-Origin header to origin to enable withCredentials + * Adding configuration variable matchOriginProtocol + * Fixes location mismatch error in Safari. + * Use tty to detect if we should add colors or not by default. + * Updated the package location. + +0.8.7 / 2011-11-05 +================== + + * Fixed memory leaks in closed clients. + * Fixed memory leaks in namespaces. + * Fixed websocket handling for malformed requests from proxies. [einaros] + * Node 0.6 compatibility. [einaros] [3rd-Eden] + * Adapted tests and examples. + +0.8.6 / 2011-10-27 +================== + + * Added JSON decoding on jsonp-polling transport. + * Fixed README example. + * Major speed optimizations [3rd-Eden] [einaros] [visionmedia] + * Added decode/encode benchmarks [visionmedia] + * Added support for black-listing client sent events. + * Fixed logging options, closes #540 [3rd-Eden] + * Added vary header for gzip [3rd-Eden] + * Properly cleaned up async websocket / flashsocket tests, after patching node-websocket-client + * Patched to properly shut down when a finishClose call is made during connection establishment + * Added support for socket.io version on url and far-future Expires [3rd-Eden] [getify] + * Began IE10 compatibility [einaros] [tbranyen] + * Misc WebSocket fixes [einaros] + * Added UTF8 to respone headers for htmlfile [3rd-Eden] + +0.8.5 / 2011-10-07 +================== + + * Added websocket draft HyBi-16 support. [einaros] + * Fixed websocket continuation bugs. [einaros] + * Fixed flashsocket transport name. + * Fixed websocket tests. + * Ensured `parser#decodePayload` doesn't choke. + * Added http referrer verification to manager verifyOrigin. + * Added access control for cross domain xhr handshakes [3rd-Eden] + * Added support for automatic generation of socket.io files [3rd-Eden] + * Added websocket binary support [einaros] + * Added gzip support for socket.io.js [3rd-Eden] + * Expose socket.transport [3rd-Eden] + * Updated client. + +0.8.4 / 2011-09-06 +================== + + * Client build + +0.8.3 / 2011-09-03 +================== + + * Fixed `\n` parsing for non-JSON packets (fixes #479). + * Fixed parsing of certain unicode characters (fixes #451). + * Fixed transport message packet logging. + * Fixed emission of `error` event resulting in an uncaught exception if unhandled (fixes #476). + * Fixed; allow for falsy values as the configuration value of `log level` (fixes #491). + * Fixed repository URI in `package.json`. Fixes #504. + * Added text/plain content-type to handshake responses [einaros] + * Improved single byte writes [einaros] + * Updated socket.io-flashsocket default port from 843 to 10843 [3rd-Eden] + * Updated client. + +0.8.2 / 2011-08-29 +================== + + * Updated client. + +0.8.1 / 2011-08-29 +================== + + * Fixed utf8 bug in send framing in websocket [einaros] + * Fixed typo in docs [Znarkus] + * Fixed bug in send framing for over 64kB of data in websocket [einaros] + * Corrected ping handling in websocket transport [einaros] + +0.8.0 / 2011-08-28 +================== + + * Updated to work with two-level websocket versioning. [einaros] + * Added hybi07 support. [einaros] + * Added hybi10 support. [einaros] + * Added http referrer verification to manager.js verifyOrigin. [einaors] + +0.7.11 / 2011-08-27 +=================== + + * Updated socket.io-client. + +0.7.10 / 2011-08-27 +=================== + + * Updated socket.io-client. + +0.7.9 / 2011-08-12 +================== + + * Updated socket.io-client. + * Make sure we only do garbage collection when the server we receive is actually run. + +0.7.8 / 2011-08-08 +================== + + * Changed; make sure sio#listen passes options to both HTTP server and socket.io manager. + * Added docs for sio#listen. + * Added options parameter support for Manager constructor. + * Added memory leaks tests and test-leaks Makefile task. + * Removed auto npm-linking from make test. + * Make sure that you can disable heartbeats. [3rd-Eden] + * Fixed rooms memory leak [3rd-Eden] + * Send response once we got all POST data, not immediately [Pita] + * Fixed onLeave behavior with missing clientsk [3rd-Eden] + * Prevent duplicate references in rooms. + * Added alias for `to` to `in` and `in` to `to`. + * Fixed roomClients definition. + * Removed dependency on redis for installation without npm [3rd-Eden] + * Expose path and querystring in handshakeData [3rd-Eden] + +0.7.7 / 2011-07-12 +================== + + * Fixed double dispatch handling with emit to closed clients. + * Added test for emitting to closed clients to prevent regression. + * Fixed race condition in redis test. + * Changed Transport#end instrumentation. + * Leveraged $emit instead of emit internally. + * Made tests faster. + * Fixed double disconnect events. + * Fixed disconnect logic + * Simplified remote events handling in Socket. + * Increased testcase timeout. + * Fixed unknown room emitting (GH-291). [3rd-Eden] + * Fixed `address` in handshakeData. [3rd-Eden] + * Removed transports definition in chat example. + * Fixed room cleanup + * Fixed; make sure the client is cleaned up after booting. + * Make sure to mark the client as non-open if the connection is closed. + * Removed unneeded `buffer` declarations. + * Fixed; make sure to clear socket handlers and subscriptions upon transport close. + +0.7.6 / 2011-06-30 +================== + + * Fixed general dispatching when a client has closed. + +0.7.5 / 2011-06-30 +================== + + * Fixed dispatching to clients that are disconnected. + +0.7.4 / 2011-06-30 +================== + + * Fixed; only clear handlers if they were set. [level09] + +0.7.3 / 2011-06-30 +================== + + * Exposed handshake data to clients. + * Refactored dispatcher interface. + * Changed; Moved id generation method into the manager. + * Added sub-namespace authorization. [3rd-Eden] + * Changed; normalized SocketNamespace local eventing [dvv] + * Changed; Use packet.reason or default to 'packet' [3rd-Eden] + * Changed console.error to console.log. + * Fixed; bind both servers at the same time do that the test never times out. + * Added 304 support. + * Removed `Transport#name` for abstract interface. + * Changed; lazily require http and https module only when needed. [3rd-Eden] + +0.7.2 / 2011-06-22 +================== + + * Make sure to write a packet (of type `noop`) when closing a poll. + This solves a problem with cross-domain requests being flagged as aborted and + reconnection being triggered. + * Added `noop` message type. + +0.7.1 / 2011-06-21 +================== + + * Fixed cross-domain XHR. + * Added CORS test to xhr-polling suite. + +0.7.0 / 2010-06-21 +================== + + * http://socket.io/announcement.html diff --git a/dist/node_modules/socket.io/LICENSE b/dist/node_modules/socket.io/LICENSE new file mode 100644 index 0000000..0f4acd4 --- /dev/null +++ b/dist/node_modules/socket.io/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2011 Guillermo Rauch + +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/dist/node_modules/socket.io/Makefile b/dist/node_modules/socket.io/Makefile new file mode 100644 index 0000000..832cba8 --- /dev/null +++ b/dist/node_modules/socket.io/Makefile @@ -0,0 +1,31 @@ + +ALL_TESTS = $(shell find test/ -name '*.test.js') +ALL_BENCH = $(shell find benchmarks -name '*.bench.js') + +run-tests: + @./node_modules/.bin/expresso \ + -t 3000 \ + -I support \ + --serial \ + $(TESTFLAGS) \ + $(TESTS) + +test: + @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests + +test-cov: + @TESTFLAGS=--cov $(MAKE) test + +test-leaks: + @ls test/leaks/* | xargs node --expose_debug_as=debug --expose_gc + +run-bench: + @node $(PROFILEFLAGS) benchmarks/runner.js + +bench: + @$(MAKE) BENCHMARKS="$(ALL_BENCH)" run-bench + +profile: + @PROFILEFLAGS='--prof --trace-opt --trace-bailout --trace-deopt' $(MAKE) bench + +.PHONY: test bench profile diff --git a/dist/node_modules/socket.io/Readme.md b/dist/node_modules/socket.io/Readme.md new file mode 100644 index 0000000..41f21f6 --- /dev/null +++ b/dist/node_modules/socket.io/Readme.md @@ -0,0 +1,364 @@ +# Socket.IO + +Socket.IO is a Node.JS project that makes WebSockets and realtime possible in +all browsers. It also enhances WebSockets by providing built-in multiplexing, +horizontal scalability, automatic JSON encoding/decoding, and more. + +## How to Install + +```bash +npm install socket.io +``` + +## How to use + +First, require `socket.io`: + +```js +var io = require('socket.io'); +``` + +Next, attach it to a HTTP/HTTPS server. If you're using the fantastic `express` +web framework: + +#### Express 3.x + +```js +var app = express() + , server = require('http').createServer(app) + , io = io.listen(server); + +server.listen(80); + +io.sockets.on('connection', function (socket) { + socket.emit('news', { hello: 'world' }); + socket.on('my other event', function (data) { + console.log(data); + }); +}); +``` + +#### Express 2.x + +```js +var app = express.createServer() + , io = io.listen(app); + +app.listen(80); + +io.sockets.on('connection', function (socket) { + socket.emit('news', { hello: 'world' }); + socket.on('my other event', function (data) { + console.log(data); + }); +}); +``` + +Finally, load it from the client side code: + +```html + + +``` + +For more thorough examples, look at the `examples/` directory. + +## Short recipes + +### Sending and receiving events. + +Socket.IO allows you to emit and receive custom events. +Besides `connect`, `message` and `disconnect`, you can emit custom events: + +```js +// note, io.listen() will create a http server for you +var io = require('socket.io').listen(80); + +io.sockets.on('connection', function (socket) { + io.sockets.emit('this', { will: 'be received by everyone' }); + + socket.on('private message', function (from, msg) { + console.log('I received a private message by ', from, ' saying ', msg); + }); + + socket.on('disconnect', function () { + io.sockets.emit('user disconnected'); + }); +}); +``` + +### Storing data associated to a client + +Sometimes it's necessary to store data associated with a client that's +necessary for the duration of the session. + +#### Server side + +```js +var io = require('socket.io').listen(80); + +io.sockets.on('connection', function (socket) { + socket.on('set nickname', function (name) { + socket.set('nickname', name, function () { socket.emit('ready'); }); + }); + + socket.on('msg', function () { + socket.get('nickname', function (err, name) { + console.log('Chat message by ', name); + }); + }); +}); +``` + +#### Client side + +```html + +``` + +### Restricting yourself to a namespace + +If you have control over all the messages and events emitted for a particular +application, using the default `/` namespace works. + +If you want to leverage 3rd-party code, or produce code to share with others, +socket.io provides a way of namespacing a `socket`. + +This has the benefit of `multiplexing` a single connection. Instead of +socket.io using two `WebSocket` connections, it'll use one. + +The following example defines a socket that listens on '/chat' and one for +'/news': + +#### Server side + +```js +var io = require('socket.io').listen(80); + +var chat = io + .of('/chat') + .on('connection', function (socket) { + socket.emit('a message', { that: 'only', '/chat': 'will get' }); + chat.emit('a message', { everyone: 'in', '/chat': 'will get' }); + }); + +var news = io + .of('/news'); + .on('connection', function (socket) { + socket.emit('item', { news: 'item' }); + }); +``` + +#### Client side: + +```html + +``` + +### Sending volatile messages. + +Sometimes certain messages can be dropped. Let's say you have an app that +shows realtime tweets for the keyword `bieber`. + +If a certain client is not ready to receive messages (because of network slowness +or other issues, or because he's connected through long polling and is in the +middle of a request-response cycle), if he doesn't receive ALL the tweets related +to bieber your application won't suffer. + +In that case, you might want to send those messages as volatile messages. + +#### Server side + +```js +var io = require('socket.io').listen(80); + +io.sockets.on('connection', function (socket) { + var tweets = setInterval(function () { + getBieberTweet(function (tweet) { + socket.volatile.emit('bieber tweet', tweet); + }); + }, 100); + + socket.on('disconnect', function () { + clearInterval(tweets); + }); +}); +``` + +#### Client side + +In the client side, messages are received the same way whether they're volatile +or not. + +### Getting acknowledgements + +Sometimes, you might want to get a callback when the client confirmed the message +reception. + +To do this, simply pass a function as the last parameter of `.send` or `.emit`. +What's more, when you use `.emit`, the acknowledgement is done by you, which +means you can also pass data along: + +#### Server side + +```js +var io = require('socket.io').listen(80); + +io.sockets.on('connection', function (socket) { + socket.on('ferret', function (name, fn) { + fn('woot'); + }); +}); +``` + +#### Client side + +```html + +``` + +### Broadcasting messages + +To broadcast, simply add a `broadcast` flag to `emit` and `send` method calls. +Broadcasting means sending a message to everyone else except for the socket +that starts it. + +#### Server side + +```js +var io = require('socket.io').listen(80); + +io.sockets.on('connection', function (socket) { + socket.broadcast.emit('user connected'); + socket.broadcast.json.send({ a: 'message' }); +}); +``` + +### Rooms + +Sometimes you want to put certain sockets in the same room, so that it's easy +to broadcast to all of them together. + +Think of this as built-in channels for sockets. Sockets `join` and `leave` +rooms in each socket. + +#### Server side + +```js +var io = require('socket.io').listen(80); + +io.sockets.on('connection', function (socket) { + socket.join('justin bieber fans'); + socket.broadcast.to('justin bieber fans').emit('new fan'); + io.sockets.in('rammstein fans').emit('new non-fan'); +}); +``` + +### Using it just as a cross-browser WebSocket + +If you just want the WebSocket semantics, you can do that too. +Simply leverage `send` and listen on the `message` event: + +#### Server side + +```js +var io = require('socket.io').listen(80); + +io.sockets.on('connection', function (socket) { + socket.on('message', function () { }); + socket.on('disconnect', function () { }); +}); +``` + +#### Client side + +```html + +``` + +### Changing configuration + +Configuration in socket.io is TJ-style: + +#### Server side + +```js +var io = require('socket.io').listen(80); + +io.configure(function () { + io.set('transports', ['websocket', 'flashsocket', 'xhr-polling']); +}); + +io.configure('development', function () { + io.set('transports', ['websocket', 'xhr-polling']); + io.enable('log'); +}); +``` + +## License + +(The MIT License) + +Copyright (c) 2011 Guillermo Rauch <guillermo@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE 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/dist/node_modules/socket.io/benchmarks/decode.bench.js b/dist/node_modules/socket.io/benchmarks/decode.bench.js new file mode 100644 index 0000000..4855d80 --- /dev/null +++ b/dist/node_modules/socket.io/benchmarks/decode.bench.js @@ -0,0 +1,64 @@ + +/** + * Module dependencies. + */ + +var benchmark = require('benchmark') + , colors = require('colors') + , io = require('../') + , parser = io.parser + , suite = new benchmark.Suite('Decode packet'); + +suite.add('string', function () { + parser.decodePacket('4:::"2"'); +}); + +suite.add('event', function () { + parser.decodePacket('5:::{"name":"woot"}'); +}); + +suite.add('event+ack', function () { + parser.decodePacket('5:1+::{"name":"tobi"}'); +}); + +suite.add('event+data', function () { + parser.decodePacket('5:::{"name":"edwald","args":[{"a": "b"},2,"3"]}'); +}); + +suite.add('heartbeat', function () { + parser.decodePacket('2:::'); +}); + +suite.add('error', function () { + parser.decodePacket('7:::2+0'); +}); + +var payload = parser.encodePayload([ + parser.encodePacket({ type: 'message', data: '5', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) +]); + +suite.add('payload', function () { + parser.decodePayload(payload); +}); + +suite.on('cycle', function (bench, details) { + console.log('\n' + suite.name.grey, details.name.white.bold); + console.log([ + details.hz.toFixed(2).cyan + ' ops/sec'.grey + , details.count.toString().white + ' times executed'.grey + , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey + , + ].join(', '.grey)); +}); + +if (!module.parent) { + suite.run(); +} else { + module.exports = suite; +} diff --git a/dist/node_modules/socket.io/benchmarks/encode.bench.js b/dist/node_modules/socket.io/benchmarks/encode.bench.js new file mode 100644 index 0000000..5037702 --- /dev/null +++ b/dist/node_modules/socket.io/benchmarks/encode.bench.js @@ -0,0 +1,90 @@ + +/** + * Module dependencies. + */ + +var benchmark = require('benchmark') + , colors = require('colors') + , io = require('../') + , parser = io.parser + , suite = new benchmark.Suite('Encode packet'); + +suite.add('string', function () { + parser.encodePacket({ + type: 'json' + , endpoint: '' + , data: '2' + }); +}); + +suite.add('event', function () { + parser.encodePacket({ + type: 'event' + , name: 'woot' + , endpoint: '' + , args: [] + }); +}); + +suite.add('event+ack', function () { + parser.encodePacket({ + type: 'json' + , id: 1 + , ack: 'data' + , endpoint: '' + , data: { a: 'b' } + }); +}); + +suite.add('event+data', function () { + parser.encodePacket({ + type: 'event' + , name: 'edwald' + , endpoint: '' + , args: [{a: 'b'}, 2, '3'] + }); +}); + +suite.add('heartbeat', function () { + parser.encodePacket({ + type: 'heartbeat' + , endpoint: '' + }) +}); + +suite.add('error', function () { + parser.encodePacket({ + type: 'error' + , reason: 'unauthorized' + , advice: 'reconnect' + , endpoint: '' + }) +}) + +suite.add('payload', function () { + parser.encodePayload([ + parser.encodePacket({ type: 'message', data: '5', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: '53d', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' }) + , parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' }) + ]); +}); + +suite.on('cycle', function (bench, details) { + console.log('\n' + suite.name.grey, details.name.white.bold); + console.log([ + details.hz.toFixed(2).cyan + ' ops/sec'.grey + , details.count.toString().white + ' times executed'.grey + , 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey + , + ].join(', '.grey)); +}); + +if (!module.parent) { + suite.run(); +} else { + module.exports = suite; +} diff --git a/dist/node_modules/socket.io/benchmarks/runner.js b/dist/node_modules/socket.io/benchmarks/runner.js new file mode 100644 index 0000000..81e55ca --- /dev/null +++ b/dist/node_modules/socket.io/benchmarks/runner.js @@ -0,0 +1,55 @@ +/** + * Benchmark runner dependencies + */ + +var colors = require('colors') + , path = require('path'); + +/** + * Find all the benchmarks + */ + +var benchmarks_files = process.env.BENCHMARKS.split(' ') + , all = [].concat(benchmarks_files) + , first = all.shift() + , benchmarks = {}; + +// find the benchmarks and load them all in our obj +benchmarks_files.forEach(function (file) { + benchmarks[file] = require(path.join(__dirname, '..', file)); +}); + +// setup the complete listeners +benchmarks_files.forEach(function (file) { + var benchmark = benchmarks[file] + , next_file = all.shift() + , next = benchmarks[next_file]; + + /** + * Generate a oncomplete function for the tests, either we are done or we + * have more benchmarks to process. + */ + + function complete () { + if (!next) { + console.log( + '\n\nBenchmark completed in'.grey + , (Date.now() - start).toString().green + ' ms'.grey + ); + } else { + console.log('\nStarting benchmark '.grey + next_file.yellow); + next.run(); + } + } + + // attach the listener + benchmark.on('complete', complete); +}); + +/** + * Start the benchmark + */ + +var start = Date.now(); +console.log('Starting benchmark '.grey + first.yellow); +benchmarks[first].run(); diff --git a/dist/node_modules/socket.io/index.js b/dist/node_modules/socket.io/index.js new file mode 100644 index 0000000..cc00c10 --- /dev/null +++ b/dist/node_modules/socket.io/index.js @@ -0,0 +1,8 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +module.exports = require('./lib/socket.io'); diff --git a/dist/node_modules/socket.io/lib/client.js b/dist/node_modules/socket.io/lib/client.js new file mode 100644 index 0000000..d80067b --- /dev/null +++ b/dist/node_modules/socket.io/lib/client.js @@ -0,0 +1,167 @@ + +/** + * Module dependencies. + */ + +var parser = require('socket.io-client').parser + , EventEmitter = require('events').EventEmitter + +/** + * Client constructor. + * + * @api public + */ + +function Client (id, server) { + this.id = id; + this.acks = {}; + this.store = server.store; + + var self = this; + + store.subscribe(id, function (packet) { + + }); + + store.subscribe(id + '.disconect', function () { + self.onDisconnect(); + }); +} + +/** + * Inherits from EventEmitter. + */ + +Client.prototype.__proto__ = EventEmitter.prototype; + +/** + * Save reference to original `emit`. + * + * @api private + */ + +Client.prototype._emit = Client.prototype.emit; + +/** + * Broadcast flag. + * + * @api public + */ + +Client.prototype.__defineGetter__('broadcast', function () { + this.flags.broadcast = true; +}); + +/** + * JSON flag (deprecated) + * + * @api public + */ + +Client.prototype.__defineGetter__('json', function () { + this.flags.broadcast = true; +}); + +/** + * Joins a group. + * + * @param {String} group + * @return {Client} for chaining + * @api public + */ + +Client.prototype.join = function (group, fn) { + if (!~this.subscriptions.indexOf(group)) { + var self = this; + this.subscriptions.push(group); + this.store.addToGroup(group, this.sid, function (ev, args) { + self.onGroupEvent(ev, args); + }, fn); + } else { + fn && fn(); + } + + return this; +}; + +/** + * Leaves a group. + * + * @return {Client} for chaining + * @api public + */ + +Client.prototype.leave = function (group) { + var index = this.subscriptions.indexOf(group); + if (~index) { + this.subscriptions.splice(index, 1); + } + return this; +}; + +Client.prototype.disconnect = function () { + if (this.socket) { + this.socket.disconnect(); + } else { + this.publish('disconnect'); + } +} + +/** + * Called upon disconnect. + * + * @api private + */ + +Client.prototype.onDisconnect = function () { + for (var i = 0, l = this.subscriptions; i < l; i++) { + this.store.removeFromGroup(id, group, fn); + } +}; + +/** + * Registers ACK. + */ + +Client.prototype.ack = function (fn, callback) { + this.subscribe('ack'); +}; + +/** + * Emits an event. + */ + +Client.prototype.emit = function () { + var args = toArray(arguments), fn; + + if ('function' == typeof args[args.length - 1]) { + fn = args.pop(); + } + + var data = args.shift(); + if (args.length) { + data += '\n' + JSON.stringify(args); + } + + if (fn) { + this.ack(fn, function (id) { + self.sendPacket('event', data, id); + }); + } else { + this.sendPacket('event', data); + } + + return this; +}; + +/** + * Sends a packet. + */ + +Client.prototype.sendPacket = function (type, data, id) { + var data = parser.encode({ type: type, data: data, id: id }); + + if (this.server.sockets[id]) { + this.server.sockets[id].write(data); + } +}; diff --git a/dist/node_modules/socket.io/lib/logger.js b/dist/node_modules/socket.io/lib/logger.js new file mode 100644 index 0000000..49d02c9 --- /dev/null +++ b/dist/node_modules/socket.io/lib/logger.js @@ -0,0 +1,97 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var util = require('./util') + , toArray = util.toArray; + +/** + * Log levels. + */ + +var levels = [ + 'error' + , 'warn' + , 'info' + , 'debug' +]; + +/** + * Colors for log levels. + */ + +var colors = [ + 31 + , 33 + , 36 + , 90 +]; + +/** + * Pads the nice output to the longest log level. + */ + +function pad (str) { + var max = 0; + + for (var i = 0, l = levels.length; i < l; i++) + max = Math.max(max, levels[i].length); + + if (str.length < max) + return str + new Array(max - str.length + 1).join(' '); + + return str; +}; + +/** + * Logger (console). + * + * @api public + */ + +var Logger = module.exports = function (opts) { + opts = opts || {} + this.colors = false !== opts.colors; + this.level = 3; + this.enabled = true; +}; + +/** + * Log method. + * + * @api public + */ + +Logger.prototype.log = function (type) { + var index = levels.indexOf(type); + + if (index > this.level || !this.enabled) + return this; + + console.log.apply( + console + , [this.colors + ? ' \033[' + colors[index] + 'm' + pad(type) + ' -\033[39m' + : type + ':' + ].concat(toArray(arguments).slice(1)) + ); + + return this; +}; + +/** + * Generate methods. + */ + +levels.forEach(function (name) { + Logger.prototype[name] = function () { + this.log.apply(this, [name].concat(toArray(arguments))); + }; +}); diff --git a/dist/node_modules/socket.io/lib/manager.js b/dist/node_modules/socket.io/lib/manager.js new file mode 100644 index 0000000..9d72a54 --- /dev/null +++ b/dist/node_modules/socket.io/lib/manager.js @@ -0,0 +1,1025 @@ +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , url = require('url') + , tty = require('tty') + , crypto = require('crypto') + , util = require('./util') + , store = require('./store') + , client = require('socket.io-client') + , transports = require('./transports') + , Logger = require('./logger') + , Socket = require('./socket') + , MemoryStore = require('./stores/memory') + , SocketNamespace = require('./namespace') + , Static = require('./static') + , EventEmitter = process.EventEmitter; + +/** + * Export the constructor. + */ + +exports = module.exports = Manager; + +/** + * Default transports. + */ + +var defaultTransports = exports.defaultTransports = [ + 'websocket' + , 'htmlfile' + , 'xhr-polling' + , 'jsonp-polling' +]; + +/** + * Inherited defaults. + */ + +var parent = module.parent.exports + , protocol = parent.protocol + , jsonpolling_re = /^\d+$/; + +/** + * Manager constructor. + * + * @param {HTTPServer} server + * @param {Object} options, optional + * @api public + */ + +function Manager (server, options) { + this.server = server; + this.namespaces = {}; + this.sockets = this.of(''); + this.settings = { + origins: '*:*' + , log: true + , store: new MemoryStore + , logger: new Logger + , static: new Static(this) + , heartbeats: true + , resource: '/socket.io' + , transports: defaultTransports + , authorization: false + , blacklist: ['disconnect'] + , 'log level': 3 + , 'log colors': tty.isatty(process.stdout.fd) + , 'close timeout': 60 + , 'heartbeat interval': 25 + , 'heartbeat timeout': 60 + , 'polling duration': 20 + , 'flash policy server': true + , 'flash policy port': 10843 + , 'destroy upgrade': true + , 'destroy buffer size': 10E7 + , 'browser client': true + , 'browser client cache': true + , 'browser client minification': false + , 'browser client etag': false + , 'browser client expires': 315360000 + , 'browser client gzip': false + , 'browser client handler': false + , 'client store expiration': 15 + , 'match origin protocol': false + }; + + for (var i in options) { + if (options.hasOwnProperty(i)) { + this.settings[i] = options[i]; + } + } + + var self = this; + + // default error handler + server.on('error', function(err) { + self.log.warn('error raised: ' + err); + }); + + this.initStore(); + + this.on('set:store', function() { + self.initStore(); + }); + + // reset listeners + this.oldListeners = server.listeners('request').splice(0); + + server.on('request', function (req, res) { + self.handleRequest(req, res); + }); + + server.on('upgrade', function (req, socket, head) { + self.handleUpgrade(req, socket, head); + }); + + server.on('close', function () { + clearInterval(self.gc); + }); + + server.once('listening', function () { + self.gc = setInterval(self.garbageCollection.bind(self), 10000); + }); + + for (var i in transports) { + if (transports.hasOwnProperty(i)) { + if (transports[i].init) { + transports[i].init(this); + } + } + } + + // forward-compatibility with 1.0 + var self = this; + this.sockets.on('connection', function (conn) { + self.emit('connection', conn); + }); + + this.sequenceNumber = Date.now() | 0; + + this.log.info('socket.io started'); +}; + +Manager.prototype.__proto__ = EventEmitter.prototype + +/** + * Store accessor shortcut. + * + * @api public + */ + +Manager.prototype.__defineGetter__('store', function () { + var store = this.get('store'); + store.manager = this; + return store; +}); + +/** + * Logger accessor. + * + * @api public + */ + +Manager.prototype.__defineGetter__('log', function () { + var logger = this.get('logger'); + + logger.level = this.get('log level') || -1; + logger.colors = this.get('log colors'); + logger.enabled = this.enabled('log'); + + return logger; +}); + +/** + * Static accessor. + * + * @api public + */ + +Manager.prototype.__defineGetter__('static', function () { + return this.get('static'); +}); + +/** + * Get settings. + * + * @api public + */ + +Manager.prototype.get = function (key) { + return this.settings[key]; +}; + +/** + * Set settings + * + * @api public + */ + +Manager.prototype.set = function (key, value) { + if (arguments.length == 1) return this.get(key); + this.settings[key] = value; + this.emit('set:' + key, this.settings[key], key); + return this; +}; + +/** + * Enable a setting + * + * @api public + */ + +Manager.prototype.enable = function (key) { + this.settings[key] = true; + this.emit('set:' + key, this.settings[key], key); + return this; +}; + +/** + * Disable a setting + * + * @api public + */ + +Manager.prototype.disable = function (key) { + this.settings[key] = false; + this.emit('set:' + key, this.settings[key], key); + return this; +}; + +/** + * Checks if a setting is enabled + * + * @api public + */ + +Manager.prototype.enabled = function (key) { + return !!this.settings[key]; +}; + +/** + * Checks if a setting is disabled + * + * @api public + */ + +Manager.prototype.disabled = function (key) { + return !this.settings[key]; +}; + +/** + * Configure callbacks. + * + * @api public + */ + +Manager.prototype.configure = function (env, fn) { + if ('function' == typeof env) { + env.call(this); + } else if (env == (process.env.NODE_ENV || 'development')) { + fn.call(this); + } + + return this; +}; + +/** + * Initializes everything related to the message dispatcher. + * + * @api private + */ + +Manager.prototype.initStore = function () { + this.handshaken = {}; + this.connected = {}; + this.open = {}; + this.closed = {}; + this.rooms = {}; + this.roomClients = {}; + + var self = this; + + this.store.subscribe('handshake', function (id, data) { + self.onHandshake(id, data); + }); + + this.store.subscribe('connect', function (id) { + self.onConnect(id); + }); + + this.store.subscribe('open', function (id) { + self.onOpen(id); + }); + + this.store.subscribe('join', function (id, room) { + self.onJoin(id, room); + }); + + this.store.subscribe('leave', function (id, room) { + self.onLeave(id, room); + }); + + this.store.subscribe('close', function (id) { + self.onClose(id); + }); + + this.store.subscribe('dispatch', function (room, packet, volatile, exceptions) { + self.onDispatch(room, packet, volatile, exceptions); + }); + + this.store.subscribe('disconnect', function (id) { + self.onDisconnect(id); + }); +}; + +/** + * Called when a client handshakes. + * + * @param text + */ + +Manager.prototype.onHandshake = function (id, data) { + this.handshaken[id] = data; +}; + +/** + * Called when a client connects (ie: transport first opens) + * + * @api private + */ + +Manager.prototype.onConnect = function (id) { + this.connected[id] = true; +}; + +/** + * Called when a client opens a request in a different node. + * + * @api private + */ + +Manager.prototype.onOpen = function (id) { + this.open[id] = true; + + if (this.closed[id]) { + var self = this; + + this.store.unsubscribe('dispatch:' + id, function () { + var transport = self.transports[id]; + if (self.closed[id] && self.closed[id].length && transport) { + + // if we have buffered messages that accumulate between calling + // onOpen an this async callback, send them if the transport is + // still open, otherwise leave them buffered + if (transport.open) { + transport.payload(self.closed[id]); + self.closed[id] = []; + } + } + }); + } + + // clear the current transport + if (this.transports[id]) { + this.transports[id].discard(); + this.transports[id] = null; + } +}; + +/** + * Called when a message is sent to a namespace and/or room. + * + * @api private + */ + +Manager.prototype.onDispatch = function (room, packet, volatile, exceptions) { + if (this.rooms[room]) { + for (var i = 0, l = this.rooms[room].length; i < l; i++) { + var id = this.rooms[room][i]; + + if (!~exceptions.indexOf(id)) { + if (this.transports[id] && this.transports[id].open) { + this.transports[id].onDispatch(packet, volatile); + } else if (!volatile) { + this.onClientDispatch(id, packet); + } + } + } + } +}; + +/** + * Called when a client joins a nsp / room. + * + * @api private + */ + +Manager.prototype.onJoin = function (id, name) { + if (!this.roomClients[id]) { + this.roomClients[id] = {}; + } + + if (!this.rooms[name]) { + this.rooms[name] = []; + } + + if (!~this.rooms[name].indexOf(id)) { + this.rooms[name].push(id); + this.roomClients[id][name] = true; + } +}; + +/** + * Called when a client leaves a nsp / room. + * + * @param private + */ + +Manager.prototype.onLeave = function (id, room) { + if (this.rooms[room]) { + var index = this.rooms[room].indexOf(id); + + if (index >= 0) { + this.rooms[room].splice(index, 1); + } + + if (!this.rooms[room].length) { + delete this.rooms[room]; + } + + if (this.roomClients[id]) { + delete this.roomClients[id][room]; + } + } +}; + +/** + * Called when a client closes a request in different node. + * + * @api private + */ + +Manager.prototype.onClose = function (id) { + if (this.open[id]) { + delete this.open[id]; + } + + this.closed[id] = []; + + var self = this; + + this.store.subscribe('dispatch:' + id, function (packet, volatile) { + if (!volatile) { + self.onClientDispatch(id, packet); + } + }); +}; + +/** + * Dispatches a message for a closed client. + * + * @api private + */ + +Manager.prototype.onClientDispatch = function (id, packet) { + if (this.closed[id]) { + this.closed[id].push(packet); + } +}; + +/** + * Receives a message for a client. + * + * @api private + */ + +Manager.prototype.onClientMessage = function (id, packet) { + if (this.namespaces[packet.endpoint]) { + this.namespaces[packet.endpoint].handlePacket(id, packet); + } +}; + +/** + * Fired when a client disconnects (not triggered). + * + * @api private + */ + +Manager.prototype.onClientDisconnect = function (id, reason) { + for (var name in this.namespaces) { + if (this.namespaces.hasOwnProperty(name)) { + this.namespaces[name].handleDisconnect(id, reason, typeof this.roomClients[id] !== 'undefined' && + typeof this.roomClients[id][name] !== 'undefined'); + } + } + + this.onDisconnect(id); +}; + +/** + * Called when a client disconnects. + * + * @param text + */ + +Manager.prototype.onDisconnect = function (id, local) { + delete this.handshaken[id]; + + if (this.open[id]) { + delete this.open[id]; + } + + if (this.connected[id]) { + delete this.connected[id]; + } + + if (this.transports[id]) { + this.transports[id].discard(); + delete this.transports[id]; + } + + if (this.closed[id]) { + delete this.closed[id]; + } + + if (this.roomClients[id]) { + for (var room in this.roomClients[id]) { + if (this.roomClients[id].hasOwnProperty(room)) { + this.onLeave(id, room); + } + } + delete this.roomClients[id] + } + + this.store.destroyClient(id, this.get('client store expiration')); + + this.store.unsubscribe('dispatch:' + id); + + if (local) { + this.store.unsubscribe('message:' + id); + this.store.unsubscribe('disconnect:' + id); + } +}; + +/** + * Handles an HTTP request. + * + * @api private + */ + +Manager.prototype.handleRequest = function (req, res) { + var data = this.checkRequest(req); + + if (!data) { + for (var i = 0, l = this.oldListeners.length; i < l; i++) { + this.oldListeners[i].call(this.server, req, res); + } + + return; + } + + if (data.static || !data.transport && !data.protocol) { + if (data.static && this.enabled('browser client')) { + this.static.write(data.path, req, res); + } else { + res.writeHead(200); + res.end('Welcome to socket.io.'); + + this.log.info('unhandled socket.io url'); + } + + return; + } + + if (data.protocol != protocol) { + res.writeHead(500); + res.end('Protocol version not supported.'); + + this.log.info('client protocol version unsupported'); + } else { + if (data.id) { + this.handleHTTPRequest(data, req, res); + } else { + this.handleHandshake(data, req, res); + } + } +}; + +/** + * Handles an HTTP Upgrade. + * + * @api private + */ + +Manager.prototype.handleUpgrade = function (req, socket, head) { + var data = this.checkRequest(req) + , self = this; + + if (!data) { + if (this.enabled('destroy upgrade')) { + socket.end(); + this.log.debug('destroying non-socket.io upgrade'); + } + + return; + } + + req.head = head; + this.handleClient(data, req); +}; + +/** + * Handles a normal handshaken HTTP request (eg: long-polling) + * + * @api private + */ + +Manager.prototype.handleHTTPRequest = function (data, req, res) { + req.res = res; + this.handleClient(data, req); +}; + +/** + * Intantiantes a new client. + * + * @api private + */ + +Manager.prototype.handleClient = function (data, req) { + var socket = req.socket + , store = this.store + , self = this; + + // handle sync disconnect xhrs + if (undefined != data.query.disconnect) { + if (this.transports[data.id] && this.transports[data.id].open) { + this.transports[data.id].onForcedDisconnect(); + } else { + this.store.publish('disconnect-force:' + data.id); + } + req.res.writeHead(200); + req.res.end(); + return; + } + + if (!~this.get('transports').indexOf(data.transport)) { + this.log.warn('unknown transport: "' + data.transport + '"'); + req.connection.end(); + return; + } + + var transport = new transports[data.transport](this, data, req) + , handshaken = this.handshaken[data.id]; + + if (transport.disconnected) { + // failed during transport setup + req.connection.end(); + return; + } + if (handshaken) { + if (transport.open) { + if (this.closed[data.id] && this.closed[data.id].length) { + transport.payload(this.closed[data.id]); + this.closed[data.id] = []; + } + + this.onOpen(data.id); + this.store.publish('open', data.id); + this.transports[data.id] = transport; + } + + if (!this.connected[data.id]) { + this.onConnect(data.id); + this.store.publish('connect', data.id); + + // flag as used + delete handshaken.issued; + this.onHandshake(data.id, handshaken); + this.store.publish('handshake', data.id, handshaken); + + // initialize the socket for all namespaces + for (var i in this.namespaces) { + if (this.namespaces.hasOwnProperty(i)) { + var socket = this.namespaces[i].socket(data.id, true); + + // echo back connect packet and fire connection event + if (i === '') { + this.namespaces[i].handlePacket(data.id, { type: 'connect' }); + } + } + } + + this.store.subscribe('message:' + data.id, function (packet) { + self.onClientMessage(data.id, packet); + }); + + this.store.subscribe('disconnect:' + data.id, function (reason) { + self.onClientDisconnect(data.id, reason); + }); + } + } else { + if (transport.open) { + transport.error('client not handshaken', 'reconnect'); + } + + transport.discard(); + } +}; + +/** + * Generates a session id. + * + * @api private + */ + +Manager.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) { + crypto.randomBytes(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, '-'); +}; + +/** + * Handles a handshake request. + * + * @api private + */ + +Manager.prototype.handleHandshake = function (data, req, res) { + var self = this + , origin = req.headers.origin + , headers = { + 'Content-Type': 'text/plain' + }; + + function writeErr (status, message) { + if (data.query.jsonp && jsonpolling_re.test(data.query.jsonp)) { + res.writeHead(200, { 'Content-Type': 'application/javascript' }); + res.end('io.j[' + data.query.jsonp + '](new Error("' + message + '"));'); + } else { + res.writeHead(status, headers); + res.end(message); + } + }; + + function error (err) { + writeErr(500, 'handshake error'); + self.log.warn('handshake error ' + err); + }; + + if (!this.verifyOrigin(req)) { + writeErr(403, 'handshake bad origin'); + return; + } + + var handshakeData = this.handshakeData(data); + + if (origin) { + // https://developer.mozilla.org/En/HTTP_Access_Control + headers['Access-Control-Allow-Origin'] = origin; + headers['Access-Control-Allow-Credentials'] = 'true'; + } + + this.authorize(handshakeData, function (err, authorized, newData) { + if (err) return error(err); + + if (authorized) { + var id = self.generateId() + , hs = [ + id + , self.enabled('heartbeats') ? self.get('heartbeat timeout') || '' : '' + , self.get('close timeout') || '' + , self.transports(data).join(',') + ].join(':'); + + if (data.query.jsonp && jsonpolling_re.test(data.query.jsonp)) { + hs = 'io.j[' + data.query.jsonp + '](' + JSON.stringify(hs) + ');'; + res.writeHead(200, { 'Content-Type': 'application/javascript' }); + } else { + res.writeHead(200, headers); + } + + res.end(hs); + + self.onHandshake(id, newData || handshakeData); + self.store.publish('handshake', id, newData || handshakeData); + + self.log.info('handshake authorized', id); + } else { + writeErr(403, 'handshake unauthorized'); + self.log.info('handshake unauthorized'); + } + }) +}; + +/** + * Gets normalized handshake data + * + * @api private + */ + +Manager.prototype.handshakeData = function (data) { + var connection = data.request.connection + , connectionAddress + , date = new Date; + + if (connection.remoteAddress) { + connectionAddress = { + address: connection.remoteAddress + , port: connection.remotePort + }; + } else if (connection.socket && connection.socket.remoteAddress) { + connectionAddress = { + address: connection.socket.remoteAddress + , port: connection.socket.remotePort + }; + } + + return { + headers: data.headers + , address: connectionAddress + , time: date.toString() + , query: data.query + , url: data.request.url + , xdomain: !!data.request.headers.origin + , secure: data.request.connection.secure + , issued: +date + }; +}; + +/** + * Verifies the origin of a request. + * + * @api private + */ + +Manager.prototype.verifyOrigin = function (request) { + var origin = request.headers.origin || request.headers.referer + , origins = this.get('origins'); + + if (origin === 'null') origin = '*'; + + if (origins.indexOf('*:*') !== -1) { + return true; + } + + if (origin) { + try { + var parts = url.parse(origin); + parts.port = parts.port || 80; + var ok = + ~origins.indexOf(parts.hostname + ':' + parts.port) || + ~origins.indexOf(parts.hostname + ':*') || + ~origins.indexOf('*:' + parts.port); + if (!ok) this.log.warn('illegal origin: ' + origin); + return ok; + } catch (ex) { + this.log.warn('error parsing origin'); + } + } + else { + this.log.warn('origin missing from handshake, yet required by config'); + } + return false; +}; + +/** + * Handles an incoming packet. + * + * @api private + */ + +Manager.prototype.handlePacket = function (sessid, packet) { + this.of(packet.endpoint || '').handlePacket(sessid, packet); +}; + +/** + * Performs authentication. + * + * @param Object client request data + * @api private + */ + +Manager.prototype.authorize = function (data, fn) { + if (this.get('authorization')) { + var self = this; + + this.get('authorization').call(this, data, function (err, authorized) { + self.log.debug('client ' + authorized ? 'authorized' : 'unauthorized'); + fn(err, authorized); + }); + } else { + this.log.debug('client authorized'); + fn(null, true); + } + + return this; +}; + +/** + * Retrieves the transports adviced to the user. + * + * @api private + */ + +Manager.prototype.transports = function (data) { + var transp = this.get('transports') + , ret = []; + + for (var i = 0, l = transp.length; i < l; i++) { + var transport = transp[i]; + + if (transport) { + if (!transport.checkClient || transport.checkClient(data)) { + ret.push(transport); + } + } + } + + return ret; +}; + +/** + * Checks whether a request is a socket.io one. + * + * @return {Object} a client request data object or `false` + * @api private + */ + +var regexp = /^\/([^\/]+)\/?([^\/]+)?\/?([^\/]+)?\/?$/ + +Manager.prototype.checkRequest = function (req) { + var resource = this.get('resource'); + + var match; + if (typeof resource === 'string') { + match = req.url.substr(0, resource.length); + if (match !== resource) match = null; + } else { + match = resource.exec(req.url); + if (match) match = match[0]; + } + + if (match) { + var uri = url.parse(req.url.substr(match.length), true) + , path = uri.pathname || '' + , pieces = path.match(regexp); + + // client request data + var data = { + query: uri.query || {} + , headers: req.headers + , request: req + , path: path + }; + + if (pieces) { + data.protocol = Number(pieces[1]); + data.transport = pieces[2]; + data.id = pieces[3]; + data.static = !!this.static.has(path); + }; + + return data; + } + + return false; +}; + +/** + * Declares a socket namespace + * + * @api public + */ + +Manager.prototype.of = function (nsp) { + if (this.namespaces[nsp]) { + return this.namespaces[nsp]; + } + + return this.namespaces[nsp] = new SocketNamespace(this, nsp); +}; + +/** + * Perform garbage collection on long living objects and properties that cannot + * be removed automatically. + * + * @api private + */ + +Manager.prototype.garbageCollection = function () { + // clean up unused handshakes + var ids = Object.keys(this.handshaken) + , i = ids.length + , now = Date.now() + , handshake; + + while (i--) { + handshake = this.handshaken[ids[i]]; + + if ('issued' in handshake && (now - handshake.issued) >= 3E4) { + this.onDisconnect(ids[i]); + } + } +}; diff --git a/dist/node_modules/socket.io/lib/namespace.js b/dist/node_modules/socket.io/lib/namespace.js new file mode 100644 index 0000000..6e1e1c9 --- /dev/null +++ b/dist/node_modules/socket.io/lib/namespace.js @@ -0,0 +1,355 @@ +/** + * Module dependencies. + */ + +var Socket = require('./socket') + , EventEmitter = process.EventEmitter + , parser = require('./parser') + , util = require('./util'); + +/** + * Exports the constructor. + */ + +exports = module.exports = SocketNamespace; + +/** + * Constructor. + * + * @api public. + */ + +function SocketNamespace (mgr, name) { + this.manager = mgr; + this.name = name || ''; + this.sockets = {}; + this.auth = false; + this.setFlags(); +}; + +/** + * Inherits from EventEmitter. + */ + +SocketNamespace.prototype.__proto__ = EventEmitter.prototype; + +/** + * Copies emit since we override it. + * + * @api private + */ + +SocketNamespace.prototype.$emit = EventEmitter.prototype.emit; + +/** + * Retrieves all clients as Socket instances as an array. + * + * @api public + */ + +SocketNamespace.prototype.clients = function (room) { + var room = this.name + (room !== undefined ? + '/' + room : ''); + + if (!this.manager.rooms[room]) { + return []; + } + + return this.manager.rooms[room].map(function (id) { + return this.socket(id); + }, this); +}; + +/** + * Access logger interface. + * + * @api public + */ + +SocketNamespace.prototype.__defineGetter__('log', function () { + return this.manager.log; +}); + +/** + * Access store. + * + * @api public + */ + +SocketNamespace.prototype.__defineGetter__('store', function () { + return this.manager.store; +}); + +/** + * JSON message flag. + * + * @api public + */ + +SocketNamespace.prototype.__defineGetter__('json', function () { + this.flags.json = true; + return this; +}); + +/** + * Volatile message flag. + * + * @api public + */ + +SocketNamespace.prototype.__defineGetter__('volatile', function () { + this.flags.volatile = true; + return this; +}); + +/** + * Overrides the room to relay messages to (flag). + * + * @api public + */ + +SocketNamespace.prototype.in = SocketNamespace.prototype.to = function (room) { + this.flags.endpoint = this.name + (room ? '/' + room : ''); + return this; +}; + +/** + * Adds a session id we should prevent relaying messages to (flag). + * + * @api public + */ + +SocketNamespace.prototype.except = function (id) { + this.flags.exceptions.push(id); + return this; +}; + +/** + * Sets the default flags. + * + * @api private + */ + +SocketNamespace.prototype.setFlags = function () { + this.flags = { + endpoint: this.name + , exceptions: [] + }; + return this; +}; + +/** + * Sends out a packet. + * + * @api private + */ + +SocketNamespace.prototype.packet = function (packet) { + packet.endpoint = this.name; + + var store = this.store + , log = this.log + , volatile = this.flags.volatile + , exceptions = this.flags.exceptions + , packet = parser.encodePacket(packet); + + this.manager.onDispatch(this.flags.endpoint, packet, volatile, exceptions); + this.store.publish('dispatch', this.flags.endpoint, packet, volatile, exceptions); + + this.setFlags(); + + return this; +}; + +/** + * Sends to everyone. + * + * @api public + */ + +SocketNamespace.prototype.send = function (data) { + return this.packet({ + type: this.flags.json ? 'json' : 'message' + , data: data + }); +}; + +/** + * Emits to everyone (override). + * + * @api public + */ + +SocketNamespace.prototype.emit = function (name) { + if (name == 'newListener') { + return this.$emit.apply(this, arguments); + } + + return this.packet({ + type: 'event' + , name: name + , args: util.toArray(arguments).slice(1) + }); +}; + +/** + * Retrieves or creates a write-only socket for a client, unless specified. + * + * @param {Boolean} whether the socket will be readable when initialized + * @api public + */ + +SocketNamespace.prototype.socket = function (sid, readable) { + if (!this.sockets[sid]) { + this.sockets[sid] = new Socket(this.manager, sid, this, readable); + } + + return this.sockets[sid]; +}; + +/** + * Sets authorization for this namespace. + * + * @api public + */ + +SocketNamespace.prototype.authorization = function (fn) { + this.auth = fn; + return this; +}; + +/** + * Called when a socket disconnects entirely. + * + * @api private + */ + +SocketNamespace.prototype.handleDisconnect = function (sid, reason, raiseOnDisconnect) { + if (this.sockets[sid] && this.sockets[sid].readable) { + if (raiseOnDisconnect) this.sockets[sid].onDisconnect(reason); + delete this.sockets[sid]; + } +}; + +/** + * Performs authentication. + * + * @param Object client request data + * @api private + */ + +SocketNamespace.prototype.authorize = function (data, fn) { + if (this.auth) { + var self = this; + + this.auth.call(this, data, function (err, authorized) { + self.log.debug('client ' + + (authorized ? '' : 'un') + 'authorized for ' + self.name); + fn(err, authorized); + }); + } else { + this.log.debug('client authorized for ' + this.name); + fn(null, true); + } + + return this; +}; + +/** + * Handles a packet. + * + * @api private + */ + +SocketNamespace.prototype.handlePacket = function (sessid, packet) { + var socket = this.socket(sessid) + , dataAck = packet.ack == 'data' + , manager = this.manager + , self = this; + + function ack () { + self.log.debug('sending data ack packet'); + socket.packet({ + type: 'ack' + , args: util.toArray(arguments) + , ackId: packet.id + }); + }; + + function error (err) { + self.log.warn('handshake error ' + err + ' for ' + self.name); + socket.packet({ type: 'error', reason: err }); + }; + + function connect () { + self.manager.onJoin(sessid, self.name); + self.store.publish('join', sessid, self.name); + + // packet echo + socket.packet({ type: 'connect' }); + + // emit connection event + self.$emit('connection', socket); + }; + + switch (packet.type) { + case 'connect': + if (packet.endpoint == '') { + connect(); + } else { + var handshakeData = manager.handshaken[sessid]; + + this.authorize(handshakeData, function (err, authorized, newData) { + if (err) return error(err); + + if (authorized) { + manager.onHandshake(sessid, newData || handshakeData); + self.store.publish('handshake', sessid, newData || handshakeData); + connect(); + } else { + error('unauthorized'); + } + }); + } + break; + + case 'ack': + if (socket.acks[packet.ackId]) { + socket.acks[packet.ackId].apply(socket, packet.args); + } else { + this.log.info('unknown ack packet'); + } + break; + + case 'event': + // check if the emitted event is not blacklisted + if (-~manager.get('blacklist').indexOf(packet.name)) { + this.log.debug('ignoring blacklisted event `' + packet.name + '`'); + } else { + var params = [packet.name].concat(packet.args); + + if (dataAck) { + params.push(ack); + } + + socket.$emit.apply(socket, params); + } + break; + + case 'disconnect': + this.manager.onLeave(sessid, this.name); + this.store.publish('leave', sessid, this.name); + + socket.$emit('disconnect', packet.reason || 'packet'); + break; + + case 'json': + case 'message': + var params = ['message', packet.data]; + + if (dataAck) + params.push(ack); + + socket.$emit.apply(socket, params); + }; +}; diff --git a/dist/node_modules/socket.io/lib/parser.js b/dist/node_modules/socket.io/lib/parser.js new file mode 100644 index 0000000..d56b550 --- /dev/null +++ b/dist/node_modules/socket.io/lib/parser.js @@ -0,0 +1,249 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +/** + * Packet types. + */ + +var packets = exports.packets = { + 'disconnect': 0 + , 'connect': 1 + , 'heartbeat': 2 + , 'message': 3 + , 'json': 4 + , 'event': 5 + , 'ack': 6 + , 'error': 7 + , 'noop': 8 + } + , packetslist = Object.keys(packets); + +/** + * Errors reasons. + */ + +var reasons = exports.reasons = { + 'transport not supported': 0 + , 'client not handshaken': 1 + , 'unauthorized': 2 + } + , reasonslist = Object.keys(reasons); + +/** + * Errors advice. + */ + +var advice = exports.advice = { + 'reconnect': 0 + } + , advicelist = Object.keys(advice); + +/** + * Encodes a packet. + * + * @api private + */ + +exports.encodePacket = function (packet) { + var type = packets[packet.type] + , id = packet.id || '' + , endpoint = packet.endpoint || '' + , ack = packet.ack + , data = null; + + switch (packet.type) { + case 'message': + if (packet.data !== '') + data = packet.data; + break; + + case 'event': + var ev = { name: packet.name }; + + if (packet.args && packet.args.length) { + ev.args = packet.args; + } + + data = JSON.stringify(ev); + break; + + case 'json': + data = JSON.stringify(packet.data); + break; + + case 'ack': + data = packet.ackId + + (packet.args && packet.args.length + ? '+' + JSON.stringify(packet.args) : ''); + break; + + case 'connect': + if (packet.qs) + data = packet.qs; + break; + + case 'error': + var reason = packet.reason ? reasons[packet.reason] : '' + , adv = packet.advice ? advice[packet.advice] : '' + + if (reason !== '' || adv !== '') + data = reason + (adv !== '' ? ('+' + adv) : '') + + break; + } + + // construct packet with required fragments + var encoded = type + ':' + id + (ack == 'data' ? '+' : '') + ':' + endpoint; + + // data fragment is optional + if (data !== null && data !== undefined) + encoded += ':' + data; + + return encoded; +}; + +/** + * Encodes multiple messages (payload). + * + * @param {Array} messages + * @api private + */ + +exports.encodePayload = function (packets) { + var decoded = ''; + + if (packets.length == 1) + return packets[0]; + + for (var i = 0, l = packets.length; i < l; i++) { + var packet = packets[i]; + decoded += '\ufffd' + packet.length + '\ufffd' + packets[i] + } + + return decoded; +}; + +/** + * Decodes a packet + * + * @api private + */ + +var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; + +/** + * Wrap the JSON.parse in a seperate function the crankshaft optimizer will + * only punish this function for the usage for try catch + * + * @api private + */ + +function parse (data) { + try { return JSON.parse(data) } + catch (e) { return false } +} + +exports.decodePacket = function (data) { + var pieces = data.match(regexp); + + if (!pieces) return {}; + + var id = pieces[2] || '' + , data = pieces[5] || '' + , packet = { + type: packetslist[pieces[1]] + , endpoint: pieces[4] || '' + }; + + // whether we need to acknowledge the packet + if (id) { + packet.id = id; + if (pieces[3]) + packet.ack = 'data'; + else + packet.ack = true; + } + + // handle different packet types + switch (packet.type) { + case 'message': + packet.data = data || ''; + break; + + case 'event': + pieces = parse(data); + if (pieces) { + packet.name = pieces.name; + packet.args = pieces.args; + } + + packet.args = packet.args || []; + break; + + case 'json': + packet.data = parse(data); + break; + + case 'connect': + packet.qs = data || ''; + break; + + case 'ack': + pieces = data.match(/^([0-9]+)(\+)?(.*)/); + if (pieces) { + packet.ackId = pieces[1]; + packet.args = []; + + if (pieces[3]) { + packet.args = parse(pieces[3]) || []; + } + } + break; + + case 'error': + pieces = data.split('+'); + packet.reason = reasonslist[pieces[0]] || ''; + packet.advice = advicelist[pieces[1]] || ''; + } + + return packet; +}; + +/** + * Decodes data payload. Detects multiple messages + * + * @return {Array} messages + * @api public + */ + +exports.decodePayload = function (data) { + if (undefined == data || null == data) { + return []; + } + + if (data[0] == '\ufffd') { + var ret = []; + + for (var i = 1, length = ''; i < data.length; i++) { + if (data[i] == '\ufffd') { + ret.push(exports.decodePacket(data.substr(i + 1, length))); + i += Number(length) + 1; + length = ''; + } else { + length += data[i]; + } + } + + return ret; + } else { + return [exports.decodePacket(data)]; + } +}; diff --git a/dist/node_modules/socket.io/lib/socket.io.js b/dist/node_modules/socket.io/lib/socket.io.js new file mode 100644 index 0000000..bf59036 --- /dev/null +++ b/dist/node_modules/socket.io/lib/socket.io.js @@ -0,0 +1,143 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var client = require('socket.io-client'); + +/** + * Version. + */ + +exports.version = '0.9.11'; + +/** + * Supported protocol version. + */ + +exports.protocol = 1; + +/** + * Client that we serve. + */ + +exports.clientVersion = client.version; + +/** + * Attaches a manager + * + * @param {HTTPServer/Number} a HTTP/S server or a port number to listen on. + * @param {Object} opts to be passed to Manager and/or http server + * @param {Function} callback if a port is supplied + * @api public + */ + +exports.listen = function (server, options, fn) { + if ('function' == typeof server) { + console.warn('Socket.IO\'s `listen()` method expects an `http.Server` instance\n' + + 'as its first parameter. Are you migrating from Express 2.x to 3.x?\n' + + 'If so, check out the "Socket.IO compatibility" section at:\n' + + 'https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x'); + } + + if ('function' == typeof options) { + fn = options; + options = {}; + } + + if ('undefined' == typeof server) { + // create a server that listens on port 80 + server = 80; + } + + if ('number' == typeof server) { + // if a port number is passed + var port = server; + + if (options && options.key) + server = require('https').createServer(options); + else + server = require('http').createServer(); + + // default response + server.on('request', function (req, res) { + res.writeHead(200); + res.end('Welcome to socket.io.'); + }); + + server.listen(port, fn); + } + + // otherwise assume a http/s server + return new exports.Manager(server, options); +}; + +/** + * Manager constructor. + * + * @api public + */ + +exports.Manager = require('./manager'); + +/** + * Transport constructor. + * + * @api public + */ + +exports.Transport = require('./transport'); + +/** + * Socket constructor. + * + * @api public + */ + +exports.Socket = require('./socket'); + +/** + * Static constructor. + * + * @api public + */ + +exports.Static = require('./static'); + +/** + * Store constructor. + * + * @api public + */ + +exports.Store = require('./store'); + +/** + * Memory Store constructor. + * + * @api public + */ + +exports.MemoryStore = require('./stores/memory'); + +/** + * Redis Store constructor. + * + * @api public + */ + +exports.RedisStore = require('./stores/redis'); + +/** + * Parser. + * + * @api public + */ + +exports.parser = require('./parser'); diff --git a/dist/node_modules/socket.io/lib/socket.js b/dist/node_modules/socket.io/lib/socket.js new file mode 100644 index 0000000..d9807f6 --- /dev/null +++ b/dist/node_modules/socket.io/lib/socket.js @@ -0,0 +1,369 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var parser = require('./parser') + , util = require('./util') + , EventEmitter = process.EventEmitter + +/** + * Export the constructor. + */ + +exports = module.exports = Socket; + +/** + * Default error event listener to prevent uncaught exceptions. + */ + +var defaultError = function () {}; + +/** + * Socket constructor. + * + * @param {Manager} manager instance + * @param {String} session id + * @param {Namespace} namespace the socket belongs to + * @param {Boolean} whether the + * @api public + */ + +function Socket (manager, id, nsp, readable) { + this.id = id; + this.namespace = nsp; + this.manager = manager; + this.disconnected = false; + this.ackPackets = 0; + this.acks = {}; + this.setFlags(); + this.readable = readable; + this.store = this.manager.store.client(this.id); + this.on('error', defaultError); +}; + +/** + * Inherits from EventEmitter. + */ + +Socket.prototype.__proto__ = EventEmitter.prototype; + +/** + * Accessor shortcut for the handshake data + * + * @api private + */ + +Socket.prototype.__defineGetter__('handshake', function () { + return this.manager.handshaken[this.id]; +}); + +/** + * Accessor shortcut for the transport type + * + * @api private + */ + +Socket.prototype.__defineGetter__('transport', function () { + return this.manager.transports[this.id].name; +}); + +/** + * Accessor shortcut for the logger. + * + * @api private + */ + +Socket.prototype.__defineGetter__('log', function () { + return this.manager.log; +}); + +/** + * JSON message flag. + * + * @api public + */ + +Socket.prototype.__defineGetter__('json', function () { + this.flags.json = true; + return this; +}); + +/** + * Volatile message flag. + * + * @api public + */ + +Socket.prototype.__defineGetter__('volatile', function () { + this.flags.volatile = true; + return this; +}); + +/** + * Broadcast message flag. + * + * @api public + */ + +Socket.prototype.__defineGetter__('broadcast', function () { + this.flags.broadcast = true; + return this; +}); + +/** + * Overrides the room to broadcast messages to (flag) + * + * @api public + */ + +Socket.prototype.to = Socket.prototype.in = function (room) { + this.flags.room = room; + return this; +}; + +/** + * Resets flags + * + * @api private + */ + +Socket.prototype.setFlags = function () { + this.flags = { + endpoint: this.namespace.name + , room: '' + }; + return this; +}; + +/** + * Triggered on disconnect + * + * @api private + */ + +Socket.prototype.onDisconnect = function (reason) { + if (!this.disconnected) { + this.$emit('disconnect', reason); + this.disconnected = true; + } +}; + +/** + * Joins a user to a room. + * + * @api public + */ + +Socket.prototype.join = function (name, fn) { + var nsp = this.namespace.name + , name = (nsp + '/') + name; + + this.manager.onJoin(this.id, name); + this.manager.store.publish('join', this.id, name); + + if (fn) { + this.log.warn('Client#join callback is deprecated'); + fn(); + } + + return this; +}; + +/** + * Un-joins a user from a room. + * + * @api public + */ + +Socket.prototype.leave = function (name, fn) { + var nsp = this.namespace.name + , name = (nsp + '/') + name; + + this.manager.onLeave(this.id, name); + this.manager.store.publish('leave', this.id, name); + + if (fn) { + this.log.warn('Client#leave callback is deprecated'); + fn(); + } + + return this; +}; + +/** + * Transmits a packet. + * + * @api private + */ + +Socket.prototype.packet = function (packet) { + if (this.flags.broadcast) { + this.log.debug('broadcasting packet'); + this.namespace.in(this.flags.room).except(this.id).packet(packet); + } else { + packet.endpoint = this.flags.endpoint; + packet = parser.encodePacket(packet); + + this.dispatch(packet, this.flags.volatile); + } + + this.setFlags(); + + return this; +}; + +/** + * Dispatches a packet + * + * @api private + */ + +Socket.prototype.dispatch = function (packet, volatile) { + if (this.manager.transports[this.id] && this.manager.transports[this.id].open) { + this.manager.transports[this.id].onDispatch(packet, volatile); + } else { + if (!volatile) { + this.manager.onClientDispatch(this.id, packet, volatile); + } + + this.manager.store.publish('dispatch:' + this.id, packet, volatile); + } +}; + +/** + * Stores data for the client. + * + * @api public + */ + +Socket.prototype.set = function (key, value, fn) { + this.store.set(key, value, fn); + return this; +}; + +/** + * Retrieves data for the client + * + * @api public + */ + +Socket.prototype.get = function (key, fn) { + this.store.get(key, fn); + return this; +}; + +/** + * Checks data for the client + * + * @api public + */ + +Socket.prototype.has = function (key, fn) { + this.store.has(key, fn); + return this; +}; + +/** + * Deletes data for the client + * + * @api public + */ + +Socket.prototype.del = function (key, fn) { + this.store.del(key, fn); + return this; +}; + +/** + * Kicks client + * + * @api public + */ + +Socket.prototype.disconnect = function () { + if (!this.disconnected) { + this.log.info('booting client'); + + if ('' === this.namespace.name) { + if (this.manager.transports[this.id] && this.manager.transports[this.id].open) { + this.manager.transports[this.id].onForcedDisconnect(); + } else { + this.manager.onClientDisconnect(this.id); + this.manager.store.publish('disconnect:' + this.id); + } + } else { + this.packet({type: 'disconnect'}); + this.manager.onLeave(this.id, this.namespace.name); + this.$emit('disconnect', 'booted'); + } + + } + + return this; +}; + +/** + * Send a message. + * + * @api public + */ + +Socket.prototype.send = function (data, fn) { + var packet = { + type: this.flags.json ? 'json' : 'message' + , data: data + }; + + if (fn) { + packet.id = ++this.ackPackets; + packet.ack = true; + this.acks[packet.id] = fn; + } + + return this.packet(packet); +}; + +/** + * Original emit function. + * + * @api private + */ + +Socket.prototype.$emit = EventEmitter.prototype.emit; + +/** + * Emit override for custom events. + * + * @api public + */ + +Socket.prototype.emit = function (ev) { + if (ev == 'newListener') { + return this.$emit.apply(this, arguments); + } + + var args = util.toArray(arguments).slice(1) + , lastArg = args[args.length - 1] + , packet = { + type: 'event' + , name: ev + }; + + if ('function' == typeof lastArg) { + packet.id = ++this.ackPackets; + packet.ack = lastArg.length ? 'data' : true; + this.acks[packet.id] = lastArg; + args = args.slice(0, args.length - 1); + } + + packet.args = args; + + return this.packet(packet); +}; diff --git a/dist/node_modules/socket.io/lib/static.js b/dist/node_modules/socket.io/lib/static.js new file mode 100644 index 0000000..fe50593 --- /dev/null +++ b/dist/node_modules/socket.io/lib/static.js @@ -0,0 +1,395 @@ + +/*! +* socket.io-node +* Copyright(c) 2011 LearnBoost +* MIT Licensed +*/ + +/** + * Module dependencies. + */ + +var client = require('socket.io-client') + , cp = require('child_process') + , fs = require('fs') + , util = require('./util'); + +/** + * File type details. + * + * @api private + */ + +var mime = { + js: { + type: 'application/javascript' + , encoding: 'utf8' + , gzip: true + } + , swf: { + type: 'application/x-shockwave-flash' + , encoding: 'binary' + , gzip: false + } +}; + +/** + * Regexp for matching custom transport patterns. Users can configure their own + * socket.io bundle based on the url structure. Different transport names are + * concatinated using the `+` char. /socket.io/socket.io+websocket.js should + * create a bundle that only contains support for the websocket. + * + * @api private + */ + +var bundle = /\+((?:\+)?[\w\-]+)*(?:\.v\d+\.\d+\.\d+)?(?:\.js)$/ + , versioning = /\.v\d+\.\d+\.\d+(?:\.js)$/; + +/** + * Export the constructor + */ + +exports = module.exports = Static; + +/** + * Static constructor + * + * @api public + */ + +function Static (manager) { + this.manager = manager; + this.cache = {}; + this.paths = {}; + + this.init(); +} + +/** + * Initialize the Static by adding default file paths. + * + * @api public + */ + +Static.prototype.init = function () { + /** + * Generates a unique id based the supplied transports array + * + * @param {Array} transports The array with transport types + * @api private + */ + function id (transports) { + var id = transports.join('').split('').map(function (char) { + return ('' + char.charCodeAt(0)).split('').pop(); + }).reduce(function (char, id) { + return char +id; + }); + + return client.version + ':' + id; + } + + /** + * Generates a socket.io-client file based on the supplied transports. + * + * @param {Array} transports The array with transport types + * @param {Function} callback Callback for the static.write + * @api private + */ + + function build (transports, callback) { + client.builder(transports, { + minify: self.manager.enabled('browser client minification') + }, function (err, content) { + callback(err, content ? new Buffer(content) : null, id(transports)); + } + ); + } + + var self = this; + + // add our default static files + this.add('/static/flashsocket/WebSocketMain.swf', { + file: client.dist + '/WebSocketMain.swf' + }); + + this.add('/static/flashsocket/WebSocketMainInsecure.swf', { + file: client.dist + '/WebSocketMainInsecure.swf' + }); + + // generates dedicated build based on the available transports + this.add('/socket.io.js', function (path, callback) { + build(self.manager.get('transports'), callback); + }); + + this.add('/socket.io.v', { mime: mime.js }, function (path, callback) { + build(self.manager.get('transports'), callback); + }); + + // allow custom builds based on url paths + this.add('/socket.io+', { mime: mime.js }, function (path, callback) { + var available = self.manager.get('transports') + , matches = path.match(bundle) + , transports = []; + + if (!matches) return callback('No valid transports'); + + // make sure they valid transports + matches[0].split('.')[0].split('+').slice(1).forEach(function (transport) { + if (!!~available.indexOf(transport)) { + transports.push(transport); + } + }); + + if (!transports.length) return callback('No valid transports'); + build(transports, callback); + }); + + // clear cache when transports change + this.manager.on('set:transports', function (key, value) { + delete self.cache['/socket.io.js']; + Object.keys(self.cache).forEach(function (key) { + if (bundle.test(key)) { + delete self.cache[key]; + } + }); + }); +}; + +/** + * Gzip compress buffers. + * + * @param {Buffer} data The buffer that needs gzip compression + * @param {Function} callback + * @api public + */ + +Static.prototype.gzip = function (data, callback) { + var gzip = cp.spawn('gzip', ['-9', '-c', '-f', '-n']) + , encoding = Buffer.isBuffer(data) ? 'binary' : 'utf8' + , buffer = [] + , err; + + gzip.stdout.on('data', function (data) { + buffer.push(data); + }); + + gzip.stderr.on('data', function (data) { + err = data +''; + buffer.length = 0; + }); + + gzip.on('close', function () { + if (err) return callback(err); + + var size = 0 + , index = 0 + , i = buffer.length + , content; + + while (i--) { + size += buffer[i].length; + } + + content = new Buffer(size); + i = buffer.length; + + buffer.forEach(function (buffer) { + var length = buffer.length; + + buffer.copy(content, index, 0, length); + index += length; + }); + + buffer.length = 0; + callback(null, content); + }); + + gzip.stdin.end(data, encoding); +}; + +/** + * Is the path a static file? + * + * @param {String} path The path that needs to be checked + * @api public + */ + +Static.prototype.has = function (path) { + // fast case + if (this.paths[path]) return this.paths[path]; + + var keys = Object.keys(this.paths) + , i = keys.length; + + while (i--) { + if (-~path.indexOf(keys[i])) return this.paths[keys[i]]; + } + + return false; +}; + +/** + * Add new paths new paths that can be served using the static provider. + * + * @param {String} path The path to respond to + * @param {Options} options Options for writing out the response + * @param {Function} [callback] Optional callback if no options.file is + * supplied this would be called instead. + * @api public + */ + +Static.prototype.add = function (path, options, callback) { + var extension = /(?:\.(\w{1,4}))$/.exec(path); + + if (!callback && typeof options == 'function') { + callback = options; + options = {}; + } + + options.mime = options.mime || (extension ? mime[extension[1]] : false); + + if (callback) options.callback = callback; + if (!(options.file || options.callback) || !options.mime) return false; + + this.paths[path] = options; + + return true; +}; + +/** + * Writes a static response. + * + * @param {String} path The path for the static content + * @param {HTTPRequest} req The request object + * @param {HTTPResponse} res The response object + * @api public + */ + +Static.prototype.write = function (path, req, res) { + /** + * Write a response without throwing errors because can throw error if the + * response is no longer writable etc. + * + * @api private + */ + + function write (status, headers, content, encoding) { + try { + res.writeHead(status, headers || undefined); + + // only write content if it's not a HEAD request and we actually have + // some content to write (304's doesn't have content). + res.end( + req.method !== 'HEAD' && content ? content : '' + , encoding || undefined + ); + } catch (e) {} + } + + /** + * Answers requests depending on the request properties and the reply object. + * + * @param {Object} reply The details and content to reply the response with + * @api private + */ + + function answer (reply) { + var cached = req.headers['if-none-match'] === reply.etag; + if (cached && self.manager.enabled('browser client etag')) { + return write(304); + } + + var accept = req.headers['accept-encoding'] || '' + , gzip = !!~accept.toLowerCase().indexOf('gzip') + , mime = reply.mime + , versioned = reply.versioned + , headers = { + 'Content-Type': mime.type + }; + + // check if we can add a etag + if (self.manager.enabled('browser client etag') && reply.etag && !versioned) { + headers['Etag'] = reply.etag; + } + + // see if we need to set Expire headers because the path is versioned + if (versioned) { + var expires = self.manager.get('browser client expires'); + headers['Cache-Control'] = 'private, x-gzip-ok="", max-age=' + expires; + headers['Date'] = new Date().toUTCString(); + headers['Expires'] = new Date(Date.now() + (expires * 1000)).toUTCString(); + } + + if (gzip && reply.gzip) { + headers['Content-Length'] = reply.gzip.length; + headers['Content-Encoding'] = 'gzip'; + headers['Vary'] = 'Accept-Encoding'; + write(200, headers, reply.gzip.content, mime.encoding); + } else { + headers['Content-Length'] = reply.length; + write(200, headers, reply.content, mime.encoding); + } + + self.manager.log.debug('served static content ' + path); + } + + var self = this + , details; + + // most common case first + if (this.manager.enabled('browser client cache') && this.cache[path]) { + return answer(this.cache[path]); + } else if (this.manager.get('browser client handler')) { + return this.manager.get('browser client handler').call(this, req, res); + } else if ((details = this.has(path))) { + /** + * A small helper function that will let us deal with fs and dynamic files + * + * @param {Object} err Optional error + * @param {Buffer} content The data + * @api private + */ + + function ready (err, content, etag) { + if (err) { + self.manager.log.warn('Unable to serve file. ' + (err.message || err)); + return write(500, null, 'Error serving static ' + path); + } + + // store the result in the cache + var reply = self.cache[path] = { + content: content + , length: content.length + , mime: details.mime + , etag: etag || client.version + , versioned: versioning.test(path) + }; + + // check if gzip is enabled + if (details.mime.gzip && self.manager.enabled('browser client gzip')) { + self.gzip(content, function (err, content) { + if (!err) { + reply.gzip = { + content: content + , length: content.length + } + } + + answer(reply); + }); + } else { + answer(reply); + } + } + + if (details.file) { + fs.readFile(details.file, ready); + } else if(details.callback) { + details.callback.call(this, path, ready); + } else { + write(404, null, 'File handle not found'); + } + } else { + write(404, null, 'File not found'); + } +}; diff --git a/dist/node_modules/socket.io/lib/store.js b/dist/node_modules/socket.io/lib/store.js new file mode 100644 index 0000000..06c0389 --- /dev/null +++ b/dist/node_modules/socket.io/lib/store.js @@ -0,0 +1,98 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Expose the constructor. + */ + +exports = module.exports = Store; + +/** + * Module dependencies. + */ + +var EventEmitter = process.EventEmitter; + +/** + * Store interface + * + * @api public + */ + +function Store (options) { + this.options = options; + this.clients = {}; +}; + +/** + * Inherit from EventEmitter. + */ + +Store.prototype.__proto__ = EventEmitter.prototype; + +/** + * Initializes a client store + * + * @param {String} id + * @api public + */ + +Store.prototype.client = function (id) { + if (!this.clients[id]) { + this.clients[id] = new (this.constructor.Client)(this, id); + } + + return this.clients[id]; +}; + +/** + * Destroys a client + * + * @api {String} sid + * @param {Number} number of seconds to expire client data + * @api private + */ + +Store.prototype.destroyClient = function (id, expiration) { + if (this.clients[id]) { + this.clients[id].destroy(expiration); + delete this.clients[id]; + } + + return this; +}; + +/** + * Destroys the store + * + * @param {Number} number of seconds to expire client data + * @api private + */ + +Store.prototype.destroy = function (clientExpiration) { + var keys = Object.keys(this.clients) + , count = keys.length; + + for (var i = 0, l = count; i < l; i++) { + this.destroyClient(keys[i], clientExpiration); + } + + this.clients = {}; + + return this; +}; + +/** + * Client. + * + * @api public + */ + +Store.Client = function (store, id) { + this.store = store; + this.id = id; +}; diff --git a/dist/node_modules/socket.io/lib/stores/memory.js b/dist/node_modules/socket.io/lib/stores/memory.js new file mode 100644 index 0000000..8b731a7 --- /dev/null +++ b/dist/node_modules/socket.io/lib/stores/memory.js @@ -0,0 +1,143 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var crypto = require('crypto') + , Store = require('../store'); + +/** + * Exports the constructor. + */ + +exports = module.exports = Memory; +Memory.Client = Client; + +/** + * Memory store + * + * @api public + */ + +function Memory (opts) { + Store.call(this, opts); +}; + +/** + * Inherits from Store. + */ + +Memory.prototype.__proto__ = Store.prototype; + +/** + * Publishes a message. + * + * @api private + */ + +Memory.prototype.publish = function () { }; + +/** + * Subscribes to a channel + * + * @api private + */ + +Memory.prototype.subscribe = function () { }; + +/** + * Unsubscribes + * + * @api private + */ + +Memory.prototype.unsubscribe = function () { }; + +/** + * Client constructor + * + * @api private + */ + +function Client () { + Store.Client.apply(this, arguments); + this.data = {}; +}; + +/** + * Inherits from Store.Client + */ + +Client.prototype.__proto__ = Store.Client; + +/** + * Gets a key + * + * @api public + */ + +Client.prototype.get = function (key, fn) { + fn(null, this.data[key] === undefined ? null : this.data[key]); + return this; +}; + +/** + * Sets a key + * + * @api public + */ + +Client.prototype.set = function (key, value, fn) { + this.data[key] = value; + fn && fn(null); + return this; +}; + +/** + * Has a key + * + * @api public + */ + +Client.prototype.has = function (key, fn) { + fn(null, key in this.data); +}; + +/** + * Deletes a key + * + * @api public + */ + +Client.prototype.del = function (key, fn) { + delete this.data[key]; + fn && fn(null); + return this; +}; + +/** + * Destroys the client. + * + * @param {Number} number of seconds to expire data + * @api private + */ + +Client.prototype.destroy = function (expiration) { + if ('number' != typeof expiration) { + this.data = {}; + } else { + var self = this; + + setTimeout(function () { + self.data = {}; + }, expiration * 1000); + } + + return this; +}; diff --git a/dist/node_modules/socket.io/lib/stores/redis.js b/dist/node_modules/socket.io/lib/stores/redis.js new file mode 100644 index 0000000..8fea235 --- /dev/null +++ b/dist/node_modules/socket.io/lib/stores/redis.js @@ -0,0 +1,269 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var crypto = require('crypto') + , Store = require('../store') + , assert = require('assert'); + +/** + * Exports the constructor. + */ + +exports = module.exports = Redis; +Redis.Client = Client; + +/** + * Redis store. + * Options: + * - nodeId (fn) gets an id that uniquely identifies this node + * - redis (fn) redis constructor, defaults to redis + * - redisPub (object) options to pass to the pub redis client + * - redisSub (object) options to pass to the sub redis client + * - redisClient (object) options to pass to the general redis client + * - pack (fn) custom packing, defaults to JSON or msgpack if installed + * - unpack (fn) custom packing, defaults to JSON or msgpack if installed + * + * @api public + */ + +function Redis (opts) { + opts = opts || {}; + + // node id to uniquely identify this node + var nodeId = opts.nodeId || function () { + // by default, we generate a random id + return Math.abs(Math.random() * Math.random() * Date.now() | 0); + }; + + this.nodeId = nodeId(); + + // packing / unpacking mechanism + if (opts.pack) { + this.pack = opts.pack; + this.unpack = opts.unpack; + } else { + try { + var msgpack = require('msgpack'); + this.pack = msgpack.pack; + this.unpack = msgpack.unpack; + } catch (e) { + this.pack = JSON.stringify; + this.unpack = JSON.parse; + } + } + + var redis = opts.redis || require('redis') + , RedisClient = redis.RedisClient; + + // initialize a pubsub client and a regular client + if (opts.redisPub instanceof RedisClient) { + this.pub = opts.redisPub; + } else { + opts.redisPub || (opts.redisPub = {}); + this.pub = redis.createClient(opts.redisPub.port, opts.redisPub.host, opts.redisPub); + } + if (opts.redisSub instanceof RedisClient) { + this.sub = opts.redisSub; + } else { + opts.redisSub || (opts.redisSub = {}); + this.sub = redis.createClient(opts.redisSub.port, opts.redisSub.host, opts.redisSub); + } + if (opts.redisClient instanceof RedisClient) { + this.cmd = opts.redisClient; + } else { + opts.redisClient || (opts.redisClient = {}); + this.cmd = redis.createClient(opts.redisClient.port, opts.redisClient.host, opts.redisClient); + } + + Store.call(this, opts); + + this.sub.setMaxListeners(0); + this.setMaxListeners(0); +}; + +/** + * Inherits from Store. + */ + +Redis.prototype.__proto__ = Store.prototype; + +/** + * Publishes a message. + * + * @api private + */ + +Redis.prototype.publish = function (name) { + var args = Array.prototype.slice.call(arguments, 1); + this.pub.publish(name, this.pack({ nodeId: this.nodeId, args: args })); + this.emit.apply(this, ['publish', name].concat(args)); +}; + +/** + * Subscribes to a channel + * + * @api private + */ + +Redis.prototype.subscribe = function (name, consumer, fn) { + this.sub.subscribe(name); + + if (consumer || fn) { + var self = this; + + self.sub.on('subscribe', function subscribe (ch) { + if (name == ch) { + function message (ch, msg) { + if (name == ch) { + msg = self.unpack(msg); + + // we check that the message consumed wasnt emitted by this node + if (self.nodeId != msg.nodeId) { + consumer.apply(null, msg.args); + } + } + }; + + self.sub.on('message', message); + + self.on('unsubscribe', function unsubscribe (ch) { + if (name == ch) { + self.sub.removeListener('message', message); + self.removeListener('unsubscribe', unsubscribe); + } + }); + + self.sub.removeListener('subscribe', subscribe); + + fn && fn(); + } + }); + } + + this.emit('subscribe', name, consumer, fn); +}; + +/** + * Unsubscribes + * + * @api private + */ + +Redis.prototype.unsubscribe = function (name, fn) { + this.sub.unsubscribe(name); + + if (fn) { + var client = this.sub; + + client.on('unsubscribe', function unsubscribe (ch) { + if (name == ch) { + fn(); + client.removeListener('unsubscribe', unsubscribe); + } + }); + } + + this.emit('unsubscribe', name, fn); +}; + +/** + * Destroys the store + * + * @api public + */ + +Redis.prototype.destroy = function () { + Store.prototype.destroy.call(this); + + this.pub.end(); + this.sub.end(); + this.cmd.end(); +}; + +/** + * Client constructor + * + * @api private + */ + +function Client (store, id) { + Store.Client.call(this, store, id); +}; + +/** + * Inherits from Store.Client + */ + +Client.prototype.__proto__ = Store.Client; + +/** + * Redis hash get + * + * @api private + */ + +Client.prototype.get = function (key, fn) { + this.store.cmd.hget(this.id, key, fn); + return this; +}; + +/** + * Redis hash set + * + * @api private + */ + +Client.prototype.set = function (key, value, fn) { + this.store.cmd.hset(this.id, key, value, fn); + return this; +}; + +/** + * Redis hash del + * + * @api private + */ + +Client.prototype.del = function (key, fn) { + this.store.cmd.hdel(this.id, key, fn); + return this; +}; + +/** + * Redis hash has + * + * @api private + */ + +Client.prototype.has = function (key, fn) { + this.store.cmd.hexists(this.id, key, function (err, has) { + if (err) return fn(err); + fn(null, !!has); + }); + return this; +}; + +/** + * Destroys client + * + * @param {Number} number of seconds to expire data + * @api private + */ + +Client.prototype.destroy = function (expiration) { + if ('number' != typeof expiration) { + this.store.cmd.del(this.id); + } else { + this.store.cmd.expire(this.id, expiration); + } + + return this; +}; diff --git a/dist/node_modules/socket.io/lib/transport.js b/dist/node_modules/socket.io/lib/transport.js new file mode 100644 index 0000000..2e4c08b --- /dev/null +++ b/dist/node_modules/socket.io/lib/transport.js @@ -0,0 +1,534 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var parser = require('./parser'); + +/** + * Expose the constructor. + */ + +exports = module.exports = Transport; + +/** + * Transport constructor. + * + * @api public + */ + +function Transport (mng, data, req) { + this.manager = mng; + this.id = data.id; + this.disconnected = false; + this.drained = true; + this.handleRequest(req); +}; + +/** + * Access the logger. + * + * @api public + */ + +Transport.prototype.__defineGetter__('log', function () { + return this.manager.log; +}); + +/** + * Access the store. + * + * @api public + */ + +Transport.prototype.__defineGetter__('store', function () { + return this.manager.store; +}); + +/** + * Handles a request when it's set. + * + * @api private + */ + +Transport.prototype.handleRequest = function (req) { + this.log.debug('setting request', req.method, req.url); + this.req = req; + + if (req.method == 'GET') { + this.socket = req.socket; + this.open = true; + this.drained = true; + this.setHeartbeatInterval(); + + this.setHandlers(); + this.onSocketConnect(); + } +}; + +/** + * Called when a connection is first set. + * + * @api private + */ + +Transport.prototype.onSocketConnect = function () { }; + +/** + * Sets transport handlers + * + * @api private + */ + +Transport.prototype.setHandlers = function () { + var self = this; + + // we need to do this in a pub/sub way since the client can POST the message + // over a different socket (ie: different Transport instance) + this.store.subscribe('heartbeat-clear:' + this.id, function () { + self.onHeartbeatClear(); + }); + + this.store.subscribe('disconnect-force:' + this.id, function () { + self.onForcedDisconnect(); + }); + + this.store.subscribe('dispatch:' + this.id, function (packet, volatile) { + self.onDispatch(packet, volatile); + }); + + this.bound = { + end: this.onSocketEnd.bind(this) + , close: this.onSocketClose.bind(this) + , error: this.onSocketError.bind(this) + , drain: this.onSocketDrain.bind(this) + }; + + this.socket.on('end', this.bound.end); + this.socket.on('close', this.bound.close); + this.socket.on('error', this.bound.error); + this.socket.on('drain', this.bound.drain); + + this.handlersSet = true; +}; + +/** + * Removes transport handlers + * + * @api private + */ + +Transport.prototype.clearHandlers = function () { + if (this.handlersSet) { + this.store.unsubscribe('disconnect-force:' + this.id); + this.store.unsubscribe('heartbeat-clear:' + this.id); + this.store.unsubscribe('dispatch:' + this.id); + + this.socket.removeListener('end', this.bound.end); + this.socket.removeListener('close', this.bound.close); + this.socket.removeListener('error', this.bound.error); + this.socket.removeListener('drain', this.bound.drain); + } +}; + +/** + * Called when the connection dies + * + * @api private + */ + +Transport.prototype.onSocketEnd = function () { + this.end('socket end'); +}; + +/** + * Called when the connection dies + * + * @api private + */ + +Transport.prototype.onSocketClose = function (error) { + this.end(error ? 'socket error' : 'socket close'); +}; + +/** + * Called when the connection has an error. + * + * @api private + */ + +Transport.prototype.onSocketError = function (err) { + if (this.open) { + this.socket.destroy(); + this.onClose(); + } + + this.log.info('socket error ' + err.stack); +}; + +/** + * Called when the connection is drained. + * + * @api private + */ + +Transport.prototype.onSocketDrain = function () { + this.drained = true; +}; + +/** + * Called upon receiving a heartbeat packet. + * + * @api private + */ + +Transport.prototype.onHeartbeatClear = function () { + this.clearHeartbeatTimeout(); + this.setHeartbeatInterval(); +}; + +/** + * Called upon a forced disconnection. + * + * @api private + */ + +Transport.prototype.onForcedDisconnect = function () { + if (!this.disconnected) { + this.log.info('transport end by forced client disconnection'); + if (this.open) { + this.packet({ type: 'disconnect' }); + } + this.end('booted'); + } +}; + +/** + * Dispatches a packet. + * + * @api private + */ + +Transport.prototype.onDispatch = function (packet, volatile) { + if (volatile) { + this.writeVolatile(packet); + } else { + this.write(packet); + } +}; + +/** + * Sets the close timeout. + */ + +Transport.prototype.setCloseTimeout = function () { + if (!this.closeTimeout) { + var self = this; + + this.closeTimeout = setTimeout(function () { + self.log.debug('fired close timeout for client', self.id); + self.closeTimeout = null; + self.end('close timeout'); + }, this.manager.get('close timeout') * 1000); + + this.log.debug('set close timeout for client', this.id); + } +}; + +/** + * Clears the close timeout. + */ + +Transport.prototype.clearCloseTimeout = function () { + if (this.closeTimeout) { + clearTimeout(this.closeTimeout); + this.closeTimeout = null; + + this.log.debug('cleared close timeout for client', this.id); + } +}; + +/** + * Sets the heartbeat timeout + */ + +Transport.prototype.setHeartbeatTimeout = function () { + if (!this.heartbeatTimeout && this.manager.enabled('heartbeats')) { + var self = this; + + this.heartbeatTimeout = setTimeout(function () { + self.log.debug('fired heartbeat timeout for client', self.id); + self.heartbeatTimeout = null; + self.end('heartbeat timeout'); + }, this.manager.get('heartbeat timeout') * 1000); + + this.log.debug('set heartbeat timeout for client', this.id); + } +}; + +/** + * Clears the heartbeat timeout + * + * @param text + */ + +Transport.prototype.clearHeartbeatTimeout = function () { + if (this.heartbeatTimeout && this.manager.enabled('heartbeats')) { + clearTimeout(this.heartbeatTimeout); + this.heartbeatTimeout = null; + this.log.debug('cleared heartbeat timeout for client', this.id); + } +}; + +/** + * Sets the heartbeat interval. To be called when a connection opens and when + * a heartbeat is received. + * + * @api private + */ + +Transport.prototype.setHeartbeatInterval = function () { + if (!this.heartbeatInterval && this.manager.enabled('heartbeats')) { + var self = this; + + this.heartbeatInterval = setTimeout(function () { + self.heartbeat(); + self.heartbeatInterval = null; + }, this.manager.get('heartbeat interval') * 1000); + + this.log.debug('set heartbeat interval for client', this.id); + } +}; + +/** + * Clears all timeouts. + * + * @api private + */ + +Transport.prototype.clearTimeouts = function () { + this.clearCloseTimeout(); + this.clearHeartbeatTimeout(); + this.clearHeartbeatInterval(); +}; + +/** + * Sends a heartbeat + * + * @api private + */ + +Transport.prototype.heartbeat = function () { + if (this.open) { + this.log.debug('emitting heartbeat for client', this.id); + this.packet({ type: 'heartbeat' }); + this.setHeartbeatTimeout(); + } + + return this; +}; + +/** + * Handles a message. + * + * @param {Object} packet object + * @api private + */ + +Transport.prototype.onMessage = function (packet) { + var current = this.manager.transports[this.id]; + + if ('heartbeat' == packet.type) { + this.log.debug('got heartbeat packet'); + + if (current && current.open) { + current.onHeartbeatClear(); + } else { + this.store.publish('heartbeat-clear:' + this.id); + } + } else { + if ('disconnect' == packet.type && packet.endpoint == '') { + this.log.debug('got disconnection packet'); + + if (current) { + current.onForcedDisconnect(); + } else { + this.store.publish('disconnect-force:' + this.id); + } + + return; + } + + if (packet.id && packet.ack != 'data') { + this.log.debug('acknowledging packet automatically'); + + var ack = parser.encodePacket({ + type: 'ack' + , ackId: packet.id + , endpoint: packet.endpoint || '' + }); + + if (current && current.open) { + current.onDispatch(ack); + } else { + this.manager.onClientDispatch(this.id, ack); + this.store.publish('dispatch:' + this.id, ack); + } + } + + // handle packet locally or publish it + if (current) { + this.manager.onClientMessage(this.id, packet); + } else { + this.store.publish('message:' + this.id, packet); + } + } +}; + +/** + * Clears the heartbeat interval + * + * @api private + */ + +Transport.prototype.clearHeartbeatInterval = function () { + if (this.heartbeatInterval && this.manager.enabled('heartbeats')) { + clearTimeout(this.heartbeatInterval); + this.heartbeatInterval = null; + this.log.debug('cleared heartbeat interval for client', this.id); + } +}; + +/** + * Finishes the connection and makes sure client doesn't reopen + * + * @api private + */ + +Transport.prototype.disconnect = function (reason) { + this.packet({ type: 'disconnect' }); + this.end(reason); + + return this; +}; + +/** + * Closes the connection. + * + * @api private + */ + +Transport.prototype.close = function () { + if (this.open) { + this.doClose(); + this.onClose(); + } +}; + +/** + * Called upon a connection close. + * + * @api private + */ + +Transport.prototype.onClose = function () { + if (this.open) { + this.setCloseTimeout(); + this.clearHandlers(); + this.open = false; + this.manager.onClose(this.id); + this.store.publish('close', this.id); + } +}; + +/** + * Cleans up the connection, considers the client disconnected. + * + * @api private + */ + +Transport.prototype.end = function (reason) { + if (!this.disconnected) { + this.log.info('transport end (' + reason + ')'); + + var local = this.manager.transports[this.id]; + + this.close(); + this.clearTimeouts(); + this.disconnected = true; + + if (local) { + this.manager.onClientDisconnect(this.id, reason, true); + } else { + this.store.publish('disconnect:' + this.id, reason); + } + } +}; + +/** + * Signals that the transport should pause and buffer data. + * + * @api public + */ + +Transport.prototype.discard = function () { + this.log.debug('discarding transport'); + this.discarded = true; + this.clearTimeouts(); + this.clearHandlers(); + + return this; +}; + +/** + * Writes an error packet with the specified reason and advice. + * + * @param {Number} advice + * @param {Number} reason + * @api public + */ + +Transport.prototype.error = function (reason, advice) { + this.packet({ + type: 'error' + , reason: reason + , advice: advice + }); + + this.log.warn(reason, advice ? ('client should ' + advice) : ''); + this.end('error'); +}; + +/** + * Write a packet. + * + * @api public + */ + +Transport.prototype.packet = function (obj) { + return this.write(parser.encodePacket(obj)); +}; + +/** + * Writes a volatile message. + * + * @api private + */ + +Transport.prototype.writeVolatile = function (msg) { + if (this.open) { + if (this.drained) { + this.write(msg); + } else { + this.log.debug('ignoring volatile packet, buffer not drained'); + } + } else { + this.log.debug('ignoring volatile packet, transport not open'); + } +}; diff --git a/dist/node_modules/socket.io/lib/transports/flashsocket.js b/dist/node_modules/socket.io/lib/transports/flashsocket.js new file mode 100644 index 0000000..dc2d78b --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/flashsocket.js @@ -0,0 +1,129 @@ +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ +var WebSocket = require('./websocket'); + +/** + * Export the constructor. + */ + +exports = module.exports = FlashSocket; + +/** + * The FlashSocket transport is just a proxy + * for WebSocket connections. + * + * @api public + */ + +function FlashSocket (mng, data, req) { + return WebSocket.call(this, mng, data, req); +} + +/** + * Inherits from WebSocket. + */ + +FlashSocket.prototype.__proto__ = WebSocket.prototype; + +/** + * Transport name + * + * @api public + */ + +FlashSocket.prototype.name = 'flashsocket'; + +/** + * Listens for new configuration changes of the Manager + * this way we can enable and disable the flash server. + * + * @param {Manager} Manager instance. + * @api private + */ + + +FlashSocket.init = function (manager) { + var server; + function create () { + + // Drop out immediately if the user has + // disabled the flash policy server + if (!manager.get('flash policy server')) { + return; + } + + server = require('policyfile').createServer({ + log: function(msg){ + manager.log.info(msg); + } + }, manager.get('origins')); + + server.on('close', function (e) { + server = null; + }); + + server.listen(manager.get('flash policy port'), manager.server); + + manager.flashPolicyServer = server; + } + + // listen for origin changes, so we can update the server + manager.on('set:origins', function (value, key) { + if (!server) return; + + // update the origins and compile a new response buffer + server.origins = Array.isArray(value) ? value : [value]; + server.compile(); + }); + + // destory the server and create a new server + manager.on('set:flash policy port', function (value, key) { + var transports = manager.get('transports'); + if (~transports.indexOf('flashsocket')) { + if (server) { + if (server.port === value) return; + // destroy the server and rebuild it on a new port + try { + server.close(); + } + catch (e) { /* ignore exception. could e.g. be that the server isn't started yet */ } + } + create(); + } + }); + + // create or destroy the server + manager.on('set:flash policy server', function (value, key) { + var transports = manager.get('transports'); + if (~transports.indexOf('flashsocket')) { + if (server && !value) { + // destroy the server + try { + server.close(); + } + catch (e) { /* ignore exception. could e.g. be that the server isn't started yet */ } + } + } else if (!server && value) { + // create the server + create(); + } + }); + + // only start the server + manager.on('set:transports', function (value, key){ + if (!server && ~manager.get('transports').indexOf('flashsocket')) { + create(); + } + }); + // check if we need to initialize at start + if (~manager.get('transports').indexOf('flashsocket')){ + create(); + } +}; diff --git a/dist/node_modules/socket.io/lib/transports/htmlfile.js b/dist/node_modules/socket.io/lib/transports/htmlfile.js new file mode 100644 index 0000000..e8709a3 --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/htmlfile.js @@ -0,0 +1,82 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var HTTPTransport = require('./http'); + +/** + * Export the constructor. + */ + +exports = module.exports = HTMLFile; + +/** + * HTMLFile transport constructor. + * + * @api public + */ + +function HTMLFile (mng, data, req) { + HTTPTransport.call(this, mng, data, req); +}; + +/** + * Inherits from Transport. + */ + +HTMLFile.prototype.__proto__ = HTTPTransport.prototype; + +/** + * Transport name + * + * @api public + */ + +HTMLFile.prototype.name = 'htmlfile'; + +/** + * Handles the request. + * + * @api private + */ + +HTMLFile.prototype.handleRequest = function (req) { + HTTPTransport.prototype.handleRequest.call(this, req); + + if (req.method == 'GET') { + req.res.writeHead(200, { + 'Content-Type': 'text/html; charset=UTF-8' + , 'Connection': 'keep-alive' + , 'Transfer-Encoding': 'chunked' + }); + + req.res.write( + '' + + '' + + new Array(174).join(' ') + ); + } +}; + +/** + * Performs the write. + * + * @api private + */ + +HTMLFile.prototype.write = function (data) { + data = ''; + + if (this.response.write(data)) { + this.drained = true; + } + + this.log.debug(this.name + ' writing', data); +}; diff --git a/dist/node_modules/socket.io/lib/transports/http-polling.js b/dist/node_modules/socket.io/lib/transports/http-polling.js new file mode 100644 index 0000000..89b7e04 --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/http-polling.js @@ -0,0 +1,147 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var HTTPTransport = require('./http'); + +/** + * Exports the constructor. + */ + +exports = module.exports = HTTPPolling; + +/** + * HTTP polling constructor. + * + * @api public. + */ + +function HTTPPolling (mng, data, req) { + HTTPTransport.call(this, mng, data, req); +}; + +/** + * Inherits from HTTPTransport. + * + * @api public. + */ + +HTTPPolling.prototype.__proto__ = HTTPTransport.prototype; + +/** + * Transport name + * + * @api public + */ + +HTTPPolling.prototype.name = 'httppolling'; + +/** + * Override setHandlers + * + * @api private + */ + +HTTPPolling.prototype.setHandlers = function () { + HTTPTransport.prototype.setHandlers.call(this); + this.socket.removeListener('end', this.bound.end); + this.socket.removeListener('close', this.bound.close); +}; + +/** + * Removes heartbeat timeouts for polling. + */ + +HTTPPolling.prototype.setHeartbeatInterval = function () { + return this; +}; + +/** + * Handles a request + * + * @api private + */ + +HTTPPolling.prototype.handleRequest = function (req) { + HTTPTransport.prototype.handleRequest.call(this, req); + + if (req.method == 'GET') { + var self = this; + + this.pollTimeout = setTimeout(function () { + self.packet({ type: 'noop' }); + self.log.debug(self.name + ' closed due to exceeded duration'); + }, this.manager.get('polling duration') * 1000); + + this.log.debug('setting poll timeout'); + } +}; + +/** + * Clears polling timeout + * + * @api private + */ + +HTTPPolling.prototype.clearPollTimeout = function () { + if (this.pollTimeout) { + clearTimeout(this.pollTimeout); + this.pollTimeout = null; + this.log.debug('clearing poll timeout'); + } + + return this; +}; + +/** + * Override clear timeouts to clear the poll timeout + * + * @api private + */ + +HTTPPolling.prototype.clearTimeouts = function () { + HTTPTransport.prototype.clearTimeouts.call(this); + + this.clearPollTimeout(); +}; + +/** + * doWrite to clear poll timeout + * + * @api private + */ + +HTTPPolling.prototype.doWrite = function () { + this.clearPollTimeout(); +}; + +/** + * Performs a write. + * + * @api private. + */ + +HTTPPolling.prototype.write = function (data, close) { + this.doWrite(data); + this.response.end(); + this.onClose(); +}; + +/** + * Override end. + * + * @api private + */ + +HTTPPolling.prototype.end = function (reason) { + this.clearPollTimeout(); + return HTTPTransport.prototype.end.call(this, reason); +}; + diff --git a/dist/node_modules/socket.io/lib/transports/http.js b/dist/node_modules/socket.io/lib/transports/http.js new file mode 100644 index 0000000..28db794 --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/http.js @@ -0,0 +1,121 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var Transport = require('../transport') + , parser = require('../parser') + , qs = require('querystring'); + +/** + * Export the constructor. + */ + +exports = module.exports = HTTPTransport; + +/** + * HTTP interface constructor. For all non-websocket transports. + * + * @api public + */ + +function HTTPTransport (mng, data, req) { + Transport.call(this, mng, data, req); +}; + +/** + * Inherits from Transport. + */ + +HTTPTransport.prototype.__proto__ = Transport.prototype; + +/** + * Handles a request. + * + * @api private + */ + +HTTPTransport.prototype.handleRequest = function (req) { + + // Always set the response in case an error is returned to the client + this.response = req.res; + + if (req.method == 'POST') { + var buffer = '' + , res = req.res + , origin = req.headers.origin + , headers = { 'Content-Length': 1, 'Content-Type': 'text/plain; charset=UTF-8' } + , self = this; + + req.on('data', function (data) { + buffer += data; + + if (Buffer.byteLength(buffer) >= self.manager.get('destroy buffer size')) { + buffer = ''; + req.connection.destroy(); + } + }); + + req.on('end', function () { + res.writeHead(200, headers); + res.end('1'); + + self.onData(self.postEncoded ? qs.parse(buffer).d : buffer); + }); + + // prevent memory leaks for uncompleted requests + req.on('close', function () { + buffer = ''; + self.onClose(); + }); + + if (origin) { + // https://developer.mozilla.org/En/HTTP_Access_Control + headers['Access-Control-Allow-Origin'] = origin; + headers['Access-Control-Allow-Credentials'] = 'true'; + } + } else { + Transport.prototype.handleRequest.call(this, req); + } +}; + +/** + * Handles data payload. + * + * @api private + */ + +HTTPTransport.prototype.onData = function (data) { + var messages = parser.decodePayload(data); + this.log.debug(this.name + ' received data packet', data); + + for (var i = 0, l = messages.length; i < l; i++) { + this.onMessage(messages[i]); + } +}; + +/** + * Closes the request-response cycle + * + * @api private + */ + +HTTPTransport.prototype.doClose = function () { + this.response.end(); +}; + +/** + * Writes a payload of messages + * + * @api private + */ + +HTTPTransport.prototype.payload = function (msgs) { + this.write(parser.encodePayload(msgs)); +}; diff --git a/dist/node_modules/socket.io/lib/transports/index.js b/dist/node_modules/socket.io/lib/transports/index.js new file mode 100644 index 0000000..b865559 --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/index.js @@ -0,0 +1,12 @@ + +/** + * Export transports. + */ + +module.exports = { + websocket: require('./websocket') + , flashsocket: require('./flashsocket') + , htmlfile: require('./htmlfile') + , 'xhr-polling': require('./xhr-polling') + , 'jsonp-polling': require('./jsonp-polling') +}; diff --git a/dist/node_modules/socket.io/lib/transports/jsonp-polling.js b/dist/node_modules/socket.io/lib/transports/jsonp-polling.js new file mode 100644 index 0000000..ad7d5af --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/jsonp-polling.js @@ -0,0 +1,97 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var HTTPPolling = require('./http-polling'); +var jsonpolling_re = /^\d+$/ + +/** + * Export the constructor. + */ + +exports = module.exports = JSONPPolling; + +/** + * JSON-P polling transport. + * + * @api public + */ + +function JSONPPolling (mng, data, req) { + HTTPPolling.call(this, mng, data, req); + + this.head = 'io.j[0]('; + this.foot = ');'; + + if (data.query.i && jsonpolling_re.test(data.query.i)) { + this.head = 'io.j[' + data.query.i + ']('; + } +}; + +/** + * Inherits from Transport. + */ + +JSONPPolling.prototype.__proto__ = HTTPPolling.prototype; + +/** + * Transport name + * + * @api public + */ + +JSONPPolling.prototype.name = 'jsonppolling'; + +/** + * Make sure POST are decoded. + */ + +JSONPPolling.prototype.postEncoded = true; + +/** + * Handles incoming data. + * Due to a bug in \n handling by browsers, we expect a JSONified string. + * + * @api private + */ + +JSONPPolling.prototype.onData = function (data) { + try { + data = JSON.parse(data); + } catch (e) { + this.error('parse', 'reconnect'); + return; + } + + HTTPPolling.prototype.onData.call(this, data); +}; + +/** + * Performs the write. + * + * @api private + */ + +JSONPPolling.prototype.doWrite = function (data) { + HTTPPolling.prototype.doWrite.call(this); + + var data = data === undefined + ? '' : this.head + JSON.stringify(data) + this.foot; + + this.response.writeHead(200, { + 'Content-Type': 'text/javascript; charset=UTF-8' + , 'Content-Length': Buffer.byteLength(data) + , 'Connection': 'Keep-Alive' + , 'X-XSS-Protection': '0' + }); + + this.response.write(data); + this.log.debug(this.name + ' writing', data); +}; diff --git a/dist/node_modules/socket.io/lib/transports/websocket.js b/dist/node_modules/socket.io/lib/transports/websocket.js new file mode 100644 index 0000000..78a4304 --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/websocket.js @@ -0,0 +1,36 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var protocolVersions = require('./websocket/'); + +/** + * Export the constructor. + */ + +exports = module.exports = WebSocket; + +/** + * HTTP interface constructor. Interface compatible with all transports that + * depend on request-response cycles. + * + * @api public + */ + +function WebSocket (mng, data, req) { + var transport + , version = req.headers['sec-websocket-version']; + if (typeof version !== 'undefined' && typeof protocolVersions[version] !== 'undefined') { + transport = new protocolVersions[version](mng, data, req); + } + else transport = new protocolVersions['default'](mng, data, req); + if (typeof this.name !== 'undefined') transport.name = this.name; + return transport; +}; diff --git a/dist/node_modules/socket.io/lib/transports/websocket/default.js b/dist/node_modules/socket.io/lib/transports/websocket/default.js new file mode 100644 index 0000000..091fdd4 --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/websocket/default.js @@ -0,0 +1,362 @@ +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var Transport = require('../../transport') + , EventEmitter = process.EventEmitter + , crypto = require('crypto') + , parser = require('../../parser'); + +/** + * Export the constructor. + */ + +exports = module.exports = WebSocket; + +/** + * HTTP interface constructor. Interface compatible with all transports that + * depend on request-response cycles. + * + * @api public + */ + +function WebSocket (mng, data, req) { + // parser + var self = this; + + this.parser = new Parser(); + this.parser.on('data', function (packet) { + self.log.debug(self.name + ' received data packet', packet); + self.onMessage(parser.decodePacket(packet)); + }); + this.parser.on('close', function () { + self.end(); + }); + this.parser.on('error', function () { + self.end(); + }); + + Transport.call(this, mng, data, req); +}; + +/** + * Inherits from Transport. + */ + +WebSocket.prototype.__proto__ = Transport.prototype; + +/** + * Transport name + * + * @api public + */ + +WebSocket.prototype.name = 'websocket'; + +/** + * Websocket draft version + * + * @api public + */ + +WebSocket.prototype.protocolVersion = 'hixie-76'; + +/** + * Called when the socket connects. + * + * @api private + */ + +WebSocket.prototype.onSocketConnect = function () { + var self = this; + + this.socket.setNoDelay(true); + + this.buffer = true; + this.buffered = []; + + if (this.req.headers.upgrade !== 'WebSocket') { + this.log.warn(this.name + ' connection invalid'); + this.end(); + return; + } + + var origin = this.req.headers['origin'] + , waitingForNonce = false; + if(this.manager.settings['match origin protocol']){ + location = (origin.indexOf('https')>-1 ? 'wss' : 'ws') + '://' + this.req.headers.host + this.req.url; + }else if(this.socket.encrypted){ + location = 'wss://' + this.req.headers.host + this.req.url; + }else{ + location = 'ws://' + this.req.headers.host + this.req.url; + } + + if (this.req.headers['sec-websocket-key1']) { + // If we don't have the nonce yet, wait for it (HAProxy compatibility). + if (! (this.req.head && this.req.head.length >= 8)) { + waitingForNonce = true; + } + + var headers = [ + 'HTTP/1.1 101 WebSocket Protocol Handshake' + , 'Upgrade: WebSocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Origin: ' + origin + , 'Sec-WebSocket-Location: ' + location + ]; + + if (this.req.headers['sec-websocket-protocol']){ + headers.push('Sec-WebSocket-Protocol: ' + + this.req.headers['sec-websocket-protocol']); + } + } else { + var headers = [ + 'HTTP/1.1 101 Web Socket Protocol Handshake' + , 'Upgrade: WebSocket' + , 'Connection: Upgrade' + , 'WebSocket-Origin: ' + origin + , 'WebSocket-Location: ' + location + ]; + } + + try { + this.socket.write(headers.concat('', '').join('\r\n')); + this.socket.setTimeout(0); + this.socket.setNoDelay(true); + this.socket.setEncoding('utf8'); + } catch (e) { + this.end(); + return; + } + + if (waitingForNonce) { + this.socket.setEncoding('binary'); + } else if (this.proveReception(headers)) { + self.flush(); + } + + var headBuffer = ''; + + this.socket.on('data', function (data) { + if (waitingForNonce) { + headBuffer += data; + + if (headBuffer.length < 8) { + return; + } + + // Restore the connection to utf8 encoding after receiving the nonce + self.socket.setEncoding('utf8'); + waitingForNonce = false; + + // Stuff the nonce into the location where it's expected to be + self.req.head = headBuffer.substr(0, 8); + headBuffer = ''; + + if (self.proveReception(headers)) { + self.flush(); + } + + return; + } + + self.parser.add(data); + }); +}; + +/** + * Writes to the socket. + * + * @api private + */ + +WebSocket.prototype.write = function (data) { + if (this.open) { + this.drained = false; + + if (this.buffer) { + this.buffered.push(data); + return this; + } + + var length = Buffer.byteLength(data) + , buffer = new Buffer(2 + length); + + buffer.write('\x00', 'binary'); + buffer.write(data, 1, 'utf8'); + buffer.write('\xff', 1 + length, 'binary'); + + try { + if (this.socket.write(buffer)) { + this.drained = true; + } + } catch (e) { + this.end(); + } + + this.log.debug(this.name + ' writing', data); + } +}; + +/** + * Flushes the internal buffer + * + * @api private + */ + +WebSocket.prototype.flush = function () { + this.buffer = false; + + for (var i = 0, l = this.buffered.length; i < l; i++) { + this.write(this.buffered.splice(0, 1)[0]); + } +}; + +/** + * Finishes the handshake. + * + * @api private + */ + +WebSocket.prototype.proveReception = function (headers) { + var self = this + , k1 = this.req.headers['sec-websocket-key1'] + , k2 = this.req.headers['sec-websocket-key2']; + + if (k1 && k2){ + var md5 = crypto.createHash('md5'); + + [k1, k2].forEach(function (k) { + var n = parseInt(k.replace(/[^\d]/g, '')) + , spaces = k.replace(/[^ ]/g, '').length; + + if (spaces === 0 || n % spaces !== 0){ + self.log.warn('Invalid ' + self.name + ' key: "' + k + '".'); + self.end(); + return false; + } + + n /= spaces; + + md5.update(String.fromCharCode( + n >> 24 & 0xFF, + n >> 16 & 0xFF, + n >> 8 & 0xFF, + n & 0xFF)); + }); + + md5.update(this.req.head.toString('binary')); + + try { + this.socket.write(md5.digest('binary'), 'binary'); + } catch (e) { + this.end(); + } + } + + return true; +}; + +/** + * Writes a payload. + * + * @api private + */ + +WebSocket.prototype.payload = function (msgs) { + for (var i = 0, l = msgs.length; i < l; i++) { + this.write(msgs[i]); + } + + return this; +}; + +/** + * Closes the connection. + * + * @api private + */ + +WebSocket.prototype.doClose = function () { + this.socket.end(); +}; + +/** + * WebSocket parser + * + * @api public + */ + +function Parser () { + this.buffer = ''; + this.i = 0; +}; + +/** + * Inherits from EventEmitter. + */ + +Parser.prototype.__proto__ = EventEmitter.prototype; + +/** + * Adds data to the buffer. + * + * @api public + */ + +Parser.prototype.add = function (data) { + this.buffer += data; + this.parse(); +}; + +/** + * Parses the buffer. + * + * @api private + */ + +Parser.prototype.parse = function () { + for (var i = this.i, chr, l = this.buffer.length; i < l; i++){ + chr = this.buffer[i]; + + if (this.buffer.length == 2 && this.buffer[1] == '\u0000') { + this.emit('close'); + this.buffer = ''; + this.i = 0; + return; + } + + if (i === 0){ + if (chr != '\u0000') + this.error('Bad framing. Expected null byte as first frame'); + else + continue; + } + + if (chr == '\ufffd'){ + this.emit('data', this.buffer.substr(1, i - 1)); + this.buffer = this.buffer.substr(i + 1); + this.i = 0; + return this.parse(); + } + } +}; + +/** + * Handles an error + * + * @api private + */ + +Parser.prototype.error = function (reason) { + this.buffer = ''; + this.i = 0; + this.emit('error', reason); + return this; +}; diff --git a/dist/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js b/dist/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js new file mode 100644 index 0000000..44f666a --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/websocket/hybi-07-12.js @@ -0,0 +1,622 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var Transport = require('../../transport') + , EventEmitter = process.EventEmitter + , crypto = require('crypto') + , url = require('url') + , parser = require('../../parser') + , util = require('../../util'); + +/** + * Export the constructor. + */ + +exports = module.exports = WebSocket; +exports.Parser = Parser; + +/** + * HTTP interface constructor. Interface compatible with all transports that + * depend on request-response cycles. + * + * @api public + */ + +function WebSocket (mng, data, req) { + // parser + var self = this; + + this.manager = mng; + this.parser = new Parser(); + this.parser.on('data', function (packet) { + self.onMessage(parser.decodePacket(packet)); + }); + this.parser.on('ping', function () { + // version 8 ping => pong + try { + self.socket.write('\u008a\u0000'); + } + catch (e) { + self.end(); + return; + } + }); + this.parser.on('close', function () { + self.end(); + }); + this.parser.on('error', function (reason) { + self.log.warn(self.name + ' parser error: ' + reason); + self.end(); + }); + + Transport.call(this, mng, data, req); +}; + +/** + * Inherits from Transport. + */ + +WebSocket.prototype.__proto__ = Transport.prototype; + +/** + * Transport name + * + * @api public + */ + +WebSocket.prototype.name = 'websocket'; + +/** + * Websocket draft version + * + * @api public + */ + +WebSocket.prototype.protocolVersion = '07-12'; + +/** + * Called when the socket connects. + * + * @api private + */ + +WebSocket.prototype.onSocketConnect = function () { + var self = this; + + if (typeof this.req.headers.upgrade === 'undefined' || + this.req.headers.upgrade.toLowerCase() !== 'websocket') { + this.log.warn(this.name + ' connection invalid'); + this.end(); + return; + } + + var origin = this.req.headers['sec-websocket-origin'] + , location = ((this.manager.settings['match origin protocol'] ? + origin.match(/^https/) : this.socket.encrypted) ? + 'wss' : 'ws') + + '://' + this.req.headers.host + this.req.url; + + if (!this.verifyOrigin(origin)) { + this.log.warn(this.name + ' connection invalid: origin mismatch'); + this.end(); + return; + } + + if (!this.req.headers['sec-websocket-key']) { + this.log.warn(this.name + ' connection invalid: received no key'); + this.end(); + return; + } + + // calc key + var key = this.req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + try { + this.socket.write(headers.concat('', '').join('\r\n')); + this.socket.setTimeout(0); + this.socket.setNoDelay(true); + } catch (e) { + this.end(); + return; + } + + this.socket.on('data', function (data) { + self.parser.add(data); + }); +}; + +/** + * Verifies the origin of a request. + * + * @api private + */ + +WebSocket.prototype.verifyOrigin = function (origin) { + var origins = this.manager.get('origins'); + + if (origin === 'null') origin = '*'; + + if (origins.indexOf('*:*') !== -1) { + return true; + } + + if (origin) { + try { + var parts = url.parse(origin); + parts.port = parts.port || 80; + var ok = + ~origins.indexOf(parts.hostname + ':' + parts.port) || + ~origins.indexOf(parts.hostname + ':*') || + ~origins.indexOf('*:' + parts.port); + if (!ok) this.log.warn('illegal origin: ' + origin); + return ok; + } catch (ex) { + this.log.warn('error parsing origin'); + } + } + else { + this.log.warn('origin missing from websocket call, yet required by config'); + } + return false; +}; + +/** + * Writes to the socket. + * + * @api private + */ + +WebSocket.prototype.write = function (data) { + if (this.open) { + var buf = this.frame(0x81, data); + try { + this.socket.write(buf, 'binary'); + } + catch (e) { + this.end(); + return; + } + this.log.debug(this.name + ' writing', data); + } +}; + +/** + * Writes a payload. + * + * @api private + */ + +WebSocket.prototype.payload = function (msgs) { + for (var i = 0, l = msgs.length; i < l; i++) { + this.write(msgs[i]); + } + + return this; +}; + +/** + * Frame server-to-client output as a text packet. + * + * @api private + */ + +WebSocket.prototype.frame = function (opcode, str) { + var dataBuffer = new Buffer(str) + , dataLength = dataBuffer.length + , startOffset = 2 + , secondByte = dataLength; + if (dataLength > 65536) { + startOffset = 10; + secondByte = 127; + } + else if (dataLength > 125) { + startOffset = 4; + secondByte = 126; + } + var outputBuffer = new Buffer(dataLength + startOffset); + outputBuffer[0] = opcode; + outputBuffer[1] = secondByte; + dataBuffer.copy(outputBuffer, startOffset); + switch (secondByte) { + case 126: + outputBuffer[2] = dataLength >>> 8; + outputBuffer[3] = dataLength % 256; + break; + case 127: + var l = dataLength; + for (var i = 1; i <= 8; ++i) { + outputBuffer[startOffset - i] = l & 0xff; + l >>>= 8; + } + } + return outputBuffer; +}; + +/** + * Closes the connection. + * + * @api private + */ + +WebSocket.prototype.doClose = function () { + this.socket.end(); +}; + +/** + * WebSocket parser + * + * @api public + */ + +function Parser () { + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0 + }; + this.overflow = null; + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.currentMessage = ''; + + var self = this; + this.opcodeHandlers = { + // text + '1': function(data) { + var finish = function(mask, data) { + self.currentMessage += self.unmask(mask, data); + if (self.state.lastFragment) { + self.emit('data', self.currentMessage); + self.currentMessage = ''; + } + self.endPacket(); + } + + var expectData = function(length) { + if (self.state.masked) { + self.expect('Mask', 4, function(data) { + var mask = data; + self.expect('Data', length, function(data) { + finish(mask, data); + }); + }); + } + else { + self.expect('Data', length, function(data) { + finish(null, data); + }); + } + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + expectData(firstLength); + } + else if (firstLength == 126) { + self.expect('Length', 2, function(data) { + expectData(util.unpack(data)); + }); + } + else if (firstLength == 127) { + self.expect('Length', 8, function(data) { + if (util.unpack(data.slice(0, 4)) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported'); + return; + } + var lengthBytes = data.slice(4); // note: cap to 32 bit length + expectData(util.unpack(data)); + }); + } + }, + // binary + '2': function(data) { + var finish = function(mask, data) { + if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list + self.currentMessage.push(self.unmask(mask, data, true)); + if (self.state.lastFragment) { + self.emit('binary', self.concatBuffers(self.currentMessage)); + self.currentMessage = ''; + } + self.endPacket(); + } + + var expectData = function(length) { + if (self.state.masked) { + self.expect('Mask', 4, function(data) { + var mask = data; + self.expect('Data', length, function(data) { + finish(mask, data); + }); + }); + } + else { + self.expect('Data', length, function(data) { + finish(null, data); + }); + } + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + expectData(firstLength); + } + else if (firstLength == 126) { + self.expect('Length', 2, function(data) { + expectData(util.unpack(data)); + }); + } + else if (firstLength == 127) { + self.expect('Length', 8, function(data) { + if (util.unpack(data.slice(0, 4)) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported'); + return; + } + var lengthBytes = data.slice(4); // note: cap to 32 bit length + expectData(util.unpack(data)); + }); + } + }, + // close + '8': function(data) { + self.emit('close'); + self.reset(); + }, + // ping + '9': function(data) { + if (self.state.lastFragment == false) { + self.error('fragmented ping is not supported'); + return; + } + + var finish = function(mask, data) { + self.emit('ping', self.unmask(mask, data)); + self.endPacket(); + } + + var expectData = function(length) { + if (self.state.masked) { + self.expect('Mask', 4, function(data) { + var mask = data; + self.expect('Data', length, function(data) { + finish(mask, data); + }); + }); + } + else { + self.expect('Data', length, function(data) { + finish(null, data); + }); + } + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength == 0) { + finish(null, null); + } + else if (firstLength < 126) { + expectData(firstLength); + } + else if (firstLength == 126) { + self.expect('Length', 2, function(data) { + expectData(util.unpack(data)); + }); + } + else if (firstLength == 127) { + self.expect('Length', 8, function(data) { + expectData(util.unpack(data)); + }); + } + } + } + + this.expect('Opcode', 2, this.processPacket); +}; + +/** + * Inherits from EventEmitter. + */ + +Parser.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add new data to the parser. + * + * @api public + */ + +Parser.prototype.add = function(data) { + if (this.expectBuffer == null) { + this.addToOverflow(data); + return; + } + var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset); + data.copy(this.expectBuffer, this.expectOffset, 0, toRead); + this.expectOffset += toRead; + if (toRead < data.length) { + // at this point the overflow buffer shouldn't at all exist + this.overflow = new Buffer(data.length - toRead); + data.copy(this.overflow, 0, toRead, toRead + this.overflow.length); + } + if (this.expectOffset == this.expectBuffer.length) { + var bufferForHandler = this.expectBuffer; + this.expectBuffer = null; + this.expectOffset = 0; + this.expectHandler.call(this, bufferForHandler); + } +} + +/** + * Adds a piece of data to the overflow. + * + * @api private + */ + +Parser.prototype.addToOverflow = function(data) { + if (this.overflow == null) this.overflow = data; + else { + var prevOverflow = this.overflow; + this.overflow = new Buffer(this.overflow.length + data.length); + prevOverflow.copy(this.overflow, 0); + data.copy(this.overflow, prevOverflow.length); + } +} + +/** + * Waits for a certain amount of bytes to be available, then fires a callback. + * + * @api private + */ + +Parser.prototype.expect = function(what, length, handler) { + this.expectBuffer = new Buffer(length); + this.expectOffset = 0; + this.expectHandler = handler; + if (this.overflow != null) { + var toOverflow = this.overflow; + this.overflow = null; + this.add(toOverflow); + } +} + +/** + * Start processing a new packet. + * + * @api private + */ + +Parser.prototype.processPacket = function (data) { + if ((data[0] & 0x70) != 0) { + this.error('reserved fields must be empty'); + } + this.state.lastFragment = (data[0] & 0x80) == 0x80; + this.state.masked = (data[1] & 0x80) == 0x80; + var opcode = data[0] & 0xf; + if (opcode == 0) { + // continuation frame + this.state.opcode = this.state.activeFragmentedOperation; + if (!(this.state.opcode == 1 || this.state.opcode == 2)) { + this.error('continuation frame cannot follow current opcode') + return; + } + } + else { + this.state.opcode = opcode; + if (this.state.lastFragment === false) { + this.state.activeFragmentedOperation = opcode; + } + } + var handler = this.opcodeHandlers[this.state.opcode]; + if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode); + else handler(data); +} + +/** + * Endprocessing a packet. + * + * @api private + */ + +Parser.prototype.endPacket = function() { + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + if (this.state.lastFragment && this.state.opcode == this.state.activeFragmentedOperation) { + // end current fragmented operation + this.state.activeFragmentedOperation = null; + } + this.state.lastFragment = false; + this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; + this.state.masked = false; + this.expect('Opcode', 2, this.processPacket); +} + +/** + * Reset the parser state. + * + * @api private + */ + +Parser.prototype.reset = function() { + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0 + }; + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.overflow = null; + this.currentMessage = ''; +} + +/** + * Unmask received data. + * + * @api private + */ + +Parser.prototype.unmask = function (mask, buf, binary) { + if (mask != null) { + for (var i = 0, ll = buf.length; i < ll; i++) { + buf[i] ^= mask[i % 4]; + } + } + if (binary) return buf; + return buf != null ? buf.toString('utf8') : ''; +} + +/** + * Concatenates a list of buffers. + * + * @api private + */ + +Parser.prototype.concatBuffers = function(buffers) { + var length = 0; + for (var i = 0, l = buffers.length; i < l; ++i) { + length += buffers[i].length; + } + var mergedBuffer = new Buffer(length); + var offset = 0; + for (var i = 0, l = buffers.length; i < l; ++i) { + buffers[i].copy(mergedBuffer, offset); + offset += buffers[i].length; + } + return mergedBuffer; +} + +/** + * Handles an error + * + * @api private + */ + +Parser.prototype.error = function (reason) { + this.reset(); + this.emit('error', reason); + return this; +}; diff --git a/dist/node_modules/socket.io/lib/transports/websocket/hybi-16.js b/dist/node_modules/socket.io/lib/transports/websocket/hybi-16.js new file mode 100644 index 0000000..69967da --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/websocket/hybi-16.js @@ -0,0 +1,622 @@ +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var Transport = require('../../transport') + , EventEmitter = process.EventEmitter + , crypto = require('crypto') + , url = require('url') + , parser = require('../../parser') + , util = require('../../util'); + +/** + * Export the constructor. + */ + +exports = module.exports = WebSocket; +exports.Parser = Parser; + +/** + * HTTP interface constructor. Interface compatible with all transports that + * depend on request-response cycles. + * + * @api public + */ + +function WebSocket (mng, data, req) { + // parser + var self = this; + + this.manager = mng; + this.parser = new Parser(); + this.parser.on('data', function (packet) { + self.onMessage(parser.decodePacket(packet)); + }); + this.parser.on('ping', function () { + // version 8 ping => pong + try { + self.socket.write('\u008a\u0000'); + } + catch (e) { + self.end(); + return; + } + }); + this.parser.on('close', function () { + self.end(); + }); + this.parser.on('error', function (reason) { + self.log.warn(self.name + ' parser error: ' + reason); + self.end(); + }); + + Transport.call(this, mng, data, req); +}; + +/** + * Inherits from Transport. + */ + +WebSocket.prototype.__proto__ = Transport.prototype; + +/** + * Transport name + * + * @api public + */ + +WebSocket.prototype.name = 'websocket'; + +/** + * Websocket draft version + * + * @api public + */ + +WebSocket.prototype.protocolVersion = '16'; + +/** + * Called when the socket connects. + * + * @api private + */ + +WebSocket.prototype.onSocketConnect = function () { + var self = this; + + if (typeof this.req.headers.upgrade === 'undefined' || + this.req.headers.upgrade.toLowerCase() !== 'websocket') { + this.log.warn(this.name + ' connection invalid'); + this.end(); + return; + } + + var origin = this.req.headers['origin'] || '' + , location = ((this.manager.settings['match origin protocol'] ? + origin.match(/^https/) : this.socket.encrypted) ? + 'wss' : 'ws') + + '://' + this.req.headers.host + this.req.url; + + if (!this.verifyOrigin(origin)) { + this.log.warn(this.name + ' connection invalid: origin mismatch'); + this.end(); + return; + } + + if (!this.req.headers['sec-websocket-key']) { + this.log.warn(this.name + ' connection invalid: received no key'); + this.end(); + return; + } + + // calc key + var key = this.req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + try { + this.socket.write(headers.concat('', '').join('\r\n')); + this.socket.setTimeout(0); + this.socket.setNoDelay(true); + } catch (e) { + this.end(); + return; + } + + this.socket.on('data', function (data) { + self.parser.add(data); + }); +}; + +/** + * Verifies the origin of a request. + * + * @api private + */ + +WebSocket.prototype.verifyOrigin = function (origin) { + var origins = this.manager.get('origins'); + + if (origin === 'null') origin = '*'; + + if (origins.indexOf('*:*') !== -1) { + return true; + } + + if (origin) { + try { + var parts = url.parse(origin); + parts.port = parts.port || 80; + var ok = + ~origins.indexOf(parts.hostname + ':' + parts.port) || + ~origins.indexOf(parts.hostname + ':*') || + ~origins.indexOf('*:' + parts.port); + if (!ok) this.log.warn('illegal origin: ' + origin); + return ok; + } catch (ex) { + this.log.warn('error parsing origin'); + } + } + else { + this.log.warn('origin missing from websocket call, yet required by config'); + } + return false; +}; + +/** + * Writes to the socket. + * + * @api private + */ + +WebSocket.prototype.write = function (data) { + if (this.open) { + var buf = this.frame(0x81, data); + try { + this.socket.write(buf, 'binary'); + } + catch (e) { + this.end(); + return; + } + this.log.debug(this.name + ' writing', data); + } +}; + +/** + * Writes a payload. + * + * @api private + */ + +WebSocket.prototype.payload = function (msgs) { + for (var i = 0, l = msgs.length; i < l; i++) { + this.write(msgs[i]); + } + + return this; +}; + +/** + * Frame server-to-client output as a text packet. + * + * @api private + */ + +WebSocket.prototype.frame = function (opcode, str) { + var dataBuffer = new Buffer(str) + , dataLength = dataBuffer.length + , startOffset = 2 + , secondByte = dataLength; + if (dataLength > 65536) { + startOffset = 10; + secondByte = 127; + } + else if (dataLength > 125) { + startOffset = 4; + secondByte = 126; + } + var outputBuffer = new Buffer(dataLength + startOffset); + outputBuffer[0] = opcode; + outputBuffer[1] = secondByte; + dataBuffer.copy(outputBuffer, startOffset); + switch (secondByte) { + case 126: + outputBuffer[2] = dataLength >>> 8; + outputBuffer[3] = dataLength % 256; + break; + case 127: + var l = dataLength; + for (var i = 1; i <= 8; ++i) { + outputBuffer[startOffset - i] = l & 0xff; + l >>>= 8; + } + } + return outputBuffer; +}; + +/** + * Closes the connection. + * + * @api private + */ + +WebSocket.prototype.doClose = function () { + this.socket.end(); +}; + +/** + * WebSocket parser + * + * @api public + */ + +function Parser () { + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0 + }; + this.overflow = null; + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.currentMessage = ''; + + var self = this; + this.opcodeHandlers = { + // text + '1': function(data) { + var finish = function(mask, data) { + self.currentMessage += self.unmask(mask, data); + if (self.state.lastFragment) { + self.emit('data', self.currentMessage); + self.currentMessage = ''; + } + self.endPacket(); + } + + var expectData = function(length) { + if (self.state.masked) { + self.expect('Mask', 4, function(data) { + var mask = data; + self.expect('Data', length, function(data) { + finish(mask, data); + }); + }); + } + else { + self.expect('Data', length, function(data) { + finish(null, data); + }); + } + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + expectData(firstLength); + } + else if (firstLength == 126) { + self.expect('Length', 2, function(data) { + expectData(util.unpack(data)); + }); + } + else if (firstLength == 127) { + self.expect('Length', 8, function(data) { + if (util.unpack(data.slice(0, 4)) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported'); + return; + } + var lengthBytes = data.slice(4); // note: cap to 32 bit length + expectData(util.unpack(data)); + }); + } + }, + // binary + '2': function(data) { + var finish = function(mask, data) { + if (typeof self.currentMessage == 'string') self.currentMessage = []; // build a buffer list + self.currentMessage.push(self.unmask(mask, data, true)); + if (self.state.lastFragment) { + self.emit('binary', self.concatBuffers(self.currentMessage)); + self.currentMessage = ''; + } + self.endPacket(); + } + + var expectData = function(length) { + if (self.state.masked) { + self.expect('Mask', 4, function(data) { + var mask = data; + self.expect('Data', length, function(data) { + finish(mask, data); + }); + }); + } + else { + self.expect('Data', length, function(data) { + finish(null, data); + }); + } + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength < 126) { + expectData(firstLength); + } + else if (firstLength == 126) { + self.expect('Length', 2, function(data) { + expectData(util.unpack(data)); + }); + } + else if (firstLength == 127) { + self.expect('Length', 8, function(data) { + if (util.unpack(data.slice(0, 4)) != 0) { + self.error('packets with length spanning more than 32 bit is currently not supported'); + return; + } + var lengthBytes = data.slice(4); // note: cap to 32 bit length + expectData(util.unpack(data)); + }); + } + }, + // close + '8': function(data) { + self.emit('close'); + self.reset(); + }, + // ping + '9': function(data) { + if (self.state.lastFragment == false) { + self.error('fragmented ping is not supported'); + return; + } + + var finish = function(mask, data) { + self.emit('ping', self.unmask(mask, data)); + self.endPacket(); + } + + var expectData = function(length) { + if (self.state.masked) { + self.expect('Mask', 4, function(data) { + var mask = data; + self.expect('Data', length, function(data) { + finish(mask, data); + }); + }); + } + else { + self.expect('Data', length, function(data) { + finish(null, data); + }); + } + } + + // decode length + var firstLength = data[1] & 0x7f; + if (firstLength == 0) { + finish(null, null); + } + else if (firstLength < 126) { + expectData(firstLength); + } + else if (firstLength == 126) { + self.expect('Length', 2, function(data) { + expectData(util.unpack(data)); + }); + } + else if (firstLength == 127) { + self.expect('Length', 8, function(data) { + expectData(util.unpack(data)); + }); + } + } + } + + this.expect('Opcode', 2, this.processPacket); +}; + +/** + * Inherits from EventEmitter. + */ + +Parser.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add new data to the parser. + * + * @api public + */ + +Parser.prototype.add = function(data) { + if (this.expectBuffer == null) { + this.addToOverflow(data); + return; + } + var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset); + data.copy(this.expectBuffer, this.expectOffset, 0, toRead); + this.expectOffset += toRead; + if (toRead < data.length) { + // at this point the overflow buffer shouldn't at all exist + this.overflow = new Buffer(data.length - toRead); + data.copy(this.overflow, 0, toRead, toRead + this.overflow.length); + } + if (this.expectOffset == this.expectBuffer.length) { + var bufferForHandler = this.expectBuffer; + this.expectBuffer = null; + this.expectOffset = 0; + this.expectHandler.call(this, bufferForHandler); + } +} + +/** + * Adds a piece of data to the overflow. + * + * @api private + */ + +Parser.prototype.addToOverflow = function(data) { + if (this.overflow == null) this.overflow = data; + else { + var prevOverflow = this.overflow; + this.overflow = new Buffer(this.overflow.length + data.length); + prevOverflow.copy(this.overflow, 0); + data.copy(this.overflow, prevOverflow.length); + } +} + +/** + * Waits for a certain amount of bytes to be available, then fires a callback. + * + * @api private + */ + +Parser.prototype.expect = function(what, length, handler) { + this.expectBuffer = new Buffer(length); + this.expectOffset = 0; + this.expectHandler = handler; + if (this.overflow != null) { + var toOverflow = this.overflow; + this.overflow = null; + this.add(toOverflow); + } +} + +/** + * Start processing a new packet. + * + * @api private + */ + +Parser.prototype.processPacket = function (data) { + if ((data[0] & 0x70) != 0) { + this.error('reserved fields must be empty'); + return; + } + this.state.lastFragment = (data[0] & 0x80) == 0x80; + this.state.masked = (data[1] & 0x80) == 0x80; + var opcode = data[0] & 0xf; + if (opcode == 0) { + // continuation frame + this.state.opcode = this.state.activeFragmentedOperation; + if (!(this.state.opcode == 1 || this.state.opcode == 2)) { + this.error('continuation frame cannot follow current opcode') + return; + } + } + else { + this.state.opcode = opcode; + if (this.state.lastFragment === false) { + this.state.activeFragmentedOperation = opcode; + } + } + var handler = this.opcodeHandlers[this.state.opcode]; + if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode); + else handler(data); +} + +/** + * Endprocessing a packet. + * + * @api private + */ + +Parser.prototype.endPacket = function() { + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + if (this.state.lastFragment && this.state.opcode == this.state.activeFragmentedOperation) { + // end current fragmented operation + this.state.activeFragmentedOperation = null; + } + this.state.lastFragment = false; + this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; + this.state.masked = false; + this.expect('Opcode', 2, this.processPacket); +} + +/** + * Reset the parser state. + * + * @api private + */ + +Parser.prototype.reset = function() { + this.state = { + activeFragmentedOperation: null, + lastFragment: false, + masked: false, + opcode: 0 + }; + this.expectOffset = 0; + this.expectBuffer = null; + this.expectHandler = null; + this.overflow = null; + this.currentMessage = ''; +} + +/** + * Unmask received data. + * + * @api private + */ + +Parser.prototype.unmask = function (mask, buf, binary) { + if (mask != null) { + for (var i = 0, ll = buf.length; i < ll; i++) { + buf[i] ^= mask[i % 4]; + } + } + if (binary) return buf; + return buf != null ? buf.toString('utf8') : ''; +} + +/** + * Concatenates a list of buffers. + * + * @api private + */ + +Parser.prototype.concatBuffers = function(buffers) { + var length = 0; + for (var i = 0, l = buffers.length; i < l; ++i) { + length += buffers[i].length; + } + var mergedBuffer = new Buffer(length); + var offset = 0; + for (var i = 0, l = buffers.length; i < l; ++i) { + buffers[i].copy(mergedBuffer, offset); + offset += buffers[i].length; + } + return mergedBuffer; +} + +/** + * Handles an error + * + * @api private + */ + +Parser.prototype.error = function (reason) { + this.reset(); + this.emit('error', reason); + return this; +}; diff --git a/dist/node_modules/socket.io/lib/transports/websocket/index.js b/dist/node_modules/socket.io/lib/transports/websocket/index.js new file mode 100644 index 0000000..3a952b7 --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/websocket/index.js @@ -0,0 +1,11 @@ + +/** + * Export websocket versions. + */ + +module.exports = { + 7: require('./hybi-07-12'), + 8: require('./hybi-07-12'), + 13: require('./hybi-16'), + default: require('./default') +}; diff --git a/dist/node_modules/socket.io/lib/transports/xhr-polling.js b/dist/node_modules/socket.io/lib/transports/xhr-polling.js new file mode 100644 index 0000000..1db5aee --- /dev/null +++ b/dist/node_modules/socket.io/lib/transports/xhr-polling.js @@ -0,0 +1,69 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module requirements. + */ + +var HTTPPolling = require('./http-polling'); + +/** + * Export the constructor. + */ + +exports = module.exports = XHRPolling; + +/** + * Ajax polling transport. + * + * @api public + */ + +function XHRPolling (mng, data, req) { + HTTPPolling.call(this, mng, data, req); +}; + +/** + * Inherits from Transport. + */ + +XHRPolling.prototype.__proto__ = HTTPPolling.prototype; + +/** + * Transport name + * + * @api public + */ + +XHRPolling.prototype.name = 'xhr-polling'; + +/** + * Frames data prior to write. + * + * @api private + */ + +XHRPolling.prototype.doWrite = function (data) { + HTTPPolling.prototype.doWrite.call(this); + + var origin = this.req.headers.origin + , headers = { + 'Content-Type': 'text/plain; charset=UTF-8' + , 'Content-Length': data === undefined ? 0 : Buffer.byteLength(data) + , 'Connection': 'Keep-Alive' + }; + + if (origin) { + // https://developer.mozilla.org/En/HTTP_Access_Control + headers['Access-Control-Allow-Origin'] = origin; + headers['Access-Control-Allow-Credentials'] = 'true'; + } + + this.response.writeHead(200, headers); + this.response.write(data); + this.log.debug(this.name + ' writing', data); +}; diff --git a/dist/node_modules/socket.io/lib/util.js b/dist/node_modules/socket.io/lib/util.js new file mode 100644 index 0000000..f7d9f2b --- /dev/null +++ b/dist/node_modules/socket.io/lib/util.js @@ -0,0 +1,50 @@ + +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +/** + * Converts an enumerable to an array. + * + * @api public + */ + +exports.toArray = function (enu) { + var arr = []; + + for (var i = 0, l = enu.length; i < l; i++) + arr.push(enu[i]); + + return arr; +}; + +/** + * Unpacks a buffer to a number. + * + * @api public + */ + +exports.unpack = function (buffer) { + var n = 0; + for (var i = 0; i < buffer.length; ++i) { + n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; + } + return n; +} + +/** + * Left pads a string. + * + * @api public + */ + +exports.padl = function (s,n,c) { + return new Array(1 + n - s.length).join(c) + s; +} + diff --git a/dist/node_modules/socket.io/node_modules/policyfile/.npmignore b/dist/node_modules/socket.io/node_modules/policyfile/.npmignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/.npmignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/policyfile/LICENSE b/dist/node_modules/socket.io/node_modules/policyfile/LICENSE new file mode 100644 index 0000000..bdb8f61 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Arnout Kazemier,3rd-Eden + +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/dist/node_modules/socket.io/node_modules/policyfile/Makefile b/dist/node_modules/socket.io/node_modules/policyfile/Makefile new file mode 100644 index 0000000..1362d66 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/Makefile @@ -0,0 +1,7 @@ +doc: + dox --title "FlashPolicyFileServer" lib/* > doc/index.html + +test: + expresso -I lib $(TESTFLAGS) tests/*.test.js + +.PHONY: test doc \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/policyfile/README.md b/dist/node_modules/socket.io/node_modules/policyfile/README.md new file mode 100644 index 0000000..527921e --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/README.md @@ -0,0 +1,98 @@ +## LOL, WUT? +It basically allows you to allow or disallow Flash Player sockets from accessing your site. + +## Installation + +```bash +npm install policyfile +``` +## Usage + +The server is based on the regular and know `net` and `http` server patterns. So it you can just listen +for all the events that a `net` based server emits etc. But there is one extra event, the `connect_failed` +event. This event is triggered when we are unable to listen on the supplied port number. + +### createServer +Creates a new server instance and accepts 2 optional arguments: + +- `options` **Object** Options to configure the server instance + - `log` **Boolean** Enable logging to STDOUT and STDERR (defaults to true) +- `origins` **Array** An Array of origins that are allowed by the server (defaults to *:*) + +```js +var pf = require('policyfile'); +pf.createServer(); +pf.listen(); +``` + +#### server.listen +Start listening on the server and it takes 3 optional arguments + +- `port` **Number** On which port number should we listen? (defaults to 843, which is the first port number the FlashPlayer checks) +- `server` **Server** A http server, if we are unable to accept requests or run the server we can also answer the policy requests inline over the supplied HTTP server. +- `callback` **Function** A callback function that is called when listening to the server was successful. + +```js +var pf = require('policyfile'); +pf.createServer(); +pf.listen(1337, function(){ + console.log(':3 yay') +}); +``` + +Changing port numbers can be handy if you do not want to run your server as root and have port 843 forward to a non root port number (aka a number above 1024). + +```js +var pf = require('policyfile') + , http = require('http'); + +server = http.createServer(function(q,r){r.writeHead(200);r.end('hello world')}); +server.listen(80); + +pf.createServer(); +pf.listen(1337, server, function(){ + console.log(':3 yay') +}); +``` + +Support for serving inline requests over a existing HTTP connection as the FlashPlayer will first check port 843, but if it's unable to get a response there it will send a policy file request over port 80, which is usually your http server. + +#### server.add +Adds more origins to the policy file you can add as many arguments as you like. + +```js +var pf = require('policyfile'); +pf.createServer(['google.com:80']); +pf.listen(); +pf.add('blog.3rd-Eden.com:80', 'blog.3rd-Eden.com:8080'); // now has 3 origins +``` + +#### server.add +Adds more origins to the policy file you can add as many arguments as you like. + +```js +var pf = require('policyfile'); +pf.createServer(['blog.3rd-Eden.com:80', 'blog.3rd-Eden.com:8080']); +pf.listen(); +pf.remove('blog.3rd-Eden.com:8080'); // only contains the :80 version now +``` + +#### server.close +Shuts down the server + +```js +var pf = require('policyfile'); +pf.createServer(); +pf.listen(); +pf.close(); // OH NVM. +``` + +## API +http://3rd-eden.com/FlashPolicyFileServer/ + +## Examples +See https://github.com/3rd-Eden/FlashPolicyFileServer/tree/master/examples for examples + +## Licence + +MIT see LICENSE file in the repository \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/policyfile/doc/index.html b/dist/node_modules/socket.io/node_modules/policyfile/doc/index.html new file mode 100644 index 0000000..743fcda --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/doc/index.html @@ -0,0 +1,375 @@ + + + FlashPolicyFileServer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      FlashPolicyFileServer

      server

      lib/server.js
      +

      Module dependencies and cached references. +

      +
      +
      var slice = Array.prototype.slice
      +  , net = require('net');
      +
      +

      The server that does the Policy File severing

      + +

      Options

      + +
      • log false or a function that can output log information, defaults to console.log?
      + +

      + +
      • param: Object options Options to customize the servers functionality.

      • param: Array origins The origins that are allowed on this server, defaults to *:*.

      • api: public

      +
      +
      function Server(options, origins){
      +  var me = this;
      +  
      +  this.origins = origins || ['*:*'];
      +  this.port = 843;
      +  this.log = console.log;
      +  
      +  // merge `this` with the options
      +  Object.keys(options).forEach(function(key){
      +    me[key] &amp;&amp; (me[key] = options[key])
      +  });
      +  
      +  // create the net server
      +  this.socket = net.createServer(function createServer(socket){
      +    socket.on('error', function socketError(){ me.responder.call(me, socket) });
      +    me.responder.call(me, socket);
      +  });
      +  
      +  // Listen for errors as the port might be blocked because we do not have root priv.
      +  this.socket.on('error', function serverError(err){
      +    // Special and common case error handling
      +    if (err.errno == 13){
      +      me.log &amp;&amp; me.log(
      +        'Unable to listen to port `' + me.port + '` as your Node.js instance does not have root privileges. ' +
      +        (
      +          me.server
      +          ? 'The Flash Policy file will now be served inline over the supplied HTTP server, Flash Policy files request will suffer.'
      +          : 'No fallback server supplied.'
      +        )
      +      );
      +      
      +      me.socket.removeAllListeners();
      +      delete me.socket;
      +
      +      me.emit('connect_failed', err);
      +    } else {
      +      me.log &amp;&amp; me.log('FlashPolicyFileServer received a error event:\n' + (err.message ? err.message : err));
      +    }
      +  });
      +  
      +  this.socket.on('timeout', function serverTimeout(){});
      +  this.socket.on('close', function serverClosed(err){
      +    err &amp;&amp; me.log &amp;&amp; me.log('Server closing due to an error: \n' + (err.message ? err.message : err));
      +    
      +    if (me.server){
      +      // not online anymore
      +      delete me.server.online;
      +      
      +      // Remove the inline policy listener if we close down
      +      // but only when the server was `online` (see listen prototype)
      +      if( me.server['@'] &amp;&amp; me.server.online){
      +        me.server.removeListener('connection', me.server['@']);
      +      }
      +    }
      +    me.log &amp;&amp; me.log('Shutting down FlashPolicyFileServer');
      +  });
      +  
      +  // Compile the initial `buffer`
      +  this.compile();
      +}
      +
      +

      Start listening for requests

      + +

      + +
      • param: Number port The port number it should be listening to.

      • param: Server server A HTTP server instance, this will be used to listen for inline requests

      • param: Function cb The callback needs to be called once server is ready

      • api: public

      +
      +
      Server.prototype.listen = function listen(port, server, cb){
      +  var me = this
      +    , args = slice.call(arguments, 0)
      +    , callback;
      +  
      +  // assign the correct vars, for flexible arguments
      +  args.forEach(function args(arg){
      +    var type = typeof arg;
      +    
      +    if (type === 'number') me.port = arg;
      +    if (type === 'function') callback = arg;
      +    if (type === 'object') me.server = arg;
      +  });
      +  
      +  if (this.server){
      +    
      +    // no one in their right mind would ever create a `@` prototype, so Im just gonna store
      +    // my function on the server, so I can remove it later again once the server(s) closes
      +    this.server['@'] = function connection(socket){
      +      socket.once('data', function requestData(data){
      +        // if it's a Flash policy request, and we can write to the 
      +        if (
      +             data
      +          &amp;&amp; data[0] === 60
      +          &amp;&amp; data.toString() === '<policy-file-request/>\0'
      +          &amp;&amp; socket
      +          &amp;&amp; (socket.readyState === 'open' || socket.readyState === 'writeOnly')
      +        ){
      +          // send the buffer
      +          socket.end(me.buffer);
      +        }
      +      });
      +    };
      +    // attach it
      +    this.server.on('connection', this.server['@']);
      +  }
      +  
      +  // We add a callback method, so we can set a flag for when the server is `enabled` or `online`.
      +  // this flag is needed because if a error occurs and the we cannot boot up the server the
      +  // fallback functionality should not be removed during the `close` event
      +  this.socket.listen(this.port, function serverListening(){
      +   me.socket.online = true;
      +   
      +   if (callback) callback(), callback = undefined;
      +   
      +  });
      +  
      +  return this;
      +};
      +
      +

      Adds a new origin to the Flash Policy File.

      + +

      + +
      • param: Arguments The origins that need to be added.

      • api: public

      +
      +
      Server.prototype.add = function add(){
      +  var args = slice.call(arguments, 0)
      +    , i = args.length;
      +  
      +  // flag duplicates
      +  while (i--){
      +    if (this.origins.indexOf(args[i]) &gt;= 0){
      +      args[i] = null;
      +    }
      +  }
      +  
      +  // Add all the arguments to the array
      +  // but first we want to remove all `falsy` values from the args
      +  Array.prototype.push.apply(
      +    this.origins
      +  , args.filter(function(value){ return !!value })
      +  );
      +  
      +  this.compile();
      +  return this;
      +};
      +
      +

      Removes a origin from the Flash Policy File.

      + +

      + +
      • param: String origin The origin that needs to be removed from the server

      • api: public

      +
      +
      Server.prototype.remove = function remove(origin){
      +  var position = this.origins.indexOf(origin);
      +  
      +  // only remove and recompile if we have a match
      +  if (position &gt; 0){
      +    this.origins.splice(position,1);
      +    this.compile();
      +  }
      +  
      +  return this;
      +};
      +
      +

      Closes and cleans up the server

      + +
      • api: public

      +
      +
      Server.prototype.close = function close(){
      +  this.socket.removeAllListeners();
      +  this.socket.close();
      +  
      +  return this;
      +};
      +
      +

      Proxy the event listener requests to the created Net server +

      +
      +
      Object.keys(process.EventEmitter.prototype).forEach(function proxy(key){
      +  Server.prototype[key] = Server.prototype[key] || function (){
      +    if (this.socket) this.socket[key].apply(this.socket, arguments);
      +    return this;
      +  };
      +});
      +
      +

      Creates a new server instance.

      + +

      + +
      • param: Object options A options object to override the default config

      • param: Array origins The origins that should be allowed by the server

      • api: public

      +
      +
      exports.createServer = function createServer(options, origins){
      +  origins = Array.isArray(origins) ? origins : (Array.isArray(options) ? options : false);
      +  options = !Array.isArray(options) &amp;&amp; options ? options : {};
      +  
      +  return new Server(options, origins);
      +};
      +
      +

      Provide a hook to the original server, so it can be extended if needed. +

      +
      +
      exports.Server = Server;
      +
      +

      Module version +

      +
      +
      exports.version = '0.0.2';
      +
      +
      \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/policyfile/examples/basic.fallback.js b/dist/node_modules/socket.io/node_modules/policyfile/examples/basic.fallback.js new file mode 100644 index 0000000..b439449 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/examples/basic.fallback.js @@ -0,0 +1,8 @@ +var http = require('http') + , fspfs = require('../'); + +var server = http.createServer(function(q,r){ r.writeHead(200); r.end(':3') }) + , flash = fspfs.createServer(); + +server.listen(8080); +flash.listen(8081,server); \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/policyfile/examples/basic.js b/dist/node_modules/socket.io/node_modules/policyfile/examples/basic.js new file mode 100644 index 0000000..5e2290f --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/examples/basic.js @@ -0,0 +1,5 @@ +var http = require('http') + , fspfs = require('../'); + +var flash = fspfs.createServer(); +flash.listen(); \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/policyfile/index.js b/dist/node_modules/socket.io/node_modules/policyfile/index.js new file mode 100644 index 0000000..60cf298 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/server.js'); \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/policyfile/lib/server.js b/dist/node_modules/socket.io/node_modules/policyfile/lib/server.js new file mode 100644 index 0000000..a525772 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/lib/server.js @@ -0,0 +1,289 @@ +/** + * Module dependencies and cached references. + */ + +var slice = Array.prototype.slice + , net = require('net'); + +/** + * The server that does the Policy File severing + * + * Options: + * - `log` false or a function that can output log information, defaults to console.log? + * + * @param {Object} options Options to customize the servers functionality. + * @param {Array} origins The origins that are allowed on this server, defaults to `*:*`. + * @api public + */ + +function Server (options, origins) { + var me = this; + + this.origins = origins || ['*:*']; + this.port = 843; + this.log = console.log; + + // merge `this` with the options + Object.keys(options).forEach(function (key) { + me[key] && (me[key] = options[key]) + }); + + // create the net server + this.socket = net.createServer(function createServer (socket) { + socket.on('error', function socketError () { + me.responder.call(me, socket); + }); + + me.responder.call(me, socket); + }); + + // Listen for errors as the port might be blocked because we do not have root priv. + this.socket.on('error', function serverError (err) { + // Special and common case error handling + if (err.errno == 13) { + me.log && me.log( + 'Unable to listen to port `' + me.port + '` as your Node.js instance does not have root privileges. ' + + ( + me.server + ? 'The Flash Policy File requests will only be served inline over the supplied HTTP server. Inline serving is slower than a dedicated server instance.' + : 'No fallback server supplied, we will be unable to answer Flash Policy File requests.' + ) + ); + + me.emit('connect_failed', err); + me.socket.removeAllListeners(); + delete me.socket; + } else { + me.log && me.log('FlashPolicyFileServer received an error event:\n' + (err.message ? err.message : err)); + } + }); + + this.socket.on('timeout', function serverTimeout () {}); + this.socket.on('close', function serverClosed (err) { + err && me.log && me.log('Server closing due to an error: \n' + (err.message ? err.message : err)); + + if (me.server) { + // Remove the inline policy listener if we close down + // but only when the server was `online` (see listen prototype) + if (me.server['@'] && me.server.online) { + me.server.removeListener('connection', me.server['@']); + } + + // not online anymore + delete me.server.online; + } + }); + + // Compile the initial `buffer` + this.compile(); +} + +/** + * Start listening for requests + * + * @param {Number} port The port number it should be listening to. + * @param {Server} server A HTTP server instance, this will be used to listen for inline requests + * @param {Function} cb The callback needs to be called once server is ready + * @api public + */ + +Server.prototype.listen = function listen (port, server, cb){ + var me = this + , args = slice.call(arguments, 0) + , callback; + + // assign the correct vars, for flexible arguments + args.forEach(function args (arg){ + var type = typeof arg; + + if (type === 'number') me.port = arg; + if (type === 'function') callback = arg; + if (type === 'object') me.server = arg; + }); + + if (this.server) { + + // no one in their right mind would ever create a `@` prototype, so Im just gonna store + // my function on the server, so I can remove it later again once the server(s) closes + this.server['@'] = function connection (socket) { + socket.once('data', function requestData (data) { + // if it's a Flash policy request, and we can write to the + if ( + data + && data[0] === 60 + && data.toString() === '\0' + && socket + && (socket.readyState === 'open' || socket.readyState === 'writeOnly') + ){ + // send the buffer + try { + socket.end(me.buffer); + } catch (e) {} + } + }); + }; + + // attach it + this.server.on('connection', this.server['@']); + } + + // We add a callback method, so we can set a flag for when the server is `enabled` or `online`. + // this flag is needed because if a error occurs and the we cannot boot up the server the + // fallback functionality should not be removed during the `close` event + this.port >= 0 && this.socket.listen(this.port, function serverListening () { + me.socket.online = true; + if (callback) { + callback.call(me); + callback = undefined; + } + }); + + return this; +}; + +/** + * Responds to socket connects and writes the compile policy file. + * + * @param {net.Socket} socket The socket that needs to receive the message + * @api private + */ + +Server.prototype.responder = function responder (socket){ + if (socket && socket.readyState == 'open' && socket.end) { + try { + socket.end(this.buffer); + } catch (e) {} + } +}; + +/** + * Compiles the supplied origins to a Flash Policy File format and stores it in a Node.js Buffer + * this way it can be send over the wire without any performance loss. + * + * @api private + */ + +Server.prototype.compile = function compile (){ + var xml = [ + '' + , '' + , '' + ]; + + // add the allow access element + this.origins.forEach(function origin (origin){ + var parts = origin.split(':'); + xml.push(''); + }); + + xml.push(''); + + // store the result in a buffer so we don't have to re-generate it all the time + this.buffer = new Buffer(xml.join(''), 'utf8'); + + return this; +}; + +/** + * Adds a new origin to the Flash Policy File. + * + * @param {Arguments} The origins that need to be added. + * @api public + */ + +Server.prototype.add = function add(){ + var args = slice.call(arguments, 0) + , i = args.length; + + // flag duplicates + while (i--) { + if (this.origins.indexOf(args[i]) >= 0){ + args[i] = null; + } + } + + // Add all the arguments to the array + // but first we want to remove all `falsy` values from the args + Array.prototype.push.apply( + this.origins + , args.filter(function filter (value) { + return !!value; + }) + ); + + this.compile(); + return this; +}; + +/** + * Removes a origin from the Flash Policy File. + * + * @param {String} origin The origin that needs to be removed from the server + * @api public + */ + +Server.prototype.remove = function remove (origin){ + var position = this.origins.indexOf(origin); + + // only remove and recompile if we have a match + if (position > 0) { + this.origins.splice(position,1); + this.compile(); + } + + return this; +}; + +/** + * Closes and cleans up the server + * + * @api public + */ + +Server.prototype.close = function close () { + this.socket.removeAllListeners(); + this.socket.close(); + + return this; +}; + +/** + * Proxy the event listener requests to the created Net server + */ + +Object.keys(process.EventEmitter.prototype).forEach(function proxy (key){ + Server.prototype[key] = Server.prototype[key] || function () { + if (this.socket) { + this.socket[key].apply(this.socket, arguments); + } + + return this; + }; +}); + +/** + * Creates a new server instance. + * + * @param {Object} options A options object to override the default config + * @param {Array} origins The origins that should be allowed by the server + * @api public + */ + +exports.createServer = function createServer(options, origins){ + origins = Array.isArray(origins) ? origins : (Array.isArray(options) ? options : false); + options = !Array.isArray(options) && options ? options : {}; + + return new Server(options, origins); +}; + +/** + * Provide a hook to the original server, so it can be extended if needed. + */ + +exports.Server = Server; + +/** + * Module version + */ + +exports.version = '0.0.4'; diff --git a/dist/node_modules/socket.io/node_modules/policyfile/package.json b/dist/node_modules/socket.io/node_modules/policyfile/package.json new file mode 100644 index 0000000..78526c5 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/package.json @@ -0,0 +1,32 @@ +{ + "name": "policyfile" + , "version": "0.0.4" + , "author": "Arnout Kazemier" + , "description": "Flash Socket Policy File Server. A server to respond to Flash Socket Policy requests, both inline and through a dedicated server instance." + , "main": "index" + , "keywords":[ + "flash" + , "socket" + , "policy" + , "file" + , "server" + , "Flash Socket Policy File Server" + , "cross domain" + ] + , "directories": { + "lib": "./lib" + } + , "maintainers": [{ + "name":"Arnout Kazemier" + , "email":"info@3rd-Eden.com" + , "web":"http://blog.3rd-Eden.com" + }] + , "licenses": [{ + "type": "MIT" + , "url": "https://github.com/3rd-Eden/FlashPolicyFileServer/blob/master/LICENSE" + }] + , "repositories": [{ + "type": "git" + , "url" : "https://github.com/3rd-Eden/FlashPolicyFileServer.git" + }] +} diff --git a/dist/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.crt b/dist/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.crt new file mode 100644 index 0000000..5883cd4 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAMUSOvlaeyQHMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTAxMTE2MDkzMjQ5WhcNMTMxMTE1MDkzMjQ5WjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEVwfPQQp4X +wtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+1FAE0c5o +exPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404WthquTqg +S7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy25IyBK3QJ +c+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWAQsqW+COL +0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABo1AwTjAdBgNVHQ4EFgQUDnV4d6mD +tOnluLoCjkUHTX/n4agwHwYDVR0jBBgwFoAUDnV4d6mDtOnluLoCjkUHTX/n4agw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAFwV4MQfTo+qMv9JMiyno +IEiqfOz4RgtmBqRnXUffcjS2dhc7/z+FPZnM79Kej8eLHoVfxCyWRHFlzm93vEdv +wxOCrD13EDOi08OOZfxWyIlCa6Bg8cMAKqQzd2OvQOWqlRWBTThBJIhWflU33izX +Qn5GdmYqhfpc+9ZHHGhvXNydtRQkdxVK2dZNzLBvBlLlRmtoClU7xm3A+/5dddeP +AQHEPtyFlUw49VYtZ3ru6KqPms7MKvcRhYLsy9rwSfuuniMlx4d0bDR7TOkw0QQS +A0N8MGQRQpzl4mw4jLzyM5d5QtuGBh2P6hPGa0YQxtI3RPT/p6ENzzBiAKXiSfzo +xw== +-----END CERTIFICATE----- diff --git a/dist/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.private.key b/dist/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.private.key new file mode 100644 index 0000000..f31ff3d --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/tests/ssl/ssl.private.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAz+LXZOjcQCJq3+ZKUFabj71oo/ex/XsBcFqtBThjjTw9CVEV +wfPQQp4XwtPiB204vnYXwQ1/R2NdTQqCZu47l79LssL/u2a5Y9+0NEU3nQA5qdt+ +1FAE0c5oexPimXOrR3GWfKz7PmZ2O0117IeCUUXPG5U8umhDe/4mDF4ZNJiKc404 +WthquTqgS7rLQZHhZ6D0EnGnOkzlmxJMYPNHSOY1/6ivdNUUcC87awNEA3lgfhy2 +5IyBK3QJc+aYKNTbt70Lery3bu2wWLFGtmNiGlQTS4JsxImRsECTI727ObS7/FWA +QsqW+COL0Sa5BuMFrFIpjPrEe0ih7vRRbdmXRwIDAQABAoIBAGe4+9VqZfJN+dsq +8Osyuz01uQ8OmC0sAWTIqUlQgENIyf9rCJsUBlYmwR5BT6Z69XP6QhHdpSK+TiAR +XUz0EqG9HYzcxHIBaACP7j6iRoQ8R4kbbiWKo0z3WqQGIOqFjvD/mKEuQdE5mEYw +eOUCG6BnX1WY2Yr8WKd2AA/tp0/Y4d8z04u9eodMpSTbHTzYMJb5SbBN1vo6FY7q +8zSuO0BMzXlAxUsCwHsk1GQHFr8Oh3zIR7bQGtMBouI+6Lhh7sjFYsfxJboqMTBV +IKaA216M6ggHG7MU1/jeKcMGDmEfqQLQoyWp29rMK6TklUgipME2L3UD7vTyAVzz +xbVOpZkCgYEA8CXW4sZBBrSSrLR5SB+Ubu9qNTggLowOsC/kVKB2WJ4+xooc5HQo +mFhq1v/WxPQoWIxdYsfg2odlL+JclK5Qcy6vXmRSdAQ5lK9gBDKxZSYc3NwAw2HA +zyHCTK+I0n8PBYQ+yGcrxu0WqTGnlLW+Otk4CejO34WlgHwbH9bbY5UCgYEA3ZvT +C4+OoMHXlmICSt29zUrYiL33IWsR3/MaONxTEDuvgkOSXXQOl/8Ebd6Nu+3WbsSN +bjiPC/JyL1YCVmijdvFpl4gjtgvfJifs4G+QHvO6YfsYoVANk4u6g6rUuBIOwNK4 +RwYxwDc0oysp+g7tPxoSgDHReEVKJNzGBe9NGGsCgYEA4O4QP4gCEA3B9BF2J5+s +n9uPVxmiyvZUK6Iv8zP4pThTBBMIzNIf09G9AHPQ7djikU2nioY8jXKTzC3xGTHM +GJZ5m6fLsu7iH+nDvSreDSeNkTBfZqGAvoGYQ8uGE+L+ZuRfCcXYsxIOT5s6o4c3 +Dle2rVFpsuKzCY00urW796ECgYBn3go75+xEwrYGQSer6WR1nTgCV29GVYXKPooy +zmmMOT1Yw80NSkEw0pFD4cTyqVYREsTrPU0mn1sPfrOXxnGfZSVFpcR/Je9QVfQ7 +eW7GYxwfom335aqHVj10SxRqteP+UoWWnHujCPz94VRKZMakBddYCIGSan+G6YdS +7sdmwwKBgBc2qj0wvGXDF2kCLwSGfWoMf8CS1+5fIiUIdT1e/+7MfDdbmLMIFVjF +QKS3zVViXCbrG5SY6wS9hxoc57f6E2A8vcaX6zy2xkZlGHQCpWRtEM5R01OWJQaH +HsHMmQZGUQVoDm1oRkDhrTFK4K3ukc3rAxzeTZ96utOQN8/KJsTv +-----END RSA PRIVATE KEY----- diff --git a/dist/node_modules/socket.io/node_modules/policyfile/tests/unit.test.js b/dist/node_modules/socket.io/node_modules/policyfile/tests/unit.test.js new file mode 100644 index 0000000..932b3c1 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/policyfile/tests/unit.test.js @@ -0,0 +1,231 @@ +var fspfs = require('../') + , fs = require('fs') + , http = require('http') + , https = require('https') + , net = require('net') + , should = require('should') + , assert = require('assert'); + +module.exports = { + // Library version should be Semver compatible + 'Library version': function(){ + fspfs.version.should.match(/^\d+\.\d+\.\d+$/); + } + + // Creating a server instace should not cause any problems + // either using the new Server or createServer method. +, 'Create Server instance': function(){ + var server = fspfs.createServer() + , server2 = new fspfs.Server({log:false}, ['blog.3rd-Eden.com:1337']); + + // server 2 options test + server2.log.should.be.false; + server2.origins.length.should.equal(1); + server2.origins[0].should.equal('blog.3rd-Eden.com:1337'); + + // server defaults + (typeof server.log).should.be.equal('function'); + server.origins.length.should.equal(1); + server.origins[0].should.equal('*:*'); + + // instance checking, sanity check + assert.ok(server instanceof fspfs.Server); + assert.ok(!!server.buffer); + + // more options testing + server = fspfs.createServer(['blog.3rd-Eden.com:80']); + server.origins.length.should.equal(1); + server.origins[0].should.equal('blog.3rd-Eden.com:80'); + + server = fspfs.createServer({log:false},['blog.3rd-Eden.com:80']); + server.log.should.be.false; + server.origins.length.should.equal(1); + server.origins[0].should.equal('blog.3rd-Eden.com:80'); + + } + +, 'Add origin': function(){ + var server = fspfs.createServer(); + server.add('google.com:80', 'blog.3rd-Eden.com:1337'); + + server.origins.length.should.equal(3); + server.origins.indexOf('google.com:80').should.be.above(0); + + // don't allow duplicates + server.add('google.com:80', 'google.com:80'); + + var i = server.origins.length + , count = 0; + + while(i--){ + if (server.origins[i] === 'google.com:80'){ + count++; + } + } + + count.should.equal(1); + } + +, 'Remove origin': function(){ + var server = fspfs.createServer(); + server.add('google.com:80', 'blog.3rd-Eden.com:1337'); + server.origins.length.should.equal(3); + + server.remove('google.com:80'); + server.origins.length.should.equal(2); + server.origins.indexOf('google.com:80').should.equal(-1); + } + +, 'Buffer': function(){ + var server = fspfs.createServer(); + + Buffer.isBuffer(server.buffer).should.be.true; + server.buffer.toString().indexOf('to-ports="*"').should.be.above(0); + server.buffer.toString().indexOf('domain="*"').should.be.above(0); + server.buffer.toString().indexOf('domain="google.com"').should.equal(-1); + + // The buffers should be rebuild when new origins are added + server.add('google.com:80'); + server.buffer.toString().indexOf('to-ports="80"').should.be.above(0); + server.buffer.toString().indexOf('domain="google.com"').should.be.above(0); + + server.remove('google.com:80'); + server.buffer.toString().indexOf('to-ports="80"').should.equal(-1); + server.buffer.toString().indexOf('domain="google.com"').should.equal(-1); + } + +, 'Responder': function(){ + var server = fspfs.createServer() + , calls = 0 + // dummy socket to emulate a `real` socket + , dummySocket = { + readyState: 'open' + , end: function(buffer){ + calls++; + Buffer.isBuffer(buffer).should.be.true; + buffer.toString().should.equal(server.buffer.toString()); + } + }; + + server.responder(dummySocket); + calls.should.equal(1); + } + +, 'Event proxy': function(){ + var server = fspfs.createServer() + , calls = 0; + + Object.keys(process.EventEmitter.prototype).forEach(function proxy(key){ + assert.ok(!!server[key] && typeof server[key] === 'function'); + }); + + // test if it works by calling a none default event + server.on('pew', function(){ + calls++; + }); + + server.emit('pew'); + calls.should.equal(1); + } + +, 'inline response http': function(){ + var port = 1335 + , httpserver = http.createServer(function(q,r){r.writeHead(200);r.end(':3')}) + , server = fspfs.createServer(); + + httpserver.listen(port, function(){ + server.listen(port + 1, httpserver, function(){ + var client = net.createConnection(port); + client.write('\0'); + client.on('error', function(err){ + assert.ok(!err, err) + }); + client.on('data', function(data){ + + var response = data.toString(); + console.log(response); + + response.indexOf('to-ports="*"').should.be.above(0); + response.indexOf('domain="*"').should.be.above(0); + response.indexOf('domain="google.com"').should.equal(-1); + + // clean up + client.destroy(); + server.close(); + httpserver.close(); + }); + }); + }); + } + +, 'server response': function(){ + var port = 1340 + , server = fspfs.createServer(); + + server.listen(port, function(){ + var client = net.createConnection(port); + client.write('\0'); + client.on('error', function(err){ + assert.ok(!err, err) + }); + client.on('data', function(data){ + + var response = data.toString(); + + response.indexOf('to-ports="*"').should.be.above(0); + response.indexOf('domain="*"').should.be.above(0); + response.indexOf('domain="google.com"').should.equal(-1); + + // clean up + client.destroy(); + server.close(); + }); + }); + } + +, 'inline response https': function(){ + var port = 1345 + , ssl = { + key: fs.readFileSync(__dirname + '/ssl/ssl.private.key').toString() + , cert: fs.readFileSync(__dirname + '/ssl/ssl.crt').toString() + } + , httpserver = https.createServer(ssl, function(q,r){r.writeHead(200);r.end(':3')}) + , server = fspfs.createServer(); + + httpserver.listen(port, function(){ + server.listen(port + 1, httpserver, function(){ + var client = net.createConnection(port); + client.write('\0'); + client.on('error', function(err){ + assert.ok(!err, err) + }); + client.on('data', function(data){ + + var response = data.toString(); + + response.indexOf('to-ports="*"').should.be.above(0); + response.indexOf('domain="*"').should.be.above(0); + response.indexOf('domain="google.com"').should.equal(-1); + + // clean up + client.destroy(); + server.close(); + httpserver.close(); + }); + }); + }); + } + +, 'connect_failed': function(){ + var server = fspfs.createServer(); + + server.on('connect_failed', function(){ + assert.ok(true); + }); + + server.listen(function(){ + assert.ok(false, 'Run this test without root access'); + server.close(); + }); + } +}; \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/.npmignore b/dist/node_modules/socket.io/node_modules/socket.io-client/.npmignore new file mode 100644 index 0000000..c27cb50 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/.npmignore @@ -0,0 +1,2 @@ +test/node_modules +support diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/History.md b/dist/node_modules/socket.io/node_modules/socket.io-client/History.md new file mode 100644 index 0000000..8ee017d --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/History.md @@ -0,0 +1,226 @@ + +0.9.11 / 2012-11-02 +=================== + + * Enable use of 'xhr' transport in Node.js + * Fix the problem with disconnecting xhr-polling users + * Add should to devDependencies + * Prefer XmlHttpRequest if CORS is available + * Make client compatible with AMD loaders. + +0.9.10 / 2012-08-10 +=================== + + * fix removeAllListeners to behave as expected. + * set withCredentials to true only if xdomain. + * socket: disable disconnect on unload by default. + +0.9.9 / 2012-08-01 +================== + + * socket: fixed disconnect xhr url and made it actually sync + * *: bump xmlhttprequest dep + +0.9.8 / 2012-07-24 +================== + + * Fixed build. + +0.9.7 / 2012-07-24 +================== + + * iOS websocket crash fix. + * Fixed potential `open` collision. + * Fixed disconnectSync. + +0.9.6 / 2012-04-17 +================== + + * Don't position the jsonp form off the screen (android fix). + +0.9.5 / 2012-04-05 +================== + + * Bumped version. + +0.9.4 / 2012-04-01 +================== + + * Fixes polling loop upon reconnect advice (fixes #438). + +0.9.3 / 2012-03-28 +================== + + * Fix XHR.check, which was throwing an error transparently and causing non-IE browsers to fall back to JSONP [mikito] + * Fixed forced disconnect on window close [zzzaaa] + +0.9.2 / 2012-03-13 +================== + + * Transport order set by "options" [zzzaaa] + +0.9.1-1 / 2012-03-02 +==================== + + * Fixed active-x-obfuscator NPM dependency. + +0.9.1 / 2012-03-02 +================== + + * Misc corrections. + * Added warning within Firefox about webworker test in test runner. + * Update ws dependency [einaros] + * Implemented client side heartbeat checks. [felixge] + * Improved Firewall support with ActiveX obfuscation. [felixge] + * Fixed error handling during connection process. [Outsideris] + +0.9.0 / 2012-02-26 +================== + + * Added DS_Store to gitignore. + * Updated depedencies. + * Bumped uglify + * Tweaking code so it doesn't throw an exception when used inside a WebWorker in Firefox + * Do not rely on Array.prototype.indexOf as it breaks with pages that use the Prototype.js library. + * Windows support landed + * Use @einaros ws module instead of the old crap one + * Fix for broken closeTimeout and 'IE + xhr' goes into infinite loop on disconnection + * Disabled reconnection on error if reconnect option is set to false + * Set withCredentials to true before xhr to fix authentication + * Clears the timeout from reconnection attempt when there is a successful or failed reconnection. + This fixes the issue of setTimeout's carrying over from previous reconnection + and changing (skipping) values of self.reconnectionDelay in the newer reconnection. + * Removed decoding of parameters when chunking the query string. + This was used later on to construct the url to post to the socket.io server + for connection and if we're adding custom parameters of our own to this url + (for example for OAuth authentication) they were being sent decoded, which is wrong. + +0.8.7 / 2011-11-05 +================== + + * Bumped client + +0.8.6 / 2011-10-27 +================== + + * Added WebWorker support. + * Fixed swfobject and web_socket.js to not assume window. + * Fixed CORS detection for webworker. + * Fix `defer` for webkit in a webworker. + * Fixed io.util.request to not rely on window. + * FIxed; use global instead of window and dont rely on document. + * Fixed; JSON-P handshake if CORS is not available. + * Made underlying Transport disconnection trigger immediate socket.io disconnect. + * Fixed warning when compressing with Google Closure Compiler. + * Fixed builder's uglify utf-8 support. + * Added workaround for loading indicator in FF jsonp-polling. [3rd-Eden] + * Fixed host discovery lookup. [holic] + * Fixed close timeout when disconnected/reconnecting. [jscharlach] + * Fixed jsonp-polling feature detection. + * Fixed jsonp-polling client POSTing of \n. + * Fixed test runner on IE6/7 + +0.8.5 / 2011-10-07 +================== + + * Bumped client + +0.8.4 / 2011-09-06 +================== + + * Corrected build + +0.8.3 / 2011-09-03 +================== + + * Fixed `\n` parsing for non-JSON packets. + * Fixed; make Socket.IO XHTML doctype compatible (fixes #460 from server) + * Fixed support for Node.JS running `socket.io-client`. + * Updated repository name in `package.json`. + * Added support for different policy file ports without having to port + forward 843 on the server side [3rd-Eden] + +0.8.2 / 2011-08-29 +================== + + * Fixed flashsocket detection. + +0.8.1 / 2011-08-29 +================== + + * Bump version. + +0.8.0 / 2011-08-28 +================== + + * Added MozWebSocket support (hybi-10 doesn't require API changes) [einaros]. + +0.7.11 / 2011-08-27 +=================== + + * Corrected previous release (missing build). + +0.7.10 / 2011-08-27 +=================== + + * Fix for failing fallback in websockets + +0.7.9 / 2011-08-12 +================== + + * Added check on `Socket#onConnect` to prevent double `connect` events on the main manager. + * Fixed socket namespace connect test. Remove broken alternative namespace connect test. + * Removed test handler for removed test. + * Bumped version to match `socket.io` server. + +0.7.5 / 2011-08-08 +================== + + * Added querystring support for `connect` [3rd-Eden] + * Added partial Node.JS transports support [3rd-Eden, josephg] + * Fixed builder test. + * Changed `util.inherit` to replicate Object.create / __proto__. + * Changed and cleaned up some acceptance tests. + * Fixed race condition with a test that could not be run multiple times. + * Added test for encoding a payload. + * Added the ability to override the transport to use in acceptance test [3rd-Eden] + * Fixed multiple connect packets [DanielBaulig] + * Fixed jsonp-polling over-buffering [3rd-Eden] + * Fixed ascii preservation in minified socket.io client [3rd-Eden] + * Fixed socket.io in situations where the page is not served through utf8. + * Fixed namespaces not reconnecting after disconnect [3rd-Eden] + * Fixed default port for secure connections. + +0.7.4 / 2011-07-12 +================== + + * Added `SocketNamespace#of` shortcut. [3rd-Eden] + * Fixed a IE payload decoding bug. [3rd-Eden] + * Honor document protocol, unless overriden. [dvv] + * Fixed new builder dependencies. [3rd-Eden] + +0.7.3 / 2011-06-30 +================== + + * Fixed; acks don't depend on arity. They're automatic for `.send` and + callback based for `.emit`. [dvv] + * Added support for sub-sockets authorization. [3rd-Eden] + * Added BC support for `new io.connect`. [fat] + * Fixed double `connect` events. [3rd-Eden] + * Fixed reconnection with jsonp-polling maintaining old sessionid. [franck34] + +0.7.2 / 2011-06-22 +================== + + * Added `noop` message type. + +0.7.1 / 2011-06-21 +================== + + * Bumped socket.io dependency version for acceptance tests. + +0.7.0 / 2011-06-21 +================== + + * http://socket.io/announcement.html + diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/Makefile b/dist/node_modules/socket.io/node_modules/socket.io-client/Makefile new file mode 100644 index 0000000..f2d2f41 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/Makefile @@ -0,0 +1,20 @@ + +ALL_TESTS = $(shell find test/ -name '*.test.js') + +run-tests: + @./node_modules/.bin/expresso \ + -I lib \ + -I support \ + --serial \ + $(TESTS) + +test: + @$(MAKE) TESTS="$(ALL_TESTS)" run-tests + +test-acceptance: + @node support/test-runner/app $(TRANSPORT) + +build: + @node ./bin/builder.js + +.PHONY: test diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/README.md b/dist/node_modules/socket.io/node_modules/socket.io-client/README.md new file mode 100644 index 0000000..cdb7715 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/README.md @@ -0,0 +1,246 @@ +socket.io +========= + +#### Sockets for the rest of us + +The `socket.io` client is basically a simple HTTP Socket interface implementation. +It looks similar to WebSocket while providing additional features and +leveraging other transports when WebSocket is not supported by the user's +browser. + +```js +var socket = io.connect('http://domain.com'); +socket.on('connect', function () { + // socket connected +}); +socket.on('custom event', function () { + // server emitted a custom event +}); +socket.on('disconnect', function () { + // socket disconnected +}); +socket.send('hi there'); +``` + +### Recipes + +#### Utilizing namespaces (ie: multiple sockets) + +If you want to namespace all the messages and events emitted to a particular +endpoint, simply specify it as part of the `connect` uri: + +```js +var chat = io.connect('http://localhost/chat'); +chat.on('connect', function () { + // chat socket connected +}); + +var news = io.connect('/news'); // io.connect auto-detects host +news.on('connect', function () { + // news socket connected +}); +``` + +#### Emitting custom events + +To ease with the creation of applications, you can emit custom events outside +of the global `message` event. + +```js +var socket = io.connect(); +socket.emit('server custom event', { my: 'data' }); +``` + +#### Forcing disconnection + +```js +var socket = io.connect(); +socket.on('connect', function () { + socket.disconnect(); +}); +``` + +### Documentation + +#### io#connect + +```js +io.connect(uri, [options]); +``` + +##### Options: + +- *resource* + + socket.io + + The resource is what allows the `socket.io` server to identify incoming connections by `socket.io` clients. In other words, any HTTP server can implement socket.io and still serve other normal, non-realtime HTTP requests. + +- *transports* + +```js +['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling', 'jsonp-polling'] +``` + + A list of the transports to attempt to utilize (in order of preference). + +- *'connect timeout'* + +```js +5000 +``` + + The amount of milliseconds a transport has to create a connection before we consider it timed out. + +- *'try multiple transports'* + +```js +true +``` + + A boolean indicating if we should try other transports when the connectTimeout occurs. + +- *reconnect* + +```js +true +``` + + A boolean indicating if we should automatically reconnect if a connection is disconnected. + +- *'reconnection delay'* + +```js +500 +``` + + The amount of milliseconds before we try to connect to the server again. We are using a exponential back off algorithm for the following reconnections, on each reconnect attempt this value will get multiplied (500 > 1000 > 2000 > 4000 > 8000). + + +- *'max reconnection attempts'* + +```js +10 +``` + + The amount of attempts should we make using the current transport to connect to the server? After this we will do one final attempt, and re-try with all enabled transport methods before we give up. + +##### Properties: + +- *options* + + The passed in options combined with the defaults. + +- *connected* + + Whether the socket is connected or not. + +- *connecting* + + Whether the socket is connecting or not. + +- *reconnecting* + + Whether we are reconnecting or not. + +- *transport* + + The transport instance. + +##### Methods: + +- *connect(λ)* + + Establishes a connection. If λ is supplied as argument, it will be called once the connection is established. + +- *send(message)* + + A string of data to send. + +- *disconnect* + + Closes the connection. + +- *on(event, λ)* + + Adds a listener for the event *event*. + +- *once(event, λ)* + + Adds a one time listener for the event *event*. The listener is removed after the first time the event is fired. + +- *removeListener(event, λ)* + + Removes the listener λ for the event *event*. + +##### Events: + +- *connect* + + Fired when the connection is established and the handshake successful. + +- *connecting(transport_type)* + + Fired when a connection is attempted, passing the transport name. + +- *connect_failed* + + Fired when the connection timeout occurs after the last connection attempt. + This only fires if the `connectTimeout` option is set. + If the `tryTransportsOnConnectTimeout` option is set, this only fires once all + possible transports have been tried. + +- *message(message)* + + Fired when a message arrives from the server + +- *close* + + Fired when the connection is closed. Be careful with using this event, as some transports will fire it even under temporary, expected disconnections (such as XHR-Polling). + +- *disconnect* + + Fired when the connection is considered disconnected. + +- *reconnect(transport_type,reconnectionAttempts)* + + Fired when the connection has been re-established. This only fires if the `reconnect` option is set. + +- *reconnecting(reconnectionDelay,reconnectionAttempts)* + + Fired when a reconnection is attempted, passing the next delay for the next reconnection. + +- *reconnect_failed* + + Fired when all reconnection attempts have failed and we where unsuccessful in reconnecting to the server. + +### Contributors + +Guillermo Rauch <guillermo@learnboost.com> + +Arnout Kazemier <info@3rd-eden.com> + +### License + +(The MIT License) + +Copyright (c) 2010 LearnBoost <dev@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE 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/dist/node_modules/socket.io/node_modules/socket.io-client/bin/builder.js b/dist/node_modules/socket.io/node_modules/socket.io-client/bin/builder.js new file mode 100755 index 0000000..7383c75 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/bin/builder.js @@ -0,0 +1,303 @@ +/*! + * socket.io-node + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , socket = require('../lib/io') + , uglify = require('uglify-js') + , activeXObfuscator = require('active-x-obfuscator'); + +/** + * License headers. + * + * @api private + */ + +var template = '/*! Socket.IO.%ext% build:' + socket.version + ', %type%. Copyright(c) 2011 LearnBoost MIT Licensed */\n' + , development = template.replace('%type%', 'development').replace('%ext%', 'js') + , production = template.replace('%type%', 'production').replace('%ext%', 'min.js'); + +/** + * If statements, these allows you to create serveride & client side compatible + * code using specially designed `if` statements that remove serverside + * designed code from the source files + * + * @api private + */ + +var starttagIF = '// if node' + , endtagIF = '// end node'; + +/** + * The modules that are required to create a base build of Socket.IO. + * + * @const + * @type {Array} + * @api private + */ + +var base = [ + 'io.js' + , 'util.js' + , 'events.js' + , 'json.js' + , 'parser.js' + , 'transport.js' + , 'socket.js' + , 'namespace.js' + ]; + +/** + * The available transports for Socket.IO. These are mapped as: + * + * - `key` the name of the transport + * - `value` the dependencies for the transport + * + * @const + * @type {Object} + * @api public + */ + +var baseTransports = { + 'websocket': ['transports/websocket.js'] + , 'flashsocket': [ + 'transports/websocket.js' + , 'transports/flashsocket.js' + , 'vendor/web-socket-js/swfobject.js' + , 'vendor/web-socket-js/web_socket.js' + ] + , 'htmlfile': ['transports/xhr.js', 'transports/htmlfile.js'] + /* FIXME: re-enable me once we have multi-part support + , 'xhr-multipart': ['transports/xhr.js', 'transports/xhr-multipart.js'] */ + , 'xhr-polling': ['transports/xhr.js', 'transports/xhr-polling.js'] + , 'jsonp-polling': [ + 'transports/xhr.js' + , 'transports/xhr-polling.js' + , 'transports/jsonp-polling.js' + ] +}; + +/** + * Wrappers for client-side usage. + * This enables usage in top-level browser window, client-side CommonJS systems and AMD loaders. + * If doing a node build for server-side client, this wrapper is NOT included. + * @api private + */ +var wrapperPre = "\nvar io = ('undefined' === typeof module ? {} : module.exports);\n(function() {\n"; + +var wrapperPost = "\nif (typeof define === \"function\" && define.amd) {" + + "\n define([], function () { return io; });" + + "\n}\n})();"; + + +/** + * Builds a custom Socket.IO distribution based on the transports that you + * need. You can configure the build to create development build or production + * build (minified). + * + * @param {Array} transports The transports that needs to be bundled. + * @param {Object} [options] Options to configure the building process. + * @param {Function} callback Last argument should always be the callback + * @callback {String|Boolean} err An optional argument, if it exists than an error + * occurred during the build process. + * @callback {String} result The result of the build process. + * @api public + */ + +var builder = module.exports = function () { + var transports, options, callback, error = null + , args = Array.prototype.slice.call(arguments, 0) + , settings = { + minify: true + , node: false + , custom: [] + }; + + // Fancy pancy argument support this makes any pattern possible mainly + // because we require only one of each type + args.forEach(function (arg) { + var type = Object.prototype.toString.call(arg) + .replace(/\[object\s(\w+)\]/gi , '$1' ).toLowerCase(); + + switch (type) { + case 'array': + return transports = arg; + case 'object': + return options = arg; + case 'function': + return callback = arg; + } + }); + + // Add defaults + options = options || {}; + transports = transports || Object.keys(baseTransports); + + // Merge the data + for(var option in options) { + settings[option] = options[option]; + } + + // Start creating a dependencies chain with all the required files for the + // custom Socket.IO bundle. + var files = []; + base.forEach(function (file) { + files.push(__dirname + '/../lib/' + file); + }); + + transports.forEach(function (transport) { + var dependencies = baseTransports[transport]; + if (!dependencies) { + error = 'Unsupported transport `' + transport + '` supplied as argument.'; + return; + } + + // Add the files to the files list, but only if they are not added before + dependencies.forEach(function (file) { + var path = __dirname + '/../lib/' + file; + if (!~files.indexOf(path)) files.push(path); + }) + }); + + // check to see if the files tree compilation generated any errors. + if (error) return callback(error); + + var results = {}; + files.forEach(function (file) { + fs.readFile(file, function (err, content) { + if (err) error = err; + results[file] = content; + + // check if we are done yet, or not.. Just by checking the size of the result + // object. + if (Object.keys(results).length !== files.length) return; + + // we are done, did we error? + if (error) return callback(error); + + // start with the license header + var code = development + , ignore = 0; + + // pre-wrapper for non-server-side builds + if (!settings.node) code += wrapperPre; + + // concatenate the file contents in order + files.forEach(function (file) { + code += results[file]; + }); + + // check if we need to add custom code + if (settings.custom.length) { + settings.custom.forEach(function (content) { + code += content; + }); + } + + // post-wrapper for non-server-side builds + if (!settings.node) { + code += wrapperPost; + } + + code = activeXObfuscator(code); + + // Search for conditional code blocks that need to be removed as they + // where designed for a server side env. but only if we don't want to + // make this build node compatible. + if (!settings.node) { + code = code.split('\n').filter(function (line) { + // check if there are tags in here + var start = line.indexOf(starttagIF) >= 0 + , end = line.indexOf(endtagIF) >= 0 + , ret = ignore; + + // ignore the current line + if (start) { + ignore++; + ret = ignore; + } + + // stop ignoring the next line + if (end) { + ignore--; + } + + return ret == 0; + }).join('\n'); + } + + // check if we need to process it any further + if (settings.minify) { + var ast = uglify.parser.parse(code); + ast = uglify.uglify.ast_mangle(ast); + ast = uglify.uglify.ast_squeeze(ast); + + code = production + uglify.uglify.gen_code(ast, { ascii_only: true }); + } + + callback(error, code); + }) + }) +}; + +/** + * Builder version is also the current client version + * this way we don't have to do another include for the + * clients version number and we can just include the builder. + * + * @type {String} + * @api public + */ + +builder.version = socket.version; + +/** + * A list of all build in transport types. + * + * @type {Object} + * @api public + */ + +builder.transports = baseTransports; + +/** + * Command line support, this allows us to generate builds without having + * to load it as module. + */ + +if (!module.parent){ + // the first 2 are `node` and the path to this file, we don't need them + var args = process.argv.slice(2); + + // build a development build + builder(args.length ? args : false, { minify:false }, function (err, content) { + if (err) return console.error(err); + + fs.write( + fs.openSync(__dirname + '/../dist/socket.io.js', 'w') + , content + , 0 + , 'utf8' + ); + console.log('Successfully generated the development build: socket.io.js'); + }); + + // and build a production build + builder(args.length ? args : false, function (err, content) { + if (err) return console.error(err); + + fs.write( + fs.openSync(__dirname + '/../dist/socket.io.min.js', 'w') + , content + , 0 + , 'utf8' + ); + console.log('Successfully generated the production build: socket.io.min.js'); + }); +} diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMain.swf b/dist/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMain.swf new file mode 100644 index 0000000..20a451f Binary files /dev/null and b/dist/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMain.swf differ diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMainInsecure.swf b/dist/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMainInsecure.swf new file mode 100644 index 0000000..5949ff3 Binary files /dev/null and b/dist/node_modules/socket.io/node_modules/socket.io-client/dist/WebSocketMainInsecure.swf differ diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js b/dist/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js new file mode 100644 index 0000000..6ea5dd0 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/dist/socket.io.js @@ -0,0 +1,3871 @@ +/*! Socket.IO.js build:0.9.11, development. Copyright(c) 2011 LearnBoost MIT Licensed */ + +var io = ('undefined' === typeof module ? {} : module.exports); +(function() { + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, global) { + + /** + * IO namespace. + * + * @namespace + */ + + var io = exports; + + /** + * Socket.IO version + * + * @api public + */ + + io.version = '0.9.11'; + + /** + * Protocol implemented. + * + * @api public + */ + + io.protocol = 1; + + /** + * Available transports, these will be populated with the available transports + * + * @api public + */ + + io.transports = []; + + /** + * Keep track of jsonp callbacks. + * + * @api private + */ + + io.j = []; + + /** + * Keep track of our io.Sockets + * + * @api private + */ + io.sockets = {}; + + + /** + * Manages connections to hosts. + * + * @param {String} uri + * @Param {Boolean} force creation of new socket (defaults to false) + * @api public + */ + + io.connect = function (host, details) { + var uri = io.util.parseUri(host) + , uuri + , socket; + + if (global && global.location) { + uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); + uri.host = uri.host || (global.document + ? global.document.domain : global.location.hostname); + uri.port = uri.port || global.location.port; + } + + uuri = io.util.uniqueUri(uri); + + var options = { + host: uri.host + , secure: 'https' == uri.protocol + , port: uri.port || ('https' == uri.protocol ? 443 : 80) + , query: uri.query || '' + }; + + io.util.merge(options, details); + + if (options['force new connection'] || !io.sockets[uuri]) { + socket = new io.Socket(options); + } + + if (!options['force new connection'] && socket) { + io.sockets[uuri] = socket; + } + + socket = socket || io.sockets[uuri]; + + // if path is different from '' or / + return socket.of(uri.path.length > 1 ? uri.path : ''); + }; + +})('object' === typeof module ? module.exports : (this.io = {}), this); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, global) { + + /** + * Utilities namespace. + * + * @namespace + */ + + var util = exports.util = {}; + + /** + * Parses an URI + * + * @author Steven Levithan (MIT license) + * @api public + */ + + var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; + + var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', + 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', + 'anchor']; + + util.parseUri = function (str) { + var m = re.exec(str || '') + , uri = {} + , i = 14; + + while (i--) { + uri[parts[i]] = m[i] || ''; + } + + return uri; + }; + + /** + * Produces a unique url that identifies a Socket.IO connection. + * + * @param {Object} uri + * @api public + */ + + util.uniqueUri = function (uri) { + var protocol = uri.protocol + , host = uri.host + , port = uri.port; + + if ('document' in global) { + host = host || document.domain; + port = port || (protocol == 'https' + && document.location.protocol !== 'https:' ? 443 : document.location.port); + } else { + host = host || 'localhost'; + + if (!port && protocol == 'https') { + port = 443; + } + } + + return (protocol || 'http') + '://' + host + ':' + (port || 80); + }; + + /** + * Mergest 2 query strings in to once unique query string + * + * @param {String} base + * @param {String} addition + * @api public + */ + + util.query = function (base, addition) { + var query = util.chunkQuery(base || '') + , components = []; + + util.merge(query, util.chunkQuery(addition || '')); + for (var part in query) { + if (query.hasOwnProperty(part)) { + components.push(part + '=' + query[part]); + } + } + + return components.length ? '?' + components.join('&') : ''; + }; + + /** + * Transforms a querystring in to an object + * + * @param {String} qs + * @api public + */ + + util.chunkQuery = function (qs) { + var query = {} + , params = qs.split('&') + , i = 0 + , l = params.length + , kv; + + for (; i < l; ++i) { + kv = params[i].split('='); + if (kv[0]) { + query[kv[0]] = kv[1]; + } + } + + return query; + }; + + /** + * Executes the given function when the page is loaded. + * + * io.util.load(function () { console.log('page loaded'); }); + * + * @param {Function} fn + * @api public + */ + + var pageLoaded = false; + + util.load = function (fn) { + if ('document' in global && document.readyState === 'complete' || pageLoaded) { + return fn(); + } + + util.on(global, 'load', fn, false); + }; + + /** + * Adds an event. + * + * @api private + */ + + util.on = function (element, event, fn, capture) { + if (element.attachEvent) { + element.attachEvent('on' + event, fn); + } else if (element.addEventListener) { + element.addEventListener(event, fn, capture); + } + }; + + /** + * Generates the correct `XMLHttpRequest` for regular and cross domain requests. + * + * @param {Boolean} [xdomain] Create a request that can be used cross domain. + * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest. + * @api private + */ + + util.request = function (xdomain) { + + if (xdomain && 'undefined' != typeof XDomainRequest && !util.ua.hasCORS) { + return new XDomainRequest(); + } + + if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { + return new XMLHttpRequest(); + } + + if (!xdomain) { + try { + return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP'); + } catch(e) { } + } + + return null; + }; + + /** + * XHR based transport constructor. + * + * @constructor + * @api public + */ + + /** + * Change the internal pageLoaded value. + */ + + if ('undefined' != typeof window) { + util.load(function () { + pageLoaded = true; + }); + } + + /** + * Defers a function to ensure a spinner is not displayed by the browser + * + * @param {Function} fn + * @api public + */ + + util.defer = function (fn) { + if (!util.ua.webkit || 'undefined' != typeof importScripts) { + return fn(); + } + + util.load(function () { + setTimeout(fn, 100); + }); + }; + + /** + * Merges two objects. + * + * @api public + */ + + util.merge = function merge (target, additional, deep, lastseen) { + var seen = lastseen || [] + , depth = typeof deep == 'undefined' ? 2 : deep + , prop; + + for (prop in additional) { + if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) { + if (typeof target[prop] !== 'object' || !depth) { + target[prop] = additional[prop]; + seen.push(additional[prop]); + } else { + util.merge(target[prop], additional[prop], depth - 1, seen); + } + } + } + + return target; + }; + + /** + * Merges prototypes from objects + * + * @api public + */ + + util.mixin = function (ctor, ctor2) { + util.merge(ctor.prototype, ctor2.prototype); + }; + + /** + * Shortcut for prototypical and static inheritance. + * + * @api private + */ + + util.inherit = function (ctor, ctor2) { + function f() {}; + f.prototype = ctor2.prototype; + ctor.prototype = new f; + }; + + /** + * Checks if the given object is an Array. + * + * io.util.isArray([]); // true + * io.util.isArray({}); // false + * + * @param Object obj + * @api public + */ + + util.isArray = Array.isArray || function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + + /** + * Intersects values of two arrays into a third + * + * @api public + */ + + util.intersect = function (arr, arr2) { + var ret = [] + , longest = arr.length > arr2.length ? arr : arr2 + , shortest = arr.length > arr2.length ? arr2 : arr; + + for (var i = 0, l = shortest.length; i < l; i++) { + if (~util.indexOf(longest, shortest[i])) + ret.push(shortest[i]); + } + + return ret; + }; + + /** + * Array indexOf compatibility. + * + * @see bit.ly/a5Dxa2 + * @api public + */ + + util.indexOf = function (arr, o, i) { + + for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0; + i < j && arr[i] !== o; i++) {} + + return j <= i ? -1 : i; + }; + + /** + * Converts enumerables to array. + * + * @api public + */ + + util.toArray = function (enu) { + var arr = []; + + for (var i = 0, l = enu.length; i < l; i++) + arr.push(enu[i]); + + return arr; + }; + + /** + * UA / engines detection namespace. + * + * @namespace + */ + + util.ua = {}; + + /** + * Whether the UA supports CORS for XHR. + * + * @api public + */ + + util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { + try { + var a = new XMLHttpRequest(); + } catch (e) { + return false; + } + + return a.withCredentials != undefined; + })(); + + /** + * Detect webkit. + * + * @api public + */ + + util.ua.webkit = 'undefined' != typeof navigator + && /webkit/i.test(navigator.userAgent); + + /** + * Detect iPad/iPhone/iPod. + * + * @api public + */ + + util.ua.iDevice = 'undefined' != typeof navigator + && /iPad|iPhone|iPod/i.test(navigator.userAgent); + +})('undefined' != typeof io ? io : module.exports, this); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.EventEmitter = EventEmitter; + + /** + * Event emitter constructor. + * + * @api public. + */ + + function EventEmitter () {}; + + /** + * Adds a listener + * + * @api public + */ + + EventEmitter.prototype.on = function (name, fn) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = fn; + } else if (io.util.isArray(this.$events[name])) { + this.$events[name].push(fn); + } else { + this.$events[name] = [this.$events[name], fn]; + } + + return this; + }; + + EventEmitter.prototype.addListener = EventEmitter.prototype.on; + + /** + * Adds a volatile listener. + * + * @api public + */ + + EventEmitter.prototype.once = function (name, fn) { + var self = this; + + function on () { + self.removeListener(name, on); + fn.apply(this, arguments); + }; + + on.listener = fn; + this.on(name, on); + + return this; + }; + + /** + * Removes a listener. + * + * @api public + */ + + EventEmitter.prototype.removeListener = function (name, fn) { + if (this.$events && this.$events[name]) { + var list = this.$events[name]; + + if (io.util.isArray(list)) { + var pos = -1; + + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { + pos = i; + break; + } + } + + if (pos < 0) { + return this; + } + + list.splice(pos, 1); + + if (!list.length) { + delete this.$events[name]; + } + } else if (list === fn || (list.listener && list.listener === fn)) { + delete this.$events[name]; + } + } + + return this; + }; + + /** + * Removes all listeners for an event. + * + * @api public + */ + + EventEmitter.prototype.removeAllListeners = function (name) { + if (name === undefined) { + this.$events = {}; + return this; + } + + if (this.$events && this.$events[name]) { + this.$events[name] = null; + } + + return this; + }; + + /** + * Gets all listeners for a certain event. + * + * @api publci + */ + + EventEmitter.prototype.listeners = function (name) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = []; + } + + if (!io.util.isArray(this.$events[name])) { + this.$events[name] = [this.$events[name]]; + } + + return this.$events[name]; + }; + + /** + * Emits an event. + * + * @api public + */ + + EventEmitter.prototype.emit = function (name) { + if (!this.$events) { + return false; + } + + var handler = this.$events[name]; + + if (!handler) { + return false; + } + + var args = Array.prototype.slice.call(arguments, 1); + + if ('function' == typeof handler) { + handler.apply(this, args); + } else if (io.util.isArray(handler)) { + var listeners = handler.slice(); + + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + } else { + return false; + } + + return true; + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Based on JSON2 (http://www.JSON.org/js.html). + */ + +(function (exports, nativeJSON) { + "use strict"; + + // use native JSON if it's available + if (nativeJSON && nativeJSON.parse){ + return exports.JSON = { + parse: nativeJSON.parse + , stringify: nativeJSON.stringify + }; + } + + var JSON = exports.JSON = {}; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + function date(d, key) { + return isFinite(d.valueOf()) ? + d.getUTCFullYear() + '-' + + f(d.getUTCMonth() + 1) + '-' + + f(d.getUTCDate()) + 'T' + + f(d.getUTCHours()) + ':' + + f(d.getUTCMinutes()) + ':' + + f(d.getUTCSeconds()) + 'Z' : null; + }; + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + 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. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(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. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value instanceof Date) { + value = date(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. + + 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. + + 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, v, 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); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, 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 (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + // 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'); + }; + +})( + 'undefined' != typeof io ? io : module.exports + , typeof JSON !== 'undefined' ? JSON : undefined +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Parser namespace. + * + * @namespace + */ + + var parser = exports.parser = {}; + + /** + * Packet types. + */ + + var packets = parser.packets = [ + 'disconnect' + , 'connect' + , 'heartbeat' + , 'message' + , 'json' + , 'event' + , 'ack' + , 'error' + , 'noop' + ]; + + /** + * Errors reasons. + */ + + var reasons = parser.reasons = [ + 'transport not supported' + , 'client not handshaken' + , 'unauthorized' + ]; + + /** + * Errors advice. + */ + + var advice = parser.advice = [ + 'reconnect' + ]; + + /** + * Shortcuts. + */ + + var JSON = io.JSON + , indexOf = io.util.indexOf; + + /** + * Encodes a packet. + * + * @api private + */ + + parser.encodePacket = function (packet) { + var type = indexOf(packets, packet.type) + , id = packet.id || '' + , endpoint = packet.endpoint || '' + , ack = packet.ack + , data = null; + + switch (packet.type) { + case 'error': + var reason = packet.reason ? indexOf(reasons, packet.reason) : '' + , adv = packet.advice ? indexOf(advice, packet.advice) : ''; + + if (reason !== '' || adv !== '') + data = reason + (adv !== '' ? ('+' + adv) : ''); + + break; + + case 'message': + if (packet.data !== '') + data = packet.data; + break; + + case 'event': + var ev = { name: packet.name }; + + if (packet.args && packet.args.length) { + ev.args = packet.args; + } + + data = JSON.stringify(ev); + break; + + case 'json': + data = JSON.stringify(packet.data); + break; + + case 'connect': + if (packet.qs) + data = packet.qs; + break; + + case 'ack': + data = packet.ackId + + (packet.args && packet.args.length + ? '+' + JSON.stringify(packet.args) : ''); + break; + } + + // construct packet with required fragments + var encoded = [ + type + , id + (ack == 'data' ? '+' : '') + , endpoint + ]; + + // data fragment is optional + if (data !== null && data !== undefined) + encoded.push(data); + + return encoded.join(':'); + }; + + /** + * Encodes multiple messages (payload). + * + * @param {Array} messages + * @api private + */ + + parser.encodePayload = function (packets) { + var decoded = ''; + + if (packets.length == 1) + return packets[0]; + + for (var i = 0, l = packets.length; i < l; i++) { + var packet = packets[i]; + decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]; + } + + return decoded; + }; + + /** + * Decodes a packet + * + * @api private + */ + + var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; + + parser.decodePacket = function (data) { + var pieces = data.match(regexp); + + if (!pieces) return {}; + + var id = pieces[2] || '' + , data = pieces[5] || '' + , packet = { + type: packets[pieces[1]] + , endpoint: pieces[4] || '' + }; + + // whether we need to acknowledge the packet + if (id) { + packet.id = id; + if (pieces[3]) + packet.ack = 'data'; + else + packet.ack = true; + } + + // handle different packet types + switch (packet.type) { + case 'error': + var pieces = data.split('+'); + packet.reason = reasons[pieces[0]] || ''; + packet.advice = advice[pieces[1]] || ''; + break; + + case 'message': + packet.data = data || ''; + break; + + case 'event': + try { + var opts = JSON.parse(data); + packet.name = opts.name; + packet.args = opts.args; + } catch (e) { } + + packet.args = packet.args || []; + break; + + case 'json': + try { + packet.data = JSON.parse(data); + } catch (e) { } + break; + + case 'connect': + packet.qs = data || ''; + break; + + case 'ack': + var pieces = data.match(/^([0-9]+)(\+)?(.*)/); + if (pieces) { + packet.ackId = pieces[1]; + packet.args = []; + + if (pieces[3]) { + try { + packet.args = pieces[3] ? JSON.parse(pieces[3]) : []; + } catch (e) { } + } + } + break; + + case 'disconnect': + case 'heartbeat': + break; + }; + + return packet; + }; + + /** + * Decodes data payload. Detects multiple messages + * + * @return {Array} messages + * @api public + */ + + parser.decodePayload = function (data) { + // IE doesn't like data[i] for unicode chars, charAt works fine + if (data.charAt(0) == '\ufffd') { + var ret = []; + + for (var i = 1, length = ''; i < data.length; i++) { + if (data.charAt(i) == '\ufffd') { + ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length))); + i += Number(length) + 1; + length = ''; + } else { + length += data.charAt(i); + } + } + + return ret; + } else { + return [parser.decodePacket(data)]; + } + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.Transport = Transport; + + /** + * This is the transport template for all supported transport methods. + * + * @constructor + * @api public + */ + + function Transport (socket, sessid) { + this.socket = socket; + this.sessid = sessid; + }; + + /** + * Apply EventEmitter mixin. + */ + + io.util.mixin(Transport, io.EventEmitter); + + + /** + * Indicates whether heartbeats is enabled for this transport + * + * @api private + */ + + Transport.prototype.heartbeats = function () { + return true; + }; + + /** + * Handles the response from the server. When a new response is received + * it will automatically update the timeout, decode the message and + * forwards the response to the onMessage function for further processing. + * + * @param {String} data Response from the server. + * @api private + */ + + Transport.prototype.onData = function (data) { + this.clearCloseTimeout(); + + // If the connection in currently open (or in a reopening state) reset the close + // timeout since we have just received data. This check is necessary so + // that we don't reset the timeout on an explicitly disconnected connection. + if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) { + this.setCloseTimeout(); + } + + if (data !== '') { + // todo: we should only do decodePayload for xhr transports + var msgs = io.parser.decodePayload(data); + + if (msgs && msgs.length) { + for (var i = 0, l = msgs.length; i < l; i++) { + this.onPacket(msgs[i]); + } + } + } + + return this; + }; + + /** + * Handles packets. + * + * @api private + */ + + Transport.prototype.onPacket = function (packet) { + this.socket.setHeartbeatTimeout(); + + if (packet.type == 'heartbeat') { + return this.onHeartbeat(); + } + + if (packet.type == 'connect' && packet.endpoint == '') { + this.onConnect(); + } + + if (packet.type == 'error' && packet.advice == 'reconnect') { + this.isOpen = false; + } + + this.socket.onPacket(packet); + + return this; + }; + + /** + * Sets close timeout + * + * @api private + */ + + Transport.prototype.setCloseTimeout = function () { + if (!this.closeTimeout) { + var self = this; + + this.closeTimeout = setTimeout(function () { + self.onDisconnect(); + }, this.socket.closeTimeout); + } + }; + + /** + * Called when transport disconnects. + * + * @api private + */ + + Transport.prototype.onDisconnect = function () { + if (this.isOpen) this.close(); + this.clearTimeouts(); + this.socket.onDisconnect(); + return this; + }; + + /** + * Called when transport connects + * + * @api private + */ + + Transport.prototype.onConnect = function () { + this.socket.onConnect(); + return this; + }; + + /** + * Clears close timeout + * + * @api private + */ + + Transport.prototype.clearCloseTimeout = function () { + if (this.closeTimeout) { + clearTimeout(this.closeTimeout); + this.closeTimeout = null; + } + }; + + /** + * Clear timeouts + * + * @api private + */ + + Transport.prototype.clearTimeouts = function () { + this.clearCloseTimeout(); + + if (this.reopenTimeout) { + clearTimeout(this.reopenTimeout); + } + }; + + /** + * Sends a packet + * + * @param {Object} packet object. + * @api private + */ + + Transport.prototype.packet = function (packet) { + this.send(io.parser.encodePacket(packet)); + }; + + /** + * Send the received heartbeat message back to server. So the server + * knows we are still connected. + * + * @param {String} heartbeat Heartbeat response from the server. + * @api private + */ + + Transport.prototype.onHeartbeat = function (heartbeat) { + this.packet({ type: 'heartbeat' }); + }; + + /** + * Called when the transport opens. + * + * @api private + */ + + Transport.prototype.onOpen = function () { + this.isOpen = true; + this.clearCloseTimeout(); + this.socket.onOpen(); + }; + + /** + * Notifies the base when the connection with the Socket.IO server + * has been disconnected. + * + * @api private + */ + + Transport.prototype.onClose = function () { + var self = this; + + /* FIXME: reopen delay causing a infinit loop + this.reopenTimeout = setTimeout(function () { + self.open(); + }, this.socket.options['reopen delay']);*/ + + this.isOpen = false; + this.socket.onClose(); + this.onDisconnect(); + }; + + /** + * Generates a connection url based on the Socket.IO URL Protocol. + * See for more details. + * + * @returns {String} Connection url + * @api private + */ + + Transport.prototype.prepareUrl = function () { + var options = this.socket.options; + + return this.scheme() + '://' + + options.host + ':' + options.port + '/' + + options.resource + '/' + io.protocol + + '/' + this.name + '/' + this.sessid; + }; + + /** + * Checks if the transport is ready to start a connection. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + Transport.prototype.ready = function (socket, fn) { + fn.call(this); + }; +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + */ + + exports.Socket = Socket; + + /** + * Create a new `Socket.IO client` which can establish a persistent + * connection with a Socket.IO enabled server. + * + * @api public + */ + + function Socket (options) { + this.options = { + port: 80 + , secure: false + , document: 'document' in global ? document : false + , resource: 'socket.io' + , transports: io.transports + , 'connect timeout': 10000 + , 'try multiple transports': true + , 'reconnect': true + , 'reconnection delay': 500 + , 'reconnection limit': Infinity + , 'reopen delay': 3000 + , 'max reconnection attempts': 10 + , 'sync disconnect on unload': false + , 'auto connect': true + , 'flash policy port': 10843 + , 'manualFlush': false + }; + + io.util.merge(this.options, options); + + this.connected = false; + this.open = false; + this.connecting = false; + this.reconnecting = false; + this.namespaces = {}; + this.buffer = []; + this.doBuffer = false; + + if (this.options['sync disconnect on unload'] && + (!this.isXDomain() || io.util.ua.hasCORS)) { + var self = this; + io.util.on(global, 'beforeunload', function () { + self.disconnectSync(); + }, false); + } + + if (this.options['auto connect']) { + this.connect(); + } +}; + + /** + * Apply EventEmitter mixin. + */ + + io.util.mixin(Socket, io.EventEmitter); + + /** + * Returns a namespace listener/emitter for this socket + * + * @api public + */ + + Socket.prototype.of = function (name) { + if (!this.namespaces[name]) { + this.namespaces[name] = new io.SocketNamespace(this, name); + + if (name !== '') { + this.namespaces[name].packet({ type: 'connect' }); + } + } + + return this.namespaces[name]; + }; + + /** + * Emits the given event to the Socket and all namespaces + * + * @api private + */ + + Socket.prototype.publish = function () { + this.emit.apply(this, arguments); + + var nsp; + + for (var i in this.namespaces) { + if (this.namespaces.hasOwnProperty(i)) { + nsp = this.of(i); + nsp.$emit.apply(nsp, arguments); + } + } + }; + + /** + * Performs the handshake + * + * @api private + */ + + function empty () { }; + + Socket.prototype.handshake = function (fn) { + var self = this + , options = this.options; + + function complete (data) { + if (data instanceof Error) { + self.connecting = false; + self.onError(data.message); + } else { + fn.apply(null, data.split(':')); + } + }; + + var url = [ + 'http' + (options.secure ? 's' : '') + ':/' + , options.host + ':' + options.port + , options.resource + , io.protocol + , io.util.query(this.options.query, 't=' + +new Date) + ].join('/'); + + if (this.isXDomain() && !io.util.ua.hasCORS) { + var insertAt = document.getElementsByTagName('script')[0] + , script = document.createElement('script'); + + script.src = url + '&jsonp=' + io.j.length; + insertAt.parentNode.insertBefore(script, insertAt); + + io.j.push(function (data) { + complete(data); + script.parentNode.removeChild(script); + }); + } else { + var xhr = io.util.request(); + + xhr.open('GET', url, true); + if (this.isXDomain()) { + xhr.withCredentials = true; + } + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + xhr.onreadystatechange = empty; + + if (xhr.status == 200) { + complete(xhr.responseText); + } else if (xhr.status == 403) { + self.onError(xhr.responseText); + } else { + self.connecting = false; + !self.reconnecting && self.onError(xhr.responseText); + } + } + }; + xhr.send(null); + } + }; + + /** + * Find an available transport based on the options supplied in the constructor. + * + * @api private + */ + + Socket.prototype.getTransport = function (override) { + var transports = override || this.transports, match; + + for (var i = 0, transport; transport = transports[i]; i++) { + if (io.Transport[transport] + && io.Transport[transport].check(this) + && (!this.isXDomain() || io.Transport[transport].xdomainCheck(this))) { + return new io.Transport[transport](this, this.sessionid); + } + } + + return null; + }; + + /** + * Connects to the server. + * + * @param {Function} [fn] Callback. + * @returns {io.Socket} + * @api public + */ + + Socket.prototype.connect = function (fn) { + if (this.connecting) { + return this; + } + + var self = this; + self.connecting = true; + + this.handshake(function (sid, heartbeat, close, transports) { + self.sessionid = sid; + self.closeTimeout = close * 1000; + self.heartbeatTimeout = heartbeat * 1000; + if(!self.transports) + self.transports = self.origTransports = (transports ? io.util.intersect( + transports.split(',') + , self.options.transports + ) : self.options.transports); + + self.setHeartbeatTimeout(); + + function connect (transports){ + if (self.transport) self.transport.clearTimeouts(); + + self.transport = self.getTransport(transports); + if (!self.transport) return self.publish('connect_failed'); + + // once the transport is ready + self.transport.ready(self, function () { + self.connecting = true; + self.publish('connecting', self.transport.name); + self.transport.open(); + + if (self.options['connect timeout']) { + self.connectTimeoutTimer = setTimeout(function () { + if (!self.connected) { + self.connecting = false; + + if (self.options['try multiple transports']) { + var remaining = self.transports; + + while (remaining.length > 0 && remaining.splice(0,1)[0] != + self.transport.name) {} + + if (remaining.length){ + connect(remaining); + } else { + self.publish('connect_failed'); + } + } + } + }, self.options['connect timeout']); + } + }); + } + + connect(self.transports); + + self.once('connect', function (){ + clearTimeout(self.connectTimeoutTimer); + + fn && typeof fn == 'function' && fn(); + }); + }); + + return this; + }; + + /** + * Clears and sets a new heartbeat timeout using the value given by the + * server during the handshake. + * + * @api private + */ + + Socket.prototype.setHeartbeatTimeout = function () { + clearTimeout(this.heartbeatTimeoutTimer); + if(this.transport && !this.transport.heartbeats()) return; + + var self = this; + this.heartbeatTimeoutTimer = setTimeout(function () { + self.transport.onClose(); + }, this.heartbeatTimeout); + }; + + /** + * Sends a message. + * + * @param {Object} data packet. + * @returns {io.Socket} + * @api public + */ + + Socket.prototype.packet = function (data) { + if (this.connected && !this.doBuffer) { + this.transport.packet(data); + } else { + this.buffer.push(data); + } + + return this; + }; + + /** + * Sets buffer state + * + * @api private + */ + + Socket.prototype.setBuffer = function (v) { + this.doBuffer = v; + + if (!v && this.connected && this.buffer.length) { + if (!this.options['manualFlush']) { + this.flushBuffer(); + } + } + }; + + /** + * Flushes the buffer data over the wire. + * To be invoked manually when 'manualFlush' is set to true. + * + * @api public + */ + + Socket.prototype.flushBuffer = function() { + this.transport.payload(this.buffer); + this.buffer = []; + }; + + + /** + * Disconnect the established connect. + * + * @returns {io.Socket} + * @api public + */ + + Socket.prototype.disconnect = function () { + if (this.connected || this.connecting) { + if (this.open) { + this.of('').packet({ type: 'disconnect' }); + } + + // handle disconnection immediately + this.onDisconnect('booted'); + } + + return this; + }; + + /** + * Disconnects the socket with a sync XHR. + * + * @api private + */ + + Socket.prototype.disconnectSync = function () { + // ensure disconnection + var xhr = io.util.request(); + var uri = [ + 'http' + (this.options.secure ? 's' : '') + ':/' + , this.options.host + ':' + this.options.port + , this.options.resource + , io.protocol + , '' + , this.sessionid + ].join('/') + '/?disconnect=1'; + + xhr.open('GET', uri, false); + xhr.send(null); + + // handle disconnection immediately + this.onDisconnect('booted'); + }; + + /** + * Check if we need to use cross domain enabled transports. Cross domain would + * be a different port or different domain name. + * + * @returns {Boolean} + * @api private + */ + + Socket.prototype.isXDomain = function () { + + var port = global.location.port || + ('https:' == global.location.protocol ? 443 : 80); + + return this.options.host !== global.location.hostname + || this.options.port != port; + }; + + /** + * Called upon handshake. + * + * @api private + */ + + Socket.prototype.onConnect = function () { + if (!this.connected) { + this.connected = true; + this.connecting = false; + if (!this.doBuffer) { + // make sure to flush the buffer + this.setBuffer(false); + } + this.emit('connect'); + } + }; + + /** + * Called when the transport opens + * + * @api private + */ + + Socket.prototype.onOpen = function () { + this.open = true; + }; + + /** + * Called when the transport closes. + * + * @api private + */ + + Socket.prototype.onClose = function () { + this.open = false; + clearTimeout(this.heartbeatTimeoutTimer); + }; + + /** + * Called when the transport first opens a connection + * + * @param text + */ + + Socket.prototype.onPacket = function (packet) { + this.of(packet.endpoint).onPacket(packet); + }; + + /** + * Handles an error. + * + * @api private + */ + + Socket.prototype.onError = function (err) { + if (err && err.advice) { + if (err.advice === 'reconnect' && (this.connected || this.connecting)) { + this.disconnect(); + if (this.options.reconnect) { + this.reconnect(); + } + } + } + + this.publish('error', err && err.reason ? err.reason : err); + }; + + /** + * Called when the transport disconnects. + * + * @api private + */ + + Socket.prototype.onDisconnect = function (reason) { + var wasConnected = this.connected + , wasConnecting = this.connecting; + + this.connected = false; + this.connecting = false; + this.open = false; + + if (wasConnected || wasConnecting) { + this.transport.close(); + this.transport.clearTimeouts(); + if (wasConnected) { + this.publish('disconnect', reason); + + if ('booted' != reason && this.options.reconnect && !this.reconnecting) { + this.reconnect(); + } + } + } + }; + + /** + * Called upon reconnection. + * + * @api private + */ + + Socket.prototype.reconnect = function () { + this.reconnecting = true; + this.reconnectionAttempts = 0; + this.reconnectionDelay = this.options['reconnection delay']; + + var self = this + , maxAttempts = this.options['max reconnection attempts'] + , tryMultiple = this.options['try multiple transports'] + , limit = this.options['reconnection limit']; + + function reset () { + if (self.connected) { + for (var i in self.namespaces) { + if (self.namespaces.hasOwnProperty(i) && '' !== i) { + self.namespaces[i].packet({ type: 'connect' }); + } + } + self.publish('reconnect', self.transport.name, self.reconnectionAttempts); + } + + clearTimeout(self.reconnectionTimer); + + self.removeListener('connect_failed', maybeReconnect); + self.removeListener('connect', maybeReconnect); + + self.reconnecting = false; + + delete self.reconnectionAttempts; + delete self.reconnectionDelay; + delete self.reconnectionTimer; + delete self.redoTransports; + + self.options['try multiple transports'] = tryMultiple; + }; + + function maybeReconnect () { + if (!self.reconnecting) { + return; + } + + if (self.connected) { + return reset(); + }; + + if (self.connecting && self.reconnecting) { + return self.reconnectionTimer = setTimeout(maybeReconnect, 1000); + } + + if (self.reconnectionAttempts++ >= maxAttempts) { + if (!self.redoTransports) { + self.on('connect_failed', maybeReconnect); + self.options['try multiple transports'] = true; + self.transports = self.origTransports; + self.transport = self.getTransport(); + self.redoTransports = true; + self.connect(); + } else { + self.publish('reconnect_failed'); + reset(); + } + } else { + if (self.reconnectionDelay < limit) { + self.reconnectionDelay *= 2; // exponential back off + } + + self.connect(); + self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts); + self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay); + } + }; + + this.options['try multiple transports'] = false; + this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay); + + this.on('connect', maybeReconnect); + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.SocketNamespace = SocketNamespace; + + /** + * Socket namespace constructor. + * + * @constructor + * @api public + */ + + function SocketNamespace (socket, name) { + this.socket = socket; + this.name = name || ''; + this.flags = {}; + this.json = new Flag(this, 'json'); + this.ackPackets = 0; + this.acks = {}; + }; + + /** + * Apply EventEmitter mixin. + */ + + io.util.mixin(SocketNamespace, io.EventEmitter); + + /** + * Copies emit since we override it + * + * @api private + */ + + SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit; + + /** + * Creates a new namespace, by proxying the request to the socket. This + * allows us to use the synax as we do on the server. + * + * @api public + */ + + SocketNamespace.prototype.of = function () { + return this.socket.of.apply(this.socket, arguments); + }; + + /** + * Sends a packet. + * + * @api private + */ + + SocketNamespace.prototype.packet = function (packet) { + packet.endpoint = this.name; + this.socket.packet(packet); + this.flags = {}; + return this; + }; + + /** + * Sends a message + * + * @api public + */ + + SocketNamespace.prototype.send = function (data, fn) { + var packet = { + type: this.flags.json ? 'json' : 'message' + , data: data + }; + + if ('function' == typeof fn) { + packet.id = ++this.ackPackets; + packet.ack = true; + this.acks[packet.id] = fn; + } + + return this.packet(packet); + }; + + /** + * Emits an event + * + * @api public + */ + + SocketNamespace.prototype.emit = function (name) { + var args = Array.prototype.slice.call(arguments, 1) + , lastArg = args[args.length - 1] + , packet = { + type: 'event' + , name: name + }; + + if ('function' == typeof lastArg) { + packet.id = ++this.ackPackets; + packet.ack = 'data'; + this.acks[packet.id] = lastArg; + args = args.slice(0, args.length - 1); + } + + packet.args = args; + + return this.packet(packet); + }; + + /** + * Disconnects the namespace + * + * @api private + */ + + SocketNamespace.prototype.disconnect = function () { + if (this.name === '') { + this.socket.disconnect(); + } else { + this.packet({ type: 'disconnect' }); + this.$emit('disconnect'); + } + + return this; + }; + + /** + * Handles a packet + * + * @api private + */ + + SocketNamespace.prototype.onPacket = function (packet) { + var self = this; + + function ack () { + self.packet({ + type: 'ack' + , args: io.util.toArray(arguments) + , ackId: packet.id + }); + }; + + switch (packet.type) { + case 'connect': + this.$emit('connect'); + break; + + case 'disconnect': + if (this.name === '') { + this.socket.onDisconnect(packet.reason || 'booted'); + } else { + this.$emit('disconnect', packet.reason); + } + break; + + case 'message': + case 'json': + var params = ['message', packet.data]; + + if (packet.ack == 'data') { + params.push(ack); + } else if (packet.ack) { + this.packet({ type: 'ack', ackId: packet.id }); + } + + this.$emit.apply(this, params); + break; + + case 'event': + var params = [packet.name].concat(packet.args); + + if (packet.ack == 'data') + params.push(ack); + + this.$emit.apply(this, params); + break; + + case 'ack': + if (this.acks[packet.ackId]) { + this.acks[packet.ackId].apply(this, packet.args); + delete this.acks[packet.ackId]; + } + break; + + case 'error': + if (packet.advice){ + this.socket.onError(packet); + } else { + if (packet.reason == 'unauthorized') { + this.$emit('connect_failed', packet.reason); + } else { + this.$emit('error', packet.reason); + } + } + break; + } + }; + + /** + * Flag interface. + * + * @api private + */ + + function Flag (nsp, name) { + this.namespace = nsp; + this.name = name; + }; + + /** + * Send a message + * + * @api public + */ + + Flag.prototype.send = function () { + this.namespace.flags[this.name] = true; + this.namespace.send.apply(this.namespace, arguments); + }; + + /** + * Emit an event + * + * @api public + */ + + Flag.prototype.emit = function () { + this.namespace.flags[this.name] = true; + this.namespace.emit.apply(this.namespace, arguments); + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + */ + + exports.websocket = WS; + + /** + * The WebSocket transport uses the HTML5 WebSocket API to establish an + * persistent connection with the Socket.IO server. This transport will also + * be inherited by the FlashSocket fallback as it provides a API compatible + * polyfill for the WebSockets. + * + * @constructor + * @extends {io.Transport} + * @api public + */ + + function WS (socket) { + io.Transport.apply(this, arguments); + }; + + /** + * Inherits from Transport. + */ + + io.util.inherit(WS, io.Transport); + + /** + * Transport name + * + * @api public + */ + + WS.prototype.name = 'websocket'; + + /** + * Initializes a new `WebSocket` connection with the Socket.IO server. We attach + * all the appropriate listeners to handle the responses from the server. + * + * @returns {Transport} + * @api public + */ + + WS.prototype.open = function () { + var query = io.util.query(this.socket.options.query) + , self = this + , Socket + + + if (!Socket) { + Socket = global.MozWebSocket || global.WebSocket; + } + + this.websocket = new Socket(this.prepareUrl() + query); + + this.websocket.onopen = function () { + self.onOpen(); + self.socket.setBuffer(false); + }; + this.websocket.onmessage = function (ev) { + self.onData(ev.data); + }; + this.websocket.onclose = function () { + self.onClose(); + self.socket.setBuffer(true); + }; + this.websocket.onerror = function (e) { + self.onError(e); + }; + + return this; + }; + + /** + * Send a message to the Socket.IO server. The message will automatically be + * encoded in the correct message format. + * + * @returns {Transport} + * @api public + */ + + // Do to a bug in the current IDevices browser, we need to wrap the send in a + // setTimeout, when they resume from sleeping the browser will crash if + // we don't allow the browser time to detect the socket has been closed + if (io.util.ua.iDevice) { + WS.prototype.send = function (data) { + var self = this; + setTimeout(function() { + self.websocket.send(data); + },0); + return this; + }; + } else { + WS.prototype.send = function (data) { + this.websocket.send(data); + return this; + }; + } + + /** + * Payload + * + * @api private + */ + + WS.prototype.payload = function (arr) { + for (var i = 0, l = arr.length; i < l; i++) { + this.packet(arr[i]); + } + return this; + }; + + /** + * Disconnect the established `WebSocket` connection. + * + * @returns {Transport} + * @api public + */ + + WS.prototype.close = function () { + this.websocket.close(); + return this; + }; + + /** + * Handle the errors that `WebSocket` might be giving when we + * are attempting to connect or send messages. + * + * @param {Error} e The error. + * @api private + */ + + WS.prototype.onError = function (e) { + this.socket.onError(e); + }; + + /** + * Returns the appropriate scheme for the URI generation. + * + * @api private + */ + WS.prototype.scheme = function () { + return this.socket.options.secure ? 'wss' : 'ws'; + }; + + /** + * Checks if the browser has support for native `WebSockets` and that + * it's not the polyfill created for the FlashSocket transport. + * + * @return {Boolean} + * @api public + */ + + WS.check = function () { + return ('WebSocket' in global && !('__addTask' in WebSocket)) + || 'MozWebSocket' in global; + }; + + /** + * Check if the `WebSocket` transport support cross domain communications. + * + * @returns {Boolean} + * @api public + */ + + WS.xdomainCheck = function () { + return true; + }; + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('websocket'); + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.flashsocket = Flashsocket; + + /** + * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket + * specification. It uses a .swf file to communicate with the server. If you want + * to serve the .swf file from a other server than where the Socket.IO script is + * coming from you need to use the insecure version of the .swf. More information + * about this can be found on the github page. + * + * @constructor + * @extends {io.Transport.websocket} + * @api public + */ + + function Flashsocket () { + io.Transport.websocket.apply(this, arguments); + }; + + /** + * Inherits from Transport. + */ + + io.util.inherit(Flashsocket, io.Transport.websocket); + + /** + * Transport name + * + * @api public + */ + + Flashsocket.prototype.name = 'flashsocket'; + + /** + * Disconnect the established `FlashSocket` connection. This is done by adding a + * new task to the FlashSocket. The rest will be handled off by the `WebSocket` + * transport. + * + * @returns {Transport} + * @api public + */ + + Flashsocket.prototype.open = function () { + var self = this + , args = arguments; + + WebSocket.__addTask(function () { + io.Transport.websocket.prototype.open.apply(self, args); + }); + return this; + }; + + /** + * Sends a message to the Socket.IO server. This is done by adding a new + * task to the FlashSocket. The rest will be handled off by the `WebSocket` + * transport. + * + * @returns {Transport} + * @api public + */ + + Flashsocket.prototype.send = function () { + var self = this, args = arguments; + WebSocket.__addTask(function () { + io.Transport.websocket.prototype.send.apply(self, args); + }); + return this; + }; + + /** + * Disconnects the established `FlashSocket` connection. + * + * @returns {Transport} + * @api public + */ + + Flashsocket.prototype.close = function () { + WebSocket.__tasks.length = 0; + io.Transport.websocket.prototype.close.call(this); + return this; + }; + + /** + * The WebSocket fall back needs to append the flash container to the body + * element, so we need to make sure we have access to it. Or defer the call + * until we are sure there is a body element. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + Flashsocket.prototype.ready = function (socket, fn) { + function init () { + var options = socket.options + , port = options['flash policy port'] + , path = [ + 'http' + (options.secure ? 's' : '') + ':/' + , options.host + ':' + options.port + , options.resource + , 'static/flashsocket' + , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf' + ]; + + // Only start downloading the swf file when the checked that this browser + // actually supports it + if (!Flashsocket.loaded) { + if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') { + // Set the correct file based on the XDomain settings + WEB_SOCKET_SWF_LOCATION = path.join('/'); + } + + if (port !== 843) { + WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port); + } + + WebSocket.__initialize(); + Flashsocket.loaded = true; + } + + fn.call(self); + } + + var self = this; + if (document.body) return init(); + + io.util.load(init); + }; + + /** + * Check if the FlashSocket transport is supported as it requires that the Adobe + * Flash Player plug-in version `10.0.0` or greater is installed. And also check if + * the polyfill is correctly loaded. + * + * @returns {Boolean} + * @api public + */ + + Flashsocket.check = function () { + if ( + typeof WebSocket == 'undefined' + || !('__initialize' in WebSocket) || !swfobject + ) return false; + + return swfobject.getFlashPlayerVersion().major >= 10; + }; + + /** + * Check if the FlashSocket transport can be used as cross domain / cross origin + * transport. Because we can't see which type (secure or insecure) of .swf is used + * we will just return true. + * + * @returns {Boolean} + * @api public + */ + + Flashsocket.xdomainCheck = function () { + return true; + }; + + /** + * Disable AUTO_INITIALIZATION + */ + + if (typeof window != 'undefined') { + WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; + } + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('flashsocket'); +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); +/* SWFObject v2.2 + is released under the MIT License +*/ +if ('undefined' != typeof window) { +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab +// License: New BSD License +// Reference: http://dev.w3.org/html5/websockets/ +// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol + +(function() { + + if ('undefined' == typeof window || window.WebSocket) return; + + var console = window.console; + if (!console || !console.log || !console.error) { + console = {log: function(){ }, error: function(){ }}; + } + + if (!swfobject.hasFlashPlayerVersion("10.0.0")) { + console.error("Flash Player >= 10.0.0 is required."); + return; + } + if (location.protocol == "file:") { + console.error( + "WARNING: web-socket-js doesn't work in file:///... URL " + + "unless you set Flash Security Settings properly. " + + "Open the page via Web server i.e. http://..."); + } + + /** + * This class represents a faux web socket. + * @param {string} url + * @param {array or string} protocols + * @param {string} proxyHost + * @param {int} proxyPort + * @param {string} headers + */ + WebSocket = function(url, protocols, proxyHost, proxyPort, headers) { + var self = this; + self.__id = WebSocket.__nextId++; + WebSocket.__instances[self.__id] = self; + self.readyState = WebSocket.CONNECTING; + self.bufferedAmount = 0; + self.__events = {}; + if (!protocols) { + protocols = []; + } else if (typeof protocols == "string") { + protocols = [protocols]; + } + // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. + // Otherwise, when onopen fires immediately, onopen is called before it is set. + setTimeout(function() { + WebSocket.__addTask(function() { + WebSocket.__flash.create( + self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null); + }); + }, 0); + }; + + /** + * Send data to the web socket. + * @param {string} data The data to send to the socket. + * @return {boolean} True for success, false for failure. + */ + WebSocket.prototype.send = function(data) { + if (this.readyState == WebSocket.CONNECTING) { + throw "INVALID_STATE_ERR: Web Socket connection has not been established"; + } + // We use encodeURIComponent() here, because FABridge doesn't work if + // the argument includes some characters. We don't use escape() here + // because of this: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions + // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't + // preserve all Unicode characters either e.g. "\uffff" in Firefox. + // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require + // additional testing. + var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); + if (result < 0) { // success + return true; + } else { + this.bufferedAmount += result; + return false; + } + }; + + /** + * Close this web socket gracefully. + */ + WebSocket.prototype.close = function() { + if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { + return; + } + this.readyState = WebSocket.CLOSING; + WebSocket.__flash.close(this.__id); + }; + + /** + * Implementation of {@link
      DOM 2 EventTarget Interface} + * + * @param {string} type + * @param {function} listener + * @param {boolean} useCapture + * @return void + */ + WebSocket.prototype.addEventListener = function(type, listener, useCapture) { + if (!(type in this.__events)) { + this.__events[type] = []; + } + this.__events[type].push(listener); + }; + + /** + * Implementation of {@link DOM 2 EventTarget Interface} + * + * @param {string} type + * @param {function} listener + * @param {boolean} useCapture + * @return void + */ + WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { + if (!(type in this.__events)) return; + var events = this.__events[type]; + for (var i = events.length - 1; i >= 0; --i) { + if (events[i] === listener) { + events.splice(i, 1); + break; + } + } + }; + + /** + * Implementation of {@link DOM 2 EventTarget Interface} + * + * @param {Event} event + * @return void + */ + WebSocket.prototype.dispatchEvent = function(event) { + var events = this.__events[event.type] || []; + for (var i = 0; i < events.length; ++i) { + events[i](event); + } + var handler = this["on" + event.type]; + if (handler) handler(event); + }; + + /** + * Handles an event from Flash. + * @param {Object} flashEvent + */ + WebSocket.prototype.__handleEvent = function(flashEvent) { + if ("readyState" in flashEvent) { + this.readyState = flashEvent.readyState; + } + if ("protocol" in flashEvent) { + this.protocol = flashEvent.protocol; + } + + var jsEvent; + if (flashEvent.type == "open" || flashEvent.type == "error") { + jsEvent = this.__createSimpleEvent(flashEvent.type); + } else if (flashEvent.type == "close") { + // TODO implement jsEvent.wasClean + jsEvent = this.__createSimpleEvent("close"); + } else if (flashEvent.type == "message") { + var data = decodeURIComponent(flashEvent.message); + jsEvent = this.__createMessageEvent("message", data); + } else { + throw "unknown event type: " + flashEvent.type; + } + + this.dispatchEvent(jsEvent); + }; + + WebSocket.prototype.__createSimpleEvent = function(type) { + if (document.createEvent && window.Event) { + var event = document.createEvent("Event"); + event.initEvent(type, false, false); + return event; + } else { + return {type: type, bubbles: false, cancelable: false}; + } + }; + + WebSocket.prototype.__createMessageEvent = function(type, data) { + if (document.createEvent && window.MessageEvent && !window.opera) { + var event = document.createEvent("MessageEvent"); + event.initMessageEvent("message", false, false, data, null, null, window, null); + return event; + } else { + // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. + return {type: type, data: data, bubbles: false, cancelable: false}; + } + }; + + /** + * Define the WebSocket readyState enumeration. + */ + WebSocket.CONNECTING = 0; + WebSocket.OPEN = 1; + WebSocket.CLOSING = 2; + WebSocket.CLOSED = 3; + + WebSocket.__flash = null; + WebSocket.__instances = {}; + WebSocket.__tasks = []; + WebSocket.__nextId = 0; + + /** + * Load a new flash security policy file. + * @param {string} url + */ + WebSocket.loadFlashPolicyFile = function(url){ + WebSocket.__addTask(function() { + WebSocket.__flash.loadManualPolicyFile(url); + }); + }; + + /** + * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. + */ + WebSocket.__initialize = function() { + if (WebSocket.__flash) return; + + if (WebSocket.__swfLocation) { + // For backword compatibility. + window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; + } + if (!window.WEB_SOCKET_SWF_LOCATION) { + console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); + return; + } + var container = document.createElement("div"); + container.id = "webSocketContainer"; + // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents + // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). + // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash + // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is + // the best we can do as far as we know now. + container.style.position = "absolute"; + if (WebSocket.__isFlashLite()) { + container.style.left = "0px"; + container.style.top = "0px"; + } else { + container.style.left = "-100px"; + container.style.top = "-100px"; + } + var holder = document.createElement("div"); + holder.id = "webSocketFlash"; + container.appendChild(holder); + document.body.appendChild(container); + // See this article for hasPriority: + // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html + swfobject.embedSWF( + WEB_SOCKET_SWF_LOCATION, + "webSocketFlash", + "1" /* width */, + "1" /* height */, + "10.0.0" /* SWF version */, + null, + null, + {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, + null, + function(e) { + if (!e.success) { + console.error("[WebSocket] swfobject.embedSWF failed"); + } + }); + }; + + /** + * Called by Flash to notify JS that it's fully loaded and ready + * for communication. + */ + WebSocket.__onFlashInitialized = function() { + // We need to set a timeout here to avoid round-trip calls + // to flash during the initialization process. + setTimeout(function() { + WebSocket.__flash = document.getElementById("webSocketFlash"); + WebSocket.__flash.setCallerUrl(location.href); + WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); + for (var i = 0; i < WebSocket.__tasks.length; ++i) { + WebSocket.__tasks[i](); + } + WebSocket.__tasks = []; + }, 0); + }; + + /** + * Called by Flash to notify WebSockets events are fired. + */ + WebSocket.__onFlashEvent = function() { + setTimeout(function() { + try { + // Gets events using receiveEvents() instead of getting it from event object + // of Flash event. This is to make sure to keep message order. + // It seems sometimes Flash events don't arrive in the same order as they are sent. + var events = WebSocket.__flash.receiveEvents(); + for (var i = 0; i < events.length; ++i) { + WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); + } + } catch (e) { + console.error(e); + } + }, 0); + return true; + }; + + // Called by Flash. + WebSocket.__log = function(message) { + console.log(decodeURIComponent(message)); + }; + + // Called by Flash. + WebSocket.__error = function(message) { + console.error(decodeURIComponent(message)); + }; + + WebSocket.__addTask = function(task) { + if (WebSocket.__flash) { + task(); + } else { + WebSocket.__tasks.push(task); + } + }; + + /** + * Test if the browser is running flash lite. + * @return {boolean} True if flash lite is running, false otherwise. + */ + WebSocket.__isFlashLite = function() { + if (!window.navigator || !window.navigator.mimeTypes) { + return false; + } + var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; + if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { + return false; + } + return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; + }; + + if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { + if (window.addEventListener) { + window.addEventListener("load", function(){ + WebSocket.__initialize(); + }, false); + } else { + window.attachEvent("onload", function(){ + WebSocket.__initialize(); + }); + } + } + +})(); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + * + * @api public + */ + + exports.XHR = XHR; + + /** + * XHR constructor + * + * @costructor + * @api public + */ + + function XHR (socket) { + if (!socket) return; + + io.Transport.apply(this, arguments); + this.sendBuffer = []; + }; + + /** + * Inherits from Transport. + */ + + io.util.inherit(XHR, io.Transport); + + /** + * Establish a connection + * + * @returns {Transport} + * @api public + */ + + XHR.prototype.open = function () { + this.socket.setBuffer(false); + this.onOpen(); + this.get(); + + // we need to make sure the request succeeds since we have no indication + // whether the request opened or not until it succeeded. + this.setCloseTimeout(); + + return this; + }; + + /** + * Check if we need to send data to the Socket.IO server, if we have data in our + * buffer we encode it and forward it to the `post` method. + * + * @api private + */ + + XHR.prototype.payload = function (payload) { + var msgs = []; + + for (var i = 0, l = payload.length; i < l; i++) { + msgs.push(io.parser.encodePacket(payload[i])); + } + + this.send(io.parser.encodePayload(msgs)); + }; + + /** + * Send data to the Socket.IO server. + * + * @param data The message + * @returns {Transport} + * @api public + */ + + XHR.prototype.send = function (data) { + this.post(data); + return this; + }; + + /** + * Posts a encoded message to the Socket.IO server. + * + * @param {String} data A encoded message. + * @api private + */ + + function empty () { }; + + XHR.prototype.post = function (data) { + var self = this; + this.socket.setBuffer(true); + + function stateChange () { + if (this.readyState == 4) { + this.onreadystatechange = empty; + self.posting = false; + + if (this.status == 200){ + self.socket.setBuffer(false); + } else { + self.onClose(); + } + } + } + + function onload () { + this.onload = empty; + self.socket.setBuffer(false); + }; + + this.sendXHR = this.request('POST'); + + if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) { + this.sendXHR.onload = this.sendXHR.onerror = onload; + } else { + this.sendXHR.onreadystatechange = stateChange; + } + + this.sendXHR.send(data); + }; + + /** + * Disconnects the established `XHR` connection. + * + * @returns {Transport} + * @api public + */ + + XHR.prototype.close = function () { + this.onClose(); + return this; + }; + + /** + * Generates a configured XHR request + * + * @param {String} url The url that needs to be requested. + * @param {String} method The method the request should use. + * @returns {XMLHttpRequest} + * @api private + */ + + XHR.prototype.request = function (method) { + var req = io.util.request(this.socket.isXDomain()) + , query = io.util.query(this.socket.options.query, 't=' + +new Date); + + req.open(method || 'GET', this.prepareUrl() + query, true); + + if (method == 'POST') { + try { + if (req.setRequestHeader) { + req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); + } else { + // XDomainRequest + req.contentType = 'text/plain'; + } + } catch (e) {} + } + + return req; + }; + + /** + * Returns the scheme to use for the transport URLs. + * + * @api private + */ + + XHR.prototype.scheme = function () { + return this.socket.options.secure ? 'https' : 'http'; + }; + + /** + * Check if the XHR transports are supported + * + * @param {Boolean} xdomain Check if we support cross domain requests. + * @returns {Boolean} + * @api public + */ + + XHR.check = function (socket, xdomain) { + try { + var request = io.util.request(xdomain), + usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest), + socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'), + isXProtocol = (global.location && socketProtocol != global.location.protocol); + if (request && !(usesXDomReq && isXProtocol)) { + return true; + } + } catch(e) {} + + return false; + }; + + /** + * Check if the XHR transport supports cross domain requests. + * + * @returns {Boolean} + * @api public + */ + + XHR.xdomainCheck = function (socket) { + return XHR.check(socket, true); + }; + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.htmlfile = HTMLFile; + + /** + * The HTMLFile transport creates a `forever iframe` based transport + * for Internet Explorer. Regular forever iframe implementations will + * continuously trigger the browsers buzy indicators. If the forever iframe + * is created inside a `htmlfile` these indicators will not be trigged. + * + * @constructor + * @extends {io.Transport.XHR} + * @api public + */ + + function HTMLFile (socket) { + io.Transport.XHR.apply(this, arguments); + }; + + /** + * Inherits from XHR transport. + */ + + io.util.inherit(HTMLFile, io.Transport.XHR); + + /** + * Transport name + * + * @api public + */ + + HTMLFile.prototype.name = 'htmlfile'; + + /** + * Creates a new Ac...eX `htmlfile` with a forever loading iframe + * that can be used to listen to messages. Inside the generated + * `htmlfile` a reference will be made to the HTMLFile transport. + * + * @api private + */ + + HTMLFile.prototype.get = function () { + this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); + this.doc.open(); + this.doc.write(''); + this.doc.close(); + this.doc.parentWindow.s = this; + + var iframeC = this.doc.createElement('div'); + iframeC.className = 'socketio'; + + this.doc.body.appendChild(iframeC); + this.iframe = this.doc.createElement('iframe'); + + iframeC.appendChild(this.iframe); + + var self = this + , query = io.util.query(this.socket.options.query, 't='+ +new Date); + + this.iframe.src = this.prepareUrl() + query; + + io.util.on(window, 'unload', function () { + self.destroy(); + }); + }; + + /** + * The Socket.IO server will write script tags inside the forever + * iframe, this function will be used as callback for the incoming + * information. + * + * @param {String} data The message + * @param {document} doc Reference to the context + * @api private + */ + + HTMLFile.prototype._ = function (data, doc) { + this.onData(data); + try { + var script = doc.getElementsByTagName('script')[0]; + script.parentNode.removeChild(script); + } catch (e) { } + }; + + /** + * Destroy the established connection, iframe and `htmlfile`. + * And calls the `CollectGarbage` function of Internet Explorer + * to release the memory. + * + * @api private + */ + + HTMLFile.prototype.destroy = function () { + if (this.iframe){ + try { + this.iframe.src = 'about:blank'; + } catch(e){} + + this.doc = null; + this.iframe.parentNode.removeChild(this.iframe); + this.iframe = null; + + CollectGarbage(); + } + }; + + /** + * Disconnects the established connection. + * + * @returns {Transport} Chaining. + * @api public + */ + + HTMLFile.prototype.close = function () { + this.destroy(); + return io.Transport.XHR.prototype.close.call(this); + }; + + /** + * Checks if the browser supports this transport. The browser + * must have an `Ac...eXObject` implementation. + * + * @return {Boolean} + * @api public + */ + + HTMLFile.check = function (socket) { + if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){ + try { + var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); + return a && io.Transport.XHR.check(socket); + } catch(e){} + } + return false; + }; + + /** + * Check if cross domain requests are supported. + * + * @returns {Boolean} + * @api public + */ + + HTMLFile.xdomainCheck = function () { + // we can probably do handling for sub-domains, we should + // test that it's cross domain but a subdomain here + return false; + }; + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('htmlfile'); + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + */ + + exports['xhr-polling'] = XHRPolling; + + /** + * The XHR-polling transport uses long polling XHR requests to create a + * "persistent" connection with the server. + * + * @constructor + * @api public + */ + + function XHRPolling () { + io.Transport.XHR.apply(this, arguments); + }; + + /** + * Inherits from XHR transport. + */ + + io.util.inherit(XHRPolling, io.Transport.XHR); + + /** + * Merge the properties from XHR transport + */ + + io.util.merge(XHRPolling, io.Transport.XHR); + + /** + * Transport name + * + * @api public + */ + + XHRPolling.prototype.name = 'xhr-polling'; + + /** + * Indicates whether heartbeats is enabled for this transport + * + * @api private + */ + + XHRPolling.prototype.heartbeats = function () { + return false; + }; + + /** + * Establish a connection, for iPhone and Android this will be done once the page + * is loaded. + * + * @returns {Transport} Chaining. + * @api public + */ + + XHRPolling.prototype.open = function () { + var self = this; + + io.Transport.XHR.prototype.open.call(self); + return false; + }; + + /** + * Starts a XHR request to wait for incoming messages. + * + * @api private + */ + + function empty () {}; + + XHRPolling.prototype.get = function () { + if (!this.isOpen) return; + + var self = this; + + function stateChange () { + if (this.readyState == 4) { + this.onreadystatechange = empty; + + if (this.status == 200) { + self.onData(this.responseText); + self.get(); + } else { + self.onClose(); + } + } + }; + + function onload () { + this.onload = empty; + this.onerror = empty; + self.retryCounter = 1; + self.onData(this.responseText); + self.get(); + }; + + function onerror () { + self.retryCounter ++; + if(!self.retryCounter || self.retryCounter > 3) { + self.onClose(); + } else { + self.get(); + } + }; + + this.xhr = this.request(); + + if (global.XDomainRequest && this.xhr instanceof XDomainRequest) { + this.xhr.onload = onload; + this.xhr.onerror = onerror; + } else { + this.xhr.onreadystatechange = stateChange; + } + + this.xhr.send(null); + }; + + /** + * Handle the unclean close behavior. + * + * @api private + */ + + XHRPolling.prototype.onClose = function () { + io.Transport.XHR.prototype.onClose.call(this); + + if (this.xhr) { + this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty; + try { + this.xhr.abort(); + } catch(e){} + this.xhr = null; + } + }; + + /** + * Webkit based browsers show a infinit spinner when you start a XHR request + * before the browsers onload event is called so we need to defer opening of + * the transport until the onload event is called. Wrapping the cb in our + * defer method solve this. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + XHRPolling.prototype.ready = function (socket, fn) { + var self = this; + + io.util.defer(function () { + fn.call(self); + }); + }; + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('xhr-polling'); + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + /** + * There is a way to hide the loading indicator in Firefox. If you create and + * remove a iframe it will stop showing the current loading indicator. + * Unfortunately we can't feature detect that and UA sniffing is evil. + * + * @api private + */ + + var indicator = global.document && "MozAppearance" in + global.document.documentElement.style; + + /** + * Expose constructor. + */ + + exports['jsonp-polling'] = JSONPPolling; + + /** + * The JSONP transport creates an persistent connection by dynamically + * inserting a script tag in the page. This script tag will receive the + * information of the Socket.IO server. When new information is received + * it creates a new script tag for the new data stream. + * + * @constructor + * @extends {io.Transport.xhr-polling} + * @api public + */ + + function JSONPPolling (socket) { + io.Transport['xhr-polling'].apply(this, arguments); + + this.index = io.j.length; + + var self = this; + + io.j.push(function (msg) { + self._(msg); + }); + }; + + /** + * Inherits from XHR polling transport. + */ + + io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); + + /** + * Transport name + * + * @api public + */ + + JSONPPolling.prototype.name = 'jsonp-polling'; + + /** + * Posts a encoded message to the Socket.IO server using an iframe. + * The iframe is used because script tags can create POST based requests. + * The iframe is positioned outside of the view so the user does not + * notice it's existence. + * + * @param {String} data A encoded message. + * @api private + */ + + JSONPPolling.prototype.post = function (data) { + var self = this + , query = io.util.query( + this.socket.options.query + , 't='+ (+new Date) + '&i=' + this.index + ); + + if (!this.form) { + var form = document.createElement('form') + , area = document.createElement('textarea') + , id = this.iframeId = 'socketio_iframe_' + this.index + , iframe; + + form.className = 'socketio'; + form.style.position = 'absolute'; + form.style.top = '0px'; + form.style.left = '0px'; + form.style.display = 'none'; + form.target = id; + form.method = 'POST'; + form.setAttribute('accept-charset', 'utf-8'); + area.name = 'd'; + form.appendChild(area); + document.body.appendChild(form); + + this.form = form; + this.area = area; + } + + this.form.action = this.prepareUrl() + query; + + function complete () { + initIframe(); + self.socket.setBuffer(false); + }; + + function initIframe () { + if (self.iframe) { + self.form.removeChild(self.iframe); + } + + try { + // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) + iframe = document.createElement(''; +html += '
      '; +html += '
      '; +html += '
      Upload File
      '; +html += '
      Want to upload multiple files at once? Please upgrade to the latest Flash Player, then reload this page. For some reason our Flash based uploader did not load, so you are currently using our single file uploader.
      '; +html += spacer(1,20) + '
      '; +var url = zero_client.targetURL; +if (url.indexOf('?') > -1) url += '&'; else url += '?'; +url += 'format=jshtml&onafter=' + escape('window.parent.upload_basic_finish(response);'); +Debug.trace('upload', "Prepping basic upload: " + url); +html += '
      '; +html += '
      '; +html += '
      '; +html += '

      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('page_white_get.png', 'Upload', "upload_basic_go()") + '
      '; +html += '
      '; +html += ''; +html += '
      '; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +show_popup_dialog(528, 200, html); +} +function upload_basic_go() { +$('f_upload_basic').submit(); +$('d_upload_form').hide(); +$('d_upload_progress').show(); +} +function upload_basic_finish(response) { +Debug.trace('upload', "Basic upload complete: " + dumper(response)); +setTimeout( 'upload_basic_finish_2()', 100 ); +} +function upload_basic_finish_2() { +$('i_upload_basic').src = 'blank.html'; +setTimeout( 'upload_basic_finish_3()', 100 ); +} +function upload_basic_finish_3() { +hide_popup_dialog(); +delete session.progress; +show_progress_dialog( 0, 'Finishing Upload...', true ); +fire_callback( session.upload_callback ); +} +function upload_destroy() { +if (zero_client) { +zero_client.destroy(); +delete ZeroUpload.clients[ zero_client.id ]; +zero_client = null; +} +} +function prep_upload(dom_id, url, callback, types) { +session.upload_callback = callback; +if (url) { +if (url.indexOf('?') > -1) url += '&'; else url += '?'; +url += 'session=' + session.cookie.get('effect_session_id'); +} +upload_destroy(); +zero_client = new ZeroUpload.Client(); +if (url) zero_client.setURL( url ); +zero_client.setHandCursor( true ); +if (types) zero_client.setFileTypes( types[0], types[1] ); +zero_client.addEventListener( 'queueStart', uploadQueueStart ); +zero_client.addEventListener( 'fileStart', uploadFileStart ); +zero_client.addEventListener( 'progress', uploadProgress ); +zero_client.addEventListener( 'fileComplete', uploadFileComplete ); +zero_client.addEventListener( 'queueComplete', uploadQueueComplete ); +zero_client.addEventListener( 'error', uploadError ); +zero_client.addEventListener( 'debug', function(client, eventName, args) { +Debug.trace('upload', "Caught event: " + eventName); +} ); +if (dom_id) { +Debug.trace('upload', "Gluing ZeroUpload to: " + dom_id); +zero_client.glue( dom_id ); +} +} +Class.create( 'Debug', { +__static: { +enabled: false, +categories: { all: 1 }, +buffer: [], +max_rows: 5000, +win: null, +ie: !!navigator.userAgent.match(/MSIE/), +ie6: !!navigator.userAgent.match(/MSIE\D+6/), +init: function() { +Debug.enabled = true; +Debug.trace( 'debug', 'Debug log start' ); +var html = '

      '; +if (Debug.ie) { +setTimeout( function() { +document.body.insertAdjacentHTML('beforeEnd', +'
      ' + html + '
      ' +); +}, 1000 ); +} +else { +var div = document.createElement('DIV'); +div.id = 'd_debug'; +div.setAttribute('id', 'd_debug'); +div.style.position = Debug.ie6 ? 'absolute' : 'fixed'; +div.style.zIndex = '101'; +div.style.left = '0px'; +div.style.top = '0px'; +div.style.width = '100%'; +div.innerHTML = html; +document.getElementsByTagName('body')[0].appendChild(div); +} +}, +show: function() { +if (!Debug.win || Debug.win.closed) { +Debug.trace('debug', "Opening debug window"); +Debug.win = window.open( '', 'DebugWindow', 'width=600,height=500,menubar=no,resizable=yes,scrollbars=yes,location=no,status=no,toolbar=no,directories=no' ); +if (!Debug.win) return alert("Failed to open window. Popup blocker maybe?"); +var doc = Debug.win.document; +doc.open(); +doc.writeln( 'Debug Log' ); +doc.writeln( '
      ' ); +doc.writeln( '
      ' ); +doc.writeln( '
      ' ); +doc.writeln( '' ); +doc.writeln( '' ); +doc.writeln( '
      ' ); +doc.writeln( '' ); +doc.close(); +} +Debug.win.focus(); +}, +console_execute: function() { +var cmd = Debug.win.document.getElementById('fe_command'); +if (cmd.value.length) { +Debug.trace( 'console', cmd.value ); +try { +Debug.trace( 'console', '' + eval(cmd.value) ); +} +catch (e) { +Debug.trace( 'error', 'JavaScript Interpreter Exception: ' + e.toString() ); +} +} +}, +get_time_stamp: function(now) { +var date = new Date( now * 1000 ); +var hh = date.getHours(); if (hh < 10) hh = "0" + hh; +var mi = date.getMinutes(); if (mi < 10) mi = "0" + mi; +var ss = date.getSeconds(); if (ss < 10) ss = "0" + ss; +var sss = '' + date.getMilliseconds(); while (sss.length < 3) sss = "0" + sss; +return '' + hh + ':' + mi + ':' + ss + '.' + sss; +}, +refresh_console: function() { +if (!Debug.win || Debug.win.closed) return; +var div = Debug.win.document.getElementById('d_debug_log'); +if (div) { +var row = null; +while ( row = Debug.buffer.shift() ) { +var time_stamp = Debug.get_time_stamp(row.time); +var msg = row.msg; +msg = msg.replace(/\t/g, "    "); +msg = msg.replace(//g, ">"); +msg = msg.replace(/\n/g, "
      \n"); +var html = ''; +var sty = 'float:left; font-family: Consolas, Courier, mono; font-size: 12px; cursor:default; margin-right:10px; margin-bottom:1px; padding:2px;'; +html += '
      ' + time_stamp + '
      '; +html += '
      ' + row.cat + '
      '; +html += '
      ' + msg + '
      '; +html += '
      '; +var chunk = Debug.win.document.createElement('DIV'); +chunk.style['float'] = 'none'; +chunk.innerHTML = html; +div.appendChild(chunk); +} +var cmd = Debug.win.document.getElementById('fe_command'); +cmd.focus(); +} +Debug.dirty = 0; +Debug.win.scrollTo(0, 99999); +}, +hires_time_now: function() { +var now = new Date(); +return ( now.getTime() / 1000 ); +}, +trace: function(cat, msg) { +if (arguments.length == 1) { +msg = cat; +cat = 'debug'; +} +if (Debug.categories.all || Debug.categories[cat]) { +Debug.buffer.push({ cat: cat, msg: msg, time: Debug.hires_time_now() }); +if (Debug.buffer.length > Debug.max_rows) Debug.buffer.shift(); +if (!Debug.dirty) { +Debug.dirty = 1; +setTimeout( 'Debug.refresh_console();', 1 ); +} +} +} +} +} ); +var session = { +inited: false, +api_mod_cache: {}, +query: parseQueryString( ''+location.search ), +cookie: new CookieTree({ path: '/effect/' }), +storage: {}, +storage_dirty: false, +hooks: { +keys: {} +}, +username: '', +em_width: 11, +audioResourceMatch: /\.mp3$/i, +imageResourceMatch: /\.(jpe|jpeg|jpg|png|gif)$/i, +textResourceMatch: /\.xml$/i, +movieResourceMatch: /\.(flv|mp4|mp4v|mov|3gp|3g2)$/i, +imageResourceMatchString: '\.(jpe|jpeg|jpg|png|gif)$' +}; +session.debug = session.query.debug ? true : false; +var page_manager = null; +var preload_icons = []; +var preload_images = [ +'loading.gif', +'aquaprogressbar.gif', +'aquaprogressbar_bkgnd.gif' +]; +function get_base_url() { +return protocol + '://' + location.hostname + session.config.BaseURI; +} +function effect_init() { +if (session.inited) return; +session.inited = true; +assert( window.config, "Config not loaded" ); +session.config = window.config; +Debug.trace("Starting up"); +rendering_page = false; +preload(); +window.$R = {}; +for (var key in config.RegExpShortcuts) { +$R[key] = new RegExp( config.RegExpShortcuts[key] ); +} +ww_precalc_font("body", "effect_precalc_font_finish"); +page_manager = new Effect.PageManager( config.Pages.Page ); +var session_id = session.cookie.get('effect_session_id'); +if (session_id && session_id.match(/^login/)) { +do_session_recover(); +} +else { +show_default_login_status(); +Nav.init(); +} +Blog.search({ +stag: 'sidebar_docs', +limit: 20, +title_only: true, +sort_by: 'seq', +sort_dir: -1, +target: 'd_sidebar_documents', +outer_div_class: 'sidebar_blog_row', +title_class: 'sidebar_blog_title', +after: '' +}); +Blog.search({ +stag: 'sidebar_tutorials', +limit: 5, +title_only: true, +sort_by: 'seq', +sort_dir: -1, +target: 'd_sidebar_tutorials', +outer_div_class: 'sidebar_blog_row', +title_class: 'sidebar_blog_title', +after: '' +}); +Blog.search({ +stag: 'sidebar_plugins', +limit: 5, +title_only: true, +sort_by: 'seq', +sort_dir: -1, +target: 'd_sidebar_plugins', +outer_div_class: 'sidebar_blog_row', +title_class: 'sidebar_blog_title', +after: '' +}); +$('fe_search_bar').onkeydown = delay_onChange_input_text; +user_storage_idle(); +} +function effect_precalc_font_finish(width, height) { +session.em_width = width; +} +function preload() { +for (var idx = 0, len = preload_icons.length; idx < len; idx++) { +var url = images_uri + '/icons/' + preload_icons[idx] + '.gif'; +preload_icons[idx] = new Image(); +preload_icons[idx].src = url; +} +for (var idx = 0, len = preload_images.length; idx < len; idx++) { +var url = images_uri + '/' + preload_images[idx]; +preload_images[idx] = new Image(); +preload_images[idx].src = url; +} +} +function $P(id) { +if (!id) id = page_manager.current_page_id; +var page = page_manager.find(id); +assert( !!page, "Failed to locate page: " + id ); +return page; +} +function get_pref(name) { +if (!session.user || !session.user.Preferences) return alert("ASSERT FAILURE! Tried to lookup pref " + name + " and user is not yet loaded!"); +return session.user.Preferences[name]; +} +function get_bool_pref(name) { +return (get_pref(name) == 1); +} +function set_pref(name, value) { +session.user.Preferences[name] = value; +} +function set_bool_pref(name, value) { +set_pref(name, value ? '1' : '0'); +} +function save_prefs() { +var prefs_to_save = {}; +if (arguments.length) { +for (var idx = 0, len = arguments.length; idx < len; idx++) { +var key = arguments[idx]; +prefs_to_save[key] = get_pref(key); +} +} +else prefs_to_save = session.user.Preferences; +effect_api_mod_touch('user_get'); +effect_api_send('user_update', { +Username: session.username, +Preferences: prefs_to_save +}, 'save_prefs_2'); +} +function save_prefs_2(response) { +do_message('success', 'Preferences saved.'); +} + +function get_full_name(username) { +var user = session.users[username]; +if (!user) return username; +return user.FullName; +} +function get_buddy_icon_url(username, size) { +var mod = session.api_mod_cache.get_buddy_icon || 0; +if (!size) size = 32; +var url = '/effect/api/get_buddy_icon?username='+username + '&mod=' + mod + '&size=' + size; +return url; +} +function get_buddy_icon_display(username, show_icon, show_name) { +if ((typeof(show_icon) == 'undefined') && get_bool_pref('show_user_icons')) show_icon = 1; +if ((typeof(show_name) == 'undefined') && get_bool_pref('show_user_names')) show_name = 1; +var html = ''; +if (show_icon) html += ''; +if (show_icon && show_name) html += '
      '; +if (show_name) html += username; +return html; +} +function do_session_recover() { +session.hooks.after_error = 'do_logout'; +effect_api_send('session_recover', {}, 'do_login_2', { _from_recover: 1 } ); +} +function require_login() { +if (session.user) return true; +Debug.trace('Page requires login, showing login page'); +session.nav_after_login = Nav.currentAnchor(); +setTimeout( function() { +Nav.go( 'Login' ); +}, 1 ); +return false; +} +function popup_window(url, name) { +if (!url) url = ''; +if (!name) name = ''; +var win = window.open(url, name); +if (!win) return alert('Failed to open popup window. If you have a popup blocker, please disable it for this website and try again.'); +return win; +} +function do_login_prompt() { +hide_popup_dialog(); +delete session.progress; +if (!session.temp_password) session.temp_password = ''; +if (!session.username) session.username = ''; +var temp_username = session.open_id || session.username || ''; +var html = ''; +html += '
      '; +html += '
      '; +html += '
      Effect Developer Login
      '; +html += '
      '; +html += '
      Effect Username  or  '+icon('openid', 'OpenID', 'popup_window(\'http://openid.net/\')', 'What is OpenID?')+'


      '; +html += '
      '; +html += '
      '; +html += '

      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', "clear_login()") + ' ' + large_icon_button('check', 'Login', 'do_login()') + '
      '; +html += '
      '; +html += ''; +session.hooks.keys[ENTER_KEY] = 'do_login'; +session.hooks.keys[ESC_KEY] = 'clear_login'; +safe_focus( 'fe_username' ); +show_popup_dialog(450, 225, html); +} +function do_openid_reg(title, auto_login_button) { +hide_popup_dialog(); +delete session.progress; +if (!title) title = 'Register Account Using OpenID'; +if (typeof(auto_login_button) == 'undefined') auto_login_button = 1; +var html = ''; +html += '
      '; +html += '
      '; +html += '
      '+title+'
      '; +html += '
      '; +html += '
      '+icon('openid', 'Enter Your OpenID URL:')+'
      '; +if (auto_login_button) html += '


      '; +html += '
      '; +html += '

      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', title.match(/login/i) ? 'Login' : 'Register', 'do_openid_login()') + '
      '; +html += '
      '; +html += ''; +session.hooks.keys[ENTER_KEY] = 'do_openid_login'; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_username' ); +show_popup_dialog(450, 225, html); +} +function do_login_prompt_2() { +hide_popup_dialog(); +delete session.progress; +if (!session.temp_password) session.temp_password = ''; +if (!session.username) session.username = ''; +var html = ''; +html += '
      '; +html += ' and then append +the "child" element to the new <div>: + + put("div", child); + +Or we can do a simple append of an existing element to another element: + + put(parent, ">", child); + +We could also use sibling combinators to place the referenced element. We could place +the "second" element after (as the next sibling) the "first" element: + + put(first, "+", second); + +Or we could create a <div> and place "first" before it using the previous sibling combinator: + + put(parent, "div.second -", first); + +The put() function takes an unlimited number of arguments, so we could combine as +many selectors and elements as we want: + + put(parent, "div.child", grandchild, "div.great-grandchild", gggrandchild); + +Variable Substitution +------------------- + +The put() function also supports variable substitution, by using the "$" symbol in selectors. +The "$" can be used for attribute values and to represent text content. When a "$" +is encountered in a selector, the next argument value is consumed and used in it's +place. To create an element with a title that comes from the variable "title", we could write: + + put("div[title=$]", title); + +The value of title may have any characters (including ']'), no escaping is needed. +This approach can simplify selector string construction and avoids the need for complicated +escaping mechanisms. + +The "$" may be used as a child entity to indicate text content. For example, we could +create a set of <span> element that each have content to be substituted: + + put("span.first-name $, span.last-name $, span.age $", firstName, lastName, age); + +Assigning Properties +------------------ + +The put() function can also take an object with properties to be set on the new/referenced +element. For example, we could write: + + newDiv = put(parent, "div", { + tabIndex: 1, + innerHTML: "Hello, World" + }); + +Which is identical to writing (all the properties are set using direct property access, not setAttribute): + + newDiv = put(parent, "div"); + newDiv.tabIndex = 1; + newDiv.innerHTML = "Hello, World"; + +NodeJS/Server Side HTML Generation +---------------------------- + +While the put() function directly creates DOM elements in the browser, the put() function +can be used to generate HTML on the server, in NodeJS. When no DOM is available, +a fast lightweight pseudo-DOM is created that can generate HTML as a string or into a stream. +The API is still the same, but the put() function returns pseudo-elements with a +toString() method that can be called to return the HTML and sendTo method to direct +generated elements to a stream on the fly. For example: + + put("div.test").toString() -> '
      ' + +To use put() streaming, we create and element and call sendTo with a target stream. +In streaming mode, the elements are written to the stream as they are added to the +parent DOM structure. This approach is much more efficient because very little +needs to be kept in memory, the HTML can be immediately flushed to the network as it is created. +Once an element is added to the streamed DOM structure, +it is immediately sent to the stream, and it's attributes and classes can no longer be +altered. There are two methods on elements available for streaming purposes: + + element.sendTo(stream) + +The sendTo(stream) method will begin streaming the element to the target stream, +and any children that are added to the element will be streamed as well. + + element.end(leaveOpen) + +The end(leaveOpen) method will end the current streaming, closing all the necessary +tags and closing the stream (unless the argument is true). + +The returned elements also include a put() method so you can directly add to or apply +CSS selector-based additions to elements, for example: + + element.put('div.test'); // create a <div class="test"></div> as a child of element + +Here is an example of how we could create a full page in NodeJS that is streamed to +the response: + + var http = require('http'); + var put = require('put-selector'); + http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/html'}); + var page = put('html').sendTo(res); // create an HTML page, and pipe to the response + page.put('head script[src=app.js]'); // each element is sent immediately + page.put('body div.content', 'Hello, World'); + page.end(); // close all the tags, and end the stream + }).listen(80); + +On the server, there are some limitations to put(). The server side DOM emulation +is designed to be very fast and light and therefore omits much of the standard DOM +functionality, and only what is needed for put() is implemented. Elements can +not be moved or removed. DOM creation and updating is still supported in string +generation mode, but only creation is supported in streaming mode. Also, setting +object properties is mostly ignored (because only attributes are part of HTML), except +you can set the innerHTML of an element. + +Proper Creation of Inputs +------------------------- + +Older versions of Internet Explorer have a bug in assigning a "name" attribute to input after it +has been created, and requires a special creation technique. The put() function handles +this for you as long as you specify the name of the input in the property assignment +object after the selector string. For example, this input creation will properly work +on all browsers, including IE: + + newInput = put("input[type=checkbox]", {name: "works"}); + +Using on Different document +------------------------- + +If you are using multiple frames in your web page, you may encounter a situation where +you want to use put-selector to make DOM changes on a different HTML document. +You can create a separate instance of the put() function for a separate document by +calling the put.forDocument(document) function. For example: + + put2 = put.forDocument(frames[1].document); + put2("div") <- creates a div element that belongs to the document in the second frame. + put("div") <- the original put still functions on the main document for this window/context \ No newline at end of file diff --git a/widgets/put-selector/node-html.js b/widgets/put-selector/node-html.js new file mode 100644 index 0000000..08753ac --- /dev/null +++ b/widgets/put-selector/node-html.js @@ -0,0 +1,199 @@ +"use strict"; +var put; +function Element(tag){ + this.tag = tag; +} +// create set of elements with an empty content model, so we now to skip their closing tag +var emptyElements = {}; +["base", "link", "meta", "hr", "br", "wbr", "img", "embed", "param", "source", "track", "area", "col", "input", "keygen", "command"].forEach(function(tag){ + emptyElements[tag] = true; +}); +var prototype = Element.prototype; +var currentIndentation = ''; +prototype.nodeType = 1; +prototype.put = function(){ + var args = [this]; + args.push.apply(args, arguments); + return put.apply(null, args); +} +prototype.toString = function(noClose){ + var tag = this.tag; + var emptyElement = emptyElements[tag]; + if(put.indentation && !noClose){ + // using pretty printing with indentation + var lastIndentation = currentIndentation; + currentIndentation += put.indentation; + var html = (tag == 'html' ? '\n' + + (this.children ? this.children.join('') : '') + + (!this.mixed && !emptyElement && this.children ? '\n' +lastIndentation : '') + + (emptyElement ? '' : ('')); + + currentIndentation = lastIndentation; + return html; + } + return (this.tag == 'html' ? '\n' + + (this.children ? this.children.join('') : '') + + ((noClose || emptyElement) ? '' : ('')); +}; +prototype.sendTo = function(stream){ + if(typeof stream == 'function'){ // write function itself + stream = {write: stream, end: stream}; + } + var active = this; + var streamIndentation = ''; + function pipe(element){ + // TODO: Perhaps consider buffering if it is any faster and having a non-indentation version that is faster + var closing = returnTo(this); + if(closing){ + stream.write(closing); + } + var tag = element.tag; + if(element.tag){ + if(put.indentation){ + stream.write('\n' + streamIndentation + element.toString(true)); + streamIndentation += put.indentation; + }else{ + stream.write(element.toString(true)); + } + this.children = true; + active = element; + element.pipe = pipe; + }else{ + stream.write(element.toString()); + } + } + function returnTo(element){ + var output = ''; + while(active != element){ + if(!active){ + throw new Error("Can not add to an element that has already been streamed"); + } + var tag = active.tag; + var emptyElement = emptyElements[tag]; + if(put.indentation){ + streamIndentation = streamIndentation.slice(put.indentation.length); + if(!emptyElement){ + output += ((active.mixed || !active.children) ? '' : '\n' + streamIndentation) + ''; + } + }else if(!emptyElement){ + output += ''; + } + active = active.parentNode; + } + return output; + } + pipe.call(this, this); + // add on end() function to close all the tags and close the stream + this.end = function(leaveStreamOpen){ + stream[leaveStreamOpen ? 'write' : 'end'](returnTo(this) + '\n'); + } + return this; +}; +prototype.children = false; +prototype.attributes = false; +prototype.insertBefore = function(child, reference){ + child.parentNode = this; + if(this.pipe){ + return this.pipe(child); + //return this.s(child); + } + var children = this.children; + if(!children){ + children = this.children = []; + } + if(reference){ + for(var i = 0, l = children.length; i < l; i++){ + if(reference == children[i]){ + child.nextSibling = reference; + if(i > 0){ + children[i-1].nextSibling = child; + } + return children.splice(i, 0, child); + } + } + } + if(children.length > 0){ + children[children.length-1].nextSibling = child; + } + children.push(child); +}; +prototype.appendChild = function(child){ + if(typeof child == "string"){ + this.mixed = true; + } + if(this.pipe){ + return this.pipe(child); + } + var children = this.children; + if(!children){ + children = this.children = []; + } + children.push(child); +}; +prototype.setAttribute = function(name, value, escape){ + var attributes = this.attributes; + if(!attributes){ + attributes = this.attributes = []; + } + attributes.push(' ' + name + '="' + value + '"'); +}; +prototype.removeAttribute = function(name, value){ + var attributes = this.attributes; + if(!attributes){ + return; + } + var match = ' ' + name + '=', matchLength = match.length; + for(var i = 0, l = attributes.length; i < l; i++){ + if(attributes[i].slice(0, matchLength) == match){ + return attributes.splice(i, 1); + } + } +}; +Object.defineProperties(prototype, { + innerHTML: { + get: function(){ + return this.children.join(''); + }, + set: function(value){ + this.mixed = true; + if(this.pipe){ + return this.pipe(value); + } + this.children = [value]; + } + } +}); +function DocumentFragment(){ +} +DocumentFragment.prototype = new Element(); +DocumentFragment.prototype.toString = function(){ + return this.children ? this.children.join('') : ''; +}; + +var lessThanRegex = / ]/; // if it has any of these combinators, it is probably going to be faster with a document fragment +define([], forDocument = function(doc, newFragmentFasterHeuristic){ +"use strict"; + // module: + // put-selector/put + // summary: + // This module defines a fast lightweight function for updating and creating new elements + // terse, CSS selector-based syntax. The single function from this module creates + // new DOM elements and updates existing elements. See README.md for more information. + // examples: + // To create a simple div with a class name of "foo": + // | put("div.foo"); + fragmentFasterHeuristic = newFragmentFasterHeuristic || fragmentFasterHeuristic; + var selectorParse = /(?:\s*([-+ ,<>]))?\s*(\.|!\.?|#)?([-\w%$]+)?(?:\[([^\]=]+)=?['"]?([^\]'"]*)['"]?\])?/g, + undefined, + doc = doc || document, + ieCreateElement = typeof doc.createElement == "object"; // telltale sign of the old IE behavior with createElement that does not support later addition of name + function insertTextNode(element, text){ + element.appendChild(doc.createTextNode(text)); + } + function put(topReferenceElement){ + var fragment, lastSelectorArg, nextSibling, referenceElement, current, + args = arguments, + returnValue = args[0]; // use the first argument as the default return value in case only an element is passed in + function insertLastElement(){ + // we perform insertBefore actions after the element is fully created to work properly with + // tags in older versions of IE that require type attributes + // to be set before it is attached to a parent. + // We also handle top level as a document fragment actions in a complex creation + // are done on a detached DOM which is much faster + // Also if there is a parse error, we generally error out before doing any DOM operations (more atomic) + if(current && referenceElement && current != referenceElement){ + (referenceElement == topReferenceElement && + // top level, may use fragment for faster access + (fragment || + // fragment doesn't exist yet, check to see if we really want to create it + (fragment = fragmentFasterHeuristic.test(argument) && doc.createDocumentFragment())) + // any of the above fails just use the referenceElement + || referenceElement). + insertBefore(current, nextSibling || null); // do the actual insertion + } + } + for(var i = 0; i < args.length; i++){ + var argument = args[i]; + if(typeof argument == "object"){ + lastSelectorArg = false; + if(argument instanceof Array){ + // an array + current = doc.createDocumentFragment(); + for(var key = 0; key < argument.length; key++){ + current.appendChild(put(argument[key])); + } + argument = current; + } + if(argument.nodeType){ + current = argument; + insertLastElement(); + referenceElement = argument; + nextSibling = 0; + }else{ + // an object hash + for(var key in argument){ + current[key] = argument[key]; + } + } + }else if(lastSelectorArg){ + // a text node should be created + // take a scalar value, use createTextNode so it is properly escaped + // createTextNode is generally several times faster than doing an escaped innerHTML insertion: http://jsperf.com/createtextnode-vs-innerhtml/2 + lastSelectorArg = false; + insertTextNode(current, argument); + }else{ + if(i < 1){ + // if we are starting with a selector, there is no top element + topReferenceElement = null; + } + lastSelectorArg = true; + var leftoverCharacters = argument.replace(selectorParse, function(t, combinator, prefix, value, attrName, attrValue){ + if(combinator){ + // insert the last current object + insertLastElement(); + if(combinator == '-' || combinator == '+'){ + // + or - combinator, + // TODO: add support for >- as a means of indicating before the first child? + referenceElement = (nextSibling = (current || referenceElement)).parentNode; + current = null; + if(combinator == "+"){ + nextSibling = nextSibling.nextSibling; + }// else a - operator, again not in CSS, but obvious in it's meaning (create next element before the current/referenceElement) + }else{ + if(combinator == "<"){ + // parent combinator (not really in CSS, but theorized, and obvious in it's meaning) + referenceElement = current = (current || referenceElement).parentNode; + }else{ + if(combinator == ","){ + // comma combinator, start a new selector + referenceElement = topReferenceElement; + }else if(current){ + // else descendent or child selector (doesn't matter, treated the same), + referenceElement = current; + } + current = null; + } + nextSibling = 0; + } + if(current){ + referenceElement = current; + } + } + var tag = !prefix && value; + if(tag || (!current && (prefix || attrName))){ + if(tag == "$"){ + // this is a variable to be replaced with a text node + insertTextNode(referenceElement, args[++i]); + }else{ + // Need to create an element + tag = tag || put.defaultTag; + var ieInputName = ieCreateElement && args[i +1] && args[i +1].name; + if(ieInputName){ + // in IE, we have to use the crazy non-standard createElement to create input's that have a name + tag = '<' + tag + ' name="' + ieInputName + '">'; + } + current = doc.createElement(tag); + } + } + if(prefix){ + if(value == "$"){ + value = args[++i]; + } + if(prefix == "#"){ + // #id was specified + current.id = value; + }else{ + // we are in the className addition and removal branch + var currentClassName = current.className; + // remove the className (needed for addition or removal) + // see http://jsperf.com/remove-class-name-algorithm/2 for some tests on this + var removed = currentClassName && (" " + currentClassName + " ").replace(" " + value + " ", " "); + if(prefix == "."){ + // addition, add the className + current.className = currentClassName ? (removed + value).substring(1) : value; + }else{ + // else a '!' class removal + if(argument == "!"){ + // special signal to delete this element + // use the ol' innerHTML trick to get IE to do some cleanup + put("div", current, '<').innerHTML = ""; + }else{ + // we already have removed the class, just need to trim + removed = removed.substring(1, removed.length - 1); + // only assign if it changed, this can save a lot of time + if(removed != currentClassName){ + current.className = removed; + } + } + } + // CSS class removal + } + } + if(attrName){ + if(attrValue == "$"){ + attrValue = args[++i]; + } + // [name=value] + if(attrName == "style"){ + // handle the special case of setAttribute not working in old IE + current.style.cssText = attrValue; + }else{ + current[attrName.charAt(0) == "!" ? (attrName = attrName.substring(1)) && 'removeAttribute' : 'setAttribute'](attrName, attrValue === '' ? attrName : attrValue); + } + } + return ''; + }); + if(leftoverCharacters){ + throw new SyntaxError("Unexpected char " + leftoverCharacters + " in " + argument); + } + insertLastElement(); + referenceElement = returnValue = current || referenceElement; + } + } + if(topReferenceElement && fragment){ + // we now insert the top level elements for the fragment if it exists + topReferenceElement.appendChild(fragment); + } + return returnValue; + } + put.defaultTag = "div"; + put.forDocument = forDocument; + return put; +}); +})(typeof define == "undefined" ? function(deps, factory){ + if(typeof window == "undefined"){ + // server side JavaScript, probably (hopefully) NodeJS + require("./node-html")(module, factory); + }else{ + // plain script in a browser + put = factory(); + } +} : define); diff --git a/widgets/put-selector/test/example-server.js b/widgets/put-selector/test/example-server.js new file mode 100644 index 0000000..a0f1c23 --- /dev/null +++ b/widgets/put-selector/test/example-server.js @@ -0,0 +1,9 @@ + var http = require('http'); + var put = require('put-selector'); + http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/html'}); + var page = put('html').sendTo(res); // create an HTML page, and pipe to the response + put(page, 'head script[src=app.js]'); // each are sent immediately + put(page, 'body div.content', 'Hello, World'); + page.end(); // close all the tags, and end the stream + }).listen(81); \ No newline at end of file diff --git a/widgets/put-selector/test/node-put.js b/widgets/put-selector/test/node-put.js new file mode 100644 index 0000000..abd786f --- /dev/null +++ b/widgets/put-selector/test/node-put.js @@ -0,0 +1,38 @@ +var assert = require("assert"), + put = require("../put"); +exports.testSimple = function() { + assert.equal(put('div span.test<').toString(), '\n
      \n \n
      '); + assert.equal(put('div', ['header', 'section']).toString(), '\n
      \n
      \n
      \n
      '); +}; +exports.testPage = function() { + put.indentation = false; + var page = put('html'); + put(page, 'head script[src=test.js]+link[href=test.css]+link[href=test2.css]'); + var content = put(page, 'body div.header $\ + +div.content', 'Hello World'); + put(content, 'div.left', 'Left'); + put(content, 'div.right', {innerHTML: 'Right text'}); + assert.equal(page.toString(), '\n
      Hello World
      Left
      Right text
      '); +}; +exports.testStream = function() { + //put.indentation = ' '; + var output = ''; + var stream = { + write: function(str){ + output += str; + }, + end: function(str){ + output += str; + } + } + var page = put('html').sendTo(stream); + put(page, 'head script[src=test.js]+link[href=test.css]+link[href=test2.css]'); + var content = put(page, 'body div.header $\ + +div.content', 'Hello World'); + content.put('div.left', 'Left'); + content.put('div.right', {innerHTML: 'Right text'}); + page.end(); + assert.equal(output, '\n
      Hello World
      Left
      Right text
      \n'); +}; +if (require.main === module) + require("patr/runner").run(exports); \ No newline at end of file diff --git a/widgets/put-selector/test/put.js b/widgets/put-selector/test/put.js new file mode 100644 index 0000000..7766278 --- /dev/null +++ b/widgets/put-selector/test/put.js @@ -0,0 +1,78 @@ +var div = put("div"); +console.assert(div.tagName.toLowerCase() == "div"); +console.assert(put(div) === div); + +var body = document.body; +put(body, "h1 $", "Running put() tests"); + +var parentDiv = div; + +var span1 = put(parentDiv, "span.class-name-1.class-name-2[name=span1]"); +console.assert(span1.className == "class-name-1 class-name-2"); +console.assert(span1.getAttribute("name") == "span1"); +console.assert(span1.parentNode == div); +put(span1, "!class-name-1.class-name-3[!name]"); +console.assert(span1.className == "class-name-2 class-name-3"); +put(span1, "!.class-name-3"); +console.assert(span1.className == "class-name-2"); +console.assert(span1.getAttribute("name") == null); +put(span1, "[name=span1]"); // readd the attribute + +var defaultTag = put(parentDiv, " .class"); +console.assert(defaultTag.tagName.toLowerCase() == "div"); +var span2, span3 = put(span1, "+span[name=span2] + span[name=span3]"); +console.assert(span3.getAttribute("name") == "span3"); +console.assert((span2 = span3.previousSibling).getAttribute("name") == "span2"); +console.assert(span3.previousSibling.previousSibling.getAttribute("name") == "span1"); +var span4 = put(span2, ">", span3, "span.$[name=$]", "span3-child", "span4"); +console.assert(span3.parentNode == span2); +console.assert(span4.parentNode == span3); +console.assert(span4.className == "span3-child"); +console.assert(span4.getAttribute('name') == "span4"); +put(span2, "+", span3, "+", span4); +console.assert(span2.nextSibling == span3); +console.assert(span3.nextSibling == span4); + +var parentDiv = put("div.parent span.first $ + span.second $<", "inside first", "inside second"); +console.assert(parentDiv.firstChild.innerHTML, "inside first"); +console.assert(parentDiv.lastChild.innerHTML, "inside second"); + +put(span3, "!"); // destroy span3 +console.assert(span2.nextSibling != span3); // make sure span3 is gone + +var span0 = put(span1, "-span[name=span0]"); +console.assert(span0.getAttribute("name") == "span0"); + +var spanMinusTwo = put(span0, "-span -span"); +console.assert(spanMinusTwo.nextSibling.nextSibling == span0); + + +var spanWithId = put(parentDiv, "span#with-id"); +console.assert(spanWithId.id == "with-id"); + +var table = put(parentDiv, "table.class-name#id tr.class-name td[colSpan=2]<td,tr>td+td"); +console.assert(table.childNodes.length == 4); +console.assert(table.lastChild.childNodes.length == 2); + +var checkbox = put(div, "input[type=checkbox][checked]"); +console.assert(checkbox.type == "checkbox"); +console.assert(checkbox.getAttribute("checked") == "checked"); + +var div = put("div"); +var arrayFrag = put(div, ["span.c1", "span.c2", "span.c3"]); +console.assert(arrayFrag.tagName.toLowerCase() == "div"); +console.assert(div.firstChild.className == "c1"); +console.assert(div.lastChild.className == "c3"); + +put(div, "#encode%3A%20d"); +console.assert(div.id == "encode%3A%20d"); + +put(body, "div", {innerHTML: "finished tests, check console for errors"}); diff --git a/widgets/put-selector/test/testPut.html b/widgets/put-selector/test/testPut.html new file mode 100644 index 0000000..3c2b058 --- /dev/null +++ b/widgets/put-selector/test/testPut.html @@ -0,0 +1,12 @@ + + + + +Test put-selector/put + + + + + + + diff --git a/widgets/xstyle.js b/widgets/xstyle.js new file mode 100644 index 0000000..1ffddba --- /dev/null +++ b/widgets/xstyle.js @@ -0,0 +1 @@ +define(["xstyle/./css"], function(main){return main;}); \ No newline at end of file diff --git a/widgets/xstyle/README.md b/widgets/xstyle/README.md new file mode 100644 index 0000000..54bda4f --- /dev/null +++ b/widgets/xstyle/README.md @@ -0,0 +1,129 @@ +XStyle is a framework for shimming (or polyfilling) and extending CSS, to efficiently support various +plugins for additional CSS functionality and backwards compatibility of newer features. + +A simple example of using XStyle to shim CSS: +
      +
      +
      +
      +Here, we can use newer CSS properties like 'box-shadow' and 'transform' and XStyle +will shim (or "polyfill" or "fix") older browsers for you. XStyle will scan your stylesheet, load the shims.css which defines the CSS extensions +for the rules for shimming, and process the stylesheet. + +You can also use XStyle as a CSS loader plugin for AMD loaders like Dojo and RequireJS: +
      +define(["xstyle!./path/to/example.css"], function(){
      +	// module starts after css is loaded
      +});
      +
      + +XStyle is plugin based so that new shims and extensions can be selected and combined +without incurring additional CSS parsing overhead. XStyle is designed to allow for plugins to be +registered to handle different CSS properties, so that the shims and extensions that are +applied can be explicilty controlled for each stylesheet. + +The shims.css stylesheet (referenced in the example above) includes a number of out +of the box shims to upgrade older browsers for modern CSS properties including: opacity, +bottom, right, transition, border-radius, box-shadow, box-sizing, border-image, transform. +The shims.css stylesheet also defines shims for pseudo selectors including hover and focus. +By @import'ing shims.css into a stylesheet, these shims will be defined and we can using. +The rule definitions are transitive, so if stylesheet A @import's stylesheet B, which @import's +shims.css, both A and B stylesheets will have the shims applied. If another stylesheet C is +later independently loaded and it doesn't import any stylesheets, none of the shims +will be applied to it. + +XStyle also includes an ext.css stylesheet that enables a number of CSS extensions +including :supported and :unsupported pseudo selectors, and an -x-widget CSS property +for instantiated widgets. + +We can also explicitly define exactly which properties and other CSS elements to shim +or extend. The XStyle parser looks for extension rules. The first rule is x-property +which defines how a CSS property should be handled. A rule with an 'x-property' selector +make define properties with values indicating how the corresponding CSS property +should be handled. Let's look at a simplified example from shims.css to see how we +could shim the 'box-shadow' property to use an IE filter: +
      +x-property {
      +	box-shadow: require(xstyle/shim/ie-filter);
      +}		
      +
      +Here we defined that the CSS property 'box-shadow' should be handled by the 'xstyle/shim/ie-filter' +module. The ie-filter module converts the CSS property to an MS filter property so that +we can enable a box shadow in IE. Now, we could later create a rule that uses this property: +
      +.my-box: {
      +	box-shadow: 10px 10px 5px #888888;
      +}
      +
      +However, this was indeed a simplified. For shims, we usually only want to apply the +shimming module if the property is not natively supported. We can do this with the +default and prefix property values. The rule in shims.css looks like this: +
      +x-property {
      +	box-shadow: default, prefix, require(xstyle/shim/ie-filter);
      +}		
      +
      +This extension rule includes multiple, comma separated values. The first value is 'default'. +This indicates that first XStyle should check if the 'box-shadow' is natively supported +by the browser in standard form. If it is, then no further extensions or modifications to the CSS are applied. +The next value is 'prefix'. This indicates that first XStyle should check if the 'box-shadow' +is supported by the browser with a vendor prefix (like -webkit- or -moz-). If it is, then +the vendor prefix is added to the CSS property to enable it. Finally, if 'box-shadow' is +not supported in standard form or with a vendor prefix, then the ie-filter module is +loaded to apply the MS filter. + +

      Import Fixing

      +Another feature XStyle provides is reliable @import behavior. Internet Explorer is not +capable of loading multiples levels deep @imports. XStyle provides @import "flattening" +to fix this IE deficiency. + +XStyle also normalizes @import once behavior. If two stylesheets both @import the +same sheetsheet, XStyle ensures that the @import'ed stylesheet is only imported once (by the first +stylesheet) and the second @import is removed. This is a powerful feature because +it allows stylesheets to @import another stylesheet without worrying about overriding +another stylesheet that expected to come after the target sheet due to it's @import statement. + +

      Available Shims (and limitations)

      +The following shim modules come with XStyle: +* xstyle - XStyle itself provide vendor prefix shimming with the prefix property. This is +used to shim border-radius, box-shadow, box-sizing, and border-image. +* shim/ie-filter - This creates MS filters to emulate standard CSS properties. This is used to shim +box-shadow and transform. +* shim/transition - This provides animated CSS property changes to emulates the CSS transition property. +* shim/boxOffsets - This provides absolute positioning in older versions of IE to emulate +bottom and right CSS properties. + +

      Available Extensions

      +The following shim modules come with XStyle: +* ext/pseudo - This modules provides emulation of hover, focus and other pseudos that +are not present in older versions of IE. +* ext/scrollbar - This module provides scrollbar measurement so that elements can be sized +based on the size of the scrollbar. +* ext/supported - +* ext/widget - This module can instantiate widgets to be applied to elements that match +a rule's selector. This is designed to instantiate widgets with the form of Widget(params, targetNode), +and can be used to instantiate Dojo's Dijit widgets. + + +

      Creating Extension Modules

      +XStyle is a plugin-based and you are encouraged to create your own CSS extension modules/plugins. +An extension module that handles extension properties should return an object with an +onProperty function that will be called each time the extension property is encountered. +The onProperty function has the signature: +
      +onProperty(name, value, rule);
      +
      +Where 'name' is the CSS property name, 'value' is the value of the property, and 'rule' +is an object representing the whole rule. The onProperty function can return CSS properties +in text form to easily provide substitutionary CSS. + +Extension modules may need to do more sophisticated interaction than just CSS replacement. +If an extension module needs to actually interact with and manipulate the DOM, it may +use the 'elemental' module to add an element renderer that will be executed for each +DOM element that matches the rule's selector. diff --git a/widgets/xstyle/css-builder.js b/widgets/xstyle/css-builder.js new file mode 100644 index 0000000..9232c72 --- /dev/null +++ b/widgets/xstyle/css-builder.js @@ -0,0 +1,20 @@ +define([], function(){ + var createdCache, cache = {}; + return { + load: function(resource, parentRequire, callback, config){ +// resource = parentRequire.toAbsMid(resource); // TODO: Should we be able to normalize the resource? doesn't seem to work in RequireJS, but RequireJS gives us an absolute id here + cache[resource] = typeof readFile != "undefined" ? + readFile(parentRequire.toUrl(resource), "utf-8") : + require.nodeRequire('fs').readFileSync(parentRequire.toUrl(resource), "utf-8"); // how are we supposed to require the 'fs' module in a reliable way? + callback({}); + }, + write: function(pluginId, resource, write){ + if(!createdCache){ + createdCache = true; + write('_css_cache={};'); + } + write('_css_cache[' + JSON.stringify(resource) + ']=' + JSON.stringify(cache[resource]) + ';'); + } + } + +}); \ No newline at end of file diff --git a/widgets/xstyle/css.js b/widgets/xstyle/css.js new file mode 100644 index 0000000..406974a --- /dev/null +++ b/widgets/xstyle/css.js @@ -0,0 +1,75 @@ +define(["require"], function(moduleRequire){ +"use strict"; +var cssCache = window.cssCache || (window.cssCache = {}); +/* + * RequireJS css! plugin + * This plugin will load and wait for css files. This could be handy when + * loading css files as part of a layer or as a way to apply a run-time theme. This + * module checks to see if the CSS is already loaded before incurring the cost + * of loading the full CSS loader codebase + */ +/* function search(tag, href){ + var elements = document.getElementsByTagName(tag); + for(var i = 0; i < elements.length; i++){ + var element = elements[i]; + var sheet = alreadyLoaded(element.sheet || element.styleSheet, href) + if(sheet){ + return sheet; + } + } + } + function alreadyLoaded(sheet, href){ + if(sheet){ + var importRules = sheet.imports || sheet.rules || sheet.cssRules; + for(var i = 0; i < importRules.length; i++){ + var importRule = importRules[i]; + if(importRule.href){ + sheet = importRule.styleSheet || importRule; + if(importRule.href == href){ + return sheet; + } + sheet = alreadyLoaded(sheet, href); + if(sheet){ + return sheet; + } + } + } + } + } + function nameWithExt (name, defaultExt) { + return name.lastIndexOf('.') <= name.lastIndexOf('/') ? + name + '.' + defaultExt : name; + }*/ + return { + load: function (resourceDef, require, callback, config) { + var url = require.toUrl(resourceDef); + if(cssCache[url]){ + return createStyleSheet(cssCache[url]); + } +/* var cssIdTest = resourceDef.match(/(.+)\?(.+)/); + if(cssIdTest){*/ + // if there is an id test available, see if the referenced rule is already loaded, + // and if so we can completely avoid any dynamic CSS loading. If it is + // not present, we need to use the dynamic CSS loader. + var docElement = document.documentElement; + var testDiv = docElement.insertBefore(document.createElement('div'), docElement.firstChild); + testDiv.id = require.toAbsMid(resourceDef).replace(/\//g,'-').replace(/\..*/,'') + "-loaded"; //cssIdTest[2]; + var displayStyle = (testDiv.currentStyle || getComputedStyle(testDiv, null)).display; + docElement.removeChild(testDiv); + if(displayStyle == "none"){ + return callback(); + } + //resourceDef = cssIdTest[1]; + //} + // use dynamic loader + /*if(search("link", url) || search("style", url)){ + callback(); + }else{*/ + moduleRequire(["./load-css"], function(load){ + load(url, callback); + }); + }, + pluginBuilder: "xstyle/css-builder" + + }; +}); diff --git a/widgets/xstyle/elemental.js b/widgets/xstyle/elemental.js new file mode 100644 index 0000000..5358f7e --- /dev/null +++ b/widgets/xstyle/elemental.js @@ -0,0 +1,158 @@ +/* Provides DOM element level interaction for CSS extensions */ +define([], function(){ + var testDiv = document.createElement("div"); + var features = { + "dom-qsa2.1": !!testDiv.querySelectorAll + }; + function has(feature){ + return features[feature]; + } + var matchesSelector = testDiv.matchesSelector || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector; + var selectorRenderers = []; + var classHash = {}, propertyHash = {}; + var renderQueue = []; + var documentQueried; + require(["dojo/domReady!"], function(){ + documentQueried = true; + if(has("dom-qsa2.1")){ + for(var i = 0, l = selectorRenderers.length; i < l; i++){ + findMatches(selectorRenderers[i]); + } + renderWaiting(); + }else{ + var all = document.all; + for(var i = 0, l = all.length; i < l; i++){ + update(all[i]); + } + } + });//else rely on css expressions (or maybe we should use document.all and just scan everything) + function findMatches(renderer){ + // find the elements for a given selector and apply the renderers to it + var toRender = []; + var results = document.querySelectorAll(renderer.selector); + var name = renderer.name; + for(var i = 0, l = results.length; i < l; i++){ + var element = results[i]; + var currentStyle = element.elementalStyle; + var currentSpecificities = element.elementalSpecificities; + if(!currentStyle){ + currentStyle = element.elementalStyle = {}; + currentSpecificities = element.elementalSpecificities = {}; + } + // TODO: only override if the selector is equal or higher specificity + // var specificity = renderer.selector.match(/ /).length; + if(true || currentSpecificities[name] <= renderer.specificity){ // only process changes + var elementRenderings = element.renderings; + if(!elementRenderings){ + elementRenderings = element.renderings = []; + renderQueue.push(element); + } + + elementRenderings.push({ + name: name, + rendered: currentStyle[name] == renderer.propertyValue, + renderer: renderer + }); + currentStyle[name] = renderer.propertyValue; + } + } + + } + function renderWaiting(){ + // render all the elements in the queue to be rendered + for(var i = 0; i < renderQueue.length; i++){ + var element = renderQueue[i]; + var renderings = element.renderings, currentStyle = element.elementalStyle; + for(var j = 0; j < renderings.length; j++){ + var rendering = renderings[j]; + var renderer = rendering.renderer; + var rendered = renderer.rendered; + isCurrent = currentStyle[rendering.name] == renderer.propertyValue; // determine if this renderer matches the current computed style + if(!rendered && isCurrent){ + renderer.render(element); + } + if(rendered && !isCurrent && renderer.unrender){ + renderer.unrender(element); + renderings.splice(j--, 1); // TODO: need to remove duplicate rendered items as well + } + } + } + renderQueue = []; + } + function apply(element, renderers){ + // an element was found that matches a selector, so we apply the renderers + for(var i = 0, l = renderers.length; i < l; i++){ + renderers[i](element); + } + } + put = typeof put == "undefined" ? {} : put; + put.onaddclass = function(element, className){ + var selectorRenderers = classTriggers[className]; + var renderers = selectorRenderers[selector]; + for(var i = 0, l = selectorRenderers.length; i < l; i++){ + var renderer = selectorRenderers[i]; + if(matchesSelector.apply(element, renderer.selector)){ + renderer.render(element); + (element.renderers = element.renderers || []).push(renderer); + } + } + }; + put.onremoveclass = function(element){ + var elementRenderers = element.renderers; + if(elementRenderers){ + for(var i = elementRenderers.length - 1; i >= 0; i--){ + var renderer = elementRenderers[i]; + if(!matchesSelector.apply(element, renderer.selector)){ + renderer.unrender(element); + elementRenderers.splice(i, 1); + } + } + } + }; + put.oncreateelement = function(element){ + tagTriggers[element.tagName] + } + function update(element){ + /* At some point, might want to use getMatchedCSSRules for faster access to matching rules + if(typeof getMatchedCSSRules != "undefined"){ + // webkit gives us fast access to which rules apply + getMatchedCSSRules(element); + }else{*/ + for(var i = 0, l = selectorRenderers.length; i < l; i++){ + var renderer = selectorRenderers[i]; + if(matchesSelector ? + // use matchesSelector if available + matchesSelector.apply(element, renderer.selector) : // TODO: determine if it is higher specificity that other same name properties + // else use IE's custom css property inheritance mechanism + element.currentStyle[renderer.name] == renderer.propertyValue){ + renderer.render(element); + } + } + + } + return { + addRenderer: function(propertyName, propertyValue, rule, handler){ + var renderer = { + selector: rule.selector, + propertyValue: propertyValue, + name: propertyName, + render: handler + }; + // the main entry point for adding elemental handlers for a selector. The handler + // will be called for each element that is created that matches a given selector + selectorRenderers.push(renderer); + if(documentQueried){ + findMatches(renderer); + } + renderWaiting(); + /*if(!matchesSelector){ + // create a custom property to identify this rule in created elements + return (renderers.triggerProperty = 'selector_' + encodeURIComponent(selector).replace(/%/g, '/')) + ': 1;' + + (document.querySelectorAll ? '' : + // we use css expressions for IE6-7 to find new elements that match the selector, since qSA is not available, wonder if it is better to just use document.all... + 'zoom: expression(cssxRegister(this,"' + selector +'"));'); + }*/ + }, + update: update // this should be called for newly created dynamic elements to ensure the proper rules are applied + }; +}); \ No newline at end of file diff --git a/widgets/xstyle/ext.css b/widgets/xstyle/ext.css new file mode 100644 index 0000000..718216a --- /dev/null +++ b/widgets/xstyle/ext.css @@ -0,0 +1,10 @@ +x-function { + scrollbar: require(xstyle/ext/scrollbar); +} +x-pseudo { + supported: require(xstyle/ext/supported); + unsupported: require(xstyle/ext/supported); +} +x-property { + -x-widget: require(xstyle/ext/widget); +} \ No newline at end of file diff --git a/widgets/xstyle/ext/scrollbar.js b/widgets/xstyle/ext/scrollbar.js new file mode 100644 index 0000000..cb93c57 --- /dev/null +++ b/widgets/xstyle/ext/scrollbar.js @@ -0,0 +1,55 @@ +/* + cssx/shim/scrollbar + (c) copyright 2010, unscriptable.com + author: john + + LICENSE: see the LICENSE.txt file. If file is missing, this file is subject to the AFL 3.0 + license at the following url: http://www.opensource.org/licenses/afl-3.0.php. +*/ +define( + function () { + + var scrollbarPropRx = /-cssx-scrollbar-(width|height)/; + + // TODO: combine these two functions into one + + function getScrollbarSize () { + // summary: figures out the height and width of the scrollbars on this system. + // something like this exists in dojox, but we don't want to rely on dojox + // Returns an object with w and h properties (width and height, Number) in pixels + var sbSize = {w: 15, h: 15}; // default + var testEl = document.createElement('div'); + testEl.style.cssText = 'width:100px;height:100px;overflow:scroll;bottom:100%;right:100%;position:absolute;visibility:hidden;'; + document.body.appendChild(testEl); + try { + sbSize = { + w: testEl.offsetWidth - Math.max(testEl.clientWidth, testEl.scrollWidth), + h: testEl.offsetHeight - Math.max(testEl.clientHeight, testEl.scrollHeight) + }; + document.body.removeChild(testEl); + } + catch (ex) { + // squelch + } + return sbSize; + } + + function getSbSize () { + var sbSize = getScrollbarSize(); + sbSize = { w: sbSize.w + 'px', h: sbSize.h + 'px' }; + getSbSize = function () { return sbSize; }; + return sbSize; + } + + return { + + onValue: function (value, rule, name) { + return value.replace(scrollbarPropRx, function (full, which) { + return which == 'width' ? getSbSize().w : getSbSize().h; + }); + } + + }; + + } +); diff --git a/widgets/xstyle/ext/supported.js b/widgets/xstyle/ext/supported.js new file mode 100644 index 0000000..fcfd3d0 --- /dev/null +++ b/widgets/xstyle/ext/supported.js @@ -0,0 +1,30 @@ +define("xstyle/ext/supported",[], function(){ + var selectorParse = /(([-+])|[,<> ])?\s*(\.|!|#|:)?([-\w$]+)?(?:\[([^\]=]+)=?['"]?([^\]'"]*)['"]?\])?/g; + return { + onPseudo: function(name, rule){ + var supported = true; + rule.selector.replace(selectorParse, function(t, combinator, siblingCombinator, prefix, value, attrName, attrValue){ + var element; + if(value && !prefix){ + // test to see if the element tag name is supported + var elementString = (element = document.createElement(value)).toString(); + if(elementString == "[object HTMLUnknownElement]" || elementString == "[object]"){ + supported = false; + return; + } + } + if(attrName){ + // test to see if the attribute is supported + element.setAttribute(attrName, attrValue); + if(element[attrName] != attrValue){ + supported = false; + } + } + }); + if(supported == (name == "supported")){ + // match, add the rule without the pseudo + rule.add(rule.selector = rule.selector.replace(/:(un)?supported/, ''), rule.cssText); + } + } + }; +}); diff --git a/widgets/xstyle/ext/variables.js b/widgets/xstyle/ext/variables.js new file mode 100644 index 0000000..11c2fa2 --- /dev/null +++ b/widgets/xstyle/ext/variables.js @@ -0,0 +1,12 @@ +/* variable replacement for CSS*/ +define([], function(){ + return function(variables){ + return { + all: function(value, rule, name){ + value.replace(/$[\w+]/g, function(variable){ + return variables[variable]; + }); + } + }; + } +}); \ No newline at end of file diff --git a/widgets/xstyle/ext/widget.js b/widgets/xstyle/ext/widget.js new file mode 100644 index 0000000..b95b5f9 --- /dev/null +++ b/widgets/xstyle/ext/widget.js @@ -0,0 +1,125 @@ +define(['../elemental'], function(elemental){ + function parse(value, callback){ + var Class, prototype; + if(value.eachProperty){ + var type, props = {/*cssText: value.cssText*/}; + value.eachProperty(function(name, value){ + name = name.replace(/-\w/g, function(dashed){ + return dashed.charAt(1).toUpperCase(); + }); + value = parse(value); + if(name == "type" && callback){ + type = value; + }else{ + props[name] = value; + } + }); + value = props; + // load the class, and adjust the property types based on the class prototype + if(type){ + require(type.split(/[, ]+/), function(Class, Mixin){ + if(Mixin){ + // more than one, mix them together + // TODO: This should be Class.extend(arguments.slice(1)), but dojo.declare has a bug in extend that causes it modify the original + Class = dojo.declare([].slice.call(arguments,0)); // convert the arguments to an array of mixins + } + var prototype = Class.prototype; + for(var name in props){ + var value = props[name]; + if(name in prototype){ + var type = typeof prototype[name]; + if(type == "string" || typeof value != "string"){ + }else if(type == "number"){ + props[name] = +value; + }else{ + props[name] = eval(value); + } + } + } + callback(function(element){ + new Class(props, element); + }); + }); + } + }else if(value.charAt(0) == "'" || value.charAt(0) == '"'){ + value = eval(value); + }else if(!isNaN(value)){ + value = +value; + } + return value; + } + + function Widget(scope){ + return { + widget: function(value, rule){ + var modules = []; + value.replace(/require\s*\(\s*['"]([^'"]*)['"]\s*\)/g, function(t, moduleId){ + modules.push(moduleId); + }); + require(modules); + return function(domNode){ + require(modules, function(){ + with(scope){ + var __module = eval(value); + var prototype = __module.prototype; + var props = {}; + if(prototype){ + rule.eachProperty(function(t, name, value){ + if(name in prototype){ + var type = typeof prototype[name]; + if(type == "string" || typeof value != "string"){ + props[name] = value; + }else if(type == "number"){ + props[name] = +value; + }else{ + props[name] = eval(value); + } + } + }); + } + __module(props, domNode); + } + }); + }; + }, + role: "layout" + }; + } + var def = new Widget({}); + Widget.widget = def.widget; + Widget.role = def.role; + return { + onProperty: function(name, value, rule){ + // used for a widget property: + // widget: { + // type: 'dijit/form/Button'; + // label: 'Save'; + // } + return { + then: function(callback){ + parse(value, function(renderer){ + elemental.addRenderer(name, value, rule, renderer); + callback(); + }); + } + } + }/*, + onFunction: function(name, propertyName, value){ + // this allows us to create a CSS widget function + // x-property{ + // my-widget: widget(my/Widget); + // } + // .class{ + // my-widget: 'settings'; + // } + return function(name, propertyValue){ + require([value], function(Class){ + elemental.addRenderer(rule, function(element){ + new Class(parse(propertyValue), element); + }); + }); + }; + }*/ + + } +}) \ No newline at end of file diff --git a/widgets/xstyle/generate.js b/widgets/xstyle/generate.js new file mode 100644 index 0000000..f59cb8b --- /dev/null +++ b/widgets/xstyle/generate.js @@ -0,0 +1,35 @@ +define([], function(){ + function Generate(domNode){ + return { + layout: function(layout, rule){ + return function(domNode){ + for(var i = 0; i < layout.length; i++){ + var rule = layout[i]; + var selector = rule.selector; + var target = document.createElement("div"); + // create the appropriate additions to the elements based on the selectors + selector.replace(/\.([\w-]+)/, function(t, className){ + target.className = className; + }); + selector.replace(/#([\w-]+)/, function(t, id){ + target.id = id; + }); + rule.apply(target); + domNode.appendChild(target); + } + } + }, + content: function(content, rule){ + return function(domNode){ + domNode.innerHTML = eval(content); + } + }, + role: "layout" + }; + } + var def = new Generate({}); + Generate.layout = def.layout; + Generate.content = def.content; + Generate.role = def.role; + return Generate; +}) \ No newline at end of file diff --git a/widgets/xstyle/has-class.js b/widgets/xstyle/has-class.js new file mode 100644 index 0000000..394c4a5 --- /dev/null +++ b/widgets/xstyle/has-class.js @@ -0,0 +1,19 @@ +define(["dojo/has"], function(has){ + var tested = {}; + return function(){ + var test, args = arguments; + for(var i = 0; i < args.length; i++){ + var test = args[i]; + if(!tested[test]){ + tested[test] = true; + var parts = test.match(/^(no-)?(.+?)((-[\d\.]+)(-[\d\.]+)?)?$/), // parse the class name + hasResult = has(parts[2]), // the actual has test + lower = -parts[4]; // lower bound if it is in the form of test-4 or test-4-6 (would be 4) + if((lower > 0 ? lower <= hasResult && (-parts[5] || lower) >= hasResult : // if it has a range boundary, compare to see if we are in it + !!hasResult) == !parts[1]){ // parts[1] is the no- prefix that can negate the result + document.documentElement.className += ' has-' + test; + } + } + } + } +}); \ No newline at end of file diff --git a/widgets/xstyle/index.html b/widgets/xstyle/index.html new file mode 100644 index 0000000..01efa7b --- /dev/null +++ b/widgets/xstyle/index.html @@ -0,0 +1,83 @@ + + + + + + kriszyp/xstyle @ GitHub + + + + + + Fork me on GitHub + +
      + +
      + + + + +
      + +

      xstyle + by kriszyp

      + +
      + Extensible CSS loader +
      + +

      XStyle is a framework for shimming (or polyfilling) and extending CSS, to efficiently support various plugins for additional CSS functionality and backwards compatibility of newer features.

      Dependencies

      +

      None

      +

      Install

      +

      cpm install xstyle

      +

      License

      +

      AFL/BSD

      +

      Authors

      +

      Kris Zyp (kriszyp@gmail.com)

      +

      Contact

      +

      Kris Zyp (kriszyp@gmail.com)

      + + +

      Download

      +

      + You can download this project in either + zip or + tar formats. +

      +

      You can also clone the project with Git + by running: +

      $ git clone git://github.com/kriszyp/xstyle
      +

      + + + +
      + + + + diff --git a/widgets/xstyle/jss.js b/widgets/xstyle/jss.js new file mode 100644 index 0000000..4caa861 --- /dev/null +++ b/widgets/xstyle/jss.js @@ -0,0 +1,34 @@ +define([], function(){ + var objectWatch = {}.watch; + function JSS(data){ + //TODO: record all rules so that additional JSS can be layered on top + return { + layout: function(layout, rule){ + return { + render: function(domNode){ + for(var i = 0; i < layout.length; i++){ + var rule = layout[i]; + var selector = rule.selector.substring(1); + var value = data.get ? data.get(selector) : data[selector]; + if(data.watch != objectWatch){ + data.watch(selector, function(name, oldValue, value){ + renderValue(value) + }); + } + function renderValue(value){ + if(value !== undefined){ + var target = document.createElement("div"); + rule.renderInto(target); + } + } + rule.renderInto(target); + domNode.appendChild(target); + } + }, + cssText: rule.selector.replace(/\//g, "[data-path=$1]") + } + } + }; + } + return JSS; +}); \ No newline at end of file diff --git a/widgets/xstyle/load-css.js b/widgets/xstyle/load-css.js new file mode 100644 index 0000000..53991cf --- /dev/null +++ b/widgets/xstyle/load-css.js @@ -0,0 +1,311 @@ +/** + * This includes code from https://github.com/unscriptable/cssx + * Copyright (c) 2010 unscriptable.com + */ + +/*jslint browser:true, on:true, sub:true */ + +define([], function(){ +"use strict"; + +/* + * RequireJS css! plugin + * This plugin will load and wait for css files. This could be handy when + * loading css files as part of a layer or as a way to apply a run-time theme. + * Most browsers do not support the load event handler of the link element. + * Therefore, we have to use other means to detect when a css file loads. + * (The HTML5 spec states that the LINK element should have a load event, but + * not even Chrome 8 or FF4b7 have it, yet. + * http://www.w3.org/TR/html5/semantics.html#the-link-element) + * + * This plugin tries to use the load event and a universal work-around when + * it is invoked the first time. If the load event works, it is used on + * every successive load. Therefore, browsers that support the load event will + * just work (i.e. no need for hacks!). FYI, Feature-detecting the load + * event is tricky since most browsers have a non-functional onload property. + * + * The universal work-around watches a stylesheet until its rules are + * available (not null or undefined). There are nuances, of course, between + * the various browsers. The isLinkReady function accounts for these. + * + * Note: it appears that all browsers load @import'ed stylesheets before + * fully processing the rest of the importing stylesheet. Therefore, we + * don't need to find and wait for any @import rules explicitly. + * + * Note #2: for Opera compatibility, stylesheets must have at least one rule. + * AFAIK, there's no way to tell the difference between an empty sheet and + * one that isn't finished loading in Opera (XD or same-domain). + * + * Options: + * !nowait - does not wait for the stylesheet to be parsed, just loads it + * + * Global configuration options: + * + * cssDeferLoad: Boolean. You can also instruct this plugin to not wait + * for css resources. They'll get loaded asap, but other code won't wait + * for them. This is just like using the !nowait option on every css file. + * + * cssWatchPeriod: if direct load-detection techniques fail, this option + * determines the msec to wait between brute-force checks for rules. The + * default is 50 msec. + * + * You may specify an alternate file extension: + * require('css!myproj/component.less') // --> myproj/component.less + * require('css!myproj/component.scss') // --> myproj/component.scss + * + * When using alternative file extensions, be sure to serve the files from + * the server with the correct mime type (text/css) or some browsers won't + * parse them, causing an error in the plugin. + * + * usage: + * require(['css!myproj/comp']); // load and wait for myproj/comp.css + * define(['css!some/folder/file'], {}); // wait for some/folder/file.css + * require(['css!myWidget!nowait']); + * + * Tested in: + * Firefox 1.5, 2.0, 3.0, 3.5, 3.6, and 4.0b6 + * Safari 3.0.4, 3.2.1, 5.0 + * Chrome 7 (8+ is partly b0rked) + * Opera 9.52, 10.63, and Opera 11.00 + * IE 6, 7, and 8 + * Netscape 7.2 (WTF? SRSLY!) + * Does not work in Safari 2.x :( + * In Chrome 8+, there's no way to wait for cross-domain (XD) stylesheets. + * See comments in the code below. + * TODO: figure out how to be forward-compatible when browsers support HTML5's + * load handler without breaking IE and Opera +*/ + + + var + // compressibility shortcuts + onreadystatechange = 'onreadystatechange', + onload = 'onload', + createElement = 'createElement', + // failed is true if RequireJS threw an exception + failed = false, + doc = document, + cache = typeof _css_cache == "undefined" ? {} : _css_cache, + undef, + features = { + "event-link-onload": false, //); /*document.createElement("link").onload === null);*/ + "dom-create-style-element": !document.createStyleSheet + }, + // find the head element and set it to it's standard property if nec. + head = doc.head || (doc.head = doc.getElementsByTagName('head')[0]); + + function has (feature) { + return features[feature]; + } + function createLink (doc, optHref) { + var link = doc[createElement]('link'); + link.rel = "stylesheet"; + link.type = "text/css"; + if (optHref) { + link.href = optHref; + } + return link; + } + function nameWithExt (name, defaultExt) { + return name.lastIndexOf('.') <= name.lastIndexOf('/') ? + name + '.' + defaultExt : name; + } + function parseSuffixes (name) { + // creates a dual-structure: both an array and a hashmap + // suffixes[0] is the actual name + var parts = name.split('!'), + suf, i = 1, pair; + while ((suf = parts[i++])) { // double-parens to avoid jslint griping + pair = suf.split('=', 2); + parts[pair[0]] = pair.length == 2 ? pair[1] : true; + } + return parts; + } + + + if(!has("bundled-css")){ // if all the CSS is bundled, we don't need to the loader code + var loadDetector = function(params, cb){ + // failure detection + // we need to watch for onError when using RequireJS so we can shut off + // our setTimeouts when it encounters an error. + if (require.onError) { + require.onError = (function (orig) { + return function () { + failed = true; + orig.apply(this, arguments); + } + })(require.onError); + } + + /***** load-detection functions *****/ + + function loadHandler (params, cb) { + // We're using 'readystatechange' because IE and Opera happily support both + var link = params.link; + link[onreadystatechange] = link[onload] = function () { + if (!link.readyState || link.readyState == 'complete') { + features["event-link-onload"] = true; + cleanup(params); + cb(); + } + }; + } + + + // Chrome 8 hax0rs! + // This is an ugly hack needed by Chrome 8+ which no longer waits for rules + // to be applied to the document before exposing them to javascript. + // Unfortunately, this routine will never fire for XD stylsheets since + // Chrome will also throw an exception if attempting to access the rules + // of an XD stylesheet. Therefore, there's no way to detect the load + // event of XD stylesheets until Google fixes this, preferably with a + // functional load event! As a work-around, use ready() before rendering + // widgets / components that need the css to be ready. + var testEl; + function styleIsApplied () { + if (!testEl) { + testEl = document[createElement]('div'); + testEl.id = '_cssx_load_test'; + testEl.style.cssText = 'position:absolute;top:-999px;left:-999px;'; + doc.body.appendChild(testEl); + } + return doc.defaultView.getComputedStyle(testEl, null).marginTop == '-5px'; + } + + function isLinkReady (link) { + // This routine is a bit fragile: browser vendors seem oblivious to + // the need to know precisely when stylesheets load. Therefore, we need + // to continually test beta browsers until they all support the LINK load + // event like IE and Opera. + var sheet, rules, ready = false; + try { + // webkit's and IE's sheet is null until the sheet is loaded + sheet = link.sheet || link.styleSheet; + if(sheet){ + // mozilla's sheet throws an exception if trying to access xd rules + rules = sheet.cssRules || sheet.rules; + // webkit's xd sheet returns rules == null + // opera's sheet always returns rules, but length is zero until loaded + // friggin IE doesn't count @import rules as rules, but IE should + // never hit this routine anyways. + ready = rules ? + rules.length > 0 : // || (sheet.imports && sheet.imports.length > 0) : + rules !== undef; + // thanks, Chrome 8, for this lovely hack + if (ready && navigator.userAgent.indexOf('Chrome') >= 0) { + sheet.insertRule('#_cssx_load_test{margin-top:-5px;}', 0); + ready = styleIsApplied(); + sheet.deleteRule(0); + } + } + } + catch (ex) { + // 1000 means FF loaded an xd stylesheet + // other browsers just throw a security error here (IE uses the phrase 'Access is denied') + ready = (ex.code == 1000) || (ex.message.match(/security|denied/i)); + } + return ready; + } + + function ssWatcher (params, cb) { + // watches a stylesheet for loading signs. + if (isLinkReady(params.link)) { + cleanup(params); + cb(); + } + else if (!failed) { + setTimeout(function () { ssWatcher(params, cb); }, params.wait); + } + } + + function cleanup (params) { + var link = params.link; + link[onreadystatechange] = link[onload] = null; + } + // It would be nice to use onload everywhere, but the onload handler + // only works in IE and Opera. + // Detecting it cross-browser is completely impossible, too, since + // THE BROWSERS ARE LIARS! DON'T TELL ME YOU HAVE AN ONLOAD PROPERTY + // IF IT DOESN'T DO ANYTHING! + var loaded; + function cbOnce () { + if (!loaded) { + loaded = true; + cb(); + } + } + loadHandler(params, cbOnce); + if (!has("event-link-onload")) ssWatcher(params, cbOnce); + + }; + } + function insertCss(css){ + if(has("dom-create-style-element")){ + // we can use standard + + + + + + + +
      + + +
      + + + diff --git a/widgets/xstyle/test/testPut.html b/widgets/xstyle/test/testPut.html new file mode 100644 index 0000000..8b4d775 --- /dev/null +++ b/widgets/xstyle/test/testPut.html @@ -0,0 +1,12 @@ + + + + +Test xstyle/put + + + + + + + diff --git a/widgets/xstyle/test/testShims.html b/widgets/xstyle/test/testShims.html new file mode 100644 index 0000000..23cfb00 --- /dev/null +++ b/widgets/xstyle/test/testShims.html @@ -0,0 +1,60 @@ + + + + + Run shim Unit Tests + + + + + + + + +
      +
      {
      +	position: absolute;
      +	padding: 10px;
      +	left: 50px;
      +	top: 50px;
      +	bottom: 50px;
      +	right: 50px;
      +}
      +
      {
      +	position: absolute;
      +	left: 300px;
      +	top: 50px;
      +	border: 1px solid black;
      +	border-radius: 15px;
      +	box-shadow: 10px 10px 5px #888888;
      +	background-color: #ddd;
      +}
      + +
      {
      +	opacity: 0.7;
      +}
      +
      sized box
      +
      transition
      +
      {
      +	background-image: linear-gradient(top,  #cccccc,  #000000);
      +	transform: rotate(-20deg);
      +	position: absolute;
      +	left: 300px;
      +	top: 250px;
      +	width: 560px;
      +	height: 170px;
      +}
      + +
      + + diff --git a/widgets/xstyle/xstyle.js b/widgets/xstyle/xstyle.js new file mode 100644 index 0000000..ea10f13 --- /dev/null +++ b/widgets/xstyle/xstyle.js @@ -0,0 +1,314 @@ +if(typeof define == "undefined"){ + (function(){ + // pseudo passive loader + var modules = {}; + define = function(id, deps, factory){ + for(var i = 0;i < deps.length; i++){ + deps[i] = modules[deps[i]]; + } + modules[id] = factory.apply(this, deps); + }; + require = function(deps){ + define("", deps, factory); + }; + })(); +} +define("xstyle/xstyle", ["require"], function (require) { + "use strict"; + var undef, testDiv = document.createElement("div"); + function search(tag){ + var elements = document.getElementsByTagName(tag); + for(var i = 0; i < elements.length; i++){ + checkImports(elements[i]); + } + } + var ua = navigator.userAgent; + var vendorPrefix = ua.indexOf("WebKit") > -1 ? "-webkit-" : + ua.indexOf("Firefox") > -1 ? "-moz-" : + ua.indexOf("MSIE") > -1 ? "-ms-" : + ua.indexOf("Opera") > -1 ? "-o-" : ""; + function checkImports(element, callback, fixedImports){ + var sheet = element.sheet || element.styleSheet; + var needsParsing = sheet.needsParsing, // load-imports can check for the need to parse when it does it's recursive look at imports + cssRules = sheet.rules || sheet.cssRules; + function fixImports(){ + // need to fix imports, applying load-once semantics for all browsers, and flattening for IE to fix nested @import bugs + require(["./load-imports"], function(load){ + load(element, function(){ + checkImports(element, callback, true); + }); + }); + } + if(sheet.imports && !fixedImports && sheet.imports.length){ + // this is how we check for imports in IE + return fixImports(); + } + if(!needsParsing){ + for(var i = 0; i < cssRules.length; i++){ + var rule = cssRules[i]; + if(rule.href && !fixedImports){ + // it's an import (for non-IE browsers) + return fixImports(); + } + if(rule.selectorText && rule.selectorText.substring(0,2) == "x-"){ + // an extension is used, needs to be parsed + needsParsing = true; + } + } + } + if(needsParsing){ + // ok, determined that CSS extensions are in the CSS, need to get the source and really parse it + parse(sheet.source || sheet.ownerElement.innerHTML, sheet, callback); + } + } + function parse(css, styleSheet, callback) { + // normalize the stylesheet. + if(!styleSheet.addRule){ + // only FF doesn't have this + styleSheet.addRule = function(selector, style, index){ + return this.insertRule(selector + "{" + style + "}", index >= 0 ? index : this.cssRules.length); + } + } + if(!styleSheet.deleteRule){ + styleSheet.deleteRule = sheet.removeRule; + } + var handlers = {property:{}}; + function addHandler(type, name, module){ + var handlersForType = handlers[type] || (handlers[type] = {}); + handlersForType[name] = module; + } + function addExtensionHandler(type){ + if(!handlers[type]){ + handlers[type] = {}; + } + addHandler("selector", 'x-' + type, { + onRule: function(rule){ + rule.eachProperty(function(name, value){ + do{ + var parts = value.match(/require\s*\((.+)\)|([^, ]+)([, ]+(.+))?/); + if(parts[1]){ + return addHandler(type, name, parts[1]); + } + var first = parts[2]; + if(first == "default"){ + if((type == "property" && typeof testDiv.style[name] == "string")){ + return; + } + if(type == "pseudo"){ + try{ + document.querySelectorAll("x:" + name); + return; + }catch(e){} + } + }else if(first == "prefix"){ + if(typeof testDiv.style[vendorPrefix + name] == "string"){ + return addHandler(type, name, 'xstyle/xstyle'); + } + }else{ + return addHandler(type, name, function(){ + return value; + }); + } + }while(value = parts[4]); +/* var ifUnsupported = value.charAt(value.length - 1) == "?"; + value = value.replace(/require\s*\(|\)\??/g, ''); + if(!ifUnsupported || typeof testDiv.style[name] != "string"){ // if conditioned on support, test to see browser has that style + // not supported as a standard property, now let's check to see if we can support it with vendor prefixing + if(ifUnsupported && typeof testDiv.style[vendorPrefix + name] == "string"){ + // it does support vendor prefixing, fix it with that + value = 'xstyle/xstyle'; + } + addHandler(type, name, value); + }*/ + }); + } + }); + } + addExtensionHandler("property"); + addExtensionHandler("value"); + addExtensionHandler("pseudo"); + var waiting = 1; + var baseUrl = (styleSheet.href || location.href).replace(/[^\/]+$/,''); + var properties = [], values = []; + var valueModules = {}; + + var convertedRules = []; + var valueRegex = new RegExp("(?:^|\\W)(" + values.join("|") + ")(?:$|\\W)"); + function Rule () {} + Rule.prototype = { + eachProperty: function (onproperty, propertyRegex) { + var selector, css; + selector = this.selector; //(this.children ? onproperty(0, "layout", this.children) || this.selector : this.selector); + this.cssText.replace(/\s*([^;:]+)\s*:\s*([^;]+)?/g, function (full, name, value) { + onproperty(name, value); + }); + if(this.children){ + for(var i = 0; i < this.children.length; i++){ + var child = this.children[i]; + if(!child.selector){ // it won't have a selector if it is property with nested properties + onproperty(child.property, child); + } + } + } + }, + fullSelector: function(){ + return (this.parent ? this.parent.fullSelector() : "") + (this.selector || "") + " "; + }, + add: function(selector, cssText){ + if(cssText){ + styleSheet.addRule ? + styleSheet.addRule(selector, cssText) : + styleSheet.insertRule(selector + '{' + cssText + '}', styleSheet.cssRules.length); + } + }, + cssText: "" + }; + + var lastRule = new Rule; + lastRule.css = css; + + function onProperty(name, value) { + // this is called for each CSS property + var propertyName = name; + do{ + var handlerForName = handlers.property[name]; + if(handlerForName){ + return handler(handlerForName, "onProperty", propertyName, value); + } + // if we didn't match, we try to match property groups, for example "background-image" should match the "background" listener + name = name.substring(0, name.lastIndexOf("-")); + }while(name); + } + function onIdentifier(identifier, name, value){ + var handlerForName = handlers.value[identifier]; + if(handlerForName){ + handler(handlerForName, "onIdentifier", name, value); + } + } + function onRule(selector, rule){ + var handlerForName = handlers.selector[selector]; + if(handlerForName){ + handler(handlerForName, "onRule", rule); + } + } + function onPseudo(pseudo, rule){ + var handlerForName = handlers.pseudo[pseudo]; + if(handlerForName){ + handler(handlerForName, "onPseudo", pseudo, rule); + } + } + function handler(module, type, name, value){ + if(module){ + var rule = lastRule; + var ruleHandled = function(text){ + console.log("loaded ", module, text); + if(text){ + /* TODO: is the a way to determine the index deterministically? + var cssRules = styleSheet.rules || styleSheet.cssRules; + for(var index = rule.index || 0; index < cssRules.length; index++){ + if(cssRules[index].selectorText == rule.fullSelector(){ + break; + } + }*/ + /* TODO: merge IE filters + if(isIE){ + var filters = []; + convertedText = convertedText.replace(/filter: ([^;]+);/g, function(t, filter){ + filters.push(filter); + return ""; + }); + if(filters.length){ + console.log("filters", filters); + convertedText = "zoom: 1;filter: " + filters.join("") + ";" + convertedText; + } + } + */ + styleSheet.addRule(rule.fullSelector(), text); + } + finishedLoad(); + }; + + waiting++; + console.log("loading ", module, name, value); + var onLoad = function(module){ + var result = module[type](name, value, rule, styleSheet); + if(result && result.then){ + // a promise, return immediately defer handling + result.then(ruleHandled); + }else{ + ruleHandled(result); + } + } + typeof module == "string" ? require([module], onLoad) : onLoad(module); + } + } + // parse the CSS, finding each rule + css.replace(/\s*(?:([^{;\s]+)\s*{)?\s*([^{}]+;)?\s*(};?)?/g, function (full, selector, properties, close) { + // called for each rule + if (selector) { + // a selector was found, start a new rule (note this can be nested inside another selector) + var newRule = new Rule(); + (lastRule.children || (lastRule.children = [])).push(newRule); // add to the parent layout + newRule.parent = lastRule; + if(selector.charAt(selector.length - 1) == ":"){ + // it is property style nesting + newRule.property= selector.substring(0, selector.length - 1); + }else{ + // just this segment of selector + newRule.selector = selector; + } + lastRule = newRule; + } + if (properties) { + // some properties were found + lastRule.cssText += properties; + } + if (close) { + // rule was closed with } + // TODO: use a specialized regex that only looks for registered properties + lastRule.cssText.replace(/\s*([^;:]+)\s*:\s*([^;]+)?/g, function (full, name, value) { + onProperty(name, value); + value.replace(valueRegex, function(t, identifier){ + //onIdentifier(identifier, name, value); + }); + }); + if(lastRule.children){ + for(var i = 0; i < lastRule.children.length; i++){ + var child = lastRule.children[i]; + if(!child.selector){ // it won't have a selector if it is property with nested properties + onProperty(child.property, child); + } + } + } + onRule(lastRule.selector, lastRule); + lastRule.selector && lastRule.selector.replace(/:([-\w]+)/, function(t, pseudo){ + return onPseudo(pseudo, lastRule); + }); + lastRule = lastRule.parent; + } + }); + function finishedLoad(){ + if(--waiting == 0){ + if(callback){ + callback(styleSheet); + } + } + } + finishedLoad(); + } + search('link'); + search('style'); + var xstyle = { + process: checkImports, + vendorPrefix: vendorPrefix, + onProperty: function(name, value){ + // basically a noop for most operations, we rely on the vendor prefixing in the main property parser + if(name == "opacity" && vendorPrefix == "-ms-"){ + return 'filter: alpha(opacity=' + (value * 100) + '); zoom: 1;'; + } + return vendorPrefix + name + ':' + value + ';'; + } + }; + return xstyle; + +});
      '; +html += '
      Enter Your Password
      '; +html += '
      '; +html += '
      Password:


      '; +html += '
      '; +html += '
      '; +html += '

      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', "clear_login()") + ' ' + large_icon_button('check', 'Login', 'do_effect_login()') + '
      '; +html += '
      '; +html += ''; +session.hooks.keys[ENTER_KEY] = 'do_effect_login'; +session.hooks.keys[ESC_KEY] = 'clear_login'; +safe_focus( 'fe_lp_password' ); +show_popup_dialog(450, 225, html); +} +function clear_login() { +hide_popup_dialog(); +Nav.prev(); +} +function do_login() { +if ($('fe_username').value.match(/^\w+$/)) { +session.username = $('fe_username').value; +session.auto_login = $('fe_auto_login').checked; +do_login_prompt_2(); +return; +} +else { +do_openid_login(); +} +} +function do_openid_login() { +if (!$('fe_username').value) return; +session.openid_win = popup_window(''); +if (!session.openid_win) return; +session.open_id = $('fe_username').value; +session.auto_login = $('fe_auto_login') && $('fe_auto_login').checked; +hide_popup_dialog(); +show_progress_dialog(1, "Logging in..."); +session.hooks.before_error = 'close_openid_window'; +session.hooks.after_error = 'do_login_prompt'; +effect_api_send('openid_login', { +OpenID: session.open_id, +Infinite: session.auto_login ? 1 : 0 +}, 'do_openid_login_2'); +} +function close_openid_window() { +if (session.openid_win) { +session.openid_win.close(); +delete session.openid_win; +} +} +function do_openid_login_2(response) { +if (response.CheckURL) { +Debug.trace('openid', "Redirecting popup window to OpenID Check URL: " + response.CheckURL); +show_progress_dialog(1, "Waiting for popup window...", false, ['x', 'Cancel', 'do_login_prompt()']); +session.openid_win.location = response.CheckURL; +session.openid_win.focus(); +} +} +function receive_openid_response(iframe_response) { +var response = deep_copy_object(iframe_response); +Debug.trace('openid', "Received OpenID Response: " + dumper(response)); +hide_popup_dialog(); +if (response.Code) { +close_openid_window(); +return do_error( response.Description ); +} +delete session.hooks.before_error; +delete session.hooks.after_error; +if (response.SessionID) { +session.cookie.set( 'effect_session_id', response.SessionID ); +session.cookie.save(); +} +switch (response.Action) { +case 'popup': +show_progress_dialog(1, "Waiting for popup window...", false, ['x', 'Cancel', 'do_login_prompt()']); +Debug.trace('openid', "Redirecting popup window to OpenID Setup URL: " + response.SetupURL); +session.openid_win.location = response.SetupURL; +session.openid_win.focus(); +break; +case 'login': +close_openid_window(); +do_login_2(response); +break; +case 'register': +if (!response.Info) response.Info = {}; +close_openid_window(); +Debug.trace('openid', 'Original OpenID: ' + response.OpenID_Login); +Debug.trace('openid', 'Clean OpenID: ' + response.OpenID_Unique); +Debug.trace('openid', 'Registration Info: ' + dumper(response.Info)); +session.prereg = response.Info; +session.prereg.open_id_login = response.OpenID_Login; +session.prereg.open_id = response.OpenID_Unique; +if (session.user) { +if (!session.user.OpenIDs) session.user.OpenIDs = {}; +if (!session.user.OpenIDs.OpenID) session.user.OpenIDs.OpenID = []; +var dupe = find_object( session.user.OpenIDs.OpenID, { Unique: session.prereg.open_id } ); +if (dupe) return do_error("That OpenID is already registered and attached to your account. No need to add it again."); +session.user.OpenIDs.OpenID.push({ +Login: session.prereg.open_id_login, +Unique: session.prereg.open_id +}); +setTimeout( function() { +Nav.go('MyAccount', true); +do_message('success', 'Added new OpenID URL to account.'); +}, 1 ); +} +else { +setTimeout( function() { Nav.go('CreateAccount', true); }, 1 ); +} +break; +} +} +function do_effect_login() { +var password = $('fe_lp_password').value; +session.auto_login = $('fe_auto_login').checked; +hide_popup_dialog(); +show_progress_dialog(1, "Logging in..."); +session.hooks.after_error = 'do_login_prompt'; +effect_api_send('user_login', { +Username: session.username, +Password: password, +Infinite: session.auto_login ? 1 : 0 +}, 'do_login_2'); +} +function do_logout() { +effect_api_send('user_logout', {}, 'do_logout_2'); +} +function do_logout_2(response) { +hide_popup_dialog(); +show_default_login_status(); +delete session.hooks.after_error; +delete session.cookie.tree.effect_session_id; +session.cookie.save(); +session.storage = {}; +session.storage_dirty = false; +delete session.user; +delete session.first_login; +var old_username = session.username; +session.username = ''; +if (Nav.inited) { +Nav.go('Main'); +if (old_username) $GR.growl('success', "Logged out of account: " + old_username); +} +else { +Nav.init(); +} +} +function do_login_2(response, tx) { +if (response.FirstLogin) session.first_login = 1; +if (response.User.UserStorage) { +Debug.trace('Recovering site storage blob: session.storage = ' + response.User.UserStorage + ';'); +try { +eval( 'session.storage = ' + response.User.UserStorage + ';' ); +} +catch (e) { +Debug.trace("SITE STORAGE RECOVERY FAILED: " + e); +session.storage = {}; +} +delete response.User.UserStorage; +session.storage_dirty = false; +} +session.user = response.User; +session.username = session.user.Username; +hide_popup_dialog(); +delete session.hooks.after_error; +update_header(); +if (!tx || !tx._from_recover) $GR.growl('success', "Logged in as: " + session.username); +if (session.nav_after_login) { +Nav.go( session.nav_after_login ); +delete session.nav_after_login; +} +else if (Nav.currentAnchor().match(/^Login/)) { +Nav.go('Home'); +} +else { +Nav.refresh(); +} +Nav.init(); +} +function user_storage_mark() { +Debug.trace("Marking user storage as dirty"); +session.storage_dirty = true; +} +function user_storage_idle() { +if (session.storage_dirty && !session.mouseIsDown) { +user_storage_save(); +session.storage_dirty = false; +} +setTimeout( 'user_storage_idle()', 5000 ); +} +function user_storage_save() { +if (session.user) { +Debug.trace("Committing user storage blob"); +effect_api_send('update_user_storage', { Data: serialize(session.storage) }, 'user_storage_save_finish', { _silent: 1 } ); +} +} +function user_storage_save_finish(response, tx) { +} +function show_default_login_status() { +$('d_sidebar_wrapper_recent_games').hide(); +$('d_login_status').innerHTML = '
      ' + +'
      ' + +large_icon_button('key', "Login", '#Home') + '' + spacer(1,1) + '' + +'' + large_icon_button('user_add.png', "Signup", '#CreateAccount') + '
      ' + +'
      '; +$('d_tagline').innerHTML = +'Login' + ' | ' + +'Create Account'; +} +function update_header() { +var html = ''; +html += '
      '; +html += ''; +html += ''; +html += ''; +html += ''+spacer(2,2)+''; +html += session.user.FullName + '
      '; +html += spacer(1,5) + '
      '; +html += 'My Home  |  '; +html += 'Logout'; +html += '
      '; +$('d_login_status').innerHTML = html; +$('d_tagline').innerHTML = +'Welcome '+session.user.FirstName+'' + ' | ' + +'My Home' + ' | ' + +'Logout'; +effect_api_get( 'get_user_games', { limit:5, offset:0 }, 'receive_sidebar_recent_games', { } ); +} +function receive_sidebar_recent_games(response, tx) { +var html = ''; +if (response.Rows && response.Rows.Row) { +var games = always_array( response.Rows.Row ); +for (var idx = 0, len = games.length; idx < len; idx++) { +var game = games[idx]; +html += ''; +} +html += ''; +$('d_sidebar_recent_games').innerHTML = html; +$('d_sidebar_wrapper_recent_games').show(); +} +else { +$('d_sidebar_wrapper_recent_games').hide(); +} +} +function check_privilege(key) { +if (!session.user) return false; +if (session.user.Privileges.admin == 1) return true; +if (!key.toString().match(/^\//)) key = '/' + key; +var value = lookup_path(key, session.user.Privileges); +return( value && (value != 0) ); +} +function is_admin() { +return check_privilege('admin'); +} +function upgrade_flash_error() { +return alert("Sorry, file upload requires Adobe Flash Player 9 or higher."); +} +function cancel_user_image_manager() { +upload_destroy(); +hide_popup_dialog(); +delete session.hooks.keys[DELETE_KEY]; +} +function do_user_image_manager(callback) { +if (callback) session.uim_callback = callback; +else session.uim_callback = null; +session.temp_last_user_img = null; +session.temp_last_user_image_filename = ''; +var html = '
      '; +html += '
      Image Manager
      '; +html += '
      '; +html += ''; +html += '
      '; +html += '
      '; +html += ''; +html += ''; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', 'cancel_user_image_manager()') + ' ' + large_icon_button('bullet_upload.png', 'Upload Files...', 'upload_basic()', 'b_upload_user_image') + ' ' + large_icon_button('check', 'Choose', 'do_choose_user_image()', 'btn_choose_user_image', '', 'disabled') + '
      '; +html += '
      '; +session.hooks.keys[ENTER_KEY] = 'do_choose_user_image'; +session.hooks.keys[ESC_KEY] = 'cancel_user_image_manager'; +session.hooks.keys[DELETE_KEY] = 'do_delete_selected_user_image'; +show_popup_dialog(500, 300, html); +var self = this; +setTimeout( function() { +prep_upload('b_upload_user_image', '/effect/api/upload_user_image', [self, 'do_upload_user_image_2'], ['Image Files', '*.jpg;*.jpe;*.jpeg;*.gif;*.png']); +}, 1 ); +var args = { +limit: 50, +offset: 0, +random: Math.random() +}; +effect_api_get( 'user_images_get', args, 'uim_populate_images', { } ); +} +function do_upload_user_image_2() { +effect_api_mod_touch('user_images_get'); +effect_api_send('user_get', { +Username: session.username +}, [this, 'do_upload_user_image_3']); +} +function do_upload_user_image_3(response) { +if (response.User.LastUploadError) return do_error( "Failed to upload image: " + response.User.LastUploadError ); +do_user_image_manager( session.uim_callback ); +} +function uim_populate_images(response, tx) { +var html = ''; +var base_url = '/effect/api/view/users/' + session.username + '/images'; +if (response.Rows && response.Rows.Row) { +var imgs = always_array( response.Rows.Row ); +for (var idx = 0, len = imgs.length; idx < len; idx++) { +var img = imgs[idx]; +var class_name = ((img.Filename == session.temp_last_user_image_filename) ? 'choose_item_selected' : 'choose_item'); +html += ''; +} +} +else { +html = ''; +} +$('d_user_image_list').innerHTML = html; +} +function do_select_user_image(img, filename) { +if (session.temp_last_user_img) session.temp_last_user_img.className = 'choose_item'; +img.className = 'choose_item_selected'; +$('btn_choose_user_image').removeClass('disabled'); +session.temp_last_user_img = img; +session.temp_last_user_image_filename = filename; +} +function do_delete_selected_user_image() { +if (session.temp_last_user_image_filename) { +effect_api_send('user_image_delete', { Filename: session.temp_last_user_image_filename }, 'do_delete_selected_user_image_finish', {}); +} +} +function do_delete_selected_user_image_finish(response, tx) { +try { $('d_user_image_list').removeChild( session.temp_last_user_img ); } catch(e) {;} +session.temp_last_user_img = null; +session.temp_last_user_image_filename = null; +} +function do_choose_user_image() { +if (!session.temp_last_user_image_filename) return; +if (session.uim_callback) { +fire_callback( session.uim_callback, session.temp_last_user_image_filename ); +} +cancel_user_image_manager(); +} +function user_image_thumbnail(filename, width, height, attribs) { +var username = session.username; +if (filename.match(/^(\w+)\/(.+)$/)) { +username = RegExp.$1; +filename = RegExp.$2; +} +var url = '/effect/api/view/users/' + username + '/images/' + filename.replace(/\.(\w+)$/, '_thumb.jpg'); +return ''; +} +function get_user_display(username, full_name, base_url) { +if (!base_url) base_url = ''; +return icon('user', full_name || username, base_url + '#User/' + username); +} +function get_game_tab_bar(game_id, cur_page_name) { +return tab_bar([ +['#Game/' + game_id, 'Game', 'controller.png'], +['#GameDisplay/' + game_id, 'Display', 'monitor.png'], +['#GameAssets/' + game_id, 'Assets', 'folder_page_white.png'], +['#GameObjects/' + game_id, 'Objects', 'bricks.png'], +['#GameAudio/' + game_id, 'Audio', 'sound.gif'], +['#GameKeys/' + game_id, 'Keyboard', 'keyboard.png'], +['#GameLevels/' + game_id, 'Levels', 'world.png'], +['#GamePublisher/' + game_id, 'Publish', 'cd.png'] +], cur_page_name); +} +function get_user_tab_bar(cur_page_name) { +var tabs = [ +['#Home', 'My Home', 'house.png'] +]; +tabs.push( ['#MyAccount', 'Edit Account', 'user_edit.png'] ); +tabs.push( ['#ArticleEdit', 'Post Article', 'page_white_edit.png'] ); +if (config.ProEnabled) { +tabs.push( ['#UserPayments', 'Payments', 'money.png'] ); +} +tabs.push( ['#UserLog', 'Security Log', 'application_view_detail.png'] ); +return tab_bar(tabs, cur_page_name); +} +function get_admin_tab_bar(cur_page_name) { +var tabs = []; +tabs.push( ['#Admin', 'Admin', 'lock.png'] ); +tabs.push( ['#TicketSearch/bugs', 'Bug Tracker', 'bug.png'] ); +tabs.push( ['#TicketSearch/helpdesk', 'Help Desk', 'telephone.png'] ); +tabs.push( ['#AdminReport', 'Reports', 'chart_pie.png'] ); +return tab_bar(tabs, cur_page_name); +} +function get_string(path, args) { +assert(window.config, "get_string() called before config loaded"); +if (!args) args = {}; +args.config = config; +args.session = session; +args.query = session.query; +var value = lookup_path(path, config.Strings); +return (typeof(value) == 'string') ? substitute(value, args) : value; +} +function normalize_dir_path(path) { +if (!path.match(/^\//)) path = '/' + path; +if (!path.match(/\/$/)) path += '/'; +return path; +} +function textedit_window_save(storage_key, filename, content, callback) { +if (!callback) callback = null; +effect_api_mod_touch('textedit'); +if (storage_key.match(/^\/games\/([a-z0-9][a-z0-9\-]*[a-z0-9])\/assets(.+)$/)) { +var game_id = RegExp.$1; +var path = RegExp.$2; +show_progress_dialog(1, "Saving file..."); +effect_api_send('asset_save_file_contents', { +GameID: game_id, +Path: path, +Filename: filename, +Content: content +}, 'textedit_window_save_finish', { _mode: 'asset', _game_id: game_id, _filename: filename, _callback: callback } ); +} +else { +show_progress_dialog(1, "Saving data..."); +effect_api_send('admin_save_file_contents', { +Path: storage_key, +Filename: filename, +Content: content +}, 'textedit_window_save_finish', { _mode: 'admin', _storage_key: storage_key, _filename: filename, _callback: callback } ); +} +} +function textedit_window_save_finish(response, tx) { +hide_progress_dialog(); +if (tx._mode == 'asset') { +do_message('success', "Saved asset: \""+tx._filename+"\""); +show_glog_widget(); +} +else { +do_message('success', "Saved data: \""+tx._storage_key+'/'+tx._filename+"\""); +} +if (tx._callback) tx._callback(); +} +function do_buy(args) { +$P().hide(); +$('d_page_loading').show(); +effect_api_send('create_order', args, 'do_buy_redirect', { _buy_args: args } ); +} +function do_buy_redirect(response, tx) { +var args = tx._buy_args; +$('fe_gco_title').value = args.Title || ''; +$('fe_gco_desc').value = args.Desc || ''; +$('fe_gco_price').value = args.Price || ''; +$('fe_gco_after').value = args.After || ''; +$('fe_gco_unique_id').value = response.OrderID; +Debug.trace('payment', "Redirecting to Google Checkout"); +setTimeout( function() { $('BB_BuyButtonForm').submit(); }, 1 ); +} +function show_glog_widget(game_id) { +if (!game_id) game_id = session.glog_game_id; +if (!game_id) { +$('glog_widget').hide(); +return; +} +if (game_id != session.glog_game_id) { +$('glog_widget').hide(); +session.glog_game_id = game_id; +update_glog_widget(game_id); +} +else { +$('glog_widget').show(); +setTimeout( function() { update_glog_widget(game_id); }, 500 ); +} +} +function update_glog_widget(game_id) { +effect_api_get('game_get_log', { +id: game_id, +offset: 0, +limit: 1, +rand: Math.random() +}, 'receive_glog_data', { _game_id: game_id }); +} +function receive_glog_data(response, tx) { +var game_id = tx._game_id; +if (response && response.Rows && response.Rows.Row) { +var rows = always_array( response.Rows.Row ); +var row = rows[0]; +var html = ''; +html += '
      '; +html += '
      Latest Game Activity
      '; +html += ''; +html += ''; +html += '
      '; +html += '
      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + get_buddy_icon_display(row.Username, 1, 0) + ''; +html += '
      ' + icon( get_icon_for_glog_type(row.Type), ''+row.Message+'' ) + '
      '; +html += '
      ' + get_relative_date(row.Date, true) + '
      '; +html += '
      '; +$('glog_widget').innerHTML = html; +$('glog_widget').show(); +} +} +function show_glog_post_dialog(game_id) { +hide_popup_dialog(); +delete session.progress; +var html = ''; +html += '
      '; +html += '"; + second_cell = ""; + row = $("").attr("id", "s" + index).attr("class", "location_row").html(first_cell + second_cell); + $locationsDiv.append(row); + } + if (index === this.numSearchToDisplay) { + $locationsDiv.append(""); + return $locationsDiv.append(""); + } + }, this); + return this.geocoder.geocode({ + address: address + }, __bind(function(result, status) { + if (status !== "OK") { + $('.error_message').html(t("Search Address Failed")).fadeIn(); + return; + } + _.each(result, showResults); + $("#search_results").html($locationsDiv); + this.locationChange("search"); + this.searchResults = result; + return this.displaySearchLoc(); + }, this)); + }; + ClientsRequestView.prototype.mouseoverLocation = function(e) { + var $el, id, marker; + $el = $(e.currentTarget); + id = $el.attr("id").substring(1); + marker = this.markers[id]; + return marker.setAnimation(google.maps.Animation.BOUNCE); + }; + ClientsRequestView.prototype.mouseoutLocation = function(e) { + var $el, id, marker; + $el = $(e.currentTarget); + id = $el.attr("id").substring(1); + marker = this.markers[id]; + return marker.setAnimation(null); + }; + ClientsRequestView.prototype.searchLocation = function(e) { + e.preventDefault(); + $("#address").val($(e.currentTarget).html()); + return this.searchAddress(); + }; + ClientsRequestView.prototype.favoriteClick = function(e) { + var index, location; + e.preventDefault(); + $(".favorites").attr("href", ""); + index = $(e.currentTarget).removeAttr("href").attr("id"); + location = new google.maps.LatLng(USER.locations[index].latitude, USER.locations[index].longitude); + return this.panToLocation(location); + }; + ClientsRequestView.prototype.clickLocation = function(e) { + var id; + id = $(e.currentTarget).attr("id").substring(1); + return this.panToLocation(this.markers[id].getPosition()); + }; + ClientsRequestView.prototype.panToLocation = function(location) { + this.map.panTo(location); + this.map.setZoom(16); + return this.pickup_icon.setPosition(location); + }; + ClientsRequestView.prototype.locationLinkHandle = function(e) { + var panelName; + e.preventDefault(); + panelName = $(e.currentTarget).attr("id"); + return this.locationChange(panelName); + }; + ClientsRequestView.prototype.locationChange = function(type) { + $(".locations_link").attr("href", "").css("font-weight", "normal"); + switch (type) { + case "favorite": + $(".search_results").attr("href", ""); + $(".locations_link#favorite").removeAttr("href").css("font-weight", "bold"); + $("#search_results").hide(); + $("#favorite_results").fadeIn(); + return this.displayFavLoc(); + case "search": + $(".favorites").attr("href", ""); + $(".locations_link#search").removeAttr("href").css("font-weight", "bold"); + $("#favorite_results").hide(); + $("#search_results").fadeIn(); + return this.displaySearchLoc(); + } + }; + ClientsRequestView.prototype.rateTrip = function(e) { + var rating; + rating = $(e.currentTarget).attr("id"); + $(".stars").attr("src", "/web/img/star_inactive.png"); + return _(rating).times(function(index) { + return $(".stars#" + (index + 1)).attr("src", "/web/img/star_active.png"); + }); + }; + ClientsRequestView.prototype.pickupHandle = function(e) { + var $el, callback, message; + e.preventDefault(); + $el = $(e.currentTarget).find("span"); + switch ($el.html()) { + case t("Request Pickup"): + _.delay(this.requestRide, 3000); + $("#status_message").html(t("Sending pickup request...")); + $el.html(t("Cancel Pickup")).parent().attr("class", "button_red"); + this.pickup_icon.setDraggable(false); + this.map.panTo(this.pickup_icon.getPosition()); + return this.map.setZoom(18); + case t("Cancel Pickup"): + if (this.status === "ready") { + $el.html(t("Request Pickup")).parent().attr("class", "button_green"); + return this.pickup_icon.setDraggable(true); + } else { + callback = __bind(function(v, m, f) { + if (v) { + this.AskDispatch("PickupCanceledClient"); + return this.setStatus("ready"); + } + }, this); + message = t("Cancel Request Prompt"); + if (this.status === "arriving") { + message = 'Cancel Request Arrived Prompt'; + } + return $.prompt(message, { + buttons: { + Ok: true, + Cancel: false + }, + callback: callback + }); + } + } + }; + ClientsRequestView.prototype.requestRide = function() { + if ($("#pickupHandle").find("span").html() === t("Cancel Pickup")) { + this.AskDispatch("Pickup"); + return this.setStatus("searching"); + } + }; + ClientsRequestView.prototype.removeCabs = function() { + _.each(this.cabs, __bind(function(point) { + return point.setMap(null); + }, this)); + return this.cabs = []; + }; + ClientsRequestView.prototype.addToFavLoc = function(e) { + var $el, lat, lng, nickname; + e.preventDefault(); + $el = $(e.currentTarget); + $el.find(".error_message").html(""); + nickname = $el.find("#favLocNickname").val().toString(); + lat = $el.find("#pickupLat").val().toString(); + lng = $el.find("#pickupLng").val().toString(); + if (nickname.length < 3) { + $el.find(".error_message").html(t("Favorite Location Nickname Length Error")); + return; + } + this.ShowSpinner("submit"); + return $.ajax({ + type: 'POST', + url: API + "/locations", + dataType: 'json', + data: { + token: USER.token, + nickname: nickname, + latitude: lat, + longitude: lng + }, + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Favorite Location Save Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.find(".error_message").html(t("Favorite Location Save Failed")); + }, this), + complete: __bind(function(data) { + return this.HideSpinner(); + }, this) + }); + }; + ClientsRequestView.prototype.showFavLoc = function(e) { + $(e.currentTarget).fadeOut(); + return $("#favLoc_form").fadeIn(); + }; + ClientsRequestView.prototype.selectInputText = function(e) { + e.currentTarget.focus(); + return e.currentTarget.select(); + }; + ClientsRequestView.prototype.displayFavLoc = function() { + var alphabet, bounds; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.removeMarkers(); + bounds = new google.maps.LatLngBounds(); + _.each(USER.locations, __bind(function(location, index) { + var marker; + marker = new google.maps.Marker({ + position: new google.maps.LatLng(location.latitude, location.longitude), + map: this.map, + title: t("Favorite Location Title", { + id: alphabet != null ? alphabet[index] : void 0 + }), + icon: "https://www.google.com/mapfiles/marker" + alphabet[index] + ".png" + }); + this.markers.push(marker); + bounds.extend(marker.getPosition()); + return google.maps.event.addListener(marker, 'click', __bind(function() { + return this.pickup_icon.setPosition(marker.getPosition()); + }, this)); + }, this)); + this.pickup_icon.setPosition(_.first(this.markers).getPosition()); + return this.map.fitBounds(bounds); + }; + ClientsRequestView.prototype.displaySearchLoc = function() { + var alphabet; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + this.removeMarkers(); + return _.each(this.searchResults, __bind(function(result, index) { + var marker; + if (index < this.numSearchToDisplay) { + marker = new google.maps.Marker({ + position: result.geometry.location, + map: this.map, + title: t("Search Location Title", { + id: alphabet != null ? alphabet[index] : void 0 + }), + icon: "https://www.google.com/mapfiles/marker" + alphabet[index] + ".png" + }); + this.markers.push(marker); + return this.panToLocation(result.geometry.location); + } + }, this)); + }; + ClientsRequestView.prototype.removeMarkers = function() { + _.each(this.markers, __bind(function(marker) { + return marker.setMap(null); + }, this)); + return this.markers = []; + }; + ClientsRequestView.prototype.AskDispatch = function(ask, options) { + var attrs, lowestETA, processData, showCab; + if (ask == null) { + ask = ""; + } + if (options == null) { + options = {}; + } + switch (ask) { + case "NearestCab": + attrs = { + latitude: this.pickup_icon.getPosition().lat(), + longitude: this.pickup_icon.getPosition().lng() + }; + lowestETA = 99999; + showCab = __bind(function(cab) { + var point; + point = new google.maps.Marker({ + position: new google.maps.LatLng(cab.latitude, cab.longitude), + map: this.map, + icon: this.cabMarker, + title: t("ETA Message", { + minutes: app.helpers.FormatSeconds(cab != null ? cab.eta : void 0, true) + }) + }); + if (cab.eta < lowestETA) { + lowestETA = cab.eta; + } + return this.cabs.push(point); + }, this); + processData = __bind(function(data, textStatus, jqXHR) { + if (this.status === "ready") { + this.removeCabs(); + if (data.sorry) { + $("#status_message").html(data.sorry).fadeIn(); + } else { + _.each(data.driverLocations, showCab); + $("#status_message").html(t("Nearest Cab Message", { + minutes: app.helpers.FormatSeconds(lowestETA, true) + })).fadeIn(); + } + if (Backbone.history.fragment === "!/request") { + return _.delay(this.showCabs, this.pollInterval); + } + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "StatusClient": + processData = __bind(function(data, textStatus, jqXHR) { + var bounds, cabLocation, locationSaved, point, userLocation; + if (data.messageType === "OK") { + switch (data.status) { + case "completed": + this.removeCabs(); + this.setStatus("rate"); + return this.fetchTripDetails(data.tripID); + case "open": + return this.setStatus("ready"); + case "begintrip": + this.setStatus("riding"); + cabLocation = new google.maps.LatLng(data.latitude, data.longitude); + this.removeCabs(); + this.pickup_icon.setMap(null); + point = new google.maps.Marker({ + position: cabLocation, + map: this.map, + icon: this.cabMarker + }); + this.cabs.push(point); + this.map.panTo(point.getPosition()); + $("#rideName").html(data.driverName); + $("#ridePhone").html(data.driverMobile); + $("#ride_address_wrapper").hide(); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + break; + case "pending": + this.setStatus("searching"); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + break; + case "accepted": + case "arrived": + if (data.status === "accepted") { + this.setStatus("waiting"); + $("#status_message").html(t("Arrival ETA Message", { + minutes: app.helpers.FormatSeconds(data.eta, true) + })); + } else { + this.setStatus("arriving"); + $("#status_message").html(t("Arriving Now Message")); + } + userLocation = new google.maps.LatLng(data.pickupLocation.latitude, data.pickupLocation.longitude); + cabLocation = new google.maps.LatLng(data.latitude, data.longitude); + this.pickup_icon.setPosition(userLocation); + this.removeCabs(); + $("#rideName").html(data.driverName); + $("#ridePhone").html(data.driverMobile); + if ($("#rideAddress").html() === "") { + locationSaved = false; + _.each(USER.locations, __bind(function(location) { + if (parseFloat(location.latitude) === parseFloat(data.pickupLocation.latitude) && parseFloat(location.longitude) === parseFloat(data.pickupLocation.longitude)) { + return locationSaved = true; + } + }, this)); + if (locationSaved) { + $("#addToFavButton").hide(); + } + $("#pickupLat").val(data.pickupLocation.latitude); + $("#pickupLng").val(data.pickupLocation.longitude); + this.geocoder.geocode({ + location: userLocation + }, __bind(function(result, status) { + $("#rideAddress").html(result[0].formatted_address); + return $("#favLocNickname").val("" + result[0].address_components[0].short_name + " " + result[0].address_components[1].short_name); + }, this)); + } + point = new google.maps.Marker({ + position: cabLocation, + map: this.map, + icon: this.cabMarker + }); + this.cabs.push(point); + bounds = bounds = new google.maps.LatLngBounds(); + bounds.extend(cabLocation); + bounds.extend(userLocation); + this.map.fitBounds(bounds); + if (Backbone.history.fragment === "!/request") { + return _.delay(this.AskDispatch, this.pollInterval, "StatusClient"); + } + } + } + }, this); + return this.AjaxCall(ask, processData); + case "Pickup": + attrs = { + latitude: this.pickup_icon.getPosition().lat(), + longitude: this.pickup_icon.getPosition().lng() + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "Error") { + return $("#status_message").html(data.description); + } else { + return this.AskDispatch("StatusClient"); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "PickupCanceledClient": + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + return this.setStatus("ready"); + } else { + return $("#status_message").html(data.description); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + case "RatingDriver": + attrs = { + rating: options.rating + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + this.setStatus("init"); + } else { + $("status_message").html(t("Rating Driver Failed")); + } + return this.HideSpinner(); + }, this); + return this.AjaxCall(ask, processData, attrs); + case "Feedback": + attrs = { + message: options.message + }; + processData = __bind(function(data, textStatus, jqXHR) { + if (data.messageType === "OK") { + return alert("rated"); + } + }, this); + return this.AjaxCall(ask, processData, attrs); + } + }; + ClientsRequestView.prototype.AjaxCall = function(type, successCallback, attrs) { + if (attrs == null) { + attrs = {}; + } + _.extend(attrs, { + token: USER.token, + messageType: type, + app: "client", + version: "1.0.60", + device: "web" + }); + return $.ajax({ + type: 'POST', + url: DISPATCH + "/", + processData: false, + data: JSON.stringify(attrs), + success: successCallback, + dataType: 'json', + error: __bind(function(jqXHR, textStatus, errorThrown) { + $("#status_message").html(errorThrown); + return this.HideSpinner(); + }, this) + }); + }; + return ClientsRequestView; + })(); +}).call(this); +}, "views/clients/settings": function(exports, require, module) {(function() { + var clientsSettingsTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsSettingsTemplate = require('templates/clients/settings'); + exports.ClientsSettingsView = (function() { + __extends(ClientsSettingsView, UberView); + function ClientsSettingsView() { + this.render = __bind(this.render, this); + this.initialize = __bind(this.initialize, this); + ClientsSettingsView.__super__.constructor.apply(this, arguments); + } + ClientsSettingsView.prototype.id = 'settings_view'; + ClientsSettingsView.prototype.className = 'view_container'; + ClientsSettingsView.prototype.events = { + 'submit #profile_pic_form': 'processPicUpload', + 'click #submit_pic': 'processPicUpload', + 'click a.setting_change': "changeTab", + 'submit #edit_info_form': "submitInfo", + 'click #change_password': 'changePass' + }; + ClientsSettingsView.prototype.divs = { + 'info_div': "Information", + 'pic_div': "Picture" + }; + ClientsSettingsView.prototype.pageTitle = t("Settings") + " | " + t("Uber"); + ClientsSettingsView.prototype.tabTitle = { + 'info_div': t("Information"), + 'pic_div': t("Picture") + }; + ClientsSettingsView.prototype.initialize = function() { + return this.mixin(require('web-lib/mixins/i18n_phone_form').i18nPhoneForm); + }; + ClientsSettingsView.prototype.render = function(type) { + if (type == null) { + type = "info"; + } + this.RefreshUserInfo(__bind(function() { + var $el, alphabet; + this.delegateEvents(); + this.HideSpinner(); + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + $el = $(this.el); + $(this.el).html(clientsSettingsTemplate({ + type: type + })); + $el.find("#" + type + "_div").show(); + $el.find("a[href='" + type + "_div']").parent().addClass("active"); + return document.title = "" + this.tabTitle[type + '_div'] + " " + this.pageTitle; + }, this)); + this.delegateEvents(); + return this; + }; + ClientsSettingsView.prototype.changeTab = function(e) { + var $eTarget, $el, div, link, pageDiv, _i, _j, _len, _len2, _ref, _ref2; + e.preventDefault(); + $eTarget = $(e.currentTarget); + this.ClearGlobalStatus(); + $el = $(this.el); + _ref = $el.find(".setting_change"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + $(link).parent().removeClass("active"); + } + $eTarget.parent().addClass("active"); + _ref2 = _.keys(this.divs); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + div = _ref2[_j]; + $el.find("#" + div).hide(); + } + pageDiv = $eTarget.attr('href'); + $el.find("#" + pageDiv).show(); + Backbone.history.navigate("!/settings/" + (this.divs[pageDiv].toLowerCase().replace(" ", "-")), false); + document.title = "" + this.tabTitle[pageDiv] + " " + this.pageTitle; + if (pageDiv === "loc_div") { + try { + google.maps.event.trigger(this.map, 'resize'); + return this.map.fitBounds(this.bounds); + } catch (_e) {} + } + }; + ClientsSettingsView.prototype.submitInfo = function(e) { + var $e, attrs, client, options; + $('#global_status').find('.success_message').text(''); + $('#global_status').find('.error_message').text(''); + $('.error_message').text(''); + e.preventDefault(); + $e = $(e.currentTarget); + attrs = $e.serializeToJson(); + attrs['mobile_country_id'] = this.$('#mobile_country_id').val(); + if (attrs['password'] === '') { + delete attrs['password']; + } + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Information Update Succeeded")); + return this.RefreshUserInfo(); + }, this), + error: __bind(function(model, data) { + var errors; + if (data.status === 406) { + errors = JSON.parse(data.responseText); + return _.each(_.keys(errors), function(field) { + return $("#" + field).parent().find('span.error_message').text(errors[field]); + }); + } else { + return this.ShowError(t("Information Update Failed")); + } + }, this), + type: "PUT" + }; + client = new app.models.client({ + id: USER.id + }); + return client.save(attrs, options); + }; + ClientsSettingsView.prototype.changePass = function(e) { + e.preventDefault(); + $(e.currentTarget).hide(); + return $("#password").show(); + }; + ClientsSettingsView.prototype.processPicUpload = function(e) { + e.preventDefault(); + this.ShowSpinner("submit"); + return $.ajaxFileUpload({ + url: API + '/user_pictures', + secureuri: false, + fileElementId: 'picture', + data: { + token: USER.token + }, + dataType: 'json', + complete: __bind(function(data, status) { + this.HideSpinner(); + if (status === 'success') { + this.ShowSuccess(t("Picture Update Succeeded")); + return this.RefreshUserInfo(__bind(function() { + return $("#settingsProfPic").attr("src", USER.picture_url + ("?" + (Math.floor(Math.random() * 1000)))); + }, this)); + } else { + if (data.error) { + return this.ShowError(data.error); + } else { + return this.ShowError("Picture Update Failed"); + } + } + }, this) + }); + }; + return ClientsSettingsView; + })(); +}).call(this); +}, "views/clients/sign_up": function(exports, require, module) {(function() { + var clientsSignUpTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsSignUpTemplate = require('templates/clients/sign_up'); + exports.ClientsSignUpView = (function() { + __extends(ClientsSignUpView, UberView); + function ClientsSignUpView() { + ClientsSignUpView.__super__.constructor.apply(this, arguments); + } + ClientsSignUpView.prototype.id = 'signup_view'; + ClientsSignUpView.prototype.className = 'view_container'; + ClientsSignUpView.prototype.initialize = function() { + this.mixin(require('web-lib/mixins/i18n_phone_form').i18nPhoneForm); + return $('#location_country').live('change', function() { + if (!$('#mobile').val()) { + return $('#mobile_country').find("option[value=" + ($(this).val()) + "]").attr('selected', 'selected').end().trigger('change'); + } + }); + }; + ClientsSignUpView.prototype.events = { + 'submit form': 'signup', + 'click button': 'signup', + 'change #card_number': 'showCardType', + 'change #location_country': 'countryChange' + }; + ClientsSignUpView.prototype.render = function(invite) { + this.HideSpinner(); + $(this.el).html(clientsSignUpTemplate({ + invite: invite + })); + return this; + }; + ClientsSignUpView.prototype.signup = function(e) { + var $el, attrs, client, error_messages, options; + e.preventDefault(); + $el = $("form"); + $el.find('#terms_error').hide(); + if (!$el.find('#signup_terms input[type=checkbox]').attr('checked')) { + $('#spinner.submit').hide(); + $el.find('#terms_error').show(); + return; + } + error_messages = $el.find('.error_message').html(""); + attrs = { + first_name: $el.find('#first_name').val(), + last_name: $el.find('#last_name').val(), + email: $el.find('#email').val(), + password: $el.find('#password').val(), + location_country: $el.find('#location_country option:selected').attr('data-iso2'), + location: $el.find('#location').val(), + language: $el.find('#language').val(), + mobile_country: $el.find('#mobile_country option:selected').attr('data-iso2'), + mobile: $el.find('#mobile').val(), + card_number: $el.find('#card_number').val(), + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + card_code: $el.find('#card_code').val(), + use_case: $el.find('#use_case').val(), + promotion_code: $el.find('#promotion_code').val() + }; + options = { + statusCode: { + 200: function(response) { + $.cookie('token', response.token); + amplify.store('USERjson', response); + app.refreshMenu(); + return app.routers.clients.navigate('!/dashboard', true); + }, + 406: function(e) { + var error, errors, _i, _len, _ref, _results; + errors = JSON.parse(e.responseText); + _ref = _.keys(errors); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + error = _ref[_i]; + _results.push($('#' + error).parent().find('span').html($('#' + error).parent().find('span').html() + " " + errors[error])); + } + return _results; + } + }, + complete: __bind(function(response) { + return this.HideSpinner(); + }, this) + }; + client = new app.models.client; + $('.spinner#submit').show(); + return client.save(attrs, options); + }; + ClientsSignUpView.prototype.countryChange = function(e) { + var $e; + $e = $(e.currentTarget); + return $("#mobile_country").val($e.val()).trigger('change'); + }; + ClientsSignUpView.prototype.showCardType = function(e) { + var $el, reAmerica, reDiscover, reMaster, reVisa, validCard; + reVisa = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/; + reMaster = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/; + reAmerica = /^6011-?\d{4}-?\d{4}-?\d{4}$/; + reDiscover = /^3[4,7]\d{13}$/; + $el = $("#card_logos_signup"); + validCard = false; + if (e.currentTarget.value.match(reVisa)) { + $el.find("#overlay_left").css('width', "0px"); + return $el.find("#overlay_right").css('width', "75%"); + } else if (e.currentTarget.value.match(reMaster)) { + $el.find("#overlay_left").css('width', "25%"); + return $el.find("#overlay_right").css('width', "50%"); + } else if (e.currentTarget.value.match(reAmerica)) { + $el.find("#overlay_left").css('width', "75%"); + $el.find("#overlay_right").css('width', "0px"); + return console.log("amex"); + } else if (e.currentTarget.value.match(reDiscover)) { + $el.find("#overlay_left").css('width', "50%"); + return $el.find("#overlay_right").css('width', "25%"); + } else { + $el.find("#overlay_left").css('width', "0px"); + return $el.find("#overlay_right").css('width', "0px"); + } + }; + return ClientsSignUpView; + })(); +}).call(this); +}, "views/clients/trip_detail": function(exports, require, module) {(function() { + var clientsTripDetailTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsTripDetailTemplate = require('templates/clients/trip_detail'); + exports.TripDetailView = (function() { + __extends(TripDetailView, UberView); + function TripDetailView() { + this.resendReceipt = __bind(this.resendReceipt, this); + TripDetailView.__super__.constructor.apply(this, arguments); + } + TripDetailView.prototype.id = 'trip_detail_view'; + TripDetailView.prototype.className = 'view_container'; + TripDetailView.prototype.events = { + 'click a#fare_review': 'showFareReview', + 'click #fare_review_hide': 'hideFareReview', + 'submit #form_review_form': 'submitFareReview', + 'click #submit_fare_review': 'submitFareReview', + 'click .resendReceipt': 'resendReceipt' + }; + TripDetailView.prototype.render = function(id) { + if (id == null) { + id = 'invalid'; + } + this.ReadUserInfo(); + this.HideSpinner(); + this.model = new app.models.trip({ + id: id + }); + this.model.fetch({ + data: { + relationships: 'points,driver,city.country' + }, + dataType: 'json', + success: __bind(function() { + var trip; + trip = this.model; + $(this.el).html(clientsTripDetailTemplate({ + trip: trip + })); + this.RequireMaps(__bind(function() { + var bounds, endPos, map, myOptions, path, polyline, startPos; + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(this.model.get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + myOptions = { + zoom: 12, + center: path[0], + mapTypeId: google.maps.MapTypeId.ROADMAP, + zoomControl: false, + rotateControl: false, + panControl: false, + mapTypeControl: false, + scrollwheel: false + }; + map = new google.maps.Map(document.getElementById("trip_details_map"), myOptions); + map.fitBounds(bounds); + startPos = new google.maps.Marker({ + position: _.first(path), + map: map, + title: t("Trip started here"), + icon: 'https://uber-static.s3.amazonaws.com/marker_start.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: map, + title: t("Trip ended here"), + icon: 'https://uber-static.s3.amazonaws.com/marker_end.png' + }); + startPos.setMap(map); + endPos.setMap(map); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + return polyline.setMap(map); + }, this)); + return this.HideSpinner(); + }, this) + }); + this.ShowSpinner('load'); + this.delegateEvents(); + return this; + }; + TripDetailView.prototype.showFareReview = function(e) { + e.preventDefault(); + $('#fare_review_box').slideDown(); + return $('#fare_review').hide(); + }; + TripDetailView.prototype.hideFareReview = function(e) { + e.preventDefault(); + $('#fare_review_box').slideUp(); + return $('#fare_review').show(); + }; + TripDetailView.prototype.submitFareReview = function(e) { + var attrs, errorMessage, id, options; + e.preventDefault(); + errorMessage = $(".error_message"); + errorMessage.hide(); + id = $("#tripid").val(); + this.model = new app.models.trip({ + id: id + }); + attrs = { + note: $('#form_review_message').val(), + note_type: 'client_fare_review' + }; + options = { + success: __bind(function(response) { + $(".success_message").fadeIn(); + return $("#fare_review_form_wrapper").slideUp(); + }, this), + error: __bind(function(error) { + return errorMessage.fadeIn(); + }, this) + }; + return this.model.save(attrs, options); + }; + TripDetailView.prototype.resendReceipt = function(e) { + var $e; + e.preventDefault(); + $e = $(e.currentTarget); + this.$(".resendReceiptSuccess").empty().show(); + this.$(".resentReceiptError").empty().show(); + e.preventDefault(); + $('#spinner').show(); + return $.ajax('/api/trips/func/resend_receipt', { + data: { + token: $.cookie('token'), + trip_id: this.model.id + }, + type: 'POST', + complete: __bind(function(xhr) { + var response; + response = JSON.parse(xhr.responseText); + $('#spinner').hide(); + switch (xhr.status) { + case 200: + this.$(".resendReceiptSuccess").html("Receipt has been emailed"); + return this.$(".resendReceiptSuccess").fadeOut(2000); + default: + this.$(".resendReceiptError").html("Receipt has failed to be emailed"); + return this.$(".resendReceiptError").fadeOut(2000); + } + }, this) + }); + }; + return TripDetailView; + })(); +}).call(this); +}, "views/shared/menu": function(exports, require, module) {(function() { + var menuTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + menuTemplate = require('templates/shared/menu'); + exports.SharedMenuView = (function() { + __extends(SharedMenuView, Backbone.View); + function SharedMenuView() { + SharedMenuView.__super__.constructor.apply(this, arguments); + } + SharedMenuView.prototype.id = 'menu_view'; + SharedMenuView.prototype.render = function() { + var type; + if ($.cookie('token') === null) { + type = 'guest'; + } else { + type = 'client'; + } + $(this.el).html(menuTemplate({ + type: type + })); + return this; + }; + return SharedMenuView; + })(); +}).call(this); +}, "web-lib/collections/countries": function(exports, require, module) {(function() { + var UberCollection; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberCollection = require('web-lib/uber_collection').UberCollection; + exports.CountriesCollection = (function() { + __extends(CountriesCollection, UberCollection); + function CountriesCollection() { + CountriesCollection.__super__.constructor.apply(this, arguments); + } + CountriesCollection.prototype.model = app.models.country; + CountriesCollection.prototype.url = '/countries'; + return CountriesCollection; + })(); +}).call(this); +}, "web-lib/collections/vehicle_types": function(exports, require, module) {(function() { + var UberCollection, vehicleType, _ref; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberCollection = require('web-lib/uber_collection').UberCollection; + vehicleType = (typeof app !== "undefined" && app !== null ? (_ref = app.models) != null ? _ref.vehicleType : void 0 : void 0) || require('models/vehicle_type').VehicleType; + exports.VehicleTypesCollection = (function() { + __extends(VehicleTypesCollection, UberCollection); + function VehicleTypesCollection() { + VehicleTypesCollection.__super__.constructor.apply(this, arguments); + } + VehicleTypesCollection.prototype.model = vehicleType; + VehicleTypesCollection.prototype.url = '/vehicle_types'; + VehicleTypesCollection.prototype.defaultColumns = ['id', 'created_at', 'updated_at', 'deleted_at', 'created_by_user_id', 'updated_by_user_id', 'city_id', 'type', 'make', 'model', 'capacity', 'minimum_year', 'actions']; + VehicleTypesCollection.prototype.tableColumns = function(cols) { + var actions, c, capacity, city_id, columnValues, created_at, created_by_user_id, deleted_at, headerRow, id, make, minimum_year, model, type, updated_at, updated_by_user_id, _i, _len; + id = { + sTitle: 'Id' + }; + created_at = { + sTitle: 'Created At (UTC)', + 'sType': 'string' + }; + updated_at = { + sTitle: 'Updated At (UTC)', + 'sType': 'string' + }; + deleted_at = { + sTitle: 'Deleted At (UTC)', + 'sType': 'string' + }; + created_by_user_id = { + sTitle: 'Created By' + }; + updated_by_user_id = { + sTitle: 'Updated By' + }; + city_id = { + sTitle: 'City' + }; + type = { + sTitle: 'Type' + }; + make = { + sTitle: 'Make' + }; + model = { + sTitle: 'Model' + }; + capacity = { + sTitle: 'Capacity' + }; + minimum_year = { + sTitle: 'Min. Year' + }; + actions = { + sTitle: 'Actions' + }; + columnValues = { + id: id, + created_at: created_at, + updated_at: updated_at, + deleted_at: deleted_at, + created_by_user_id: created_by_user_id, + updated_by_user_id: updated_by_user_id, + city_id: city_id, + type: type, + make: make, + model: model, + capacity: capacity, + minimum_year: minimum_year, + actions: actions + }; + headerRow = []; + for (_i = 0, _len = cols.length; _i < _len; _i++) { + c = cols[_i]; + if (columnValues[c]) { + headerRow.push(columnValues[c]); + } + } + return headerRow; + }; + return VehicleTypesCollection; + })(); +}).call(this); +}, "web-lib/helpers": function(exports, require, module) {(function() { + var __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + exports.helpers = { + pin: function(num, color) { + if (color == null) { + color = 'FF0000'; + } + return ""; + }, + reverseGeocode: function(latitude, longitude) { + if (latitude && longitude) { + return "" + latitude + ", " + longitude + ""; + } else { + return ''; + } + }, + linkedName: function(model) { + var first_name, id, last_name, role, url; + role = model.role || model.get('role'); + id = model.id || model.get('id'); + first_name = model.first_name || model.get('first_name'); + last_name = model.last_name || model.get('last_name'); + url = "/" + role + "s/" + id; + return "" + first_name + " " + last_name + ""; + }, + linkedVehicle: function(vehicle, vehicleType) { + return " " + (vehicleType != null ? vehicleType.get('make') : void 0) + " " + (vehicleType != null ? vehicleType.get('model') : void 0) + " " + (vehicle.get('year')) + " "; + }, + linkedUserId: function(userType, userId) { + return "" + userType + " " + userId + ""; + }, + timeDelta: function(start, end) { + var delta; + if (typeof start === 'string') { + start = this.parseDate(start); + } + if (typeof end === 'string') { + end = this.parseDate(end); + } + if (end && start) { + delta = end.getTime() - start.getTime(); + return this.formatSeconds(delta / 1000); + } else { + return '00:00'; + } + }, + formatSeconds: function(s) { + var minutes, seconds; + s = Math.floor(s); + minutes = Math.floor(s / 60); + seconds = s - minutes * 60; + return "" + (this.leadingZero(minutes)) + ":" + (this.leadingZero(seconds)); + }, + formatCurrency: function(strValue, reverseSign, currency) { + var currency_locale, lc, mf; + if (reverseSign == null) { + reverseSign = false; + } + if (currency == null) { + currency = null; + } + strValue = String(strValue); + if (reverseSign) { + strValue = ~strValue.indexOf('-') ? strValue.split('-').join('') : ['-', strValue].join(''); + } + currency_locale = i18n.currencyToLocale[currency]; + try { + if (!(currency_locale != null) || currency_locale === i18n.locale) { + return i18n.jsworld.mf.format(strValue); + } else { + lc = new jsworld.Locale(POSIX_LC[currency_locale]); + mf = new jsworld.MonetaryFormatter(lc); + return mf.format(strValue); + } + } catch (error) { + i18n.log(error); + return strValue; + } + }, + formatTripFare: function(trip, type) { + var _ref, _ref2; + if (type == null) { + type = "fare"; + } + if (!trip.get('fare')) { + return 'n/a'; + } + if (((_ref = trip.get('fare_breakdown_local')) != null ? _ref.currency : void 0) != null) { + return app.helpers.formatCurrency(trip.get("" + type + "_local"), false, (_ref2 = trip.get('fare_breakdown_local')) != null ? _ref2.currency : void 0); + } else if (trip.get("" + type + "_string") != null) { + return trip.get("" + type + "_string"); + } else if (trip.get("" + type + "_local") != null) { + return trip.get("" + type + "_local"); + } else { + return 'n/a'; + } + }, + formatPhoneNumber: function(phoneNumber, countryCode) { + if (countryCode == null) { + countryCode = "+1"; + } + if (phoneNumber != null) { + phoneNumber = String(phoneNumber); + switch (countryCode) { + case '+1': + return countryCode + ' ' + phoneNumber.substring(0, 3) + '-' + phoneNumber.substring(3, 6) + '-' + phoneNumber.substring(6, 10); + case '+33': + return countryCode + ' ' + phoneNumber.substring(0, 1) + ' ' + phoneNumber.substring(1, 3) + ' ' + phoneNumber.substring(3, 5) + ' ' + phoneNumber.substring(5, 7) + ' ' + phoneNumber.substring(7, 9); + default: + countryCode + phoneNumber; + } + } + return "" + countryCode + " " + phoneNumber; + }, + parseDate: function(d, cityTime, tz) { + var city_filter, parsed, _ref; + if (cityTime == null) { + cityTime = true; + } + if (tz == null) { + tz = null; + } + if (((_ref = !d.substr(-6, 1)) === '+' || _ref === '-') || d.length === 19) { + d += '+00:00'; + } + if (/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/.test(d)) { + parsed = d.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/); + d = new Date(); + d.setUTCFullYear(parsed[1]); + d.setUTCMonth(parsed[2] - 1); + d.setUTCDate(parsed[3]); + d.setUTCHours(parsed[4]); + d.setUTCMinutes(parsed[5]); + d.setUTCSeconds(parsed[6]); + } else { + d = Date.parse(d); + } + if (typeof d === 'number') { + d = new Date(d); + } + d = new timezoneJS.Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), 'Etc/UTC'); + if (tz) { + d.convertToTimezone(tz); + } else if (cityTime) { + city_filter = $.cookie('city_filter'); + if (city_filter) { + tz = $("#city_filter option[value=" + city_filter + "]").attr('data-timezone'); + if (tz) { + d.convertToTimezone(tz); + } + } + } + return d; + }, + dateToTimezone: function(d) { + var city_filter, tz; + d = new timezoneJS.Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), 'Etc/UTC'); + city_filter = $.cookie('city_filter'); + if (city_filter) { + tz = $("#city_filter option[value=" + city_filter + "]").attr('data-timezone'); + d.convertToTimezone(tz); + } + return d; + }, + fixAMPM: function(d, formatted) { + if (d.hours >= 12) { + return formatted.replace(/\b[AP]M\b/, 'PM'); + } else { + return formatted.replace(/\b[AP]M\b/, 'AM'); + } + }, + formatDate: function(d, time, timezone) { + var formatted; + if (time == null) { + time = true; + } + if (timezone == null) { + timezone = null; + } + d = this.parseDate(d, true, timezone); + formatted = time ? ("" + (i18n.jsworld.dtf.formatDate(d)) + " ") + this.formatTime(d, d.getTimezoneInfo()) : i18n.jsworld.dtf.formatDate(d); + return this.fixAMPM(d, formatted); + }, + formatDateLong: function(d, time, timezone) { + if (time == null) { + time = true; + } + if (timezone == null) { + timezone = null; + } + d = this.parseDate(d, true, timezone); + timezone = d.getTimezoneInfo().tzAbbr; + if (time) { + return (i18n.jsworld.dtf.formatDateTime(d)) + (" " + timezone); + } else { + return i18n.jsworld.dtf.formatDate(d); + } + }, + formatTimezoneJSDate: function(d) { + var day, hours, jsDate, minutes, month, year; + year = d.getFullYear(); + month = this.leadingZero(d.getMonth()); + day = this.leadingZero(d.getDate()); + hours = this.leadingZero(d.getHours()); + minutes = this.leadingZero(d.getMinutes()); + jsDate = new Date(year, month, day, hours, minutes, 0); + return jsDate.toDateString(); + }, + formatTime: function(d, timezone) { + var formatted; + if (timezone == null) { + timezone = null; + } + formatted = ("" + (i18n.jsworld.dtf.formatTime(d))) + (timezone != null ? " " + (timezone != null ? timezone.tzAbbr : void 0) : ""); + return this.fixAMPM(d, formatted); + }, + formatISODate: function(d) { + var pad; + pad = function(n) { + if (n < 10) { + return '0' + n; + } + return n; + }; + return d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '-' + pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' + pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()) + 'Z'; + }, + formatExpDate: function(d) { + var month, year; + d = this.parseDate(d); + year = d.getFullYear(); + month = this.leadingZero(d.getMonth() + 1); + return "" + year + "-" + month; + }, + formatLatLng: function(lat, lng, precision) { + if (precision == null) { + precision = 8; + } + return parseFloat(lat).toFixed(precision) + ',' + parseFloat(lng).toFixed(precision); + }, + leadingZero: function(num) { + if (num < 10) { + return "0" + num; + } else { + return num; + } + }, + roundNumber: function(num, dec) { + return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); + }, + notesToHTML: function(notes) { + var i, note, notesHTML, _i, _len; + notesHTML = ''; + i = 1; + if (notes) { + for (_i = 0, _len = notes.length; _i < _len; _i++) { + note = notes[_i]; + notesHTML += "" + note['userid'] + "     " + (this.formatDate(note['created_at'])) + "

      " + note['note'] + "

      "; + notesHTML += "
      "; + } + } + return notesHTML.replace("'", '"e'); + }, + formatPhone: function(n) { + var parts, phone, regexObj; + n = "" + n; + regexObj = /^(?:\+?1[-. ]?)?(?:\(?([0-9]{3})\)?[-. ]?)?([0-9]{3})[-. ]?([0-9]{4})$/; + if (regexObj.test(n)) { + parts = n.match(regexObj); + phone = ""; + if (parts[1]) { + phone += "(" + parts[1] + ") "; + } + phone += "" + parts[2] + "-" + parts[3]; + } else { + phone = n; + } + return phone; + }, + usStates: ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'], + onboardingPages: ['applied', 'ready_to_interview', 'pending_interview', 'interviewed', 'accepted', 'ready_to_onboard', 'pending_onboarding', 'active', 'waitlisted', 'rejected'], + driverBreadCrumb: function(loc, model) { + var onboardingPage, out, _i, _len, _ref; + out = "Drivers > "; + if (!(model != null)) { + out += ""; + } else { + out += "" + (this.onboardingUrlToName(model.get('driver_status'))) + ""; + out += " > " + (this.linkedName(model)) + " (" + (model.get('role')) + ") #" + (model.get('id')); + } + return out; + }, + onboardingUrlToName: function(url) { + return url != null ? url.replace(/_/g, " ").replace(/(^|\s)([a-z])/g, function(m, p1, p2) { + return p1 + p2.toUpperCase(); + }) : void 0; + }, + formatVehicle: function(vehicle) { + if (vehicle.get('make') && vehicle.get('model') && vehicle.get('license_plate')) { + return "" + (vehicle.get('make')) + " " + (vehicle.get('model')) + " (" + (vehicle.get('license_plate')) + ")"; + } + }, + docArbitraryFields: function(docName, cityDocs) { + var doc, field, out, _i, _j, _len, _len2, _ref; + out = ""; + for (_i = 0, _len = cityDocs.length; _i < _len; _i++) { + doc = cityDocs[_i]; + if (doc.name === docName && __indexOf.call(_.keys(doc), "metaFields") >= 0) { + _ref = doc.metaFields; + for (_j = 0, _len2 = _ref.length; _j < _len2; _j++) { + field = _ref[_j]; + out += "" + field.label + ":
      "; + } + } + } + return out; + }, + capitaliseFirstLetter: function(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + }, + createDocUploadForm: function(docName, driverId, vehicleId, cityMeta, vehicleName, expirationRequired) { + var ddocs, expDropdowns, pdocs, vdocs; + if (driverId == null) { + driverId = "None"; + } + if (vehicleId == null) { + vehicleId = "None"; + } + if (cityMeta == null) { + cityMeta = []; + } + if (vehicleName == null) { + vehicleName = false; + } + if (expirationRequired == null) { + expirationRequired = false; + } + ddocs = cityMeta["driverRequiredDocs"] || []; + pdocs = cityMeta["partnerRequiredDocs"] || []; + vdocs = cityMeta["vehicleRequiredDocs"] || []; + expDropdowns = "Expiration Date:\n -\n"; + return " \n
      \n \n \n \n\n
      \n " + (vehicleName ? vehicleName : "") + " " + docName + "\n
      \n\n
      \n \n
      \n\n
      \n " + (expirationRequired ? expDropdowns : "") + "\n
      \n\n
      \n " + (app.helpers.docArbitraryFields(docName, _.union(ddocs, pdocs, vdocs))) + "\n
      \n\n
      \n \n
      \n\n
      \n"; + }, + countrySelector: function(name, options) { + var countries, countryCodePrefix, defaultOptions; + if (options == null) { + options = {}; + } + defaultOptions = { + selectedKey: 'telephone_code', + selectedValue: '+1', + silent: false + }; + _.extend(defaultOptions, options); + options = defaultOptions; + countries = new app.collections.countries(); + countries.fetch({ + data: { + limit: 300 + }, + success: function(countries) { + var $option, $select, country, selected, _i, _len, _ref; + selected = false; + _ref = countries.models || []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + country = _ref[_i]; + $select = $("select[name=" + name + "]"); + $option = $('').val(country.id).attr('data-iso2', country.get('iso2')).attr('data-prefix', country.get('telephone_code')).html(country.get('name')); + if (country.get(options.selectedKey) === options.selectedValue && !selected) { + selected = true; + $option.attr('selected', 'selected'); + } + $select.append($option); + } + if (selected && !options.silent) { + return $select.val(options.selected).trigger('change'); + } + } + }); + countryCodePrefix = options.countryCodePrefix ? "data-country-code-prefix='" + options.countryCodePrefix + "'" : ''; + return ""; + }, + missingDocsOnDriver: function(driver) { + var city, docsReq, documents, partnerDocs; + city = driver.get('city'); + documents = driver.get('documents'); + if ((city != null) && (documents != null)) { + docsReq = _.pluck(city != null ? city.get('meta')["driverRequiredDocs"] : void 0, "name"); + if (driver.get('role') === "partner") { + partnerDocs = _.pluck(city != null ? city.get('meta')["partnerRequiredDocs"] : void 0, "name"); + docsReq = _.union(docsReq, partnerDocs); + } + return _.reject(docsReq, __bind(function(doc) { + return __indexOf.call((documents != null ? documents.pluck("name") : void 0) || [], doc) >= 0; + }, this)); + } else { + return []; + } + } + }; +}).call(this); +}, "web-lib/i18n": function(exports, require, module) {(function() { + exports.i18n = { + defaultLocale: 'en_US', + cookieName: '_LOCALE_', + locales: { + 'en_US': "English (US)", + 'fr_FR': "Français" + }, + currencyToLocale: { + 'USD': 'en_US', + 'EUR': 'fr_FR' + }, + logglyKey: 'd2d5a9bc-7ebe-4538-a180-81e62c705b1b', + logglyHost: 'https://logs.loggly.com', + init: function() { + this.castor = new window.loggly({ + url: this.logglyHost + '/inputs/' + this.logglyKey + '?rt=1', + level: 'error' + }); + this.setLocale($.cookie(this.cookieName) || this.defaultLocale); + window.t = _.bind(this.t, this); + this.loadLocaleTranslations(this.locale); + if (!(this[this.defaultLocale] != null)) { + return this.loadLocaleTranslations(this.defaultLocale); + } + }, + loadLocaleTranslations: function(locale) { + var loadPaths, path, _i, _len, _results; + loadPaths = ['web-lib/translations/' + locale, 'web-lib/translations/' + locale.slice(0, 2), 'translations/' + locale, 'translations/' + locale.slice(0, 2)]; + _results = []; + for (_i = 0, _len = loadPaths.length; _i < _len; _i++) { + path = loadPaths[_i]; + locale = path.substring(path.lastIndexOf('/') + 1); + if (this[locale] == null) { + this[locale] = {}; + } + _results.push((function() { + try { + return _.extend(this[locale], require(path).translations); + } catch (error) { + + } + }).call(this)); + } + return _results; + }, + getLocale: function() { + return this.locale; + }, + setLocale: function(locale) { + var message, parts, _ref; + parts = locale.split('_'); + this.locale = parts[0].toLowerCase(); + if (parts.length > 1) { + this.locale += "_" + (parts[1].toUpperCase()); + } + if (this.locale) { + $.cookie(this.cookieName, this.locale, { + path: '/', + domain: '.uber.com' + }); + } + try { + ((_ref = this.jsworld) != null ? _ref : this.jsworld = {}).lc = new jsworld.Locale(POSIX_LC[this.locale]); + this.jsworld.mf = new jsworld.MonetaryFormatter(this.jsworld.lc); + this.jsworld.nf = new jsworld.NumericFormatter(this.jsworld.lc); + this.jsworld.dtf = new jsworld.DateTimeFormatter(this.jsworld.lc); + this.jsworld.np = new jsworld.NumericParser(this.jsworld.lc); + this.jsworld.mp = new jsworld.MonetaryParser(this.jsworld.lc); + return this.jsworld.dtp = new jsworld.DateTimeParser(this.jsworld.lc); + } catch (error) { + message = 'JsWorld error with locale: ' + this.locale; + return this.log({ + message: message, + error: error + }); + } + }, + getTemplate: function(id) { + var _ref, _ref2; + return ((_ref = this[this.locale]) != null ? _ref[id] : void 0) || ((_ref2 = this[this.locale.slice(0, 2)]) != null ? _ref2[id] : void 0); + }, + getTemplateDefault: function(id) { + var _ref, _ref2; + return ((_ref = this[this.defaultLocale]) != null ? _ref[id] : void 0) || ((_ref2 = this[this.defaultLocale.slice(0, 2)]) != null ? _ref2[id] : void 0); + }, + getTemplateOrDefault: function(id) { + return this.getTemplate(id) || this.getTemplateDefault(id); + }, + t: function(id, vars) { + var errStr, locale, template; + if (vars == null) { + vars = {}; + } + locale = this.getLocale(); + template = this.getTemplate(id); + if (template == null) { + if (/dev|test/.test(window.location.host)) { + template = "(?) " + id; + } else { + template = this.getTemplateDefault(id); + } + errStr = "Missing [" + locale + "] translation for [" + id + "] at [" + window.location.hash + "] - Default template is [" + template + "]"; + this.log({ + error: errStr, + locale: locale, + id: id, + defaultTemplate: template + }); + } + if (template) { + return _.template(template, vars); + } else { + return id; + } + }, + log: function(error) { + if (/dev/.test(window.location.host)) { + if ((typeof console !== "undefined" && console !== null ? console.log : void 0) != null) { + return console.log(error); + } + } else { + _.extend(error, { + host: window.location.host, + hash: window.location.hash + }); + return this.castor.error(JSON.stringify(error)); + } + } + }; +}).call(this); +}, "web-lib/mixins/i18n_phone_form": function(exports, require, module) {(function() { + exports.i18nPhoneForm = { + _events: { + 'change select[data-country-code-prefix]': 'setCountryCodePrefix' + }, + setCountryCodePrefix: function(e) { + var $el, prefix; + $el = $(e.currentTarget); + prefix = $el.find('option:selected').attr('data-prefix'); + return $("#" + ($el.attr('data-country-code-prefix'))).text(prefix); + } + }; +}).call(this); +}, "web-lib/models/country": function(exports, require, module) {(function() { + var UberModel; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberModel = require('web-lib/uber_model').UberModel; + exports.Country = (function() { + __extends(Country, UberModel); + function Country() { + Country.__super__.constructor.apply(this, arguments); + } + Country.prototype.url = function() { + if (this.id) { + return "/countries/" + this.id; + } else { + return '/countries'; + } + }; + return Country; + })(); +}).call(this); +}, "web-lib/models/vehicle_type": function(exports, require, module) {(function() { + var UberModel; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + UberModel = require('web-lib/uber_model').UberModel; + exports.VehicleType = (function() { + __extends(VehicleType, UberModel); + function VehicleType() { + this.toString = __bind(this.toString, this); + VehicleType.__super__.constructor.apply(this, arguments); + } + VehicleType.prototype.endpoint = 'vehicle_types'; + VehicleType.prototype.toTableRow = function(cols) { + var actions, c, capacity, city_id, columnValues, created_at, created_by_user_id, deleted_at, id, make, minimum_year, model, rows, type, updated_at, updated_by_user_id, _i, _len, _ref; + id = "" + (this.get('id')) + ""; + if (this.get('created_at')) { + created_at = app.helpers.formatDate(this.get('created_at')); + } + if (this.get('updated_at')) { + updated_at = app.helpers.formatDate(this.get('updated_at')); + } + if (this.get('deleted_at')) { + deleted_at = app.helpers.formatDate(this.get('deleted_at')); + } + created_by_user_id = "" + (this.get('created_by_user_id')) + ""; + updated_by_user_id = "" + (this.get('updated_by_user_id')) + ""; + city_id = (_ref = this.get('city')) != null ? _ref.get('display_name') : void 0; + type = this.get('type'); + make = this.get('make'); + model = this.get('model'); + capacity = this.get('capacity'); + minimum_year = this.get('minimum_year'); + actions = "Show"; + if (!this.get('deleted_at')) { + actions += " Edit"; + actions += " Delete"; + } + columnValues = { + id: id, + created_at: created_at, + updated_at: updated_at, + deleted_at: deleted_at, + created_by_user_id: created_by_user_id, + updated_by_user_id: updated_by_user_id, + city_id: city_id, + type: type, + make: make, + model: model, + capacity: capacity, + minimum_year: minimum_year, + actions: actions + }; + rows = []; + for (_i = 0, _len = cols.length; _i < _len; _i++) { + c = cols[_i]; + rows.push(columnValues[c] ? columnValues[c] : '-'); + } + return rows; + }; + VehicleType.prototype.toString = function() { + return this.get('make') + ' ' + this.get('model') + ' ' + this.get('type') + (" (" + (this.get('capacity')) + ")"); + }; + return VehicleType; + })(); +}).call(this); +}, "web-lib/templates/footer": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var locale, title, _ref; + __out.push('\n\n\n\n\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "web-lib/translations/en": function(exports, require, module) {(function() { + exports.translations = { + "Info": "Info", + "Learn More": "Learn More", + "Pricing": "Pricing", + "FAQ": "FAQ", + "Support": "Support", + "Support & FAQ": "Support & FAQ", + "Contact Us": "Contact Us", + "Jobs": "Jobs", + "Phones": "Phones", + "Text Message": "Text Message", + "iPhone": "iPhone", + "Android": "Android", + "Drivers": "Drivers", + "Apply": "Apply", + "Sign In": "Sign In", + "Social": "Social", + "Twitter": "Twitter", + "Facebook": "Facebook", + "Blog": "Blog", + "Legal": "Legal", + "Company_Footer": "Company", + "Privacy Policy": "Privacy Policy", + "Terms": "Terms", + "Copyright © Uber Technologies, Inc.": "Copyright © Uber Technologies, Inc.", + "Language:": "Language:", + "Apply to Drive": "Apply to Drive", + "Expiration": "Expiration", + "Fare": "Fare", + "Driver": "Driver ", + "Dashboard": "Dashboard", + "Forgot Password": "Forgot Password", + "Trip Details": "Trip Details", + "Save": "Save", + "Cancel": "Cancel", + "Edit": "Edit", + "Password": "Password", + "First Name": "First Name", + "Last Name": "Last Name", + "Email Address": "Email Address", + "Submit": "Submit", + "Mobile Number": "Mobile Number", + "Zip Code": "Zip Code", + "Sign Out": "Sign Out", + "Confirm Email Message": "Attempting to confirm email...", + "Upload": "Upload", + "Rating": "Rating", + "Pickup Time": "Pickup Time", + "2011": "2011", + "2012": "2012", + "2013": "2013", + "2014": "2014", + "2015": "2015", + "2016": "2016", + "2017": "2017", + "2018": "2018", + "2019": "2019", + "2020": "2020", + "2021": "2021", + "2022": "2022", + "01": "01", + "02": "02", + "03": "03", + "04": "04", + "05": "05", + "06": "06", + "07": "07", + "08": "08", + "09": "09", + "10": "10", + "11": "11", + "12": "12" + }; +}).call(this); +}, "web-lib/translations/fr": function(exports, require, module) {(function() { + exports.translations = { + "Info": "Info", + "Learn More": "En Savoir Plus", + "Pricing": "Calcul du Prix", + "Support & FAQ": "Aide & FAQ", + "Contact Us": "Contactez Nous", + "Jobs": "Emplois", + "Phones": "Téléphones", + "Text Message": "SMS", + "iPhone": "iPhone", + "Android": "Android", + "Apply to Drive": "Candidature Chauffeur", + "Sign In": "Connexion", + "Social": "Contact", + "Twitter": "Twitter", + "Facebook": "Facebook", + "Blog": "Blog", + "Privacy Policy": "Protection des Données Personelles", + "Terms": "Conditions Générales", + "Copyright © Uber Technologies, Inc.": "© Uber, Inc.", + "Language:": "Langue:", + "Forgot Password": "Mot de passe oublié", + "Company_Footer": "À Propos d'Uber", + "Expiration": "Expiration", + "Fare": "Tarif", + "Driver": "Chauffeur", + "Drivers": "Chauffeurs", + "Dashboard": "Tableau de bord", + "Forgot Password": "Mot de passe oublié", + "Forgot Password?": "Mot de passe oublié?", + "Trip Details": "Détails de la course", + "Save": "Enregistrer", + "Cancel": "Annuler", + "Edit": "Modifier", + "Password": "Mot de passe", + "First Name": "Prénom", + "Last Name": "Nom", + "Email Address": "E-mail", + "Submit": "Soumettre", + "Mobile Number": "Téléphone Portable", + "Zip Code": "Code Postal", + "Sign Out": "Se déconnecter", + "Confirm Email Message": "E-mail de confirmation", + "Upload": "Télécharger", + "Rating": "Notation", + "Pickup Time": "Heure de prise en charge", + "2011": "2011", + "2012": "2012", + "2013": "2013", + "2014": "2014", + "2015": "2015", + "2016": "2016", + "2017": "2017", + "2018": "2018", + "2019": "2019", + "2020": "2020", + "2021": "2021", + "2022": "2022", + "01": "01", + "02": "02", + "03": "03", + "04": "04", + "05": "05", + "06": "06", + "07": "07", + "08": "08", + "09": "09", + "10": "10", + "11": "11", + "12": "12" + }; +}).call(this); +}, "web-lib/uber_collection": function(exports, require, module) {(function() { + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberCollection = (function() { + __extends(UberCollection, Backbone.Collection); + function UberCollection() { + UberCollection.__super__.constructor.apply(this, arguments); + } + UberCollection.prototype.parse = function(data) { + var model, tmp, _i, _in, _len, _out; + _in = data.resources || data; + _out = []; + if (data.meta) { + this.meta = data.meta; + } + for (_i = 0, _len = _in.length; _i < _len; _i++) { + model = _in[_i]; + tmp = new this.model; + tmp.set(tmp.parse(model)); + _out.push(tmp); + } + return _out; + }; + UberCollection.prototype.isRenderable = function() { + if (this.models.length) { + return true; + } + }; + UberCollection.prototype.toTableRows = function(cols) { + var tableRows; + tableRows = []; + _.each(this.models, function(model) { + return tableRows.push(model.toTableRow(cols)); + }); + return tableRows; + }; + return UberCollection; + })(); +}).call(this); +}, "web-lib/uber_model": function(exports, require, module) {(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }; + exports.UberModel = (function() { + __extends(UberModel, Backbone.Model); + function UberModel() { + this.refetch = __bind(this.refetch, this); + this.fetch = __bind(this.fetch, this); + this.save = __bind(this.save, this); + this.parse = __bind(this.parse, this); + UberModel.__super__.constructor.apply(this, arguments); + } + UberModel.prototype.endpoint = 'set_api_endpoint_in_subclass'; + UberModel.prototype.refetchOptions = {}; + UberModel.prototype.url = function(type) { + var endpoint_path; + endpoint_path = "/" + this.endpoint; + if (this.get('id')) { + return endpoint_path + ("/" + (this.get('id'))); + } else { + return endpoint_path; + } + }; + UberModel.prototype.isRenderable = function() { + var i, key, value, _ref; + i = 0; + _ref = this.attributes; + for (key in _ref) { + if (!__hasProp.call(_ref, key)) continue; + value = _ref[key]; + if (this.attributes.hasOwnProperty(key)) { + i += 1; + } + if (i > 1) { + return true; + } + } + return !(i === 1); + }; + UberModel.prototype.parse = function(response) { + var attrs, key, model, models, _i, _j, _k, _len, _len2, _len3, _ref, _ref2; + if (typeof response === 'object') { + _ref = _.intersection(_.keys(app.models), _.keys(response)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if (response[key]) { + attrs = this.parse(response[key]); + if (typeof attrs === 'object') { + response[key] = new app.models[key](attrs); + } + } + } + _ref2 = _.intersection(_.keys(app.collections), _.keys(response)); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + key = _ref2[_j]; + models = response[key]; + if (_.isArray(models)) { + response[key] = new app.collections[key]; + for (_k = 0, _len3 = models.length; _k < _len3; _k++) { + model = models[_k]; + attrs = app.collections[key].prototype.model.prototype.parse(model); + response[key].add(new response[key].model(attrs)); + } + } + } + } + return response; + }; + UberModel.prototype.save = function(attributes, options) { + var attr, _i, _j, _len, _len2, _ref, _ref2; + if (options == null) { + options = {}; + } + _ref = _.intersection(_.keys(app.models), _.keys(this.attributes)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + attr = _ref[_i]; + if (typeof this.get(attr) === "object") { + this.unset(attr, { + silent: true + }); + } + } + _ref2 = _.intersection(_.keys(app.collections), _.keys(this.attributes)); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + attr = _ref2[_j]; + if (typeof this.get(attr) === "object") { + this.unset(attr, { + silent: true + }); + } + } + if ((options != null) && options.diff && (attributes != null) && attributes !== {}) { + attributes['id'] = this.get('id'); + attributes['token'] = this.get('token'); + this.clear({ + 'silent': true + }); + this.set(attributes, { + silent: true + }); + } + if (__indexOf.call(_.keys(options), "data") < 0 && __indexOf.call(_.keys(this.refetchOptions || {}), "data") >= 0) { + options.data = this.refetchOptions.data; + } + return Backbone.Model.prototype.save.call(this, attributes, options); + }; + UberModel.prototype.fetch = function(options) { + this.refetchOptions = options; + return Backbone.Model.prototype.fetch.call(this, options); + }; + UberModel.prototype.refetch = function() { + return this.fetch(this.refetchOptions); + }; + return UberModel; + })(); +}).call(this); +}, "web-lib/uber_router": function(exports, require, module) {(function() { + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberRouter = (function() { + __extends(UberRouter, Backbone.Router); + function UberRouter() { + UberRouter.__super__.constructor.apply(this, arguments); + } + UberRouter.prototype.datePickers = function(format) { + if (format == null) { + format = "%Z-%m-%dT%H:%i:%s%:"; + } + $('.datepicker').AnyTime_noPicker(); + return $('.datepicker').AnyTime_picker({ + 'format': format, + 'formatUtcOffset': '%@' + }); + }; + UberRouter.prototype.autoGrowInput = function() { + return $('.editable input').autoGrowInput(); + }; + UberRouter.prototype.windowTitle = function(title) { + return $(document).attr('title', title); + }; + return UberRouter; + })(); +}).call(this); +}, "web-lib/uber_show_view": function(exports, require, module) {(function() { + var UberView; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + UberView = require('web-lib/uber_view').UberView; + exports.UberShowView = (function() { + __extends(UberShowView, UberView); + function UberShowView() { + UberShowView.__super__.constructor.apply(this, arguments); + } + UberShowView.prototype.view = 'show'; + UberShowView.prototype.events = { + 'click #edit': 'edit', + 'submit form': 'save', + 'click .cancel': 'cancel' + }; + UberShowView.prototype.errors = null; + UberShowView.prototype.showTemplate = null; + UberShowView.prototype.editTemplate = null; + UberShowView.prototype.initialize = function() { + if (this.init_hook) { + this.init_hook(); + } + _.bindAll(this, 'render'); + return this.model.bind('change', this.render); + }; + UberShowView.prototype.render = function() { + var $el; + $el = $(this.el); + this.selectView(); + if (this.view === 'show') { + $el.html(this.showTemplate({ + model: this.model + })); + } else if (this.view === 'edit') { + $el.html(this.editTemplate({ + model: this.model, + errors: this.errors || {}, + collections: this.collections || {} + })); + } else { + $el.html(this.newTemplate({ + model: this.model, + errors: this.errors || {}, + collections: this.collections || {} + })); + } + if (this.render_hook) { + this.render_hook(); + } + this.errors = null; + this.userIdsToLinkedNames(); + this.datePickers(); + return this.place(); + }; + UberShowView.prototype.selectView = function() { + var url; + if (this.options.urlRendering) { + url = window.location.hash; + if (url.match(/\/new/)) { + return this.view = 'new'; + } else if (url.match(/\/edit/)) { + return this.view = 'edit'; + } else { + return this.view = 'show'; + } + } + }; + UberShowView.prototype.edit = function(e) { + e.preventDefault(); + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id') + '/edit'; + } else { + this.view = 'edit'; + } + return this.model.change(); + }; + UberShowView.prototype.save = function(e) { + var attributes, ele, form_attrs, _i, _len, _ref; + e.preventDefault(); + attributes = $(e.currentTarget).serializeToJson(); + form_attrs = {}; + _ref = $('input[type="radio"]'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + ele = _ref[_i]; + if ($(ele).is(':checked')) { + form_attrs[$(ele).attr('name')] = $(ele).attr('value'); + } + } + attributes = _.extend(attributes, form_attrs); + if (this.relationships) { + attributes = _.extend(attributes, { + relationships: this.relationships + }); + } + if (this.filter_attributes != null) { + this.filter_attributes(attributes); + } + return this.model.save(attributes, { + silent: true, + success: __bind(function(model) { + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id'); + } else { + this.view = 'show'; + } + return this.flash('success', "Uber save!"); + }, this), + statusCode: { + 406: __bind(function(xhr) { + this.errors = JSON.parse(xhr.responseText); + return this.flash('error', 'That was not Uber.'); + }, this) + }, + error: __bind(function(model, xhr) { + var code, message, responseJSON, responseText; + code = xhr.status; + responseText = xhr.responseText; + if (responseText) { + responseJSON = JSON.parse(responseText); + } + if (responseJSON && (typeof responseJSON === 'object') && (responseJSON.hasOwnProperty('error'))) { + message = responseJSON.error; + } + return this.flash('error', (code || 'Unknown') + ' error' + (': ' + message || '')); + }, this), + complete: __bind(function() { + return this.model.change(); + }, this) + }); + }; + UberShowView.prototype.cancel = function(e) { + e.preventDefault(); + if (this.options.urlRendering) { + window.location.hash = '#/' + this.model.endpoint + '/' + this.model.get('id'); + } else { + this.view = 'show'; + } + return this.model.fetch({ + silent: true, + complete: __bind(function() { + return this.model.change(); + }, this) + }); + }; + return UberShowView; + })(); +}).call(this); +}, "web-lib/uber_sync": function(exports, require, module) {(function() { + var methodType; + var __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; + }; + methodType = { + create: 'POST', + update: 'PUT', + "delete": 'DELETE', + read: 'GET' + }; + exports.UberSync = function(method, model, options) { + var token; + options.type = methodType[method]; + options.url = _.isString(this.url) ? '/api' + this.url : '/api' + this.url(options.type); + options.data = _.extend({}, options.data); + if (__indexOf.call(_.keys(options.data), "city_id") < 0) { + if ($.cookie('city_filter')) { + _.extend(options.data, { + city_id: $.cookie('city_filter') + }); + } + } else { + delete options.data['city_id']; + } + if (options.type === 'POST' || options.type === 'PUT') { + _.extend(options.data, model.toJSON()); + } + token = $.cookie('token') ? $.cookie('token') : typeof USER !== "undefined" && USER !== null ? USER.get('token') : ""; + _.extend(options.data, { + token: token + }); + if (method === "delete") { + options.contentType = 'application/json'; + options.data = JSON.stringify(options.data); + } + return $.ajax(options); + }; +}).call(this); +}, "web-lib/uber_view": function(exports, require, module) {(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + exports.UberView = (function() { + __extends(UberView, Backbone.View); + function UberView() { + this.processDocumentUpload = __bind(this.processDocumentUpload, this); + UberView.__super__.constructor.apply(this, arguments); + } + UberView.prototype.className = 'view_container'; + UberView.prototype.hashId = function() { + return parseInt(location.hash.split('/')[2]); + }; + UberView.prototype.place = function(content) { + var $target; + $target = this.options.scope ? this.options.scope.find(this.options.selector) : $(this.options.selector); + $target[this.options.method || 'html'](content || this.el); + this.delegateEvents(); + $('#spinner').hide(); + return this; + }; + UberView.prototype.mixin = function(m, args) { + var events, self; + if (args == null) { + args = {}; + } + self = this; + events = m._events; + _.extend(this, m); + if (m.initialize) { + m.initialize(self, args); + } + return _.each(_.keys(events), function(key) { + var event, func, selector, split; + split = key.split(' '); + event = split[0]; + selector = split[1]; + func = events[key]; + return $(self.el).find(selector).live(event, function(e) { + return self[func](e); + }); + }); + }; + UberView.prototype.datePickers = function(format) { + if (format == null) { + format = "%Z-%m-%dT%H:%i:%s%:"; + } + $('.datepicker').AnyTime_noPicker(); + return $('.datepicker').AnyTime_picker({ + 'format': format, + 'formatUtcOffset': '%@' + }); + }; + UberView.prototype.dataTable = function(collection, selector, options, params, cols) { + var defaults; + if (selector == null) { + selector = 'table'; + } + if (options == null) { + options = {}; + } + if (params == null) { + params = {}; + } + if (cols == null) { + cols = []; + } + $(selector).empty(); + if (!cols.length) { + cols = collection.defaultColumns; + } + defaults = { + aoColumns: collection.tableColumns(cols), + bDestroy: true, + bSort: false, + bProcessing: true, + bFilter: false, + bServerSide: true, + bPaginate: true, + bScrollInfinite: true, + bScrollCollapse: true, + sScrollY: '600px', + iDisplayLength: 50, + fnServerData: function(source, data, callback) { + var defaultParams; + defaultParams = { + limit: data[4].value, + offset: data[3].value + }; + return collection.fetch({ + data: _.extend(defaultParams, params), + success: function() { + return callback({ + aaData: collection.toTableRows(cols), + iTotalRecords: collection.meta.count, + iTotalDisplayRecords: collection.meta.count + }); + }, + error: function() { + return new Error({ + message: 'Loading error.' + }); + } + }); + }, + fnRowCallback: function(nRow, aData, iDisplayIndex, iDisplayIndexFull) { + $('[data-tooltip]', nRow).qtip({ + content: { + attr: 'data-tooltip' + }, + style: { + classes: "ui-tooltip-light ui-tooltip-rounded ui-tooltip-shadow" + } + }); + return nRow; + } + }; + return $(this.el).find(selector).dataTable(_.extend(defaults, options)); + }; + UberView.prototype.dataTableLocal = function(collection, selector, options, params, cols) { + var $dataTable, defaults; + if (selector == null) { + selector = 'table'; + } + if (options == null) { + options = {}; + } + if (params == null) { + params = {}; + } + if (cols == null) { + cols = []; + } + $(selector).empty(); + if (!cols.length || cols.length === 0) { + cols = collection.defaultColumns; + } + defaults = { + aaData: collection.toTableRows(cols), + aoColumns: collection.tableColumns(cols), + bDestroy: true, + bSort: false, + bProcessing: true, + bFilter: false, + bScrollInfinite: true, + bScrollCollapse: true, + sScrollY: '600px', + iDisplayLength: -1 + }; + $dataTable = $(this.el).find(selector).dataTable(_.extend(defaults, options)); + _.delay(__bind(function() { + if ($dataTable && $dataTable.length > 0) { + return $dataTable.fnAdjustColumnSizing(); + } + }, this), 1); + return $dataTable; + }; + UberView.prototype.reverseGeocode = function() { + var $el; + return ''; + $el = $(this.el); + return this.requireMaps(function() { + var geocoder; + geocoder = new google.maps.Geocoder(); + return $el.find('[data-point]').each(function() { + var $this, latLng, point; + $this = $(this); + point = JSON.parse($this.attr('data-point')); + latLng = new google.maps.LatLng(point.latitude, point.longitude); + return geocoder.geocode({ + latLng: latLng + }, function(data, status) { + if (status === google.maps.GeocoderStatus.OK) { + return $this.text(data[0].formatted_address); + } + }); + }); + }); + }; + UberView.prototype.userIdsToLinkedNames = function() { + var $el; + $el = $(this.el); + return $el.find('a[data-user-id][data-user-type]').each(function() { + var $this, user, userType; + $this = $(this); + userType = $this.attr('data-user-type') === 'user' ? 'client' : $this.attr('data-user-type'); + user = new app.models[userType]({ + id: $this.attr('data-user-id') + }); + return user.fetch({ + success: function(user) { + return $this.html(app.helpers.linkedName(user)).attr('href', "!/" + user.role + "s/" + user.id); + }, + error: function() { + if ($this.attr('data-user-type') === 'user') { + user = new app.models['driver']({ + id: $this.attr('data-user-id') + }); + return user.fetch({ + success: function(user) { + return $this.html(app.helpers.linkedName(user)).attr('href', "!/driver/" + user.id); + } + }); + } + } + }); + }); + }; + UberView.prototype.selectedCity = function() { + var $selected, city, cityFilter; + cityFilter = $.cookie('city_filter'); + $selected = $("#city_filter option[value=" + cityFilter + "]"); + if (city_filter && $selected.length) { + return city = { + lat: parseFloat($selected.attr('data-lat')), + lng: parseFloat($selected.attr('data-lng')), + timezone: $selected.attr('data-timezone') + }; + } else { + return city = { + lat: 37.775, + lng: -122.45, + timezone: 'Etc/UTC' + }; + } + }; + UberView.prototype.updateModel = function(e, success) { + var $el, attrs, model, self; + e.preventDefault(); + $el = $(e.currentTarget); + self = this; + model = new this.model.__proto__.constructor({ + id: this.model.id + }); + attrs = {}; + $el.find('[name]').each(function() { + var $this; + $this = $(this); + return attrs["" + ($this.attr('name'))] = $this.val(); + }); + self.model.set(attrs); + $el.find('span.error').text(''); + return model.save(attrs, { + complete: function(xhr) { + var response; + response = JSON.parse(xhr.responseText); + switch (xhr.status) { + case 200: + self.model = model; + $el.find('[name]').val(''); + if (success) { + return success(); + } + break; + case 406: + return _.each(response, function(error, field) { + return $el.find("[name=" + field + "]").parent().find('span.error').text(error); + }); + default: + return this.unanticipatedError(response); + } + } + }); + }; + UberView.prototype.autoUpdateModel = function(e) { + var $el, arg, model, self, val; + $el = $(e.currentTarget); + val = $el.val(); + self = this; + if (val !== this.model.get($el.attr('id'))) { + arg = {}; + arg[$el.attr('id')] = $el.is(':checkbox') ? $el.is(':checked') ? 1 : 0 : val; + $('.editable span').empty(); + this.model.set(arg); + model = new this.model.__proto__.constructor({ + id: this.model.id + }); + return model.save(arg, { + complete: function(xhr) { + var key, response, _i, _len, _ref, _results; + response = JSON.parse(xhr.responseText); + switch (xhr.status) { + case 200: + self.flash('success', 'Saved!'); + return $el.blur(); + case 406: + self.flash('error', 'That was not Uber.'); + _ref = _.keys(response); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push($el.parent().find('span').html(response[key])); + } + return _results; + break; + default: + return self.unanticipatedError; + } + } + }); + } + }; + UberView.prototype.unanticipatedError = function(response) { + return self.flash('error', response); + }; + UberView.prototype.flash = function(type, text) { + var $banner; + $banner = $("." + type); + $banner.find('p').text(text).end().css('border', '1px solid #999').animate({ + top: 0 + }, 500); + return setTimeout(function() { + return $banner.animate({ + top: -$banner.outerHeight() + }, 500); + }, 3000); + }; + UberView.prototype.requireMaps = function(callback) { + if (typeof google !== 'undefined' && google.maps) { + return callback(); + } else { + return $.getScript("https://www.google.com/jsapi?key=" + CONFIG.googleJsApiKey, function() { + return google.load('maps', 3, { + callback: callback, + other_params: 'sensor=false&language=en' + }); + }); + } + }; + UberView.prototype.select_drop_down = function(model, key) { + var value; + value = model.get(key); + if (value) { + return $("select[id='" + key + "'] option[value='" + value + "']").attr('selected', 'selected'); + } + }; + UberView.prototype.processDocumentUpload = function(e) { + var $fi, $form, arbData, curDate, data, expDate, expM, expY, expiration, fileElementId, invalid; + e.preventDefault(); + $form = $(e.currentTarget); + $fi = $("input[type=file]", $form); + $(".validationError").removeClass("validationError"); + if (!$fi.val()) { + return $fi.addClass("validationError"); + } else { + fileElementId = $fi.attr('id'); + expY = $("select[name=expiration-year]", $form).val(); + expM = $("select[name=expiration-month]", $form).val(); + invalid = false; + if (expY && expM) { + expDate = new Date(expY, expM, 28); + curDate = new Date(); + if (expDate < curDate) { + invalid = true; + $(".expiration", $form).addClass("validationError"); + } + expiration = "" + expY + "-" + expM + "-28T23:59:59Z"; + } + arbData = {}; + $(".arbitraryField", $form).each(__bind(function(i, e) { + arbData[$(e).attr('name')] = $(e).val(); + if ($(e).val() === "") { + invalid = true; + return $(e).addClass("validationError"); + } + }, this)); + if (!invalid) { + data = { + token: $.cookie('token') || USER.get('token'), + name: $("input[name=fileName]", $form).val(), + meta: escape(JSON.stringify(arbData)), + user_id: $("input[name=driver_id]", $form).val(), + vehicle_id: $("input[name=vehicle_id]", $form).val() + }; + if (expiration) { + data['expiration'] = expiration; + } + $("#spinner").show(); + return $.ajaxFileUpload({ + url: '/api/documents', + secureuri: false, + fileElementId: fileElementId, + data: data, + complete: __bind(function(resp, status) { + var key, _i, _len, _ref, _results; + $("#spinner").hide(); + if (status === "success") { + if (this.model) { + this.model.refetch(); + } else { + USER.refetch(); + } + } + if (status === "error") { + _ref = _.keys(resp); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + _results.push($("*[name=" + key + "]", $form).addClass("validationError")); + } + return _results; + } + }, this) + }); + } + } + }; + return UberView; + })(); +}).call(this); +}, "web-lib/views/footer": function(exports, require, module) {(function() { + var footerTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + footerTemplate = require('web-lib/templates/footer'); + exports.SharedFooterView = (function() { + __extends(SharedFooterView, Backbone.View); + function SharedFooterView() { + SharedFooterView.__super__.constructor.apply(this, arguments); + } + SharedFooterView.prototype.id = 'footer_view'; + SharedFooterView.prototype.events = { + 'click .language': 'intl_set_cookie_locale' + }; + SharedFooterView.prototype.render = function() { + $(this.el).html(footerTemplate()); + this.delegateEvents(); + return this; + }; + SharedFooterView.prototype.intl_set_cookie_locale = function(e) { + var _ref; + i18n.setLocale(e != null ? (_ref = e.srcElement) != null ? _ref.id : void 0 : void 0); + return location.reload(); + }; + return SharedFooterView; + })(); +}).call(this); +}}); diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/embed-tokens.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/embed-tokens.js new file mode 100755 index 0000000..61307ee --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/embed-tokens.js @@ -0,0 +1,15 @@ +#! /usr/bin/env node + +global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); +var fs = require("fs"); +var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js + jsp = uglify.parser, + pro = uglify.uglify; + +var code = fs.readFileSync("embed-tokens.js", "utf8").replace(/^#.*$/mg, ""); +var ast = jsp.parse(code, null, true); + +// trololo +function fooBar() {} + +console.log(sys.inspect(ast, null, null)); diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto.js new file mode 100644 index 0000000..945960c --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto.js @@ -0,0 +1,26 @@ +function unique(arqw) { + var a = [], i, j + outer: for (i = 0; i < arqw.length; i++) { + for (j = 0; j < a.length; j++) { + if (a[j] == arqw[i]) { + continue outer + } + } + a[a.length] = arqw[i] + } + return a +} + + +function unique(arqw) { + var crap = [], i, j + outer: for (i = 0; i < arqw.length; i++) { + for (j = 0; j < crap.length; j++) { + if (crap[j] == arqw[i]) { + continue outer + } + } + crap[crap.length] = arqw[i] + } + return crap +} diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto2.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto2.js new file mode 100644 index 0000000..d13b2bc --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/goto2.js @@ -0,0 +1,8 @@ +function q(qooo) { + var a; + foo: for(;;) { + a++; + if (something) break foo; + return qooo; + } +} diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/hoist.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/hoist.js new file mode 100644 index 0000000..4bf2b94 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/hoist.js @@ -0,0 +1,33 @@ +function foo(arg1, arg2, arg3, arg4, arg5, arg6) { + var a = 5; + { + var d = 10, mak = 20, buz = 30; + var q = buz * 2; + } + if (moo) { + var a, b, c; + } + for (var arg1 = 0, d = 20; arg1 < 10; ++arg1) + console.log(arg3); + for (var i in mak) {} + for (j in d) {} + var d; + + function test() { + + }; + + //test(); + + (function moo(first, second){ + console.log(first); + })(1); + + (function moo(first, second){ + console.log(moo()); + })(1); +} + + +var foo; +var bar; diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument.js new file mode 100644 index 0000000..c6a9d79 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument.js @@ -0,0 +1,97 @@ +// sample on how to use the parser and walker API to instrument some code + +var jsp = require("uglify-js").parser; +var pro = require("uglify-js").uglify; + +function instrument(code) { + var ast = jsp.parse(code, false, true); // true for the third arg specifies that we want + // to have start/end tokens embedded in the + // statements + var w = pro.ast_walker(); + + // we're gonna need this to push elements that we're currently looking at, to avoid + // endless recursion. + var analyzing = []; + function do_stat() { + var ret; + if (this[0].start && analyzing.indexOf(this) < 0) { + // without the `analyzing' hack, w.walk(this) would re-enter here leading + // to infinite recursion + analyzing.push(this); + ret = [ "splice", // XXX: "block" is safer + [ [ "stat", + [ "call", [ "name", "trace" ], + [ [ "string", this[0].toString() ], + [ "num", this[0].start.line ], + [ "num", this[0].start.col ], + [ "num", this[0].end.line ], + [ "num", this[0].end.col ]]]], + w.walk(this) ]]; + analyzing.pop(this); + } + return ret; + }; + var new_ast = w.with_walkers({ + "stat" : do_stat, + "label" : do_stat, + "break" : do_stat, + "continue" : do_stat, + "debugger" : do_stat, + "var" : do_stat, + "const" : do_stat, + "return" : do_stat, + "throw" : do_stat, + "try" : do_stat, + "defun" : do_stat, + "if" : do_stat, + "while" : do_stat, + "do" : do_stat, + "for" : do_stat, + "for-in" : do_stat, + "switch" : do_stat, + "with" : do_stat + }, function(){ + return w.walk(ast); + }); + return pro.gen_code(new_ast, { beautify: true }); +} + + + + +////// test code follows. + +var code = instrument(test.toString()); +console.log(code); + +function test() { + // simple stats + a = 5; + c += a + b; + "foo"; + + // var + var foo = 5; + const bar = 6, baz = 7; + + // switch block. note we can't track case lines the same way. + switch ("foo") { + case "foo": + return 1; + case "bar": + return 2; + } + + // for/for in + for (var i = 0; i < 5; ++i) { + console.log("Hello " + i); + } + for (var i in [ 1, 2, 3]) { + console.log(i); + } + + // note however that the following is broken. I guess we + // should add the block brackets in this case... + for (var i = 0; i < 5; ++i) + console.log("foo"); +} diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument2.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument2.js new file mode 100644 index 0000000..6aee5f3 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/instrument2.js @@ -0,0 +1,138 @@ +// sample on how to use the parser and walker API to instrument some code + +var jsp = require("uglify-js").parser; +var pro = require("uglify-js").uglify; + +function instrument(code) { + var ast = jsp.parse(code, false, true); // true for the third arg specifies that we want + // to have start/end tokens embedded in the + // statements + var w = pro.ast_walker(); + + function trace (line, comment) { + var code = pro.gen_code(line, { beautify: true }); + var data = line[0] + + var args = [] + if (!comment) comment = "" + if (typeof data === "object") { + code = code.split(/\n/).shift() + args = [ [ "string", data.toString() ], + [ "string", code ], + [ "num", data.start.line ], + [ "num", data.start.col ], + [ "num", data.end.line ], + [ "num", data.end.col ]] + } else { + args = [ [ "string", data ], + [ "string", code ]] + + } + return [ "call", [ "name", "trace" ], args ]; + } + + // we're gonna need this to push elements that we're currently looking at, to avoid + // endless recursion. + var analyzing = []; + function do_stat() { + var ret; + if (this[0].start && analyzing.indexOf(this) < 0) { + // without the `analyzing' hack, w.walk(this) would re-enter here leading + // to infinite recursion + analyzing.push(this); + ret = [ "splice", + [ [ "stat", trace(this) ], + w.walk(this) ]]; + analyzing.pop(this); + } + return ret; + } + + function do_cond(c, t, f) { + return [ this[0], w.walk(c), + ["seq", trace(t), w.walk(t) ], + ["seq", trace(f), w.walk(f) ]]; + } + + function do_binary(c, l, r) { + if (c !== "&&" && c !== "||") { + return [this[0], c, w.walk(l), w.walk(r)]; + } + return [ this[0], c, + ["seq", trace(l), w.walk(l) ], + ["seq", trace(r), w.walk(r) ]]; + } + + var new_ast = w.with_walkers({ + "stat" : do_stat, + "label" : do_stat, + "break" : do_stat, + "continue" : do_stat, + "debugger" : do_stat, + "var" : do_stat, + "const" : do_stat, + "return" : do_stat, + "throw" : do_stat, + "try" : do_stat, + "defun" : do_stat, + "if" : do_stat, + "while" : do_stat, + "do" : do_stat, + "for" : do_stat, + "for-in" : do_stat, + "switch" : do_stat, + "with" : do_stat, + "conditional" : do_cond, + "binary" : do_binary + }, function(){ + return w.walk(ast); + }); + return pro.gen_code(new_ast, { beautify: true }); +} + + +////// test code follows. + +var code = instrument(test.toString()); +console.log(code); + +function test() { + // simple stats + a = 5; + c += a + b; + "foo"; + + // var + var foo = 5; + const bar = 6, baz = 7; + + // switch block. note we can't track case lines the same way. + switch ("foo") { + case "foo": + return 1; + case "bar": + return 2; + } + + // for/for in + for (var i = 0; i < 5; ++i) { + console.log("Hello " + i); + } + for (var i in [ 1, 2, 3]) { + console.log(i); + } + + for (var i = 0; i < 5; ++i) + console.log("foo"); + + for (var i = 0; i < 5; ++i) { + console.log("foo"); + } + + var k = plurp() ? 1 : 0; + var x = a ? doX(y) && goZoo("zoo") + : b ? blerg({ x: y }) + : null; + + var x = X || Y; +} diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/liftvars.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/liftvars.js new file mode 100644 index 0000000..2f4b7fe --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/liftvars.js @@ -0,0 +1,8 @@ +var UNUSED_VAR1 = 19; + +function main() { + var unused_var2 = 20; + alert(100); +} + +main(); diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/test.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/test.js new file mode 100755 index 0000000..f295fba --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/test.js @@ -0,0 +1,30 @@ +#! /usr/bin/env node + +global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); +var fs = require("fs"); +var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js + jsp = uglify.parser, + pro = uglify.uglify; + +var code = fs.readFileSync("hoist.js", "utf8"); +var ast = jsp.parse(code); + +ast = pro.ast_lift_variables(ast); + +var w = pro.ast_walker(); +ast = w.with_walkers({ + "function": function() { + var node = w.dive(this); // walk depth first + console.log(pro.gen_code(node, { beautify: true })); + return node; + }, + "name": function(name) { + return [ this[0], "X" ]; + } +}, function(){ + return w.walk(ast); +}); + +console.log(pro.gen_code(ast, { + beautify: true +})); diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs.js new file mode 100644 index 0000000..0d5b7e0 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/tmp/uglify-hangs.js @@ -0,0 +1,3930 @@ +/** + * @fileoverview + * + * JsWorld + * + *

      Javascript library for localised formatting and parsing of: + *

        + *
      • Numbers + *
      • Dates and times + *
      • Currency + *
      + * + *

      The library classes are configured with standard POSIX locale definitions + * derived from Unicode's Common Locale Data Repository (CLDR). + * + *

      Website: JsWorld + * + * @author Vladimir Dzhuvinov + * @version 2.5 (2011-12-23) + */ + + + +/** + * @namespace Namespace container for the JsWorld library objects. + */ +jsworld = {}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 date/time + * string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the + * current date/time will be used. + * @param {Boolean} [withTZ] Include timezone offset, default false. + * + * @returns {String} The date/time formatted as YYYY-MM-DD HH:MM:SS. + */ +jsworld.formatIsoDateTime = function(d, withTZ) { + + if (typeof d === "undefined") + d = new Date(); // now + + if (typeof withTZ === "undefined") + withTZ = false; + + var s = jsworld.formatIsoDate(d) + " " + jsworld.formatIsoTime(d); + + if (withTZ) { + + var diff = d.getHours() - d.getUTCHours(); + var hourDiff = Math.abs(diff); + + var minuteUTC = d.getUTCMinutes(); + var minute = d.getMinutes(); + + if (minute != minuteUTC && minuteUTC < 30 && diff < 0) + hourDiff--; + + if (minute != minuteUTC && minuteUTC > 30 && diff > 0) + hourDiff--; + + var minuteDiff; + if (minute != minuteUTC) + minuteDiff = ":30"; + else + minuteDiff = ":00"; + + var timezone; + if (hourDiff < 10) + timezone = "0" + hourDiff + minuteDiff; + + else + timezone = "" + hourDiff + minuteDiff; + + if (diff < 0) + timezone = "-" + timezone; + + else + timezone = "+" + timezone; + + s = s + timezone; + } + + return s; +}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 date string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the current + * date will be used. + * + * @returns {String} The date formatted as YYYY-MM-DD. + */ +jsworld.formatIsoDate = function(d) { + + if (typeof d === "undefined") + d = new Date(); // now + + var year = d.getFullYear(); + var month = d.getMonth() + 1; + var day = d.getDate(); + + return year + "-" + jsworld._zeroPad(month, 2) + "-" + jsworld._zeroPad(day, 2); +}; + + +/** + * @function + * + * @description Formats a JavaScript Date object as an ISO-8601 time string. + * + * @param {Date} [d] A valid JavaScript Date object. If undefined the current + * time will be used. + * + * @returns {String} The time formatted as HH:MM:SS. + */ +jsworld.formatIsoTime = function(d) { + + if (typeof d === "undefined") + d = new Date(); // now + + var hour = d.getHours(); + var minute = d.getMinutes(); + var second = d.getSeconds(); + + return jsworld._zeroPad(hour, 2) + ":" + jsworld._zeroPad(minute, 2) + ":" + jsworld._zeroPad(second, 2); +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted date/time string to a JavaScript + * Date object. + * + * @param {String} isoDateTimeVal An ISO-8601 formatted date/time string. + * + *

      Accepted formats: + * + *

        + *
      • YYYY-MM-DD HH:MM:SS + *
      • YYYYMMDD HHMMSS + *
      • YYYY-MM-DD HHMMSS + *
      • YYYYMMDD HH:MM:SS + *
      + * + * @returns {Date} The corresponding Date object. + * + * @throws Error on a badly formatted date/time string or on a invalid date. + */ +jsworld.parseIsoDateTime = function(isoDateTimeVal) { + + if (typeof isoDateTimeVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "YYYY-MM-DD HH:MM:SS" format + var matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); + + // If unsuccessful, try to match "YYYYMMDD HHMMSS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/); + + // ... try to match "YYYY-MM-DD HHMMSS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/); + + // ... try to match "YYYYMMDD HH:MM:SS" format + if (matches === null) + matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date/time string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var year = parseInt(matches[1], 10); + var month = parseInt(matches[2], 10); + var day = parseInt(matches[3], 10); + + var hour = parseInt(matches[4], 10); + var mins = parseInt(matches[5], 10); + var secs = parseInt(matches[6], 10); + + // Simple value range check, leap years not checked + // Note: the originial ISO time spec for leap hours (24:00:00) and seconds (00:00:60) is not supported + if (month < 1 || month > 12 || + day < 1 || day > 31 || + hour < 0 || hour > 23 || + mins < 0 || mins > 59 || + secs < 0 || secs > 59 ) + + throw "Error: Invalid ISO-8601 date/time value"; + + var d = new Date(year, month - 1, day, hour, mins, secs); + + // Check if the input date was valid + // (JS Date does automatic forward correction) + if (d.getDate() != day || d.getMonth() +1 != month) + throw "Error: Invalid date"; + + return d; +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted date string to a JavaScript + * Date object. + * + * @param {String} isoDateVal An ISO-8601 formatted date string. + * + *

      Accepted formats: + * + *

        + *
      • YYYY-MM-DD + *
      • YYYYMMDD + *
      + * + * @returns {Date} The corresponding Date object. + * + * @throws Error on a badly formatted date string or on a invalid date. + */ +jsworld.parseIsoDate = function(isoDateVal) { + + if (typeof isoDateVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "YYYY-MM-DD" format + var matches = isoDateVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/); + + // If unsuccessful, try to match "YYYYMMDD" format + if (matches === null) + matches = isoDateVal.match(/^(\d\d\d\d)(\d\d)(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var year = parseInt(matches[1], 10); + var month = parseInt(matches[2], 10); + var day = parseInt(matches[3], 10); + + // Simple value range check, leap years not checked + if (month < 1 || month > 12 || + day < 1 || day > 31 ) + + throw "Error: Invalid ISO-8601 date value"; + + var d = new Date(year, month - 1, day); + + // Check if the input date was valid + // (JS Date does automatic forward correction) + if (d.getDate() != day || d.getMonth() +1 != month) + throw "Error: Invalid date"; + + return d; +}; + + +/** + * @function + * + * @description Parses an ISO-8601 formatted time string to a JavaScript + * Date object. + * + * @param {String} isoTimeVal An ISO-8601 formatted time string. + * + *

      Accepted formats: + * + *

        + *
      • HH:MM:SS + *
      • HHMMSS + *
      + * + * @returns {Date} The corresponding Date object, with year, month and day set + * to zero. + * + * @throws Error on a badly formatted time string. + */ +jsworld.parseIsoTime = function(isoTimeVal) { + + if (typeof isoTimeVal != "string") + throw "Error: The parameter must be a string"; + + // First, try to match "HH:MM:SS" format + var matches = isoTimeVal.match(/^(\d\d):(\d\d):(\d\d)/); + + // If unsuccessful, try to match "HHMMSS" format + if (matches === null) + matches = isoTimeVal.match(/^(\d\d)(\d\d)(\d\d)/); + + // Report bad date/time string + if (matches === null) + throw "Error: Invalid ISO-8601 date/time string"; + + // Force base 10 parse int as some values may have leading zeros! + // (to avoid implicit octal base conversion) + var hour = parseInt(matches[1], 10); + var mins = parseInt(matches[2], 10); + var secs = parseInt(matches[3], 10); + + // Simple value range check, leap years not checked + if (hour < 0 || hour > 23 || + mins < 0 || mins > 59 || + secs < 0 || secs > 59 ) + + throw "Error: Invalid ISO-8601 time value"; + + return new Date(0, 0, 0, hour, mins, secs); +}; + + +/** + * @private + * + * @description Trims leading and trailing whitespace from a string. + * + *

      Used non-regexp the method from http://blog.stevenlevithan.com/archives/faster-trim-javascript + * + * @param {String} str The string to trim. + * + * @returns {String} The trimmed string. + */ +jsworld._trim = function(str) { + + var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'; + + for (var i = 0; i < str.length; i++) { + + if (whitespace.indexOf(str.charAt(i)) === -1) { + str = str.substring(i); + break; + } + } + + for (i = str.length - 1; i >= 0; i--) { + if (whitespace.indexOf(str.charAt(i)) === -1) { + str = str.substring(0, i + 1); + break; + } + } + + return whitespace.indexOf(str.charAt(0)) === -1 ? str : ''; +}; + + + +/** + * @private + * + * @description Returns true if the argument represents a decimal number. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents a decimal number, + * otherwise false. + */ +jsworld._isNumber = function(arg) { + + if (typeof arg == "number") + return true; + + if (typeof arg != "string") + return false; + + // ensure string + var s = arg + ""; + + return (/^-?(\d+|\d*\.\d+)$/).test(s); +}; + + +/** + * @private + * + * @description Returns true if the argument represents a decimal integer. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents an integer, otherwise + * false. + */ +jsworld._isInteger = function(arg) { + + if (typeof arg != "number" && typeof arg != "string") + return false; + + // convert to string + var s = arg + ""; + + return (/^-?\d+$/).test(s); +}; + + +/** + * @private + * + * @description Returns true if the argument represents a decimal float. + * + * @param {Number|String} arg The argument to test. + * + * @returns {Boolean} true if the argument represents a float, otherwise false. + */ +jsworld._isFloat = function(arg) { + + if (typeof arg != "number" && typeof arg != "string") + return false; + + // convert to string + var s = arg + ""; + + return (/^-?\.\d+?$/).test(s); +}; + + +/** + * @private + * + * @description Checks if the specified formatting option is contained + * within the options string. + * + * @param {String} option The option to search for. + * @param {String} optionsString The options string. + * + * @returns {Boolean} true if the flag is found, else false + */ +jsworld._hasOption = function(option, optionsString) { + + if (typeof option != "string" || typeof optionsString != "string") + return false; + + if (optionsString.indexOf(option) != -1) + return true; + else + return false; +}; + + +/** + * @private + * + * @description String replacement function. + * + * @param {String} s The string to work on. + * @param {String} target The string to search for. + * @param {String} replacement The replacement. + * + * @returns {String} The new string. + */ +jsworld._stringReplaceAll = function(s, target, replacement) { + + var out; + + if (target.length == 1 && replacement.length == 1) { + // simple char/char case somewhat faster + out = ""; + + for (var i = 0; i < s.length; i++) { + + if (s.charAt(i) == target.charAt(0)) + out = out + replacement.charAt(0); + else + out = out + s.charAt(i); + } + + return out; + } + else { + // longer target and replacement strings + out = s; + + var index = out.indexOf(target); + + while (index != -1) { + + out = out.replace(target, replacement); + + index = out.indexOf(target); + } + + return out; + } +}; + + +/** + * @private + * + * @description Tests if a string starts with the specified substring. + * + * @param {String} testedString The string to test. + * @param {String} sub The string to match. + * + * @returns {Boolean} true if the test succeeds. + */ +jsworld._stringStartsWith = function (testedString, sub) { + + if (testedString.length < sub.length) + return false; + + for (var i = 0; i < sub.length; i++) { + if (testedString.charAt(i) != sub.charAt(i)) + return false; + } + + return true; +}; + + +/** + * @private + * + * @description Gets the requested precision from an options string. + * + *

      Example: ".3" returns 3 decimal places precision. + * + * @param {String} optionsString The options string. + * + * @returns {integer Number} The requested precision, -1 if not specified. + */ +jsworld._getPrecision = function (optionsString) { + + if (typeof optionsString != "string") + return -1; + + var m = optionsString.match(/\.(\d)/); + if (m) + return parseInt(m[1], 10); + else + return -1; +}; + + +/** + * @private + * + * @description Takes a decimal numeric amount (optionally as string) and + * returns its integer and fractional parts packed into an object. + * + * @param {Number|String} amount The amount, e.g. "123.45" or "-56.78" + * + * @returns {object} Parsed amount object with properties: + * {String} integer : the integer part + * {String} fraction : the fraction part + */ +jsworld._splitNumber = function (amount) { + + if (typeof amount == "number") + amount = amount + ""; + + var obj = {}; + + // remove negative sign + if (amount.charAt(0) == "-") + amount = amount.substring(1); + + // split amount into integer and decimal parts + var amountParts = amount.split("."); + if (!amountParts[1]) + amountParts[1] = ""; // we need "" instead of null + + obj.integer = amountParts[0]; + obj.fraction = amountParts[1]; + + return obj; +}; + + +/** + * @private + * + * @description Formats the integer part using the specified grouping + * and thousands separator. + * + * @param {String} intPart The integer part of the amount, as string. + * @param {String} grouping The grouping definition. + * @param {String} thousandsSep The thousands separator. + * + * @returns {String} The formatted integer part. + */ +jsworld._formatIntegerPart = function (intPart, grouping, thousandsSep) { + + // empty separator string? no grouping? + // -> return immediately with no formatting! + if (thousandsSep == "" || grouping == "-1") + return intPart; + + // turn the semicolon-separated string of integers into an array + var groupSizes = grouping.split(";"); + + // the formatted output string + var out = ""; + + // the intPart string position to process next, + // start at string end, e.g. "10000000 0) { + + // get next group size (if any, otherwise keep last) + if (groupSizes.length > 0) + size = parseInt(groupSizes.shift(), 10); + + // int parse error? + if (isNaN(size)) + throw "Error: Invalid grouping"; + + // size is -1? -> no more grouping, so just copy string remainder + if (size == -1) { + out = intPart.substring(0, pos) + out; + break; + } + + pos -= size; // move to next sep. char. position + + // position underrun? -> just copy string remainder + if (pos < 1) { + out = intPart.substring(0, pos + size) + out; + break; + } + + // extract group and apply sep. char. + out = thousandsSep + intPart.substring(pos, pos + size) + out; + } + + return out; +}; + + +/** + * @private + * + * @description Formats the fractional part to the specified decimal + * precision. + * + * @param {String} fracPart The fractional part of the amount + * @param {integer Number} precision The desired decimal precision + * + * @returns {String} The formatted fractional part. + */ +jsworld._formatFractionPart = function (fracPart, precision) { + + // append zeroes up to precision if necessary + for (var i=0; fracPart.length < precision; i++) + fracPart = fracPart + "0"; + + return fracPart; +}; + + +/** + * @private + * + * @desription Converts a number to string and pad it with leading zeroes if the + * string is shorter than length. + * + * @param {integer Number} number The number value subjected to selective padding. + * @param {integer Number} length If the number has fewer digits than this length + * apply padding. + * + * @returns {String} The formatted string. + */ +jsworld._zeroPad = function(number, length) { + + // ensure string + var s = number + ""; + + while (s.length < length) + s = "0" + s; + + return s; +}; + + +/** + * @private + * @description Converts a number to string and pads it with leading spaces if + * the string is shorter than length. + * + * @param {integer Number} number The number value subjected to selective padding. + * @param {integer Number} length If the number has fewer digits than this length + * apply padding. + * + * @returns {String} The formatted string. + */ +jsworld._spacePad = function(number, length) { + + // ensure string + var s = number + ""; + + while (s.length < length) + s = " " + s; + + return s; +}; + + + +/** + * @class + * Represents a POSIX-style locale with its numeric, monetary and date/time + * properties. Also provides a set of locale helper methods. + * + *

      The locale properties follow the POSIX standards: + * + *

      + * + * @public + * @constructor + * @description Creates a new locale object (POSIX-style) with the specified + * properties. + * + * @param {object} properties An object containing the raw locale properties: + * + * @param {String} properties.decimal_point + * + * A string containing the symbol that shall be used as the decimal + * delimiter (radix character) in numeric, non-monetary formatted + * quantities. This property cannot be omitted and cannot be set to the + * empty string. + * + * + * @param {String} properties.thousands_sep + * + * A string containing the symbol that shall be used as a separator for + * groups of digits to the left of the decimal delimiter in numeric, + * non-monetary formatted monetary quantities. + * + * + * @param {String} properties.grouping + * + * Defines the size of each group of digits in formatted non-monetary + * quantities. The operand is a sequence of integers separated by + * semicolons. Each integer specifies the number of digits in each group, + * with the initial integer defining the size of the group immediately + * preceding the decimal delimiter, and the following integers defining + * the preceding groups. If the last integer is not -1, then the size of + * the previous group (if any) shall be repeatedly used for the + * remainder of the digits. If the last integer is -1, then no further + * grouping shall be performed. + * + * + * @param {String} properties.int_curr_symbol + * + * The first three letters signify the ISO-4217 currency code, + * the fourth letter is the international symbol separation character + * (normally a space). + * + * + * @param {String} properties.currency_symbol + * + * The local shorthand currency symbol, e.g. "$" for the en_US locale + * + * + * @param {String} properties.mon_decimal_point + * + * The symbol to be used as the decimal delimiter (radix character) + * + * + * @param {String} properties.mon_thousands_sep + * + * The symbol to be used as a separator for groups of digits to the + * left of the decimal delimiter. + * + * + * @param {String} properties.mon_grouping + * + * A string that defines the size of each group of digits. The + * operand is a sequence of integers separated by semicolons (";"). + * Each integer specifies the number of digits in each group, with the + * initial integer defining the size of the group preceding the + * decimal delimiter, and the following integers defining the + * preceding groups. If the last integer is not -1, then the size of + * the previous group (if any) must be repeatedly used for the + * remainder of the digits. If the last integer is -1, then no + * further grouping is to be performed. + * + * + * @param {String} properties.positive_sign + * + * The string to indicate a non-negative monetary amount. + * + * + * @param {String} properties.negative_sign + * + * The string to indicate a negative monetary amount. + * + * + * @param {integer Number} properties.frac_digits + * + * An integer representing the number of fractional digits (those to + * the right of the decimal delimiter) to be written in a formatted + * monetary quantity using currency_symbol. + * + * + * @param {integer Number} properties.int_frac_digits + * + * An integer representing the number of fractional digits (those to + * the right of the decimal delimiter) to be written in a formatted + * monetary quantity using int_curr_symbol. + * + * + * @param {integer Number} properties.p_cs_precedes + * + * An integer set to 1 if the currency_symbol precedes the value for a + * monetary quantity with a non-negative value, and set to 0 if the + * symbol succeeds the value. + * + * + * @param {integer Number} properties.n_cs_precedes + * + * An integer set to 1 if the currency_symbol precedes the value for a + * monetary quantity with a negative value, and set to 0 if the symbol + * succeeds the value. + * + * + * @param {integer Number} properties.p_sep_by_space + * + * Set to a value indicating the separation of the currency_symbol, + * the sign string, and the value for a non-negative formatted monetary + * quantity: + * + *

      0 No space separates the currency symbol and value.

      + * + *

      1 If the currency symbol and sign string are adjacent, a space + * separates them from the value; otherwise, a space separates + * the currency symbol from the value.

      + * + *

      2 If the currency symbol and sign string are adjacent, a space + * separates them; otherwise, a space separates the sign string + * from the value.

      + * + * + * @param {integer Number} properties.n_sep_by_space + * + * Set to a value indicating the separation of the currency_symbol, + * the sign string, and the value for a negative formatted monetary + * quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.p_sign_posn + * + * An integer set to a value indicating the positioning of the + * positive_sign for a monetary quantity with a non-negative value: + * + *

      0 Parentheses enclose the quantity and the currency_symbol.

      + * + *

      1 The sign string precedes the quantity and the currency_symbol.

      + * + *

      2 The sign string succeeds the quantity and the currency_symbol.

      + * + *

      3 The sign string precedes the currency_symbol.

      + * + *

      4 The sign string succeeds the currency_symbol.

      + * + * + * @param {integer Number} properties.n_sign_posn + * + * An integer set to a value indicating the positioning of the + * negative_sign for a negative formatted monetary quantity. Rules same + * as for p_sign_posn. + * + * + * @param {integer Number} properties.int_p_cs_precedes + * + * An integer set to 1 if the int_curr_symbol precedes the value for a + * monetary quantity with a non-negative value, and set to 0 if the + * symbol succeeds the value. + * + * + * @param {integer Number} properties.int_n_cs_precedes + * + * An integer set to 1 if the int_curr_symbol precedes the value for a + * monetary quantity with a negative value, and set to 0 if the symbol + * succeeds the value. + * + * + * @param {integer Number} properties.int_p_sep_by_space + * + * Set to a value indicating the separation of the int_curr_symbol, + * the sign string, and the value for a non-negative internationally + * formatted monetary quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.int_n_sep_by_space + * + * Set to a value indicating the separation of the int_curr_symbol, + * the sign string, and the value for a negative internationally + * formatted monetary quantity. Rules same as for p_sep_by_space. + * + * + * @param {integer Number} properties.int_p_sign_posn + * + * An integer set to a value indicating the positioning of the + * positive_sign for a positive monetary quantity formatted with the + * international format. Rules same as for p_sign_posn. + * + * + * @param {integer Number} properties.int_n_sign_posn + * + * An integer set to a value indicating the positioning of the + * negative_sign for a negative monetary quantity formatted with the + * international format. Rules same as for p_sign_posn. + * + * + * @param {String[] | String} properties.abday + * + * The abbreviated weekday names, corresponding to the %a conversion + * specification. The property must be either an array of 7 strings or + * a string consisting of 7 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the abbreviated name + * of the day corresponding to Sunday, the second the abbreviated name + * of the day corresponding to Monday, and so on. + * + * + * @param {String[] | String} properties.day + * + * The full weekday names, corresponding to the %A conversion + * specification. The property must be either an array of 7 strings or + * a string consisting of 7 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the full name of the + * day corresponding to Sunday, the second the full name of the day + * corresponding to Monday, and so on. + * + * + * @param {String[] | String} properties.abmon + * + * The abbreviated month names, corresponding to the %b conversion + * specification. The property must be either an array of 12 strings or + * a string consisting of 12 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the abbreviated name + * of the first month of the year (January), the second the abbreviated + * name of the second month, and so on. + * + * + * @param {String[] | String} properties.mon + * + * The full month names, corresponding to the %B conversion + * specification. The property must be either an array of 12 strings or + * a string consisting of 12 semicolon-separated substrings, each + * surrounded by double-quotes. The first must be the full name of the + * first month of the year (January), the second the full name of the second + * month, and so on. + * + * + * @param {String} properties.d_fmt + * + * The appropriate date representation. The string may contain any + * combination of characters and conversion specifications (%). + * + * + * @param {String} properties.t_fmt + * + * The appropriate time representation. The string may contain any + * combination of characters and conversion specifications (%). + * + * + * @param {String} properties.d_t_fmt + * + * The appropriate date and time representation. The string may contain + * any combination of characters and conversion specifications (%). + * + * + * @param {String[] | String} properties.am_pm + * + * The appropriate representation of the ante-meridiem and post-meridiem + * strings, corresponding to the %p conversion specification. The property + * must be either an array of 2 strings or a string consisting of 2 + * semicolon-separated substrings, each surrounded by double-quotes. + * The first string must represent the ante-meridiem designation, the + * last string the post-meridiem designation. + * + * + * @throws @throws Error on a undefined or invalid locale property. + */ +jsworld.Locale = function(properties) { + + + /** + * @private + * + * @description Identifies the class for internal library purposes. + */ + this._className = "jsworld.Locale"; + + + /** + * @private + * + * @description Parses a day or month name definition list, which + * could be a ready JS array, e.g. ["Mon", "Tue", "Wed"...] or + * it could be a string formatted according to the classic POSIX + * definition e.g. "Mon";"Tue";"Wed";... + * + * @param {String[] | String} namesAn array or string defining + * the week/month names. + * @param {integer Number} expectedItems The number of expected list + * items, e.g. 7 for weekdays, 12 for months. + * + * @returns {String[]} The parsed (and checked) items. + * + * @throws Error on missing definition, unexpected item count or + * missing double-quotes. + */ + this._parseList = function(names, expectedItems) { + + var array = []; + + if (names == null) { + throw "Names not defined"; + } + else if (typeof names == "object") { + // we got a ready array + array = names; + } + else if (typeof names == "string") { + // we got the names in the classic POSIX form, do parse + array = names.split(";", expectedItems); + + for (var i = 0; i < array.length; i++) { + // check for and strip double quotes + if (array[i][0] == "\"" && array[i][array[i].length - 1] == "\"") + array[i] = array[i].slice(1, -1); + else + throw "Missing double quotes"; + } + } + else { + throw "Names must be an array or a string"; + } + + if (array.length != expectedItems) + throw "Expected " + expectedItems + " items, got " + array.length; + + return array; + }; + + + /** + * @private + * + * @description Validates a date/time format string, such as "H:%M:%S". + * Checks that the argument is of type "string" and is not empty. + * + * @param {String} formatString The format string. + * + * @returns {String} The validated string. + * + * @throws Error on null or empty string. + */ + this._validateFormatString = function(formatString) { + + if (typeof formatString == "string" && formatString.length > 0) + return formatString; + else + throw "Empty or no string"; + }; + + + // LC_NUMERIC + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing locale properties"; + + + if (typeof properties.decimal_point != "string") + throw "Error: Invalid/missing decimal_point property"; + + this.decimal_point = properties.decimal_point; + + + if (typeof properties.thousands_sep != "string") + throw "Error: Invalid/missing thousands_sep property"; + + this.thousands_sep = properties.thousands_sep; + + + if (typeof properties.grouping != "string") + throw "Error: Invalid/missing grouping property"; + + this.grouping = properties.grouping; + + + // LC_MONETARY + + if (typeof properties.int_curr_symbol != "string") + throw "Error: Invalid/missing int_curr_symbol property"; + + if (! /[A-Za-z]{3}.?/.test(properties.int_curr_symbol)) + throw "Error: Invalid int_curr_symbol property"; + + this.int_curr_symbol = properties.int_curr_symbol; + + + if (typeof properties.currency_symbol != "string") + throw "Error: Invalid/missing currency_symbol property"; + + this.currency_symbol = properties.currency_symbol; + + + if (typeof properties.frac_digits != "number" && properties.frac_digits < 0) + throw "Error: Invalid/missing frac_digits property"; + + this.frac_digits = properties.frac_digits; + + + // may be empty string/null for currencies with no fractional part + if (properties.mon_decimal_point === null || properties.mon_decimal_point == "") { + + if (this.frac_digits > 0) + throw "Error: Undefined mon_decimal_point property"; + else + properties.mon_decimal_point = ""; + } + + if (typeof properties.mon_decimal_point != "string") + throw "Error: Invalid/missing mon_decimal_point property"; + + this.mon_decimal_point = properties.mon_decimal_point; + + + if (typeof properties.mon_thousands_sep != "string") + throw "Error: Invalid/missing mon_thousands_sep property"; + + this.mon_thousands_sep = properties.mon_thousands_sep; + + + if (typeof properties.mon_grouping != "string") + throw "Error: Invalid/missing mon_grouping property"; + + this.mon_grouping = properties.mon_grouping; + + + if (typeof properties.positive_sign != "string") + throw "Error: Invalid/missing positive_sign property"; + + this.positive_sign = properties.positive_sign; + + + if (typeof properties.negative_sign != "string") + throw "Error: Invalid/missing negative_sign property"; + + this.negative_sign = properties.negative_sign; + + + + if (properties.p_cs_precedes !== 0 && properties.p_cs_precedes !== 1) + throw "Error: Invalid/missing p_cs_precedes property, must be 0 or 1"; + + this.p_cs_precedes = properties.p_cs_precedes; + + + if (properties.n_cs_precedes !== 0 && properties.n_cs_precedes !== 1) + throw "Error: Invalid/missing n_cs_precedes, must be 0 or 1"; + + this.n_cs_precedes = properties.n_cs_precedes; + + + if (properties.p_sep_by_space !== 0 && + properties.p_sep_by_space !== 1 && + properties.p_sep_by_space !== 2) + throw "Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2"; + + this.p_sep_by_space = properties.p_sep_by_space; + + + if (properties.n_sep_by_space !== 0 && + properties.n_sep_by_space !== 1 && + properties.n_sep_by_space !== 2) + throw "Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2"; + + this.n_sep_by_space = properties.n_sep_by_space; + + + if (properties.p_sign_posn !== 0 && + properties.p_sign_posn !== 1 && + properties.p_sign_posn !== 2 && + properties.p_sign_posn !== 3 && + properties.p_sign_posn !== 4) + throw "Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.p_sign_posn = properties.p_sign_posn; + + + if (properties.n_sign_posn !== 0 && + properties.n_sign_posn !== 1 && + properties.n_sign_posn !== 2 && + properties.n_sign_posn !== 3 && + properties.n_sign_posn !== 4) + throw "Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.n_sign_posn = properties.n_sign_posn; + + + if (typeof properties.int_frac_digits != "number" && properties.int_frac_digits < 0) + throw "Error: Invalid/missing int_frac_digits property"; + + this.int_frac_digits = properties.int_frac_digits; + + + if (properties.int_p_cs_precedes !== 0 && properties.int_p_cs_precedes !== 1) + throw "Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1"; + + this.int_p_cs_precedes = properties.int_p_cs_precedes; + + + if (properties.int_n_cs_precedes !== 0 && properties.int_n_cs_precedes !== 1) + throw "Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1"; + + this.int_n_cs_precedes = properties.int_n_cs_precedes; + + + if (properties.int_p_sep_by_space !== 0 && + properties.int_p_sep_by_space !== 1 && + properties.int_p_sep_by_space !== 2) + throw "Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2"; + + this.int_p_sep_by_space = properties.int_p_sep_by_space; + + + if (properties.int_n_sep_by_space !== 0 && + properties.int_n_sep_by_space !== 1 && + properties.int_n_sep_by_space !== 2) + throw "Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2"; + + this.int_n_sep_by_space = properties.int_n_sep_by_space; + + + if (properties.int_p_sign_posn !== 0 && + properties.int_p_sign_posn !== 1 && + properties.int_p_sign_posn !== 2 && + properties.int_p_sign_posn !== 3 && + properties.int_p_sign_posn !== 4) + throw "Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_p_sign_posn = properties.int_p_sign_posn; + + + if (properties.int_n_sign_posn !== 0 && + properties.int_n_sign_posn !== 1 && + properties.int_n_sign_posn !== 2 && + properties.int_n_sign_posn !== 3 && + properties.int_n_sign_posn !== 4) + throw "Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4"; + + this.int_n_sign_posn = properties.int_n_sign_posn; + + + // LC_TIME + + if (properties == null || typeof properties != "object") + throw "Error: Invalid/missing time locale properties"; + + + // parse the supported POSIX LC_TIME properties + + // abday + try { + this.abday = this._parseList(properties.abday, 7); + } + catch (error) { + throw "Error: Invalid abday property: " + error; + } + + // day + try { + this.day = this._parseList(properties.day, 7); + } + catch (error) { + throw "Error: Invalid day property: " + error; + } + + // abmon + try { + this.abmon = this._parseList(properties.abmon, 12); + } catch (error) { + throw "Error: Invalid abmon property: " + error; + } + + // mon + try { + this.mon = this._parseList(properties.mon, 12); + } catch (error) { + throw "Error: Invalid mon property: " + error; + } + + // d_fmt + try { + this.d_fmt = this._validateFormatString(properties.d_fmt); + } catch (error) { + throw "Error: Invalid d_fmt property: " + error; + } + + // t_fmt + try { + this.t_fmt = this._validateFormatString(properties.t_fmt); + } catch (error) { + throw "Error: Invalid t_fmt property: " + error; + } + + // d_t_fmt + try { + this.d_t_fmt = this._validateFormatString(properties.d_t_fmt); + } catch (error) { + throw "Error: Invalid d_t_fmt property: " + error; + } + + // am_pm + try { + var am_pm_strings = this._parseList(properties.am_pm, 2); + this.am = am_pm_strings[0]; + this.pm = am_pm_strings[1]; + } catch (error) { + // ignore empty/null string errors + this.am = ""; + this.pm = ""; + } + + + /** + * @public + * + * @description Returns the abbreviated name of the specified weekday. + * + * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero + * corresponds to Sunday, one to Monday, etc. If omitted the + * method will return an array of all abbreviated weekday + * names. + * + * @returns {String | String[]} The abbreviated name of the specified weekday + * or an array of all abbreviated weekday names. + * + * @throws Error on invalid argument. + */ + this.getAbbreviatedWeekdayName = function(weekdayNum) { + + if (typeof weekdayNum == "undefined" || weekdayNum === null) + return this.abday; + + if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) + throw "Error: Invalid weekday argument, must be an integer [0..6]"; + + return this.abday[weekdayNum]; + }; + + + /** + * @public + * + * @description Returns the name of the specified weekday. + * + * @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero + * corresponds to Sunday, one to Monday, etc. If omitted the + * method will return an array of all weekday names. + * + * @returns {String | String[]} The name of the specified weekday or an + * array of all weekday names. + * + * @throws Error on invalid argument. + */ + this.getWeekdayName = function(weekdayNum) { + + if (typeof weekdayNum == "undefined" || weekdayNum === null) + return this.day; + + if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) + throw "Error: Invalid weekday argument, must be an integer [0..6]"; + + return this.day[weekdayNum]; + }; + + + /** + * @public + * + * @description Returns the abbreviated name of the specified month. + * + * @param {integer Number} [monthNum] An integer between 0 and 11. Zero + * corresponds to January, one to February, etc. If omitted the + * method will return an array of all abbreviated month names. + * + * @returns {String | String[]} The abbreviated name of the specified month + * or an array of all abbreviated month names. + * + * @throws Error on invalid argument. + */ + this.getAbbreviatedMonthName = function(monthNum) { + + if (typeof monthNum == "undefined" || monthNum === null) + return this.abmon; + + if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) + throw "Error: Invalid month argument, must be an integer [0..11]"; + + return this.abmon[monthNum]; + }; + + + /** + * @public + * + * @description Returns the name of the specified month. + * + * @param {integer Number} [monthNum] An integer between 0 and 11. Zero + * corresponds to January, one to February, etc. If omitted the + * method will return an array of all month names. + * + * @returns {String | String[]} The name of the specified month or an array + * of all month names. + * + * @throws Error on invalid argument. + */ + this.getMonthName = function(monthNum) { + + if (typeof monthNum == "undefined" || monthNum === null) + return this.mon; + + if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) + throw "Error: Invalid month argument, must be an integer [0..11]"; + + return this.mon[monthNum]; + }; + + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) character for + * numeric quantities. + * + * @returns {String} The radix character. + */ + this.getDecimalPoint = function() { + + return this.decimal_point; + }; + + + /** + * @public + * + * @description Gets the local shorthand currency symbol. + * + * @returns {String} The currency symbol. + */ + this.getCurrencySymbol = function() { + + return this.currency_symbol; + }; + + + /** + * @public + * + * @description Gets the internaltion currency symbol (ISO-4217 code). + * + * @returns {String} The international currency symbol. + */ + this.getIntCurrencySymbol = function() { + + return this.int_curr_symbol.substring(0,3); + }; + + + /** + * @public + * + * @description Gets the position of the local (shorthand) currency + * symbol relative to the amount. Assumes a non-negative amount. + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.currencySymbolPrecedes = function() { + + if (this.p_cs_precedes == 1) + return true; + else + return false; + }; + + + /** + * @public + * + * @description Gets the position of the international (ISO-4217 code) + * currency symbol relative to the amount. Assumes a non-negative + * amount. + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.intCurrencySymbolPrecedes = function() { + + if (this.int_p_cs_precedes == 1) + return true; + else + return false; + + }; + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) for monetary + * quantities. + * + * @returns {String} The radix character. + */ + this.getMonetaryDecimalPoint = function() { + + return this.mon_decimal_point; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits for local + * (shorthand) symbol formatting. + * + * @returns {integer Number} The number of fractional digits. + */ + this.getFractionalDigits = function() { + + return this.frac_digits; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits for + * international (ISO-4217 code) formatting. + * + * @returns {integer Number} The number of fractional digits. + */ + this.getIntFractionalDigits = function() { + + return this.int_frac_digits; + }; +}; + + + +/** + * @class + * Class for localised formatting of numbers. + * + *

      See: + * POSIX LC_NUMERIC. + * + * + * @public + * @constructor + * @description Creates a new numeric formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_NUMERIC formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.NumericFormatter = function(locale) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + + /** + * @public + * + * @description Formats a decimal numeric value according to the preset + * locale. + * + * @param {Number|String} number The number to format. + * @param {String} [options] Options to modify the formatted output: + *

        + *
      • "^" suppress grouping + *
      • "+" force positive sign for positive amounts + *
      • "~" suppress positive/negative sign + *
      • ".n" specify decimal precision 'n' + *
      + * + * @returns {String} The formatted number. + * + * @throws "Error: Invalid input" on bad input. + */ + this.format = function(number, options) { + + if (typeof number == "string") + number = jsworld._trim(number); + + if (! jsworld._isNumber(number)) + throw "Error: The input is not a number"; + + var floatAmount = parseFloat(number, 10); + + // get the required precision + var reqPrecision = jsworld._getPrecision(options); + + // round to required precision + if (reqPrecision != -1) + floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision); + + + // convert the float number to string and parse into + // object with properties integer and fraction + var parsedAmount = jsworld._splitNumber(String(floatAmount)); + + // format integer part with grouping chars + var formattedIntegerPart; + + if (floatAmount === 0) + formattedIntegerPart = "0"; + else + formattedIntegerPart = jsworld._hasOption("^", options) ? + parsedAmount.integer : + jsworld._formatIntegerPart(parsedAmount.integer, + this.lc.grouping, + this.lc.thousands_sep); + + // format the fractional part + var formattedFractionPart = + reqPrecision != -1 ? + jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision) : + parsedAmount.fraction; + + + // join the integer and fraction parts using the decimal_point property + var formattedAmount = + formattedFractionPart.length ? + formattedIntegerPart + this.lc.decimal_point + formattedFractionPart : + formattedIntegerPart; + + // prepend sign? + if (jsworld._hasOption("~", options) || floatAmount === 0) { + // suppress both '+' and '-' signs, i.e. return abs value + return formattedAmount; + } + else { + if (jsworld._hasOption("+", options) || floatAmount < 0) { + if (floatAmount > 0) + // force '+' sign for positive amounts + return "+" + formattedAmount; + else if (floatAmount < 0) + // prepend '-' sign + return "-" + formattedAmount; + else + // zero case + return formattedAmount; + } + else { + // positive amount with no '+' sign + return formattedAmount; + } + } + }; +}; + + +/** + * @class + * Class for localised formatting of dates and times. + * + *

      See: + * POSIX LC_TIME. + * + * @public + * @constructor + * @description Creates a new date/time formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_TIME formatting properties. + * + * @throws Error on constructor failure. + */ +jsworld.DateTimeFormatter = function(locale) { + + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance."; + + this.lc = locale; + + + /** + * @public + * + * @description Formats a date according to the preset locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted date, e.g. "2010-31-03" + * or "2010-03-31 23:59:59". + * + * @returns {String} The formatted date + * + * @throws Error on invalid date argument + */ + this.formatDate = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 date string + try { + d = jsworld.parseIsoDate(date); + } catch (error) { + // try full ISO-8601 date/time string + d = jsworld.parseIsoDateTime(date); + } + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.d_fmt); + }; + + + /** + * @public + * + * @description Formats a time according to the preset locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted time, e.g. "23:59:59" + * or "2010-03-31 23:59:59". + * + * @returns {String} The formatted time. + * + * @throws Error on invalid date argument. + */ + this.formatTime = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 time string + try { + d = jsworld.parseIsoTime(date); + } catch (error) { + // try full ISO-8601 date/time string + d = jsworld.parseIsoDateTime(date); + } + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.t_fmt); + }; + + + /** + * @public + * + * @description Formats a date/time value according to the preset + * locale. + * + * @param {Date|String} date A valid Date object instance or a string + * containing a valid ISO-8601 formatted date/time, e.g. + * "2010-03-31 23:59:59". + * + * @returns {String} The formatted time. + * + * @throws Error on invalid argument. + */ + this.formatDateTime = function(date) { + + var d = null; + + if (typeof date == "string") { + // assume ISO-8601 format + d = jsworld.parseIsoDateTime(date); + } + else if (date !== null && typeof date == "object") { + // assume ready Date object + d = date; + } + else { + throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; + } + + return this._applyFormatting(d, this.lc.d_t_fmt); + }; + + + /** + * @private + * + * @description Apples formatting to the Date object according to the + * format string. + * + * @param {Date} d A valid Date instance. + * @param {String} s The formatting string with '%' placeholders. + * + * @returns {String} The formatted string. + */ + this._applyFormatting = function(d, s) { + + s = s.replace(/%%/g, '%'); + s = s.replace(/%a/g, this.lc.abday[d.getDay()]); + s = s.replace(/%A/g, this.lc.day[d.getDay()]); + s = s.replace(/%b/g, this.lc.abmon[d.getMonth()]); + s = s.replace(/%B/g, this.lc.mon[d.getMonth()]); + s = s.replace(/%d/g, jsworld._zeroPad(d.getDate(), 2)); + s = s.replace(/%e/g, jsworld._spacePad(d.getDate(), 2)); + s = s.replace(/%F/g, d.getFullYear() + + "-" + + jsworld._zeroPad(d.getMonth()+1, 2) + + "-" + + jsworld._zeroPad(d.getDate(), 2)); + s = s.replace(/%h/g, this.lc.abmon[d.getMonth()]); // same as %b + s = s.replace(/%H/g, jsworld._zeroPad(d.getHours(), 2)); + s = s.replace(/%I/g, jsworld._zeroPad(this._hours12(d.getHours()), 2)); + s = s.replace(/%k/g, d.getHours()); + s = s.replace(/%l/g, this._hours12(d.getHours())); + s = s.replace(/%m/g, jsworld._zeroPad(d.getMonth()+1, 2)); + s = s.replace(/%n/g, "\n"); + s = s.replace(/%M/g, jsworld._zeroPad(d.getMinutes(), 2)); + s = s.replace(/%p/g, this._getAmPm(d.getHours())); + s = s.replace(/%P/g, this._getAmPm(d.getHours()).toLocaleLowerCase()); // safe? + s = s.replace(/%R/g, jsworld._zeroPad(d.getHours(), 2) + + ":" + + jsworld._zeroPad(d.getMinutes(), 2)); + s = s.replace(/%S/g, jsworld._zeroPad(d.getSeconds(), 2)); + s = s.replace(/%T/g, jsworld._zeroPad(d.getHours(), 2) + + ":" + + jsworld._zeroPad(d.getMinutes(), 2) + + ":" + + jsworld._zeroPad(d.getSeconds(), 2)); + s = s.replace(/%w/g, this.lc.day[d.getDay()]); + s = s.replace(/%y/g, new String(d.getFullYear()).substring(2)); + s = s.replace(/%Y/g, d.getFullYear()); + + s = s.replace(/%Z/g, ""); // to do: ignored until a reliable TMZ method found + + s = s.replace(/%[a-zA-Z]/g, ""); // ignore all other % sequences + + return s; + }; + + + /** + * @private + * + * @description Does 24 to 12 hour conversion. + * + * @param {integer Number} hour24 Hour [0..23]. + * + * @returns {integer Number} Corresponding hour [1..12]. + */ + this._hours12 = function(hour24) { + + if (hour24 === 0) + return 12; // 00h is 12AM + + else if (hour24 > 12) + return hour24 - 12; // 1PM to 11PM + + else + return hour24; // 1AM to 12PM + }; + + + /** + * @private + * + * @description Gets the appropriate localised AM or PM string depending + * on the day hour. Special cases: midnight is 12AM, noon is 12PM. + * + * @param {integer Number} hour24 Hour [0..23]. + * + * @returns {String} The corresponding localised AM or PM string. + */ + this._getAmPm = function(hour24) { + + if (hour24 < 12) + return this.lc.am; + else + return this.lc.pm; + }; +}; + + + +/** + * @class Class for localised formatting of currency amounts. + * + *

      See: + * POSIX LC_MONETARY. + * + * @public + * @constructor + * @description Creates a new monetary formatter for the specified locale. + * + * @param {jsworld.Locale} locale A locale object specifying the required + * POSIX LC_MONETARY formatting properties. + * @param {String} [currencyCode] Set the currency explicitly by + * passing its international ISO-4217 code, e.g. "USD", "EUR", "GBP". + * Use this optional parameter to override the default local currency + * @param {String} [altIntSymbol] Non-local currencies are formatted + * with their international ISO-4217 code to prevent ambiguity. + * Use this optional argument to force a different symbol, such as the + * currency's shorthand sign. This is mostly useful when the shorthand + * sign is both internationally recognised and identifies the currency + * uniquely (e.g. the Euro sign). + * + * @throws Error on constructor failure. + */ +jsworld.MonetaryFormatter = function(locale, currencyCode, altIntSymbol) { + + if (typeof locale != "object" || locale._className != "jsworld.Locale") + throw "Constructor error: You must provide a valid jsworld.Locale instance"; + + this.lc = locale; + + /** + * @private + * @description Lookup table to determine the fraction digits for a + * specific currency; most currencies subdivide at 1/100 (2 fractional + * digits), so we store only those that deviate from the default. + * + *

      The data is from Unicode's CLDR version 1.7.0. The two currencies + * with non-decimal subunits (MGA and MRO) are marked as having no + * fractional digits as well as all currencies that have no subunits + * in circulation. + * + *

      It is "hard-wired" for referential convenience and is only looked + * up when an overriding currencyCode parameter is supplied. + */ + this.currencyFractionDigits = { + "AFN" : 0, "ALL" : 0, "AMD" : 0, "BHD" : 3, "BIF" : 0, + "BYR" : 0, "CLF" : 0, "CLP" : 0, "COP" : 0, "CRC" : 0, + "DJF" : 0, "GNF" : 0, "GYD" : 0, "HUF" : 0, "IDR" : 0, + "IQD" : 0, "IRR" : 0, "ISK" : 0, "JOD" : 3, "JPY" : 0, + "KMF" : 0, "KRW" : 0, "KWD" : 3, "LAK" : 0, "LBP" : 0, + "LYD" : 3, "MGA" : 0, "MMK" : 0, "MNT" : 0, "MRO" : 0, + "MUR" : 0, "OMR" : 3, "PKR" : 0, "PYG" : 0, "RSD" : 0, + "RWF" : 0, "SLL" : 0, "SOS" : 0, "STD" : 0, "SYP" : 0, + "TND" : 3, "TWD" : 0, "TZS" : 0, "UGX" : 0, "UZS" : 0, + "VND" : 0, "VUV" : 0, "XAF" : 0, "XOF" : 0, "XPF" : 0, + "YER" : 0, "ZMK" : 0 + }; + + + // optional currencyCode argument? + if (typeof currencyCode == "string") { + // user wanted to override the local currency + this.currencyCode = currencyCode.toUpperCase(); + + // must override the frac digits too, for some + // currencies have 0, 2 or 3! + var numDigits = this.currencyFractionDigits[this.currencyCode]; + if (typeof numDigits != "number") + numDigits = 2; // default for most currencies + this.lc.frac_digits = numDigits; + this.lc.int_frac_digits = numDigits; + } + else { + // use local currency + this.currencyCode = this.lc.int_curr_symbol.substring(0,3).toUpperCase(); + } + + // extract intl. currency separator + this.intSep = this.lc.int_curr_symbol.charAt(3); + + // flag local or intl. sign formatting? + if (this.currencyCode == this.lc.int_curr_symbol.substring(0,3)) { + // currency matches the local one? -> + // formatting with local symbol and parameters + this.internationalFormatting = false; + this.curSym = this.lc.currency_symbol; + } + else { + // currency doesn't match the local -> + + // do we have an overriding currency symbol? + if (typeof altIntSymbol == "string") { + // -> force formatting with local parameters, using alt symbol + this.curSym = altIntSymbol; + this.internationalFormatting = false; + } + else { + // -> force formatting with intl. sign and parameters + this.internationalFormatting = true; + } + } + + + /** + * @public + * + * @description Gets the currency symbol used in formatting. + * + * @returns {String} The currency symbol. + */ + this.getCurrencySymbol = function() { + + return this.curSym; + }; + + + /** + * @public + * + * @description Gets the position of the currency symbol relative to + * the amount. Assumes a non-negative amount and local formatting. + * + * @param {String} intFlag Optional flag to force international + * formatting by passing the string "i". + * + * @returns {Boolean} True if the symbol precedes the amount, false if + * the symbol succeeds the amount. + */ + this.currencySymbolPrecedes = function(intFlag) { + + if (typeof intFlag == "string" && intFlag == "i") { + // international formatting was forced + if (this.lc.int_p_cs_precedes == 1) + return true; + else + return false; + + } + else { + // check whether local formatting is on or off + if (this.internationalFormatting) { + if (this.lc.int_p_cs_precedes == 1) + return true; + else + return false; + } + else { + if (this.lc.p_cs_precedes == 1) + return true; + else + return false; + } + } + }; + + + /** + * @public + * + * @description Gets the decimal delimiter (radix) used in formatting. + * + * @returns {String} The radix character. + */ + this.getDecimalPoint = function() { + + return this.lc.mon_decimal_point; + }; + + + /** + * @public + * + * @description Gets the number of fractional digits. Assumes local + * formatting. + * + * @param {String} intFlag Optional flag to force international + * formatting by passing the string "i". + * + * @returns {integer Number} The number of fractional digits. + */ + this.getFractionalDigits = function(intFlag) { + + if (typeof intFlag == "string" && intFlag == "i") { + // international formatting was forced + return this.lc.int_frac_digits; + } + else { + // check whether local formatting is on or off + if (this.internationalFormatting) + return this.lc.int_frac_digits; + else + return this.lc.frac_digits; + } + }; + + + /** + * @public + * + * @description Formats a monetary amount according to the preset + * locale. + * + *

      +	 * For local currencies the native shorthand symbol will be used for
      +	 * formatting.
      +	 * Example:
      +	 *        locale is en_US
      +	 *        currency is USD
      +	 *        -> the "$" symbol will be used, e.g. $123.45
      +	 *        
      +	 * For non-local currencies the international ISO-4217 code will be
      +	 * used for formatting.
      +	 * Example:
      +	 *       locale is en_US (which has USD as currency)
      +	 *       currency is EUR
      +	 *       -> the ISO three-letter code will be used, e.g. EUR 123.45
      +	 *
      +	 * If the currency is non-local, but an alternative currency symbol was
      +	 * provided, this will be used instead.
      +	 * Example
      +	 *       locale is en_US (which has USD as currency)
      +	 *       currency is EUR
      +	 *       an alternative symbol is provided - "€"
      +	 *       -> the alternative symbol will be used, e.g. €123.45
      +	 * 
      + * + * @param {Number|String} amount The amount to format as currency. + * @param {String} [options] Options to modify the formatted output: + *
      '; +html += '
      Post Game Log Message
      '; +html += '
      '; +html += ''; +html += '
      Enter your log message here. Plain text only please.
      '; +html += '
      '; +html += '

      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', 'Post Message', "glog_post('"+game_id+"')") + '
      '; +html += '
      '; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_glog_body' ); +show_popup_dialog(500, 175, html); +} +function glog_post(game_id) { +var msg = trim( $('fe_glog_body').value ); +if (msg) { +hide_popup_dialog(); +effect_api_send('game_post_log', { +GameID: game_id, +Message: msg +}, [this, 'glog_post_finish'], { _game_id: game_id }); +} +} +function glog_post_finish(response, tx) { +show_glog_widget( tx._game_id ); +} +function hide_glog_widget() { +$('glog_widget').hide(); +} +function get_icon_for_glog_type(type) { +var icon = 'page_white.png'; +switch (type) { +case 'asset': icon = 'folder_page_white.png'; break; +case 'game': icon = 'controller.png'; break; +case 'member': icon = 'user'; break; +case 'comment': icon = 'comment.png'; break; +case 'level': icon = 'world.png'; break; +case 'sprite': icon = 'cog.png'; break; +case 'tile': icon = 'brick.png'; break; +case 'tileset': icon = 'color_swatch.png'; break; +case 'rev': icon = 'cd.png'; break; +case 'revision': icon = 'cd.png'; break; +case 'font': icon = 'style.png'; break; +case 'key': icon = 'keyboard.png'; break; +case 'audio': icon = 'sound'; break; +case 'payment': icon = 'money.png'; break; +case 'env': icon = 'weather.png'; break; +case 'environment': icon = 'weather.png'; break; +} +return icon; +} +function effect_load_script(url) { +Debug.trace('api', 'Loading script: ' + url); +load_script(url); +} +function effect_api_get_ie(cmd, params, userData) { +if (!session.api_state_ie) session.api_state_ie = {}; +var unique_id = get_unique_id(); +session.api_state_ie[unique_id] = userData; +params.format = 'js'; +params.onafter = 'effect_api_response_ie(' + unique_id + ', response);'; +var url = '/effect/api/' + cmd + composeQueryString(params); +Debug.trace('api', "Sending MSIE HTTP GET: " + url); +load_script(url); +} +function effect_api_response_ie(unique_id, tree) { +Debug.trace('api', "Got response from MSIE HTTP GET"); +var tx = session.api_state_ie[unique_id]; +delete session.api_state_ie[unique_id]; +if (tree.Code == 'session') { +do_logout_2(); +return; +} +if (tree.Code == 'access') { +do_notice("Access Denied", tree.Description, 'do_not_pass_go'); +return; +} +if (tree.Code != 0) { +if (tx._on_error) return fire_callback( tx._on_error, tree, tx ); +return do_error( tree.Description ); +} +if (tree.SessionID) { +if (tree.SessionID == '_DELETE_') { +delete session.cookie.tree.effect_session_id; +} +else { +session.cookie.set( 'effect_session_id', tree.SessionID ); +} +session.cookie.save(); +} +if (tx._api_callback) { +fire_callback( tx._api_callback, tree, tx ); +} +} +function effect_api_get(cmd, params, callback, userData) { +if (!userData) userData = {}; +userData._api_callback = callback; +if (!session.api_mod_cache[cmd] && session.username) session.api_mod_cache[cmd] = hires_time_now(); +if (!params.mod && session.api_mod_cache[cmd]) params.mod = session.api_mod_cache[cmd]; +if (ie) return effect_api_get_ie(cmd, params, userData); +var url = '/effect/api/' + cmd + composeQueryString(params); +Debug.trace('api', "Sending HTTP GET: " + url); +ajax.get( url, 'effect_api_response', userData ); +} +function effect_api_send(cmd, xml, callback, userData) { +if (!userData) userData = {}; +userData._api_callback = callback; +var data = compose_xml('EffectRequest', xml); +Debug.trace('api', "Sending API Command: " + cmd + ": " + data); +ajax.send({ +method: 'POST', +url: '/effect/api/' + cmd, +data: data, +headers: { 'Content-Type': 'text/xml' } +}, 'effect_api_response', userData); +} +function effect_api_response(tx) { +Debug.trace('api', "HTTP " + tx.response.code + ": " + tx.response.data); +if (tx.response.code == 999) { +if (tx.request._auto_retry) { +session.net_error = false; +show_progress_dialog(1, "Trying to reestablish connection..."); +session.net_error = true; +setTimeout( function() { ajax.send(tx.request); }, 1000 ); +return; +} +else return do_error( "HTTP ERROR: " + tx.response.code + ": " + tx.response.data + ' (URL: ' + tx.request.url + ')' ); +} +if (session.net_error) { +hide_progress_dialog(); +session.net_error = false; +} +if (tx.response.code != 200) { +if (tx._silent) return; +else return do_error( "HTTP ERROR: " + tx.response.code + ": " + tx.response.data + ' (URL: ' + tx.request.url + ')' ); +} +var tree = null; +if (!tx._raw) { +var parser = new XML({ +preserveAttributes: true, +text: tx.response.data +}); +if (parser.getLastError()) return do_error("XML PARSE ERROR: " + parser.getLastError()); +tree = parser.getTree(); +if (tree.Code == 'session') { +do_logout_2(); +return; +} +if (tree.Code == 'access') { +do_notice("Access Denied", tree.Description, 'do_not_pass_go'); +return; +} +if (tree.Code != 0) { +if (tx._on_error) return fire_callback( tx._on_error, tree, tx ); +return do_error( tree.Description ); +} +if (tree.SessionID) { +if (tree.SessionID == '_DELETE_') { +delete session.cookie.tree.effect_session_id; +} +else { +session.cookie.set( 'effect_session_id', tree.SessionID ); +} +session.cookie.save(); +} +} +if (tx._api_callback) { +fire_callback( tx._api_callback, tree, tx ); +} +} +function effect_api_mod_touch() { +for (var idx = 0, len = arguments.length; idx < len; idx++) { +session.api_mod_cache[ arguments[idx] ] = hires_time_now(); +} +} +function do_not_pass_go() { +Nav.go('Main'); +} +var Nav = { +loc: '', +old_loc: '', +inited: false, +nodes: [], +init: function() { +if (!this.inited) { +this.inited = true; +this.loc = 'init'; +this.monitor(); +} +}, +monitor: function() { +var parts = window.location.href.split(/\#/); +var anchor = parts[1]; +if (!anchor) anchor = 'Main'; +var full_anchor = '' + anchor; +var sub_anchor = ''; +anchor = anchor.replace(/\%7C/, '|'); +if (anchor.match(/\|(\w+)$/)) { +sub_anchor = RegExp.$1.toLowerCase(); +anchor = anchor.replace(/\|(\w+)$/, ''); +} +if ((anchor != this.loc) && !anchor.match(/^_/)) { +Debug.trace('nav', "Caught navigation anchor: " + full_anchor); +var page_name = ''; +var page_args = null; +if (full_anchor.match(/^\w+\?.+/)) { +parts = full_anchor.split(/\?/); +page_name = parts[0]; +page_args = parseQueryString( parts[1] ); +} +else if (full_anchor.match(/^(\w+)\/(.*)$/)) { +page_name = RegExp.$1; +page_args = RegExp.$2; +} +else { +parts = full_anchor.split(/\//); +page_name = parts[0]; +page_args = parts.slice(1); +} +Debug.trace('nav', "Calling page: " + page_name + ": " + serialize(page_args)); +hide_popup_dialog(); +var result = page_manager.click( page_name, page_args ); +if (result) { +if (window.pageTracker && (this.loc != 'init')) { +setTimeout( function() { pageTracker._trackPageview('/effect/' + anchor); }, 1000 ); +} +this.old_loc = this.loc; +if (this.old_loc == 'init') this.old_loc = 'Main'; +this.loc = anchor; +} +else { +this.go( this.loc ); +} +} +else if (sub_anchor != this.sub_anchor) { +Debug.trace('nav', "Caught sub-anchor: " + sub_anchor); +$P().gosub( sub_anchor ); +} +this.sub_anchor = sub_anchor; +setTimeout( 'Nav.monitor()', 100 ); +}, +go: function(anchor, force) { +anchor = anchor.replace(/^\#/, ''); +if (force) this.loc = 'init'; +window.location.href = '#' + anchor; +}, +prev: function() { +this.go( this.old_loc || 'Main' ); +}, +refresh: function() { +this.loc = 'refresh'; +}, +bar: function() { +var nodes = arguments; +var html = ''; +for (var idx = 0, len = nodes.length; idx < len; idx++) { +var node = nodes[idx]; +if (node) this.nodes[idx] = node; +else node = this.nodes[idx]; +if (node != '_ignore_') { +html += ''; +} +} +html += '
      '; +$('d_nav_bar').innerHTML = html; +}, +title: function(name) { +if (name) document.title = name + ' | EffectGames.com'; +else document.title = 'EffectGames.com'; +}, +currentAnchor: function() { +var parts = window.location.href.split(/\#/); +var anchor = parts[1] || ''; +var sub_anchor = ''; +anchor = anchor.replace(/\%7C/, '|'); +if (anchor.match(/\|(\w+)$/)) { +sub_anchor = RegExp.$1.toLowerCase(); +anchor = anchor.replace(/\|(\w+)$/, ''); +} +return anchor; +} +}; +var Blog = { +edit_caption: '
      *Bold*  |Italic|  {monospace}  [http://link]  Formatting Guide...
      ', +search: function(args) { +if (!args.mode) args.mode = 'and'; +if (!args.offset) args.offset = 0; +if (!args.limit) args.limit = 10; +if (!args.format) args.format = 'xml'; +var query_args = copy_object( args ); +delete query_args.callback; +effect_api_get( 'article_search', query_args, [this, 'search_response'], { _search_args: args } ); +}, +get_article_preview: function(row, args) { +var html = ''; +Debug.trace('blog', 'Row: ' + dumper(row)); +html += '
      '; +var ext_article_url = 'http://' + location.hostname + '/effect/article.psp.html' + row.Path + '/' + row.ArticleID; +var article_url = '#Article' + row.Path + '/' + row.ArticleID; +html += ''; +if (!args.title_only) { +html += '
      '; +html += row.Preview; +html += '  ' + (args.link_title || 'Read Full Story...') + ''; +html += '
      '; +html += ''; +html += '
      '; +var elem_class = args.footer_element_class || 'blog_preview_footer_element'; +if ((session.username == row.Username) || is_admin()) { +html += '
      ' + +icon('page_white_edit.png', "Edit", '#ArticleEdit?path=' + row.Path + '&id=' + row.ArticleID) + '
      '; +} +html += '
      ' + get_user_display(row.Username) + '
      '; +html += '
      ' + icon('calendar', get_short_date_time(row.Published)) + '
      '; +html += '
      ' + icon('talk', row.Comments) + '
      '; +if (0 && row.Tags) html += '
      ' + icon('note.png', make_tag_links(row.Tags, 3)) + '
      '; +html += '
      ' + icon('facebook.png', 'Facebook', "window.open('http://www.facebook.com/sharer.php?u="+encodeURIComponent(ext_article_url)+'&t='+encodeURIComponent(row.Title)+"','sharer','toolbar=0,status=0,width=626,height=436')", "Share on Facebook") + '
      '; +html += '
      ' + icon('twitter.png', 'Twitter', "window.open('http://twitter.com/home?status=Reading%20" + encodeURIComponent(row.Title) + "%3A%20" + encodeURIComponent(ext_article_url)+"')", "Share on Twitter") + '
      '; +html += '
      '; +html += '
      '; +html += '
      '; +} +html += '
      '; +return html; +}, +search_response: function(response, tx) { +var args = tx._search_args; +if (args.callback) return fire_callback(args.callback, response, args); +var div = $(args.target); +assert(div, "Could not find target DIV: " + args.target); +var html = ''; +if (response.Rows && response.Rows.Row) { +var rows = always_array( response.Rows.Row ); +for (var idx = 0, len = rows.length; idx < len; idx++) { +var row = rows[idx]; +html += this.get_article_preview( row, args ); +} +if (args.more && (rows.length == args.limit)) { +html += large_icon_button('page_white_put.png', 'More...', "Blog.more(this, "+encode_object(args)+")") + '
      '; +html += spacer(1,15) + '
      '; +} +if (args.after) html += args.after; +} +else if (response.Code != 0) { +html = 'Search Error: ' . response.Code + ': ' + response.Description; +} +else { +html = args.none_found_msg || 'No articles found.'; +} +div.innerHTML = html; +}, +more: function(div, args) { +args.offset += args.limit; +Debug.trace('blog', "More Args: " + dumper(args)); +div.innerHTML = ''; +effect_api_get( 'article_search', args, [this, 'more_response'], { _search_args: args, _div: div } ); +}, +more_response: function(response, tx) { +var args = tx._search_args; +var button = tx._div; +var html = ''; +if (response.Rows && response.Rows.Row) { +var rows = always_array( response.Rows.Row ); +for (var idx = 0, len = rows.length; idx < len; idx++) { +var row = rows[idx]; +html += this.get_article_preview( row, args ); +} +if (args.more && (rows.length == args.limit)) { +html += large_icon_button('page_white_put.png', 'More...', "Blog.more(this, "+encode_object(args)+")") + '
      '; +html += spacer(1,15) + '
      '; +} +} +else if (response.Code != 0) { +html = 'Search Error: ' . response.Code + ': ' + response.Description; +} +else { +html = args.none_found_msg || 'No more articles found.'; +} +var div = document.createElement('div'); +div.innerHTML = html; +button.parentNode.replaceChild( div, button ); +} +}; +function make_tag_links(csv, max, base_url) { +if (!base_url) base_url = ''; +var tags = csv.split(/\,\s*/); +var append = ''; +if (max && (tags.length > max)) { +tags.length = max; +append = '...'; +} +var html = ''; +for (var idx = 0, len = tags.length; idx < len; idx++) { +html += ''+tags[idx]+''; +if (idx < len - 1) html += ', '; +} +html += append; +return html; +} +function get_url_friendly_title(title) { +title = title.toString().replace(/\W+/g, '_'); +if (title.length > 40) title = title.substring(0, 40); +title = title.replace(/^_+/, ''); +title = title.replace(/_+$/, ''); +return title; +} +function get_full_url(url) { +if (url.match(/^\#/)) { +var parts = window.location.href.split(/\#/); +url = parts[0] + url; +} +return url; +} +var Comments = { +comments_per_page: 10, +get: function(page_id) { +var html = ''; +html += '
      '; +html += '
      Comments'; +html += '
      '; +html += '
      '; +html += '
      '; +setTimeout( function() { Comments.search({ page_id: page_id }); }, 1 ); +return html; +}, +search: function(args) { +if (!args.limit) args.limit = this.comments_per_page; +if (!args.offset) args.offset = 0; +assert(args.page_id, "Comments.search: No page_id specified"); +args.format = 'xml'; +this.last_search = args; +effect_api_get( 'comments_get', args, [this, 'search_response'], { _search_args: args } ); +}, +research: function(offset) { +var args = this.last_search; +if (!args) return; +args.offset = offset; +effect_api_get( 'comments_get', args, [this, 'search_response'], { _search_args: args } ); +}, +search_response: function(response, tx) { +this.comments = []; +var args = tx._search_args; +if (args.callback) return fire_callback(args.callback, response, args); +var html = ''; +html += '
      ' + +large_icon_button( 'comment_edit.png', 'Post Comment...', "Comments.add('"+args.page_id+"')" ) + '
      '; +if (args.page_id.match(/^Article\//)) { +html += '
      ' + icon('feed.png', 'RSS', '/effect/api/comment_feed/' + args.page_id + '.rss', 'Comments RSS Feed') + '
      '; +} +if (response.Items && response.Items.Item && response.List && response.List.length) { +html += ''; +html += '
      '; +var items = this.comments = always_array( response.Items.Item ); +for (var idx = 0, len = items.length; idx < len; idx++) { +var item = items[idx]; +var extra_classes = (args.highlight && (args.highlight == item.ID)) ? ' highlight' : ''; +html += '
      '; +html += '
      '; +if (item.Username) html += ''; +html += '' + item.Name.toString().toUpperCase() + ''; +if (item.Username) html += ''; +html += ', ' + get_short_date_time(item.Date) + '
      '; +html += '
      '; +html += this.get_comment_controls( args.page_id, item ); +html += '
      '; +html += '
      '; +html += '
      ' + item.Comment + '
      '; +html += '
      '; +html += ''; +if (item.LastReply && ((item.LastReply >= time_now() - (86400 * 7)) || (session.username && (session.username == item.Username)))) { +setTimeout( "Comments.show_replies('"+args.page_id+"','"+item.ID+"')", 1 ); +} +} +} +else { +} +$( 'd_comments_' + args.page_id ).innerHTML = html; +}, +get_control: function(icon, code, text, status_text) { +if (!icon.match(/\.\w+$/)) icon += '.gif'; +return '' + code_link(code, text, status_text) + ''; +}, +get_comment_controls: function(page_id, comment) { +var html = ''; +var spacer_txt = '  |  '; +if (session.user) { +html += this.get_control('comment', "Comments.reply('"+page_id+"','"+comment.ID+"')", 'Reply') + spacer_txt; +} +if (comment.Replies) { +if (comment._replies_visible) html += this.get_control('magnify_minus', "Comments.hide_replies('"+page_id+"','"+comment.ID+"')", 'Hide Replies'); +else html += this.get_control('magnify_plus', "Comments.show_replies('"+page_id+"','"+comment.ID+"')", 'Show Replies ('+comment.Replies+')'); +if (session.user) html += spacer_txt; +} +if (session.user) { +html += this.get_control( +'star', +"Comments.like('"+page_id+"','"+comment.ID+"')", +'Like' + (comment.Like ? (' ('+comment.Like+')') : ''), +comment.Like ? (comment.Like + ' ' + ((comment.Like == 1) ? 'person likes this' : 'people like this')) : 'I like this comment' +) + spacer_txt; +if (is_admin()) html += this.get_control('trash', "Comments._delete('"+page_id+"','"+comment.ID+"')", 'Delete') + spacer_txt; +html += this.get_control('warning', "Comments.report('"+page_id+"','"+comment.ID+"')", 'Report Abuse'); +} +return html; +}, +reply: function(page_id, comment_id) { +hide_popup_dialog(); +delete session.progress; +var comment = find_object( this.comments, { ID: comment_id } ); +var html = ''; +html += '
      '; +html += '\n \n \n \n'); + }; + __out.push('\n\n'); + __out.push(require('templates/clients/modules/sub_header').call(this, { + heading: t("Ride Request") + })); + __out.push('\n\n\n
      \n
      \n
      \n
      \n \n \n \n \n
      \n\n
      '; +html += '
      Reply to Comment by "'+comment.Name+'"
      '; +html += '
      '; +var name = this.get_name(); +html += '

      Posted by: ' + name; +if (!session.user) html += ' → Create Account'; +html += '


      '; +html += ''; +html += Blog.edit_caption; +html += '
      '; +html += '

      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', 'Post Reply', "Comments.post_reply('"+page_id+"','"+comment_id+"')") + '
      '; +html += '
      '; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_comment_body' ); +show_popup_dialog(600, 300, html); +}, +post_reply: function(page_id, comment_id) { +var value = $('fe_comment_body').value; +if (!value) return; +hide_popup_dialog(); +show_progress_dialog(1, "Posting reply..."); +var name = this.get_name(); +effect_api_mod_touch('comment_replies_get'); +effect_api_send('comment_post_reply', { +PageID: page_id, +CommentID: comment_id, +Username: session.username || '', +Name: name, +Comment: value, +PageURL: location.href +}, [this, 'post_reply_finish'], { _page_id: page_id, _comment_id: comment_id } ); +}, +post_reply_finish: function(response, tx) { +hide_popup_dialog(); +var page_id = tx._page_id; +var comment_id = tx._comment_id; +var comment = find_object( this.comments, { ID: comment_id } ); +do_message('success', "Comment reply posted successfully."); +this.show_replies(page_id, comment_id); +if (!comment.Replies) comment.Replies = 1; else comment.Replies++; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +}, +show_replies: function(page_id, comment_id) { +var comment = find_object( this.comments, { ID: comment_id } ); +if (!comment._replies_visible) { +$('d_comment_replies_' + comment_id).show().innerHTML = ''; +} +var args = { page_id: page_id, comment_id: comment_id, offset: 0, limit: 100 }; +effect_api_get( 'comment_replies_get', args, [this, 'receive_replies_response'], { _search_args: args } ); +}, +receive_replies_response: function(response, tx) { +var page_id = tx._search_args.page_id; +var comment_id = tx._search_args.comment_id; +var comment = find_object( this.comments, { ID: comment_id } ); +var html = ''; +var replies = always_array( response.Items.Item ); +for (var idx = 0, len = replies.length; idx < len; idx++) { +var reply = replies[idx]; +html += get_chat_balloon( +(reply.Username == session.username) ? 'blue' : 'grey', +reply.Username, +reply.Comment.replace(/^]*?>(.+)<\/div>$/i, '$1') +); +} +$('d_comment_replies_' + comment_id).innerHTML = html; +if (!comment._replies_visible) { +$('d_comment_replies_' + comment_id).hide(); +animate_div_visibility( 'd_comment_replies_' + comment_id, true ); +} +comment._replies_visible = true; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +}, +hide_replies: function(page_id, comment_id) { +var comment = find_object( this.comments, { ID: comment_id } ); +if (comment._replies_visible) { +animate_div_visibility( 'd_comment_replies_' + comment_id, false ); +comment._replies_visible = false; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +} +}, +like: function(page_id, comment_id) { +effect_api_mod_touch('comments_get'); +effect_api_send('comment_like', { +PageID: page_id, +CommentID: comment_id +}, [this, 'like_finish'], { _page_id: page_id, _comment_id: comment_id, _on_error: [this, 'like_error'] } ); +}, +like_error: function(response, tx) { +if (response.Code == 'comment_already_like') do_message('error', "You already like this comment."); +else do_error( response.Description ); +}, +like_finish: function(resopnse, tx) { +var page_id = tx._page_id; +var comment_id = tx._comment_id; +var comment = find_object( this.comments, { ID: comment_id } ); +do_message('success', "You now like this comment."); +if (!comment.Like) comment.Like = 1; else comment.Like++; +$('d_comment_controls_'+comment_id).innerHTML = this.get_comment_controls( page_id, comment ); +}, +add: function(page_id) { +hide_popup_dialog(); +delete session.progress; +var html = ''; +html += '
      '; +html += '\n \n \n \n \n \n \n \n \n \n '); + }, this); + __out.push('\n\n
      \n
      '; +html += '
      Post New Comment
      '; +html += '
      '; +var name = this.get_name(); +html += '

      Posted by: ' + name; +if (!session.user) html += ' → Create Account'; +html += '


      '; +html += ''; +html += Blog.edit_caption; +html += '
      '; +html += '

      '; +html += ''; +html += ''; +html += ''; +html += '
      ' + large_icon_button('x', 'Cancel', "hide_popup_dialog()") + ' ' + large_icon_button('check', 'Post Comment', "Comments.post('"+page_id+"')") + '
      '; +html += '
      '; +html += ''; +session.hooks.keys[ESC_KEY] = 'hide_popup_dialog'; +safe_focus( 'fe_comment_body' ); +show_popup_dialog(600, 300, html); +}, +report: function(page_id, comment_id) { +if (confirm('Are you sure you want to report this comment to the site administrators as abusive and/or spam?')) { +effect_api_send('comment_report_abuse', { +PageID: page_id, +CommentID: comment_id +}, [this, 'report_finish'], { _page_id: page_id, _comment_id: comment_id } ); +} +}, +report_finish: function(response, tx) { +do_message('success', 'Your abuse report has been received, and will be evaluated by the site administrators.'); +}, +_delete: function(page_id, comment_id) { +if (confirm('Are you sure you want to permanently delete this comment?')) { +effect_api_mod_touch('comments_get'); +effect_api_send('comment_delete', { +PageID: page_id, +CommentID: comment_id +}, [this, 'delete_finish'], { _page_id: page_id, _comment_id: comment_id } ); +} +}, +delete_finish: function(response, tx) { +do_message('success', 'The comment was deleted successfully.'); +var page_id = tx._page_id; +this.search({ page_id: page_id }); +}, +get_name: function() { +var name = '(Anonymous)'; +if (session.user) { +if (get_bool_pref('public_profile')) name = session.user.FullName; +else name = session.username; +} +return name; +}, +post: function(page_id) { +var value = $('fe_comment_body').value; +if (!value) return; +hide_popup_dialog(); +show_progress_dialog(1, "Posting comment..."); +var name = this.get_name(); +effect_api_mod_touch('comments_get'); +effect_api_send('comment_post', { +PageID: page_id, +Username: session.username || '', +Name: name, +Comment: value +}, [this, 'post_finish'], { _page_id: page_id } ); +}, +post_finish: function(response, tx) { +hide_popup_dialog(); +var comment_id = response.CommentID; +var page_id = tx._page_id; +this.search({ page_id: page_id, highlight: comment_id }); +} +}; +Class.create( 'Menu', { +id: '', +menu: null, +__construct: function(id) { +this.id = id; +}, +load: function() { +if (!this.menu) { +this.menu = $(this.id); +assert( !!this.menu, "Could not locate DOM element: " + this.id ); +} +}, +get_value: function() { +this.load(); +return this.menu.options[this.menu.selectedIndex].value; +}, +set_value: function(value, auto_add) { +value = str_value(value); +this.load(); +for (var idx = 0, len = this.menu.options.length; idx < len; idx++) { +if (this.menu.options[idx].value == value) { +this.menu.selectedIndex = idx; +return true; +} +} +if (auto_add) { +this.menu.options[this.menu.options.length] = new Option(value, value); +this.menu.selectedIndex = this.menu.options.length - 1; +return true; +} +return false; +}, +disable: function() { +this.load(); +this.menu.disabled = true; +this.menu.setAttribute( 'disabled', 'disabled' ); +}, +enable: function() { +this.load(); +this.menu.setAttribute( 'disabled', '' ); +this.menu.disabled = false; +}, +populate: function(items, sel_value) { +this.load(); +this.menu.options.length = 0; +for (var idx = 0, len = items.length; idx < len; idx++) { +var item = items[idx]; +var item_name = ''; +var item_value = ''; +if (isa_hash(item)) { +item_name = item.label; +item_value = item.data; +} +else if (isa_array(item)) { +item_name = item[0]; +item_value = item[1]; +} +else { +item_name = item_value = item; +} +this.menu.options[ this.menu.options.length ] = new Option( item_name, item_value ); +if (item_value == sel_value) this.menu.selectedIndex = idx; +} +} +} ); +Class.subclass( Menu, 'MultiMenu', { +__static: { +toggle_type: function(id) { +var menu = $(id); +assert(menu, "Could not find menu in DOM: " + id); +if (menu.disabled) return; +var obj = MenuManager.find(id); +assert(obj, "Could not find menu in MenuManager: " + id); +var div = $( 'd_inner_' + id ); +var ic = $( 'ic_' + id ); +var is_multiple = (ic.src.indexOf('contract') > -1); +obj.multi = !is_multiple; +var multiple_tag = !is_multiple ? +' multiple="multiple" size=5' : ''; +var items = []; +for (var idx = 0; idx < menu.options.length; idx++) { +var option = menu.options[idx]; +array_push( items, { +value: option.value, +text: option.text, +selected: option.selected +}); +} +var html = ''; +html += ''; +div.innerHTML = html; +ic.src = images_uri + '/menu_' + (is_multiple ? 'expand' : 'contract') + '.gif'; +obj.menu = null; +} +}, +attribs: null, +multi: false, +toggle: true, +__construct: function(id, attribs) { +this.id = id; +if (attribs) this.attribs = attribs; +}, +get_html: function(items, selected_csv, attribs) { +if (!items) items = []; +if (!selected_csv) selected_csv = ''; +if (attribs) this.attribs = attribs; +var selected = csv_to_hash(selected_csv); +this.menu = null; +if (num_keys(selected) > 1) this.multi = true; +var html = '
      '; +html += ''; +html += ''; +html += ''; +if (this.toggle) html += ''; +html += '
      ' + spacer(1,1) + '
      '+spacer(1,2)+'
      '; +html += '
      '; +return html; +}, +get_value: function() { +this.load(); +var value = ''; +for (var idx = 0; idx < this.menu.options.length; idx++) { +var option = this.menu.options[idx]; +if (option.selected && option.value.length) { +if (value.length > 0) value += ','; +value += option.value; +} +} +return value; +}, +set_value: function(value, auto_add) { +value = '' + value; +this.load(); +if (!value) { +value = ''; +for (var idx = 0; idx < this.menu.options.length; idx++) { +var option = this.menu.options[idx]; +option.selected = (option.value == value); +} +return; +} +var selected = csv_to_hash(value); +if ((num_keys(selected) > 1) && !this.multi) { +MultiMenu.toggle_type(this.id); +var self = this; +setTimeout( function() { +self.set_value(value, auto_add); +}, 1 ); +return; +} +for (var idx = 0; idx < this.menu.options.length; idx++) { +var option = this.menu.options[idx]; +option.selected = selected[option.value] ? true : false; +} +}, +populate: function(items, value) { +this.load(); +this.menu.options.length = 0; +if (!value) value = ''; +var selected = csv_to_hash(value); +for (var idx = 0, len = items.length; idx < len; idx++) { +var item = items[idx]; +var item_name = ''; +var item_value = ''; +if (isa_hash(item)) { +item_name = item.label; +item_value = item.data; +} +else if (isa_array(item)) { +item_name = item[0]; +item_value = item[1]; +} +else { +item_name = item_value = item; +} +var opt = new Option( item_name, item_value ); +this.menu.options[ this.menu.options.length ] = opt; +opt.selected = selected[item_value] ? true : false; +} +}, +collapse: function() { +if (this.multi) MultiMenu.toggle_type(this.id); +}, +expand: function() { +if (!this.multi) MultiMenu.toggle_type(this.id); +} +} ); +Class.create( 'MenuManager', { +__static: { +menus: {}, +register: function(menu) { +this.menus[ menu.id ] = menu; +return menu; +}, +find: function(id) { +return this.menus[id]; +} +} +} ); +Class.create( 'GrowlManager', { +lifetime: 10, +marginRight: 0, +marginTop: 0, +__construct: function() { +this.growls = []; +}, +growl: function(type, msg) { +if (find_object(this.growls, { type: type, msg: msg })) return; +var div = $(document.createElement('div')); +div.className = 'growl_message ' + type; +div.setOpacity(0.0); +div.innerHTML = '
      ' + msg + '
      ' + spacer(1,5) + '
      '; +$('d_growl_wrapper').insertBefore( div, $('d_growl_top').nextSibling ); +var growl = { id:get_unique_id(), type: type, msg: msg, opacity:0.0, start:hires_time_now(), div:div }; +this.growls.push(growl); +this.handle_resize(); +this.animate(growl); +var self = this; +div.onclick = function() { +delete_object(self.growls, { id: growl.id }); +$('d_growl_wrapper').removeChild( div ); +}; +}, +animate: function(growl) { +if (growl.deleted) return; +var now = hires_time_now(); +var div = growl.div; +if (now - growl.start <= 0.5) { +div.setOpacity( tweenFrame(0.0, 1.0, (now - growl.start) * 2, 'EaseOut', 'Quadratic') ); +} +else if (now - growl.start <= this.lifetime) { +if (!growl._fully_opaque) { +div.setOpacity( 1.0 ); +growl._fully_opaque = true; +} +} +else if (now - growl.start <= this.lifetime + 1.0) { +div.setOpacity( tweenFrame(1.0, 0.0, (now - growl.start) - this.lifetime, 'EaseOut', 'Quadratic') ); +} +else { +delete_object(this.growls, { id: growl.id }); +$('d_growl_wrapper').removeChild( div ); +return; +} +var self = this; +setTimeout( function() { self.animate(growl); }, 33 ); +}, +handle_resize: function() { +var div = $('d_growl_wrapper'); +if (this.growls.length) { +var size = getInnerWindowSize(); +div.style.top = '' + (10 + this.marginTop) + 'px'; +div.style.left = '' + Math.floor((size.width - 310) - this.marginRight) + 'px'; +} +else { +div.style.left = '-2000px'; +} +} +} ); +window.$GR = new GrowlManager(); +if (window.addEventListener) { +window.addEventListener( "resize", function() { +$GR.handle_resize(); +}, false ); +} +else if (window.attachEvent && !ie6) { +window.attachEvent("onresize", function() { +$GR.handle_resize(); +}); +} +Class.create( 'Effect.Page', { +ID: '', +data: null, +active: false, +__construct: function(config) { +if (!config) return; +this.data = {}; +if (!config) config = {}; +for (var key in config) this[key] = config[key]; +this.div = $('page_' + this.ID); +assert(this.div, "Cannot find page div: page_" + this.ID); +}, +onInit: function() { +}, +onActivate: function() { +return true; +}, +onDeactivate: function() { +return true; +}, +show: function() { +this.div.show(); +}, +hide: function() { +this.div.hide(); +}, +gosub: function(anchor) { +} +} ); +Class.require( 'Effect.Page' ); +Class.create( 'Effect.PageManager', { +pages: null, +current_page_id: '', +on_demand: {}, +__construct: function(page_list) { +this.pages = []; +this.page_list = page_list; +for (var idx = 0, len = page_list.length; idx < len; idx++) { +Debug.trace( 'page', "Initializing page: " + page_list[idx].ID ); +if (Effect.Page[ page_list[idx].ID ]) { +var page = new Effect.Page[ page_list[idx].ID ]( page_list[idx] ); +page.onInit(); +this.pages.push(page); +} +else { +Debug.trace( 'page', 'Page ' + page_list[idx].ID + ' will be loaded on-demand' ); +} +} +}, +find: function(id) { +var page = find_object( this.pages, { ID: id } ); +if (!page) Debug.trace('PageManager', "Could not find page: " + id); +return page; +}, +notify_load: function(file, id) { +for (var idx = 0, len = this.page_list.length; idx < len; idx++) { +var page_config = this.page_list[idx]; +if (page_config.File == file) { +Debug.trace( 'page', "Initializing page on-demand: " + page_config.ID ); +var page = new Effect.Page[ page_config.ID ]( page_config ); +page.onInit(); +this.pages.push(page); +} +} +var self = this; +setTimeout( function() { +var result = self.activate(id, self.temp_args); +delete self.temp_args; +$('d_page_loading').hide(); +if (!result) { +$('page_'+id).hide(); +self.current_page_id = ''; +} +}, 1 ); +}, +activate: function(id, args) { +if (!find_object( this.pages, { ID: id } )) { +var page_config = find_object( this.page_list, { ID: id } ); +assert(!!page_config, "Page config not found: " + id ); +Debug.trace('page', "Loading file on-demand: " + page_config.File + " for page: " + id); +var url = '/effect/api/load_page/' + page_config.File + '?onafter=' + escape('page_manager.notify_load(\''+page_config.File+'\',\''+id+'\')'); +if (page_config.Requires) { +var files = page_config.Requires.split(/\,\s*/); +for (var idx = 0, len = files.length; idx < len; idx++) { +var filename = files[idx]; +if (!this.on_demand[filename]) { +Debug.trace('page', "Also loading file: " + filename); +url += '&file=' + filename; +this.on_demand[filename] = 1; +} +} +} +$('d_page_loading').show(); +this.temp_args = args; +load_script( url ); +return true; +} +$('page_'+id).show(); +var page = this.find(id); +page.active = true; +if (!args) args = []; +if (!isa_array(args)) args = [ args ]; +var result = page.onActivate.apply(page, args); +if (typeof(result) == 'boolean') return result; +else return alert("Page " + id + " onActivate did not return a boolean!"); +}, +deactivate: function(id, new_id) { +var page = this.find(id); +var result = page.onDeactivate(new_id); +if (result) { +$('page_'+id).hide(); +page.active = false; +} +return result; +}, +click: function(id, args) { +Debug.trace('page', "Switching pages to: " + id); +var old_id = this.current_page_id; +if (this.current_page_id) { +var result = this.deactivate( this.current_page_id, id ); +if (!result) return false; +} +this.current_page_id = id; +this.old_page_id = old_id; +window.scrollTo( 0, 0 ); +var result = this.activate(id, args); +if (!result) { +$('page_'+id).hide(); +this.current_page_id = ''; +} +return true; +} +} ); +Class.subclass( Effect.Page, "Effect.Page.Main", { +inited: false, +onActivate: function() { +Nav.bar( ['Main', 'EffectGames.com'] ); +Nav.title(''); +$('d_blog_news').innerHTML = loading_image(); +$('d_blog_community').innerHTML = loading_image(); +$('d_blog_featured').innerHTML = loading_image(); +Blog.search({ +stag: 'featured_game', +limit: 4, +full: 1, +callback: [this, 'receive_featured_games'] +}); +effect_api_get( 'get_site_info', { cat: 'pop_pub_games' }, [this, 'receive_pop_pub_games'], { } ); +Blog.search({ +stag: 'front_page', +limit: 5, +target: 'd_blog_news', +more: 1 +}); +Blog.search({ +path: '/community', +limit: 5, +target: 'd_blog_community', +more: 1 +}); +if (!this.inited) { +this.inited = true; +config.Strings.MainSlideshow.Slide = always_array( config.Strings.MainSlideshow.Slide ); +this.slide_idx = 0; +this.num_slides = config.Strings.MainSlideshow.Slide.length; +this.slide_div_num = 0; +this.slide_dir = 1; +this.bk_pos = -340; +this.bk_pos_target = -340; +this.slide_images = []; +for (var idx = 0, len = this.num_slides; idx < len; idx++) { +var url = images_uri + '/' + config.Strings.MainSlideshow.Slide[idx].Photo; +this.slide_images[idx] = new Image(); +this.slide_images[idx].src = png(url, true); +} +} +this.height_target = 470; +this.height_start = $('d_header').offsetHeight; +this.time_start = hires_time_now(); +this.duration = 0.75; +if (!this.timer) this.timer = setTimeout( '$P("Main").animate_mhs()', 33 ); +if (session.user) $('d_blurb_main').hide(); +else { +$('d_blurb_main').innerHTML = get_string('/Main/Blurb'); +$('d_blurb_main').show(); +} +return true; +}, +receive_pop_pub_games: function(response, tx) { +var html = ''; +if (response.Data && response.Data.Games && response.Data.Games.Game) { +var games = always_array( response.Data.Games.Game ); +for (var idx = 0, len = Math.min(games.length, 16); idx < len; idx++) { +var game = games[idx]; +html += '
      ' + +(game.Logo ? +user_image_thumbnail(game.Logo, 80, 60) : +'' +) + '
      ' + ww_fit_box(game.Title, 80, 2, session.em_width, 1) + '
      '; +} +html += '
      '; +} +else { +html += 'No active public games found! Why not create a new one?'; +} +$('d_main_pop_pub_games').innerHTML = html; +}, +receive_featured_games: function(response, tx) { +var html = ''; +if (response.Rows && response.Rows.Row) { +html += ''; +var rows = always_array( response.Rows.Row ); +for (var idx = 0, len = rows.length; idx < len; idx++) { +var row = rows[idx]; +var image_url = row.Params.featured_image; +if (image_url && image_url.match(/^(\w+)\/(\w+\.\w+)$/)) { +image_url = '/effect/api/view/users/' + RegExp.$1 + '/images/' + RegExp.$2; +} +if (idx % 2 == 0) html += ''; +html += ''; +if (idx % 2 == 1) html += ''; +} +if (rows.length % 2 == 1) { +html += ''; +html += ''; +} +html += '
      '; +html += ''; +html += ''; +html += ''; +html += ''; +html += ''; +html += '
      '; +html += ''; +html += '' + spacer(10,1) + ''; +html += ''; +html += ''; +html += '' + spacer(15,1) + '
      '; +html += spacer(1,20); +html += '
      '; +} +$('d_blog_featured').innerHTML = html; +}, +animate_mhs: function() { +var now = hires_time_now(); +if (now - this.time_start >= this.duration) { +$('d_header').style.height = '' + this.height_target + 'px'; +$('d_shadow').style.height = '' + this.height_target + 'px'; +delete this.timer; +} +else { +var height = tweenFrame(this.height_start, this.height_target, (now - this.time_start) / this.duration, 'EaseOut', 'Circular'); +$('d_header').style.height = '' + height + 'px'; +$('d_shadow').style.height = '' + height + 'px'; +this.timer = setTimeout( '$P("Main").animate_mhs()', 33 ); +} +}, +onDeactivate: function() { +$('d_blog_news').innerHTML = ''; +$('d_blog_community').innerHTML = ''; +this.height_target = 75; +this.height_start = $('d_header').offsetHeight; +this.time_start = hires_time_now(); +if (!this.timer) this.timer = setTimeout( '$P("Main").animate_mhs()', 33 ); +return true; +}, +draw_slide: function() { +if (this.slide_timer) return; +var slide = config.Strings.MainSlideshow.Slide[ this.slide_idx ]; +this.old_photo = $('d_header_slideshow_photo_' + this.slide_div_num); +this.old_text = $('d_header_slideshow_text_' + this.slide_div_num); +this.slide_div_num = 1 - this.slide_div_num; +this.new_photo = $('d_header_slideshow_photo_' + this.slide_div_num); +this.new_text = $('d_header_slideshow_text_' + this.slide_div_num); +this.new_photo.style.backgroundImage = 'url('+png(images_uri+'/'+slide.Photo, true)+')'; +this.new_photo.setOpacity(0.0); +var html = ''; +html += slide.Text; +this.slide_width = this.new_text.offsetWidth; +this.new_text.innerHTML = html; +if (this.slide_dir == 1) this.new_text.style.left = '' + this.slide_width + 'px'; +else this.new_text.style.left = '-' + this.slide_width + 'px'; +this.slide_time_start = hires_time_now(); +this.slide_timer = setTimeout( '$P("Main").animate_mhs_slide()', 33 ); +}, +animate_mhs_slide: function() { +var now = hires_time_now(); +if (now - this.slide_time_start >= this.duration) { +this.new_text.style.left = '0px'; +this.old_text.style.left = '-' + this.slide_width + 'px'; +this.new_photo.setOpacity( 1.0 ); +this.old_photo.setOpacity( 0.0 ); +delete this.slide_timer; +this.bk_pos = this.bk_pos_target; +} +else { +var value = tweenFrame(0.0, 1.0, (now - this.slide_time_start) / this.duration, 'EaseOut', 'Circular'); +if (this.slide_dir == 1) { +this.new_text.style.left = '' + Math.floor( this.slide_width - (this.slide_width * value) ) + 'px'; +this.old_text.style.left = '-' + Math.floor( this.slide_width * value ) + 'px'; +} +else { +this.new_text.style.left = '-' + Math.floor( this.slide_width - (this.slide_width * value) ) + 'px'; +this.old_text.style.left = '' + Math.floor( this.slide_width * value ) + 'px'; +} +this.new_photo.setOpacity( value ); +this.old_photo.setOpacity( 1.0 - value ); +var bkp = Math.floor( this.bk_pos + ((this.bk_pos_target - this.bk_pos) * value) ); +$('d_header').style.backgroundPosition = '' + bkp + 'px 0px'; +this.slide_timer = setTimeout( '$P("Main").animate_mhs_slide()', 33 ); +} +}, +prev_slide: function() { +this.bk_pos_target += 200; +this.slide_idx--; +if (this.slide_idx < 0) this.slide_idx += this.num_slides; +this.slide_dir = -1; +this.draw_slide(); +}, +next_slide: function() { +this.bk_pos_target -= 200; +this.slide_idx++; +if (this.slide_idx >= this.num_slides) this.slide_idx -= this.num_slides; +this.slide_dir = 1; +this.draw_slide(); +} +} ); +Class.subclass( Effect.Page, "Effect.Page.PublicGameList", { +onActivate: function() { +Nav.bar( +['Main', 'EffectGames.com'], +['PublicGameList', "All Public Games"] +); +Nav.title( "List of All Public Game Projects" ); +effect_api_get( 'get_site_info', { cat: 'all_pub_games' }, [this, 'receive_all_pub_games'], { } ); +this.div.innerHTML = loading_image(); +return true; +}, +onDeactivate: function() { +this.div.innerHTML = ''; +return true; +}, +receive_all_pub_games: function(response, tx) { +var html = ''; +html += '

      List of All Public Game Projects

      '; +html += '
      This is the complete list of public games currently being built by our users, presented in alphabetical order. Maybe they could use some help! Check out the game project pages and see (requires user account).
      '; +if (response.Data && response.Data.Games && response.Data.Games.Game) { +var games = always_array( response.Data.Games.Game ); +for (var idx = 0, len = games.length; idx < len; idx++) { +var game = games[idx]; +html += '
      ' + +(game.Logo ? +user_image_thumbnail(game.Logo, 80, 60) : +'' +) + '
      ' + ww_fit_box(game.Title, 80, 2, session.em_width, 1) + '
      '; +} +html += '
      '; +} +else { +html += 'No public games found! Why not create a new one?'; +} +this.div.innerHTML = html; +} +} ); +Class.subclass( Effect.Page, "Effect.Page.Search", { +onActivate: function(args) { +if (!args) args = {}; +var search_text = args.q; +var start = args.s || 0; +if (!start) start = 0; +var title = 'Search results for "'+search_text+'"'; +Nav.bar( +['Main', 'EffectGames.com'], +['Search?q=' + escape(search_text), "Search Results"] +); +Nav.title( title ); +this.last_search_text = search_text; +$('d_article_search').innerHTML = loading_image(); +load_script( 'http://www.google.com/uds/GwebSearch?callback=receive_google_search_results&context=0&lstkp=0&rsz=large&hl=en&source=gsc&gss=.com&sig=&q='+escape(search_text)+'%20site%3Ahttp%3A%2F%2Fwww.effectgames.com%2F&key=notsupplied&v=1.0&start='+start+'&nocache=' + (new Date()).getTime() ); +$('h_article_search').innerHTML = title; +return true; +}, +onDeactivate: function(new_page) { +$('fe_search_bar').value = ''; +$('d_article_search').innerHTML = ''; +return true; +} +} ); +function do_search_bar() { +var search_text = $('fe_search_bar').value; +if (search_text.length) { +Nav.go('Search?q=' + escape(search_text)); +} +} +function receive_google_search_results(context, response) { +var html = ''; +html += '
      Powered by
      '; +if (response.results.length) { +for (var idx = 0, len = response.results.length; idx < len; idx++) { +var row = response.results[idx]; +var url = row.unescapedUrl.replace(/^.+article\.psp\.html/, '#Article'); +html += '
      '; +html += ''; +html += '
      ' + row.content + '
      '; +html += '
      '; +} +} +else { +html += 'No results found.'; +} +if (response.cursor.pages) { +html += '
      Page: '; +for (var idx = 0, len = response.cursor.pages.length; idx < len; idx++) { +html += ''; +var page = response.cursor.pages[idx]; +var url = '#Search?q=' + escape($P('Search').last_search_text) + '&s=' + page.start; +if (response.cursor.currentPageIndex != idx) html += ''; +else html += ''; +html += page.label; +if (response.cursor.currentPageIndex != idx) html += ''; +else html += ''; +html += ''; +} +html += '
      '; +} +$('d_article_search').innerHTML = html; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/index.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/index.js new file mode 100644 index 0000000..8b164a4 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/index.js @@ -0,0 +1 @@ +exports.ZeParser = require('./ZeParser').ZeParser; diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/package.json b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/package.json new file mode 100644 index 0000000..1e982a0 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/package.json @@ -0,0 +1,17 @@ +{ + "author": "Peter van der Zee (http://qfox.nl/)", + "name": "zeparser", + "description": "My JavaScript parser", + "version": "0.0.5", + "homepage": "https://github.com/qfox/ZeParser/", + "repository": { + "type": "git", + "url": "git://github.com/qfox/ZeParser.git" + }, + "main": "./index", + "engines": { + "node": "*" + }, + "dependencies": {}, + "devDependencies": {} +} \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-parser.html b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-parser.html new file mode 100755 index 0000000..1ff5ff4 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-parser.html @@ -0,0 +1,26 @@ + + + + Parser Test Suite Page + + + + (c) qfox.nl
      + Parser test suite
      +
      Running...
      + + + + + + + \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-tokenizer.html b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-tokenizer.html new file mode 100755 index 0000000..0e0d1b1 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/test-tokenizer.html @@ -0,0 +1,23 @@ + + + + Tokenizer Test Suite Page + + + + (c) qfox.nl
      + + + + + + \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/tests.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/tests.js new file mode 100644 index 0000000..8a4138b --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/node_modules/zeparser/tests.js @@ -0,0 +1,478 @@ +// tests for both the tokenizer and parser. Parser test results could be checked tighter. +// api: [input, token-output-count, ?regex-hints, desc] +// regex-hints are for tokenizer, will tell for each token whether it might parse regex or not (parser's job) +var Tests = [ + +["var abc;", 4, "Variable Declaration"], +["var abc = 5;", 8, "Variable Declaration, Assignment"], +["/* */", 1, "Block Comment"], +["/** **/", 1, "JSDoc-style Comment"], +["var f = function(){;};", 13, "Assignment, Function Expression"], +["hi; // moo", 4, "Trailing Line Comment"], +["hi; // moo\n;", 6, "Trailing Line Comment, Linefeed, `;`"], +["var varwithfunction;", 4, "Variable Declaration, Identifier Containing Reserved Words, `;`"], +["a + b;", 6, "Addition/Concatenation"], + +["'a'", 1, "Single-Quoted String"], +["'a';", 2, "Single-Quoted String, `;`"], // Taken from the parser test suite. + +["'a\\n'", 1, "Single-Quoted String With Escaped Linefeed"], +["'a\\n';", 2, "Single-Quoted String With Escaped Linefeed, `;`"], // Taken from the parser test suite. + +["\"a\"", 1, "Double-Quoted String"], +["\"a\";", 2, "Double-Quoted String, `;`"], // Taken from the parser test suite. + +["\"a\\n\"", 1, "Double-Quoted String With Escaped Linefeed"], +["\"a\\n\";", 2, "Double-Quoted String With Escaped Linefeed, `;`"], // Taken from the parser test suite. + +["500", 1, "Integer"], +["500;", 2, "Integer, `;`"], // Taken from the parser test suite. + +["500.", 1, "Double With Trailing Decimal Point"], +["500.;", 2, "Double With Trailing Decimal Point"], // Taken from the parser test suite. + +["500.432", 1, "Double With Decimal Component"], +["500.432;", 2, "Double With Decimal Component, `;`"], // Taken from the parser test suite. + +[".432432", 1, "Number, 0 < Double < 1"], +[".432432;", 2, "Number, 0 < Double < 1, `;`"], // Taken from the parser test suite. + +["(a,b,c)", 7, "Parentheses, Comma-separated identifiers"], +["(a,b,c);", 8, "Parentheses, Comma-separated identifiers, `;`"], // Taken from the parser test suite. + +["[1,2,abc]", 7, "Array literal"], +["[1,2,abc];", 8, "Array literal, `;`"], // Taken from the parser test suite. + +["{a:1,\"b\":2,c:c}", 13, "Object literal"], +["var o = {a:1,\"b\":2,c:c};", 20, "Assignment, Object Literal, `;`"], // Taken from the parser test suite. + +["var x;\nvar y;", 9, "2 Variable Declarations, Multiple lines"], +["var x;\nfunction n(){ }", 13, "Variable, Linefeed, Function Declaration"], +["var x;\nfunction n(abc){ }", 14, "Variable, Linefeed, Function Declaration With One Argument"], +["var x;\nfunction n(abc, def){ }", 17, "Variable, Linefeed, Function Declaration With Multiple Arguments"], +["function n(){ \"hello\"; }", 11, "Function Declaration, Body"], + +["/a/;", 2, [true, false], "RegExp Literal, `;`"], +["/a/b;", 2, [true, true], "RegExp Literal, Flags, `;`"], +["++x;", 3, "Unary Increment, Prefix, `;`"], +[" / /;", 3, [true, true, false], "RegExp, Leading Whitespace, `;`"], +["/ / / / /", 5, [true, false, false, false, true], "RegExp Containing One Space, Space, Division, Space, RegExp Containing One Space"], + +// Taken from the parser test suite. + +["\"var\";", 2, "Keyword String, `;`"], +["\"variable\";", 2, "String Beginning With Keyword, `;`"], +["\"somevariable\";", 2, "String Containing Keyword, `;`"], +["\"somevar\";", 2, "String Ending With Keyword, `;`"], + +["var varwithfunction;", 4, "Keywords should not be matched in identifiers"], + +["var o = {a:1};", 12, "Object Literal With Unquoted Property"], +["var o = {\"b\":2};", 12, "Object Literal With Quoted Property"], +["var o = {c:c};", 12, "Object Literal With Equivalent Property Name and Identifier"], + +["/a/ / /b/;", 6, [true, true, false, false, true, false], "RegExp, Division, RegExp, `;`"], +["a/b/c;", 6, "Triple Division (Identifier / Identifier / Identifier)"], + +["+function(){/regex/;};", 9, [false, false, false, false, false, true, false, false, false], "Unary `+` Operator, Function Expression Containing RegExp and Semicolon, `;`"], + +// Line Terminators. +["\r\n", 1, "CRLF Line Ending = 1 Linefeed"], +["\r", 1, "CR Line Ending = 1 Linefeed"], +["\n", 1, "LF Line Ending = 1 Linefeed"], +["\r\n\n\u2028\u2029\r", 5, "Various Line Terminators"], + +// Whitespace. +["a \t\u000b\u000c\u00a0\uFFFFb", 8, "Whitespace"], + +// Comments. +["//foo!@#^&$1234\nbar;", 4, "Line Comment, Linefeed, Identifier, `;`"], +["/* abcd!@#@$* { } && null*/;", 2, "Single-Line Block Comment, `;`"], +["/*foo\nbar*/;", 2, "Multi-Line Block Comment, `;`"], +["/*x*x*/;", 2, "Block Comment With Asterisks, `;`"], +["/**/;", 2, "Empty Comment, `;`"], + +// Identifiers. +["x;", 2, "Single-Character Identifier, `;`"], +["_x;", 2, "Identifier With Leading `_`, `;`"], +["xyz;", 2, "Identifier With Letters Only, `;`"], +["$x;", 2, "Identifier With Leading `$`, `;`"], +["x5;", 2, "Identifier With Number As Second Character, `;`"], +["x_y;", 2, "Identifier Containing `_`, `;`"], +["x+5;", 4, "Identifier, Binary `+` Operator, Identifier, `;`"], +["xyz123;", 2, "Alphanumeric Identifier, `;`"], +["x1y1z1;", 2, "Alternating Alphanumeric Identifier, `;`"], +["foo\\u00d8bar;", 2, "Identifier With Unicode Escape Sequence (`\\uXXXX`), `;`"], +["f\u00d8\u00d8bar;", 2, "Identifier With Embedded Unicode Character"], + +// Numbers. +["5;", 2, "Integer, `;`"], +["5.5;", 2, "Double, `;`"], +["0;", 2, "Integer Zero, `;`"], +["0.0;", 2, "Double Zero, `;`"], +["0.001;", 2, "0 < Decimalized Double < 1, `;`"], +["1.e2;", 2, "Integer With Decimal and Exponential Component (`e`), `;`"], +["1.e-2;", 2, "Integer With Decimal and Negative Exponential Component, `;`"], +["1.E2;", 2, "Integer With Decimal and Uppercase Exponential Component (`E`), `;`"], +["1.E-2;", 2, "Integer With Decimal and Uppercase Negative Exponential Component, `;`"], +[".5;", 2, "0 < Double < 1, `;`"], +[".5e3;", 2, "(0 < Double < 1) With Exponential Component"], +[".5e-3;", 2, "(0 < Double < 1) With Negative Exponential Component"], +["0.5e3;", 2, "(0 < Decimalized Double < 1) With Exponential Component"], +["55;", 2, "Two-Digit Integer, `;`"], +["123;", 2, "Three-Digit Integer, `;`"], +["55.55;", 2, "Two-Digit Double, `;`"], +["55.55e10;", 2, "Two-Digit Double With Exponential Component, `;`"], +["123.456;", 2, "Three-Digit Double, `;`"], +["1+e;", 4, "Additive Expression, `;`"], +["0x01;", 2, "Hexadecimal `1` With 1 Leading Zero, `;`"], +["0xcafe;", 2, "Hexadecimal `51966`, `;`"], +["0x12345678;", 2, "Hexadecimal `305419896`, `;`"], +["0x1234ABCD;", 2, "Hexadecimal `305441741` With Uppercase Letters, `;`"], +["0x0001;", 2, "Hexadecimal `1` with 3 Leading Zeros, `;`"], + +// Strings. +["\"foo\";", 2, "Multi-Character Double-Quoted String, `;`"], +["\"a\\n\";", 2, "Double-Quoted String Containing Linefeed, `;`"], +["\'foo\';", 2, "Single-Quoted String, `;`"], +["'a\\n';", 2, "Single-Quoted String Containing Linefeed, `;`"], +["\"x\";", 2, "Single-Character Double-Quoted String, `;`"], +["'';", 2, "Empty Single-Quoted String, `;`"], +["\"foo\\tbar\";", 2, "Double-Quoted String With Tab Character, `;`"], +["\"!@#$%^&*()_+{}[]\";", 2, "Double-Quoted String Containing Punctuators, `;`"], +["\"/*test*/\";", 2, "Double-Quoted String Containing Block Comment, `;`"], +["\"//test\";", 2, "Double-Quoted String Containing Line Comment, `;`"], +["\"\\\\\";", 2, "Double-Quoted String Containing Reverse Solidus, `;`"], +["\"\\u0001\";", 2, "Double-Quoted String Containing Numeric Unicode Escape Sequence, `;`"], +["\"\\uFEFF\";", 2, "Double-Quoted String Containing Alphanumeric Unicode Escape Sequence, `;`"], +["\"\\u10002\";", 2, "Double-Quoted String Containing 5-Digit Unicode Escape Sequence, `;`"], +["\"\\x55\";", 2, "Double-Quoted String Containing Hex Escape Sequence, `;`"], +["\"\\x55a\";", 2, "Double-Quoted String Containing Hex Escape Sequence and Additional Character, `;`"], +["\"a\\\\nb\";", 2, "Double-Quoted String Containing Escaped Linefeed, `;`"], +["\";\"", 1, "Double-Quoted String Containing `;`"], +["\"a\\\nb\";", 2, "Double-Quoted String Containing Reverse Solidus and Linefeed, `;`"], +["'\\\\'+ ''", 4, "Single-Quoted String Containing Reverse Solidus, `+`, Empty Single-Quoted String"], + +// `null`, `true`, and `false`. +["null;", 2, "`null`, `;`"], +["true;", 2, "`true`, `;`"], +["false;", 2, "`false`, `;`"], + +// RegExps +["/a/;", 2, [true, true], "Single-Character RegExp, `;`"], +["/abc/;", 2, [true, true], "Multi-Character RegExp, `;`"], +["/abc[a-z]*def/g;", 2, [true, true], "RegExp Containing Character Range and Quantifier, `;`"], +["/\\b/;", 2, [true, true], "RegExp Containing Control Character, `;`"], +["/[a-zA-Z]/;", 2, [true, true], "RegExp Containing Extended Character Range, `;`"], +["/foo(.*)/g;", 2, [true, false], "RegExp Containing Capturing Group and Quantifier, `;`"], + +// Array Literals. +["[];", 3, "Empty Array, `;`"], +["[\b\n\f\r\t\x20];", 9, "Array Containing Whitespace, `;`"], +["[1];", 4, "Array Containing 1 Element, `;`"], +["[1,2];", 6, "Array Containing 2 Elements, `;`"], +["[1,2,,];", 8, "Array Containing 2 Elisions, `;`"], +["[1,2,3];", 8, "Array Containing 3 Elements, `;`"], +["[1,2,3,,,];", 11, "Array Containing 3 Elisions, `;`"], + +// Object Literals. +["({x:5});", 8, "Object Literal Containing 1 Member; `;`"], +["({x:5,y:6});", 12, "Object Literal Containing 2 Members, `;`"], +["({x:5,});", 9, "Object Literal Containing 1 Member and Trailing Comma, `;`"], +["({if:5});", 8, "Object Literal Containing Reserved Word Property Name, `;`"], +["({ get x() {42;} });", 17, "Object Literal Containing Getter, `;`"], +["({ set y(a) {1;} });", 18, "Object Literal Containing Setter, `;`"], + +// Member Expressions. +["o.m;", 4, "Dot Member Accessor, `;`"], +["o['m'];", 5, "Square Bracket Member Accessor, `;`"], +["o['n']['m'];", 8, "Nested Square Bracket Member Accessor, `;`"], +["o.n.m;", 6, "Nested Dot Member Accessor, `;`"], +["o.if;", 4, "Dot Reserved Property Name Accessor, `;`"], + +// Function Calls. +["f();", 4, "Function Call Operator, `;`"], +["f(x);", 5, "Function Call Operator With 1 Argument, `;`"], +["f(x,y);", 7, "Function Call Operator With Multiple Arguments, `;`"], +["o.m();", 6, "Dot Member Accessor, Function Call, `;`"], +["o['m']();", 7, "Square Bracket Member Accessor, Function Call, `;`"], +["o.m(x);", 7, "Dot Member Accessor, Function Call With 1 Argument, `;`"], +["o['m'](x);", 8, "Square Bracket Member Accessor, Function Call With 1 Argument, `;`"], +["o.m(x,y);", 9, "Dot Member Accessor, Function Call With 2 Arguments, `;`"], +["o['m'](x,y);", 10, "Square Bracket Member Accessor, Function Call With 2 Arguments, `;`"], +["f(x)(y);", 8, "Nested Function Call With 1 Argument Each, `;`"], +["f().x;", 6, "Function Call, Dot Member Accessor, `;`"], + +// `eval` Function. +["eval('x');", 5, "`eval` Invocation With 1 Argument, `;`"], +["(eval)('x');", 7, "Direct `eval` Call Example, `;`"], +["(1,eval)('x');", 9, "Indirect `eval` Call Example, `;`"], +["eval(x,y);", 7, "`eval` Invocation With 2 Arguments, `;`"], + +// `new` Operator. +["new f();", 6, "`new` Operator, Function Call, `;`"], +["new o;", 4, "`new` Operator, Identifier, `;`"], +["new o.m;", 6, "`new` Operator, Dot Member Accessor, `;`"], +["new o.m(x);", 9, "`new` Operator, Dot Member Accessor, Function Call With 1 Argument, `;`"], +["new o.m(x,y);", 11, "``new` Operator, Dot Member Accessor, Function Call With 2 Arguments , `;`"], + +// Prefix and Postfix Increment. +["++x;", 3, "Prefix Increment, Identifier, `;`"], +["x++;", 3, "Identifier, Postfix Increment, `;`"], +["--x;", 3, "Prefix Decrement, Identifier, `;`"], +["x--;", 3, "Postfix Decrement, Identifier, `;`"], +["x ++;", 4, "Identifier, Space, Postfix Increment, `;`"], +["x /* comment */ ++;", 6, "Identifier, Block Comment, Postfix Increment, `;`"], +["++ /* comment */ x;", 6, "Prefix Increment, Block Comment, Identifier, `;`"], + +// Unary Operators. +["delete x;", 4, "`delete` Operator, Space, Identifier, `;`"], +["void x;", 4, "`void` Operator, Space, Identifier, `;`"], +["typeof x;", 4, "`typeof` Operator, Space, Identifier, `;`"], +["+x;", 3, "Unary `+` Operator, Identifier, `;`"], +["-x;", 3, "Unary Negation Operator, Identifier, `;`"], +["~x;", 3, "Bitwise NOT Operator, Identifier, `;`"], +["!x;", 3, "Logical NOT Operator, Identifier, `;`"], + +// Comma Operator. +["x, y;", 5, "Comma Operator"], + +// Miscellaneous. +["new Date++;", 5, "`new` Operator, Identifier, Postfix Increment, `;`"], +["+x++;", 4, "Unary `+`, Identifier, Postfix Increment, `;`"], + +// Expressions. +["1 * 2;", 6, "Integer, Multiplication, Integer, `;`"], +["1 / 2;", 6, "Integer, Division, Integer, `;`"], +["1 % 2;", 6, "Integer, Modulus, Integer, `;`"], +["1 + 2;", 6, "Integer, Addition, Integer, `;`"], +["1 - 2;", 6, "Integer, Subtraction, Integer, `;`"], +["1 << 2;", 6, "Integer, Bitwise Left Shift, Integer, `;`"], +["1 >>> 2;", 6, "Integer, Bitwise Zero-fill Right Shift, Integer, `;`"], +["1 >> 2;", 6, "Integer, Bitwise Sign-Propagating Right Shift, Integer, `;`"], +["1 * 2 + 3;", 10, "Order-of-Operations Expression, `;`"], +["(1+2)*3;", 8, "Parenthesized Additive Expression, Multiplication, `;`"], +["1*(2+3);", 8, "Multiplication, Parenthesized Additive Expression, `;`"], +["xy;", 4, "Greater-Than Relational Operator, `;`"], +["x<=y;", 4, "Less-Than-or-Equal-To Relational Operator, `;`"], +["x>=y;", 4, "Greater-Than-or-Equal-To Relational Operator, `;`"], +["x instanceof y;", 6, "`instanceof` Operator, `;`"], +["x in y;", 6, "`in` Operator, `;`"], +["x&y;", 4, "Bitwise AND Operator, `;`"], +["x^y;", 4, "Bitwise XOR Operator, `;`"], +["x|y;", 4, "Bitwise OR Operator, `;`"], +["x+y>>= y;", 6, "Bitwise Zero-Fill Right Shift Assignment, `;`"], +["x <<= y;", 6, "Bitwise Left Shift Assignment, `;`"], +["x += y;", 6, "Additive Assignment, `;`"], +["x -= y;", 6, "Subtractive Assignment, `;`"], +["x *= y;", 6, "Multiplicative Assignment, `;`"], +["x /= y;", 6, "Divisive Assignment, `;`"], +["x %= y;", 6, "Modulus Assignment, `;`"], +["x >>= y;", 6, "Bitwise Sign-Propagating Right Shift Assignment, `;`"], +["x &= y;", 6, "Bitwise AND Assignment, `;`"], +["x ^= y;", 6, "Bitwise XOR Assignment, `;`"], +["x |= y;", 6, "Bitwise OR Assignment, `;`"], + +// Blocks. +["{};", 3, "Empty Block, `;`"], +["{x;};", 5, "Block Containing 1 Identifier, `;`"], +["{x;y;};", 7, "Block Containing 2 Identifiers, `;`"], + +// Variable Declarations. +["var abc;", 4, "Variable Declaration"], +["var x,y;", 6, "Comma-Separated Variable Declarations, `;`"], +["var x=1,y=2;", 10, "Comma-Separated Variable Initializations, `;`"], +["var x,y=2;", 8, "Variable Declaration, Variable Initialization, `;`"], + +// Empty Statements. +[";", 1, "Empty Statement"], +["\n;", 2, "Linefeed, `;`"], + +// Expression Statements. +["x;", 2, "Identifier, `;`"], +["5;", 2, "Integer, `;`"], +["1+2;", 4, "Additive Statement, `;`"], + +// `if...else` Statements. +["if (c) x; else y;", 13, "Space-Delimited `if...else` Statement"], +["if (c) x;", 8, "Space-Delimited `if` Statement, `;`"], +["if (c) {} else {};", 14, "Empty Block-Delimited `if...else` Statement"], +["if (c1) if (c2) s1; else s2;", 19, "Nested `if...else` Statement Without Dangling `else`"], + +// `while` and `do...while` Loops. +["do s; while (e);", 11, "Space-Delimited `do...while` Loop"], +["do { s; } while (e);", 15, "Block-Delimited `do...while` Loop"], +["while (e) s;", 8, "Space-Delimited `while` Loop"], +["while (e) { s; };", 13, "Block-Delimited `while` Loop"], + +// `for` and `for...in` Loops. +["for (;;) ;", 8, "Infinite Space-Delimited `for` Loop"], +["for (;c;x++) x;", 12, "`for` Loop: Empty Initialization Condition; Space-Delimited Body"], +["for (i;i (http://debuggable.com/)", + "name": "active-x-obfuscator", + "description": "A module to (safely) obfuscate all occurrences of the string 'ActiveX' inside any JavaScript code.", + "version": "0.0.1", + "homepage": "https://github.com/felixge/node-active-x-obfuscator", + "repository": { + "type": "git", + "url": "git://github.com/felixge/node-active-x-obfuscator.git" + }, + "main": "./index", + "scripts": { + "test": "node test.js" + }, + "engines": { + "node": "*" + }, + "dependencies": { + "zeparser": "0.0.5" + }, + "devDependencies": {}, + "optionalDependencies": {} +} \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/test.js b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/test.js new file mode 100644 index 0000000..e8fc807 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/active-x-obfuscator/test.js @@ -0,0 +1,53 @@ +var activeXObfuscator = require('./index'); +var assert = require('assert'); + +var OBFUSCATED_ACTIVE_X_OBJECT = activeXObfuscator.OBFUSCATED_ACTIVE_X_OBJECT; +var OBFUSCATED_ACTIVE_X = activeXObfuscator.OBFUSCATED_ACTIVE_X; + +var input = + "foo(new ActiveXObject('Microsoft.XMLHTTP'))"; +var expected = + "foo(new window[" + OBFUSCATED_ACTIVE_X_OBJECT + "]('Microsoft.XMLHTTP'))"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo = 'ActiveXObject';"; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X_OBJECT + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + 'var foo = "ActiveXObject";'; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X_OBJECT + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + 'var foo = o.ActiveXObject;'; +var expected = + "var foo = o[" + OBFUSCATED_ACTIVE_X_OBJECT + "];"; +assert.equal(activeXObfuscator(input), expected); + +var input = + 'var foo = "ActiveX";'; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo = 'ActiveX';"; +var expected = + "var foo = " + OBFUSCATED_ACTIVE_X + ";"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo; // ActiveX is cool"; +var expected = + "var foo; // Ac...eX is cool"; +assert.equal(activeXObfuscator(input), expected); + +var input = + "var foo = 'ActiveX is cool';"; +assert.throws(function() { + activeXObfuscator(input); +}, /Unknown ActiveX occurence/); diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/.npmignore b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/.npmignore new file mode 100644 index 0000000..d97eaa0 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/.npmignore @@ -0,0 +1,4 @@ +.DS_Store +.tmp*~ +*.local.* +.pinf-* \ No newline at end of file diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.html b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.html new file mode 100644 index 0000000..5f37ac0 --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.html @@ -0,0 +1,981 @@ + + + + +UglifyJS – a JavaScript parser/compressor/beautifier + + + + + + + + + + + + + +
      + +
      + +
      +

      UglifyJS – a JavaScript parser/compressor/beautifier

      + + + + +
      +

      1 UglifyJS — a JavaScript parser/compressor/beautifier

      +
      + + +

      +This package implements a general-purpose JavaScript +parser/compressor/beautifier toolkit. It is developed on NodeJS, but it +should work on any JavaScript platform supporting the CommonJS module system +(and if your platform of choice doesn't support CommonJS, you can easily +implement it, or discard the exports.* lines from UglifyJS sources). +

      +

      +The tokenizer/parser generates an abstract syntax tree from JS code. You +can then traverse the AST to learn more about the code, or do various +manipulations on it. This part is implemented in parse-js.js and it's a +port to JavaScript of the excellent parse-js Common Lisp library from Marijn Haverbeke. +

      +

      +( See cl-uglify-js if you're looking for the Common Lisp version of +UglifyJS. ) +

      +

      +The second part of this package, implemented in process.js, inspects and +manipulates the AST generated by the parser to provide the following: +

      +
        +
      • ability to re-generate JavaScript code from the AST. Optionally + indented—you can use this if you want to “beautify” a program that has + been compressed, so that you can inspect the source. But you can also run + our code generator to print out an AST without any whitespace, so you + achieve compression as well. + +
      • +
      • shorten variable names (usually to single characters). Our mangler will + analyze the code and generate proper variable names, depending on scope + and usage, and is smart enough to deal with globals defined elsewhere, or + with eval() calls or with{} statements. In short, if eval() or + with{} are used in some scope, then all variables in that scope and any + variables in the parent scopes will remain unmangled, and any references + to such variables remain unmangled as well. + +
      • +
      • various small optimizations that may lead to faster code but certainly + lead to smaller code. Where possible, we do the following: + +
          +
        • foo["bar"] ==> foo.bar + +
        • +
        • remove block brackets {} + +
        • +
        • join consecutive var declarations: + var a = 10; var b = 20; ==> var a=10,b=20; + +
        • +
        • resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the + replacement if the result occupies less bytes; for example 1/3 would + translate to 0.333333333333, so in this case we don't replace it. + +
        • +
        • consecutive statements in blocks are merged into a sequence; in many + cases, this leaves blocks with a single statement, so then we can remove + the block brackets. + +
        • +
        • various optimizations for IF statements: + +
            +
          • if (foo) bar(); else baz(); ==> foo?bar():baz(); +
          • +
          • if (!foo) bar(); else baz(); ==> foo?baz():bar(); +
          • +
          • if (foo) bar(); ==> foo&&bar(); +
          • +
          • if (!foo) bar(); ==> foo||bar(); +
          • +
          • if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); +
          • +
          • if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} + +
          • +
          + +
        • +
        • remove some unreachable code and warn about it (code that follows a + return, throw, break or continue statement, except + function/variable declarations). + +
        • +
        • act a limited version of a pre-processor (c.f. the pre-processor of + C/C++) to allow you to safely replace selected global symbols with + specified values. When combined with the optimisations above this can + make UglifyJS operate slightly more like a compilation process, in + that when certain symbols are replaced by constant values, entire code + blocks may be optimised away as unreachable. +
        • +
        + +
      • +
      + + + +
      + +
      +

      1.1 Unsafe transformations

      +
      + + +

      +The following transformations can in theory break code, although they're +probably safe in most practical cases. To enable them you need to pass the +--unsafe flag. +

      + +
      + +
      +

      1.1.1 Calls involving the global Array constructor

      +
      + + +

      +The following transformations occur: +

      + + + +
      new Array(1, 2, 3, 4)  => [1,2,3,4]
      +Array(a, b, c)         => [a,b,c]
      +new Array(5)           => Array(5)
      +new Array(a)           => Array(a)
      +
      + + +

      +These are all safe if the Array name isn't redefined. JavaScript does allow +one to globally redefine Array (and pretty much everything, in fact) but I +personally don't see why would anyone do that. +

      +

      +UglifyJS does handle the case where Array is redefined locally, or even +globally but with a function or var declaration. Therefore, in the +following cases UglifyJS doesn't touch calls or instantiations of Array: +

      + + + +
      // case 1.  globally declared variable
      +  var Array;
      +  new Array(1, 2, 3);
      +  Array(a, b);
      +
      +  // or (can be declared later)
      +  new Array(1, 2, 3);
      +  var Array;
      +
      +  // or (can be a function)
      +  new Array(1, 2, 3);
      +  function Array() { ... }
      +
      +// case 2.  declared in a function
      +  (function(){
      +    a = new Array(1, 2, 3);
      +    b = Array(5, 6);
      +    var Array;
      +  })();
      +
      +  // or
      +  (function(Array){
      +    return Array(5, 6, 7);
      +  })();
      +
      +  // or
      +  (function(){
      +    return new Array(1, 2, 3, 4);
      +    function Array() { ... }
      +  })();
      +
      +  // etc.
      +
      + + +
      + +
      + +
      +

      1.1.2 obj.toString() ==> obj+“”

      +
      + + +
      +
      + +
      + +
      +

      1.2 Install (NPM)

      +
      + + +

      +UglifyJS is now available through NPM — npm install uglify-js should do +the job. +

      +
      + +
      + +
      +

      1.3 Install latest code from GitHub

      +
      + + + + + +
      ## clone the repository
      +mkdir -p /where/you/wanna/put/it
      +cd /where/you/wanna/put/it
      +git clone git://github.com/mishoo/UglifyJS.git
      +
      +## make the module available to Node
      +mkdir -p ~/.node_libraries/
      +cd ~/.node_libraries/
      +ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js
      +
      +## and if you want the CLI script too:
      +mkdir -p ~/bin
      +cd ~/bin
      +ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs
      +  # (then add ~/bin to your $PATH if it's not there already)
      +
      + + +
      + +
      + +
      +

      1.4 Usage

      +
      + + +

      +There is a command-line tool that exposes the functionality of this library +for your shell-scripting needs: +

      + + + +
      uglifyjs [ options... ] [ filename ]
      +
      + + +

      +filename should be the last argument and should name the file from which +to read the JavaScript code. If you don't specify it, it will read code +from STDIN. +

      +

      +Supported options: +

      +
        +
      • -b or --beautify — output indented code; when passed, additional + options control the beautifier: + +
          +
        • -i N or --indent N — indentation level (number of spaces) + +
        • +
        • -q or --quote-keys — quote keys in literal objects (by default, + only keys that cannot be identifier names will be quotes). + +
        • +
        + +
      • +
      • --ascii — pass this argument to encode non-ASCII characters as + \uXXXX sequences. By default UglifyJS won't bother to do it and will + output Unicode characters instead. (the output is always encoded in UTF8, + but if you pass this option you'll only get ASCII). + +
      • +
      • -nm or --no-mangle — don't mangle names. + +
      • +
      • -nmf or --no-mangle-functions – in case you want to mangle variable + names, but not touch function names. + +
      • +
      • -ns or --no-squeeze — don't call ast_squeeze() (which does various + optimizations that result in smaller, less readable code). + +
      • +
      • -mt or --mangle-toplevel — mangle names in the toplevel scope too + (by default we don't do this). + +
      • +
      • --no-seqs — when ast_squeeze() is called (thus, unless you pass + --no-squeeze) it will reduce consecutive statements in blocks into a + sequence. For example, "a = 10; b = 20; foo();" will be written as + "a=10,b=20,foo();". In various occasions, this allows us to discard the + block brackets (since the block becomes a single statement). This is ON + by default because it seems safe and saves a few hundred bytes on some + libs that I tested it on, but pass --no-seqs to disable it. + +
      • +
      • --no-dead-code — by default, UglifyJS will remove code that is + obviously unreachable (code that follows a return, throw, break or + continue statement and is not a function/variable declaration). Pass + this option to disable this optimization. + +
      • +
      • -nc or --no-copyright — by default, uglifyjs will keep the initial + comment tokens in the generated code (assumed to be copyright information + etc.). If you pass this it will discard it. + +
      • +
      • -o filename or --output filename — put the result in filename. If + this isn't given, the result goes to standard output (or see next one). + +
      • +
      • --overwrite — if the code is read from a file (not from STDIN) and you + pass --overwrite then the output will be written in the same file. + +
      • +
      • --ast — pass this if you want to get the Abstract Syntax Tree instead + of JavaScript as output. Useful for debugging or learning more about the + internals. + +
      • +
      • -v or --verbose — output some notes on STDERR (for now just how long + each operation takes). + +
      • +
      • -d SYMBOL[=VALUE] or --define SYMBOL[=VALUE] — will replace + all instances of the specified symbol where used as an identifier + (except where symbol has properly declared by a var declaration or + use as function parameter or similar) with the specified value. This + argument may be specified multiple times to define multiple + symbols - if no value is specified the symbol will be replaced with + the value true, or you can specify a numeric value (such as + 1024), a quoted string value (such as ="object"= or + ='https://github.com'), or the name of another symbol or keyword (such as =null or document). + This allows you, for example, to assign meaningful names to key + constant values but discard the symbolic names in the uglified + version for brevity/efficiency, or when used wth care, allows + UglifyJS to operate as a form of conditional compilation + whereby defining appropriate values may, by dint of the constant + folding and dead code removal features above, remove entire + superfluous code blocks (e.g. completely remove instrumentation or + trace code for production use). + Where string values are being defined, the handling of quotes are + likely to be subject to the specifics of your command shell + environment, so you may need to experiment with quoting styles + depending on your platform, or you may find the option + --define-from-module more suitable for use. + +
      • +
      • -define-from-module SOMEMODULE — will load the named module (as + per the NodeJS require() function) and iterate all the exported + properties of the module defining them as symbol names to be defined + (as if by the --define option) per the name of each property + (i.e. without the module name prefix) and given the value of the + property. This is a much easier way to handle and document groups of + symbols to be defined rather than a large number of --define + options. + +
      • +
      • --unsafe — enable other additional optimizations that are known to be + unsafe in some contrived situations, but could still be generally useful. + For now only these: + +
          +
        • foo.toString() ==> foo+"" +
        • +
        • new Array(x,…) ==> [x,…] +
        • +
        • new Array(x) ==> Array(x) + +
        • +
        + +
      • +
      • --max-line-len (default 32K characters) — add a newline after around + 32K characters. I've seen both FF and Chrome croak when all the code was + on a single line of around 670K. Pass –max-line-len 0 to disable this + safety feature. + +
      • +
      • --reserved-names — some libraries rely on certain names to be used, as + pointed out in issue #92 and #81, so this option allow you to exclude such + names from the mangler. For example, to keep names require and $super + intact you'd specify –reserved-names "require,$super". + +
      • +
      • --inline-script – when you want to include the output literally in an + HTML <script> tag you can use this option to prevent </script from + showing up in the output. + +
      • +
      • --lift-vars – when you pass this, UglifyJS will apply the following + transformations (see the notes in API, ast_lift_variables): + +
          +
        • put all var declarations at the start of the scope +
        • +
        • make sure a variable is declared only once +
        • +
        • discard unused function arguments +
        • +
        • discard unused inner (named) functions +
        • +
        • finally, try to merge assignments into that one var declaration, if + possible. +
        • +
        + +
      • +
      + + + +
      + +
      +

      1.4.1 API

      +
      + + +

      +To use the library from JavaScript, you'd do the following (example for +NodeJS): +

      + + + +
      var jsp = require("uglify-js").parser;
      +var pro = require("uglify-js").uglify;
      +
      +var orig_code = "... JS code here";
      +var ast = jsp.parse(orig_code); // parse code and get the initial AST
      +ast = pro.ast_mangle(ast); // get a new AST with mangled names
      +ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
      +var final_code = pro.gen_code(ast); // compressed code here
      +
      + + +

      +The above performs the full compression that is possible right now. As you +can see, there are a sequence of steps which you can apply. For example if +you want compressed output but for some reason you don't want to mangle +variable names, you would simply skip the line that calls +pro.ast_mangle(ast). +

      +

      +Some of these functions take optional arguments. Here's a description: +

      +
        +
      • jsp.parse(code, strict_semicolons) – parses JS code and returns an AST. + strict_semicolons is optional and defaults to false. If you pass + true then the parser will throw an error when it expects a semicolon and + it doesn't find it. For most JS code you don't want that, but it's useful + if you want to strictly sanitize your code. + +
      • +
      • pro.ast_lift_variables(ast) – merge and move var declarations to the + scop of the scope; discard unused function arguments or variables; discard + unused (named) inner functions. It also tries to merge assignments + following the var declaration into it. + +

        + If your code is very hand-optimized concerning var declarations, this + lifting variable declarations might actually increase size. For me it + helps out. On jQuery it adds 865 bytes (243 after gzip). YMMV. Also + note that (since it's not enabled by default) this operation isn't yet + heavily tested (please report if you find issues!). +

        +

        + Note that although it might increase the image size (on jQuery it gains + 865 bytes, 243 after gzip) it's technically more correct: in certain + situations, dead code removal might drop variable declarations, which + would not happen if the variables are lifted in advance. +

        +

        + Here's an example of what it does: +

      • +
      + + + + + +
      function f(a, b, c, d, e) {
      +    var q;
      +    var w;
      +    w = 10;
      +    q = 20;
      +    for (var i = 1; i < 10; ++i) {
      +        var boo = foo(a);
      +    }
      +    for (var i = 0; i < 1; ++i) {
      +        var boo = bar(c);
      +    }
      +    function foo(){ ... }
      +    function bar(){ ... }
      +    function baz(){ ... }
      +}
      +
      +// transforms into ==>
      +
      +function f(a, b, c) {
      +    var i, boo, w = 10, q = 20;
      +    for (i = 1; i < 10; ++i) {
      +        boo = foo(a);
      +    }
      +    for (i = 0; i < 1; ++i) {
      +        boo = bar(c);
      +    }
      +    function foo() { ... }
      +    function bar() { ... }
      +}
      +
      + + +
        +
      • pro.ast_mangle(ast, options) – generates a new AST containing mangled + (compressed) variable and function names. It supports the following + options: + +
          +
        • toplevel – mangle toplevel names (by default we don't touch them). +
        • +
        • except – an array of names to exclude from compression. +
        • +
        • defines – an object with properties named after symbols to + replace (see the --define option for the script) and the values + representing the AST replacement value. + +
        • +
        + +
      • +
      • pro.ast_squeeze(ast, options) – employs further optimizations designed + to reduce the size of the code that gen_code would generate from the + AST. Returns a new AST. options can be a hash; the supported options + are: + +
          +
        • make_seqs (default true) which will cause consecutive statements in a + block to be merged using the "sequence" (comma) operator + +
        • +
        • dead_code (default true) which will remove unreachable code. + +
        • +
        + +
      • +
      • pro.gen_code(ast, options) – generates JS code from the AST. By + default it's minified, but using the options argument you can get nicely + formatted output. options is, well, optional :-) and if you pass it it + must be an object and supports the following properties (below you can see + the default values): + +
          +
        • beautify: false – pass true if you want indented output +
        • +
        • indent_start: 0 (only applies when beautify is true) – initial + indentation in spaces +
        • +
        • indent_level: 4 (only applies when beautify is true) -- + indentation level, in spaces (pass an even number) +
        • +
        • quote_keys: false – if you pass true it will quote all keys in + literal objects +
        • +
        • space_colon: false (only applies when beautify is true) – wether + to put a space before the colon in object literals +
        • +
        • ascii_only: false – pass true if you want to encode non-ASCII + characters as \uXXXX. +
        • +
        • inline_script: false – pass true to escape occurrences of + </script in strings +
        • +
        + +
      • +
      + + +
      + +
      + +
      +

      1.4.2 Beautifier shortcoming – no more comments

      +
      + + +

      +The beautifier can be used as a general purpose indentation tool. It's +useful when you want to make a minified file readable. One limitation, +though, is that it discards all comments, so you don't really want to use it +to reformat your code, unless you don't have, or don't care about, comments. +

      +

      +In fact it's not the beautifier who discards comments — they are dumped at +the parsing stage, when we build the initial AST. Comments don't really +make sense in the AST, and while we could add nodes for them, it would be +inconvenient because we'd have to add special rules to ignore them at all +the processing stages. +

      +
      + +
      + +
      +

      1.4.3 Use as a code pre-processor

      +
      + + +

      +The --define option can be used, particularly when combined with the +constant folding logic, as a form of pre-processor to enable or remove +particular constructions, such as might be used for instrumenting +development code, or to produce variations aimed at a specific +platform. +

      +

      +The code below illustrates the way this can be done, and how the +symbol replacement is performed. +

      + + + +
      CLAUSE1: if (typeof DEVMODE === 'undefined') {
      +    DEVMODE = true;
      +}
      +
      +CLAUSE2: function init() {
      +    if (DEVMODE) {
      +        console.log("init() called");
      +    }
      +    ....
      +    DEVMODE &amp;&amp; console.log("init() complete");
      +}
      +
      +CLAUSE3: function reportDeviceStatus(device) {
      +    var DEVMODE = device.mode, DEVNAME = device.name;
      +    if (DEVMODE === 'open') {
      +        ....
      +    }
      +}
      +
      + + +

      +When the above code is normally executed, the undeclared global +variable DEVMODE will be assigned the value true (see CLAUSE1) +and so the init() function (CLAUSE2) will write messages to the +console log when executed, but in CLAUSE3 a locally declared +variable will mask access to the DEVMODE global symbol. +

      +

      +If the above code is processed by UglifyJS with an argument of +--define DEVMODE=false then UglifyJS will replace DEVMODE with the +boolean constant value false within CLAUSE1 and CLAUSE2, but it +will leave CLAUSE3 as it stands because there DEVMODE resolves to +a validly declared variable. +

      +

      +And more so, the constant-folding features of UglifyJS will recognise +that the if condition of CLAUSE1 is thus always false, and so will +remove the test and body of CLAUSE1 altogether (including the +otherwise slightly problematical statement false = true; which it +will have formed by replacing DEVMODE in the body). Similarly, +within CLAUSE2 both calls to console.log() will be removed +altogether. +

      +

      +In this way you can mimic, to a limited degree, the functionality of +the C/C++ pre-processor to enable or completely remove blocks +depending on how certain symbols are defined - perhaps using UglifyJS +to generate different versions of source aimed at different +environments +

      +

      +It is recommmended (but not made mandatory) that symbols designed for +this purpose are given names consisting of UPPER_CASE_LETTERS to +distinguish them from other (normal) symbols and avoid the sort of +clash that CLAUSE3 above illustrates. +

      +
      +
      + +
      + +
      +

      1.5 Compression – how good is it?

      +
      + + +

      +Here are updated statistics. (I also updated my Google Closure and YUI +installations). +

      +

      +We're still a lot better than YUI in terms of compression, though slightly +slower. We're still a lot faster than Closure, and compression after gzip +is comparable. +

      + + ++ + + + + + + + + + +
      FileUglifyJSUglifyJS+gzipClosureClosure+gzipYUIYUI+gzip
      jquery-1.6.2.js91001 (0:01.59)3189690678 (0:07.40)31979101527 (0:01.82)34646
      paper.js142023 (0:01.65)43334134301 (0:07.42)42495173383 (0:01.58)48785
      prototype.js88544 (0:01.09)2668086955 (0:06.97)2632692130 (0:00.79)28624
      thelib-full.js (DynarchLIB)251939 (0:02.55)72535249911 (0:09.05)72696258869 (0:01.94)76584
      + + +
      + +
      + +
      +

      1.6 Bugs?

      +
      + + +

      +Unfortunately, for the time being there is no automated test suite. But I +ran the compressor manually on non-trivial code, and then I tested that the +generated code works as expected. A few hundred times. +

      +

      +DynarchLIB was started in times when there was no good JS minifier. +Therefore I was quite religious about trying to write short code manually, +and as such DL contains a lot of syntactic hacks1 such as “foo == bar ? a += 10 : b = 20”, though the more readable version would clearly be to use +“if/else”. +

      +

      +Since the parser/compressor runs fine on DL and jQuery, I'm quite confident +that it's solid enough for production use. If you can identify any bugs, +I'd love to hear about them (use the Google Group or email me directly). +

      +
      + +
      + +
      +

      1.7 Links

      +
      + + + + + +
      + +
      + +
      +

      1.8 License

      +
      + + +

      +UglifyJS is released under the BSD license: +

      + + + +
      Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
      +Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
      +
      +Redistribution and use in source and binary forms, with or without
      +modification, are permitted provided that the following conditions
      +are met:
      +
      +    * Redistributions of source code must retain the above
      +      copyright notice, this list of conditions and the following
      +      disclaimer.
      +
      +    * Redistributions in binary form must reproduce the above
      +      copyright notice, this list of conditions and the following
      +      disclaimer in the documentation and/or other materials
      +      provided with the distribution.
      +
      +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
      +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
      +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
      +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
      +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
      +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      +SUCH DAMAGE.
      +
      + + +
      +

      Footnotes:

      +
      +

      1 I even reported a few bugs and suggested some fixes in the original + parse-js library, and Marijn pushed fixes literally in minutes. +

      +
      +
      + +
      +
      +
      + +
      +

      Date: 2011-12-09 14:59:08 EET

      +

      Author: Mihai Bazon

      +

      Org version 7.7 with Emacs version 23

      +Validate XHTML 1.0 + +
      + + diff --git a/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.org b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.org new file mode 100644 index 0000000..4d01fdf --- /dev/null +++ b/dist/node_modules/socket.io/node_modules/socket.io-client/node_modules/uglify-js/README.org @@ -0,0 +1,574 @@ +#+TITLE: UglifyJS -- a JavaScript parser/compressor/beautifier +#+KEYWORDS: javascript, js, parser, compiler, compressor, mangle, minify, minifier +#+DESCRIPTION: a JavaScript parser/compressor/beautifier in JavaScript +#+STYLE: +#+AUTHOR: Mihai Bazon +#+EMAIL: mihai.bazon@gmail.com + +* UglifyJS --- a JavaScript parser/compressor/beautifier + +This package implements a general-purpose JavaScript +parser/compressor/beautifier toolkit. It is developed on [[http://nodejs.org/][NodeJS]], but it +should work on any JavaScript platform supporting the CommonJS module system +(and if your platform of choice doesn't support CommonJS, you can easily +implement it, or discard the =exports.*= lines from UglifyJS sources). + +The tokenizer/parser generates an abstract syntax tree from JS code. You +can then traverse the AST to learn more about the code, or do various +manipulations on it. This part is implemented in [[../lib/parse-js.js][parse-js.js]] and it's a +port to JavaScript of the excellent [[http://marijn.haverbeke.nl/parse-js/][parse-js]] Common Lisp library from [[http://marijn.haverbeke.nl/][Marijn +Haverbeke]]. + +( See [[http://github.com/mishoo/cl-uglify-js][cl-uglify-js]] if you're looking for the Common Lisp version of +UglifyJS. ) + +The second part of this package, implemented in [[../lib/process.js][process.js]], inspects and +manipulates the AST generated by the parser to provide the following: + +- ability to re-generate JavaScript code from the AST. Optionally + indented---you can use this if you want to “beautify” a program that has + been compressed, so that you can inspect the source. But you can also run + our code generator to print out an AST without any whitespace, so you + achieve compression as well. + +- shorten variable names (usually to single characters). Our mangler will + analyze the code and generate proper variable names, depending on scope + and usage, and is smart enough to deal with globals defined elsewhere, or + with =eval()= calls or =with{}= statements. In short, if =eval()= or + =with{}= are used in some scope, then all variables in that scope and any + variables in the parent scopes will remain unmangled, and any references + to such variables remain unmangled as well. + +- various small optimizations that may lead to faster code but certainly + lead to smaller code. Where possible, we do the following: + + - foo["bar"] ==> foo.bar + + - remove block brackets ={}= + + - join consecutive var declarations: + var a = 10; var b = 20; ==> var a=10,b=20; + + - resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the + replacement if the result occupies less bytes; for example 1/3 would + translate to 0.333333333333, so in this case we don't replace it. + + - consecutive statements in blocks are merged into a sequence; in many + cases, this leaves blocks with a single statement, so then we can remove + the block brackets. + + - various optimizations for IF statements: + + - if (foo) bar(); else baz(); ==> foo?bar():baz(); + - if (!foo) bar(); else baz(); ==> foo?baz():bar(); + - if (foo) bar(); ==> foo&&bar(); + - if (!foo) bar(); ==> foo||bar(); + - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); + - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()} + + - remove some unreachable code and warn about it (code that follows a + =return=, =throw=, =break= or =continue= statement, except + function/variable declarations). + + - act a limited version of a pre-processor (c.f. the pre-processor of + C/C++) to allow you to safely replace selected global symbols with + specified values. When combined with the optimisations above this can + make UglifyJS operate slightly more like a compilation process, in + that when certain symbols are replaced by constant values, entire code + blocks may be optimised away as unreachable. + +** <> + +The following transformations can in theory break code, although they're +probably safe in most practical cases. To enable them you need to pass the +=--unsafe= flag. + +*** Calls involving the global Array constructor + +The following transformations occur: + +#+BEGIN_SRC js +new Array(1, 2, 3, 4) => [1,2,3,4] +Array(a, b, c) => [a,b,c] +new Array(5) => Array(5) +new Array(a) => Array(a) +#+END_SRC + +These are all safe if the Array name isn't redefined. JavaScript does allow +one to globally redefine Array (and pretty much everything, in fact) but I +personally don't see why would anyone do that. + +UglifyJS does handle the case where Array is redefined locally, or even +globally but with a =function= or =var= declaration. Therefore, in the +following cases UglifyJS *doesn't touch* calls or instantiations of Array: + +#+BEGIN_SRC js +// case 1. globally declared variable + var Array; + new Array(1, 2, 3); + Array(a, b); + + // or (can be declared later) + new Array(1, 2, 3); + var Array; + + // or (can be a function) + new Array(1, 2, 3); + function Array() { ... } + +// case 2. declared in a function + (function(){ + a = new Array(1, 2, 3); + b = Array(5, 6); + var Array; + })(); + + // or + (function(Array){ + return Array(5, 6, 7); + })(); + + // or + (function(){ + return new Array(1, 2, 3, 4); + function Array() { ... } + })(); + + // etc. +#+END_SRC + +*** =obj.toString()= ==> =obj+“”= + +** Install (NPM) + +UglifyJS is now available through NPM --- =npm install uglify-js= should do +the job. + +** Install latest code from GitHub + +#+BEGIN_SRC sh +## clone the repository +mkdir -p /where/you/wanna/put/it +cd /where/you/wanna/put/it +git clone git://github.com/mishoo/UglifyJS.git + +## make the module available to Node +mkdir -p ~/.node_libraries/ +cd ~/.node_libraries/ +ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js + +## and if you want the CLI script too: +mkdir -p ~/bin +cd ~/bin +ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs + # (then add ~/bin to your $PATH if it's not there already) +#+END_SRC + +** Usage + +There is a command-line tool that exposes the functionality of this library +for your shell-scripting needs: + +#+BEGIN_SRC sh +uglifyjs [ options... ] [ filename ] +#+END_SRC + +=filename= should be the last argument and should name the file from which +to read the JavaScript code. If you don't specify it, it will read code +from STDIN. + +Supported options: + +- =-b= or =--beautify= --- output indented code; when passed, additional + options control the beautifier: + + - =-i N= or =--indent N= --- indentation level (number of spaces) + + - =-q= or =--quote-keys= --- quote keys in literal objects (by default, + only keys that cannot be identifier names will be quotes). + +- =--ascii= --- pass this argument to encode non-ASCII characters as + =\uXXXX= sequences. By default UglifyJS won't bother to do it and will + output Unicode characters instead. (the output is always encoded in UTF8, + but if you pass this option you'll only get ASCII). + +- =-nm= or =--no-mangle= --- don't mangle names. + +- =-nmf= or =--no-mangle-functions= -- in case you want to mangle variable + names, but not touch function names. + +- =-ns= or =--no-squeeze= --- don't call =ast_squeeze()= (which does various + optimizations that result in smaller, less readable code). + +- =-mt= or =--mangle-toplevel= --- mangle names in the toplevel scope too + (by default we don't do this). + +- =--no-seqs= --- when =ast_squeeze()= is called (thus, unless you pass + =--no-squeeze=) it will reduce consecutive statements in blocks into a + sequence. For example, "a = 10; b = 20; foo();" will be written as + "a=10,b=20,foo();". In various occasions, this allows us to discard the + block brackets (since the block becomes a single statement). This is ON + by default because it seems safe and saves a few hundred bytes on some + libs that I tested it on, but pass =--no-seqs= to disable it. + +- =--no-dead-code= --- by default, UglifyJS will remove code that is + obviously unreachable (code that follows a =return=, =throw=, =break= or + =continue= statement and is not a function/variable declaration). Pass + this option to disable this optimization. + +- =-nc= or =--no-copyright= --- by default, =uglifyjs= will keep the initial + comment tokens in the generated code (assumed to be copyright information + etc.). If you pass this it will discard it. + +- =-o filename= or =--output filename= --- put the result in =filename=. If + this isn't given, the result goes to standard output (or see next one). + +- =--overwrite= --- if the code is read from a file (not from STDIN) and you + pass =--overwrite= then the output will be written in the same file. + +- =--ast= --- pass this if you want to get the Abstract Syntax Tree instead + of JavaScript as output. Useful for debugging or learning more about the + internals. + +- =-v= or =--verbose= --- output some notes on STDERR (for now just how long + each operation takes). + +- =-d SYMBOL[=VALUE]= or =--define SYMBOL[=VALUE]= --- will replace + all instances of the specified symbol where used as an identifier + (except where symbol has properly declared by a var declaration or + use as function parameter or similar) with the specified value. This + argument may be specified multiple times to define multiple + symbols - if no value is specified the symbol will be replaced with + the value =true=, or you can specify a numeric value (such as + =1024=), a quoted string value (such as ="object"= or + ='https://github.com'=), or the name of another symbol or keyword + (such as =null= or =document=). + This allows you, for example, to assign meaningful names to key + constant values but discard the symbolic names in the uglified + version for brevity/efficiency, or when used wth care, allows + UglifyJS to operate as a form of *conditional compilation* + whereby defining appropriate values may, by dint of the constant + folding and dead code removal features above, remove entire + superfluous code blocks (e.g. completely remove instrumentation or + trace code for production use). + Where string values are being defined, the handling of quotes are + likely to be subject to the specifics of your command shell + environment, so you may need to experiment with quoting styles + depending on your platform, or you may find the option + =--define-from-module= more suitable for use. + +- =-define-from-module SOMEMODULE= --- will load the named module (as + per the NodeJS =require()= function) and iterate all the exported + properties of the module defining them as symbol names to be defined + (as if by the =--define= option) per the name of each property + (i.e. without the module name prefix) and given the value of the + property. This is a much easier way to handle and document groups of + symbols to be defined rather than a large number of =--define= + options. + +- =--unsafe= --- enable other additional optimizations that are known to be + unsafe in some contrived situations, but could still be generally useful. + For now only these: + + - foo.toString() ==> foo+"" + - new Array(x,...) ==> [x,...] + - new Array(x) ==> Array(x) + +- =--max-line-len= (default 32K characters) --- add a newline after around + 32K characters. I've seen both FF and Chrome croak when all the code was + on a single line of around 670K. Pass --max-line-len 0 to disable this + safety feature. + +- =--reserved-names= --- some libraries rely on certain names to be used, as + pointed out in issue #92 and #81, so this option allow you to exclude such + names from the mangler. For example, to keep names =require= and =$super= + intact you'd specify --reserved-names "require,$super". + +- =--inline-script= -- when you want to include the output literally in an + HTML = + + + */ + +(function() { + this.loggly = function(opts) { + this.user_agent = get_agent(); + this.browser_size = get_size(); + log_methods = {'error': 5, 'warn': 4, 'info': 3, 'debug': 2, 'log': 1}; + if (!opts.url) throw new Error("Please include a Loggly HTTP URL."); + if (!opts.level) { + this.level = log_methods['info']; + } else { + this.level = log_methods[opts.level]; + } + this.log = function(data) { + if (log_methods['log'] == this.level) { + opts.data = data; + janky(opts); + } + }; + this.debug = function(data) { + if (log_methods['debug'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.info = function(data) { + if (log_methods['info'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.warn = function(data) { + if (log_methods['warn'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + this.error = function(data) { + if (log_methods['error'] >= this.level) { + opts.data = data; + janky(opts); + } + }; + }; + this.janky = function(opts) { + janky._form(function(iframe, form) { + form.setAttribute("action", opts.url); + form.setAttribute("method", "post"); + janky._input(iframe, form, opts.data); + form.submit(); + setTimeout(function(){ + document.body.removeChild(iframe); + }, 2000); + }); + }; + this.janky._form = function(cb) { + var iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + iframe.style.display = "none"; + setTimeout(function() { + var form = iframe.contentWindow.document.createElement("form"); + iframe.contentWindow.document.body.appendChild(form); + cb(iframe, form); + }, 0); + }; + this.janky._input = function(iframe, form, data) { + var inp = iframe.contentWindow.document.createElement("input"); + inp.setAttribute("type", "hidden"); + inp.setAttribute("name", "source"); + inp.value = "castor " + data; + form.appendChild(inp); + }; + this.get_agent = function () { + return navigator.appCodeName + navigator.appName + navigator.appVersion; + }; + this.get_size = function () { + var width = 0; var height = 0; + if( typeof( window.innerWidth ) == 'number' ) { + width = window.innerWidth; height = window.innerHeight; + } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { + width = document.documentElement.clientWidth; height = document.documentElement.clientHeight; + } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { + width = document.body.clientWidth; height = document.body.clientHeight; + } + return {'height': height, 'width': width}; + }; +})(); + + +jsworld={};jsworld.formatIsoDateTime=function(a,b){if(typeof a==="undefined")a=new Date;if(typeof b==="undefined")b=false;var c=jsworld.formatIsoDate(a)+" "+jsworld.formatIsoTime(a);if(b){var d=a.getHours()-a.getUTCHours();var e=Math.abs(d);var f=a.getUTCMinutes();var g=a.getMinutes();if(g!=f&&f<30&&d<0)e--;if(g!=f&&f>30&&d>0)e--;var h;if(g!=f)h=":30";else h=":00";var i;if(e<10)i="0"+e+h;else i=""+e+h;if(d<0)i="-"+i;else i="+"+i;c=c+i}return c};jsworld.formatIsoDate=function(a){if(typeof a==="undefined")a=new Date;var b=a.getFullYear();var c=a.getMonth()+1;var d=a.getDate();return b+"-"+jsworld._zeroPad(c,2)+"-"+jsworld._zeroPad(d,2)};jsworld.formatIsoTime=function(a){if(typeof a==="undefined")a=new Date;var b=a.getHours();var c=a.getMinutes();var d=a.getSeconds();return jsworld._zeroPad(b,2)+":"+jsworld._zeroPad(c,2)+":"+jsworld._zeroPad(d,2)};jsworld.parseIsoDateTime=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date/time string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);var f=parseInt(b[4],10);var g=parseInt(b[5],10);var h=parseInt(b[6],10);if(d<1||d>12||e<1||e>31||f<0||f>23||g<0||g>59||h<0||h>59)throw"Error: Invalid ISO-8601 date/time value";var i=new Date(c,d-1,e,f,g,h);if(i.getDate()!=e||i.getMonth()+1!=d)throw"Error: Invalid date";return i};jsworld.parseIsoDate=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/);if(b===null)b=a.match(/^(\d\d\d\d)(\d\d)(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);if(d<1||d>12||e<1||e>31)throw"Error: Invalid ISO-8601 date value";var f=new Date(c,d-1,e);if(f.getDate()!=e||f.getMonth()+1!=d)throw"Error: Invalid date";return f};jsworld.parseIsoTime=function(a){if(typeof a!="string")throw"Error: The parameter must be a string";var b=a.match(/^(\d\d):(\d\d):(\d\d)/);if(b===null)b=a.match(/^(\d\d)(\d\d)(\d\d)/);if(b===null)throw"Error: Invalid ISO-8601 date/time string";var c=parseInt(b[1],10);var d=parseInt(b[2],10);var e=parseInt(b[3],10);if(c<0||c>23||d<0||d>59||e<0||e>59)throw"Error: Invalid ISO-8601 time value";return new Date(0,0,0,c,d,e)};jsworld._trim=function(a){var b=" \n\r\t\f \u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000";for(var c=0;c=0;c--){if(b.indexOf(a.charAt(c))===-1){a=a.substring(0,c+1);break}}return b.indexOf(a.charAt(0))===-1?a:""};jsworld._isNumber=function(a){if(typeof a=="number")return true;if(typeof a!="string")return false;var b=a+"";return/^-?(\d+|\d*\.\d+)$/.test(b)};jsworld._isInteger=function(a){if(typeof a!="number"&&typeof a!="string")return false;var b=a+"";return/^-?\d+$/.test(b)};jsworld._isFloat=function(a){if(typeof a!="number"&&typeof a!="string")return false;var b=a+"";return/^-?\.\d+?$/.test(b)};jsworld._hasOption=function(a,b){if(typeof a!="string"||typeof b!="string")return false;if(b.indexOf(a)!=-1)return true;else return false};jsworld._stringReplaceAll=function(a,b,c){var d;if(b.length==1&&c.length==1){d="";for(var e=0;e0){if(d.length>0)g=parseInt(d.shift(),10);if(isNaN(g))throw"Error: Invalid grouping";if(g==-1){e=a.substring(0,f)+e;break}f-=g;if(f<1){e=a.substring(0,f+g)+e;break}e=c+a.substring(f,f+g)+e}return e};jsworld._formatFractionPart=function(a,b){for(var c=0;a.length0)return a;else throw"Empty or no string"};if(a==null||typeof a!="object")throw"Error: Invalid/missing locale properties";if(typeof a.decimal_point!="string")throw"Error: Invalid/missing decimal_point property";this.decimal_point=a.decimal_point;if(typeof a.thousands_sep!="string")throw"Error: Invalid/missing thousands_sep property";this.thousands_sep=a.thousands_sep;if(typeof a.grouping!="string")throw"Error: Invalid/missing grouping property";this.grouping=a.grouping;if(typeof a.int_curr_symbol!="string")throw"Error: Invalid/missing int_curr_symbol property";if(!/[A-Za-z]{3}.?/.test(a.int_curr_symbol))throw"Error: Invalid int_curr_symbol property";this.int_curr_symbol=a.int_curr_symbol;if(typeof a.currency_symbol!="string")throw"Error: Invalid/missing currency_symbol property";this.currency_symbol=a.currency_symbol;if(typeof a.frac_digits!="number"&&a.frac_digits<0)throw"Error: Invalid/missing frac_digits property";this.frac_digits=a.frac_digits;if(a.mon_decimal_point===null||a.mon_decimal_point==""){if(this.frac_digits>0)throw"Error: Undefined mon_decimal_point property";else a.mon_decimal_point=""}if(typeof a.mon_decimal_point!="string")throw"Error: Invalid/missing mon_decimal_point property";this.mon_decimal_point=a.mon_decimal_point;if(typeof a.mon_thousands_sep!="string")throw"Error: Invalid/missing mon_thousands_sep property";this.mon_thousands_sep=a.mon_thousands_sep;if(typeof a.mon_grouping!="string")throw"Error: Invalid/missing mon_grouping property";this.mon_grouping=a.mon_grouping;if(typeof a.positive_sign!="string")throw"Error: Invalid/missing positive_sign property";this.positive_sign=a.positive_sign;if(typeof a.negative_sign!="string")throw"Error: Invalid/missing negative_sign property";this.negative_sign=a.negative_sign;if(a.p_cs_precedes!==0&&a.p_cs_precedes!==1)throw"Error: Invalid/missing p_cs_precedes property, must be 0 or 1";this.p_cs_precedes=a.p_cs_precedes;if(a.n_cs_precedes!==0&&a.n_cs_precedes!==1)throw"Error: Invalid/missing n_cs_precedes, must be 0 or 1";this.n_cs_precedes=a.n_cs_precedes;if(a.p_sep_by_space!==0&&a.p_sep_by_space!==1&&a.p_sep_by_space!==2)throw"Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2";this.p_sep_by_space=a.p_sep_by_space;if(a.n_sep_by_space!==0&&a.n_sep_by_space!==1&&a.n_sep_by_space!==2)throw"Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2";this.n_sep_by_space=a.n_sep_by_space;if(a.p_sign_posn!==0&&a.p_sign_posn!==1&&a.p_sign_posn!==2&&a.p_sign_posn!==3&&a.p_sign_posn!==4)throw"Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4";this.p_sign_posn=a.p_sign_posn;if(a.n_sign_posn!==0&&a.n_sign_posn!==1&&a.n_sign_posn!==2&&a.n_sign_posn!==3&&a.n_sign_posn!==4)throw"Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4";this.n_sign_posn=a.n_sign_posn;if(typeof a.int_frac_digits!="number"&&a.int_frac_digits<0)throw"Error: Invalid/missing int_frac_digits property";this.int_frac_digits=a.int_frac_digits;if(a.int_p_cs_precedes!==0&&a.int_p_cs_precedes!==1)throw"Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1";this.int_p_cs_precedes=a.int_p_cs_precedes;if(a.int_n_cs_precedes!==0&&a.int_n_cs_precedes!==1)throw"Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1";this.int_n_cs_precedes=a.int_n_cs_precedes;if(a.int_p_sep_by_space!==0&&a.int_p_sep_by_space!==1&&a.int_p_sep_by_space!==2)throw"Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2";this.int_p_sep_by_space=a.int_p_sep_by_space;if(a.int_n_sep_by_space!==0&&a.int_n_sep_by_space!==1&&a.int_n_sep_by_space!==2)throw"Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2";this.int_n_sep_by_space=a.int_n_sep_by_space;if(a.int_p_sign_posn!==0&&a.int_p_sign_posn!==1&&a.int_p_sign_posn!==2&&a.int_p_sign_posn!==3&&a.int_p_sign_posn!==4)throw"Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4";this.int_p_sign_posn=a.int_p_sign_posn;if(a.int_n_sign_posn!==0&&a.int_n_sign_posn!==1&&a.int_n_sign_posn!==2&&a.int_n_sign_posn!==3&&a.int_n_sign_posn!==4)throw"Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4";this.int_n_sign_posn=a.int_n_sign_posn;if(a==null||typeof a!="object")throw"Error: Invalid/missing time locale properties";try{this.abday=this._parseList(a.abday,7)}catch(b){throw"Error: Invalid abday property: "+b}try{this.day=this._parseList(a.day,7)}catch(b){throw"Error: Invalid day property: "+b}try{this.abmon=this._parseList(a.abmon,12)}catch(b){throw"Error: Invalid abmon property: "+b}try{this.mon=this._parseList(a.mon,12)}catch(b){throw"Error: Invalid mon property: "+b}try{this.d_fmt=this._validateFormatString(a.d_fmt)}catch(b){throw"Error: Invalid d_fmt property: "+b}try{this.t_fmt=this._validateFormatString(a.t_fmt)}catch(b){throw"Error: Invalid t_fmt property: "+b}try{this.d_t_fmt=this._validateFormatString(a.d_t_fmt)}catch(b){throw"Error: Invalid d_t_fmt property: "+b}try{var c=this._parseList(a.am_pm,2);this.am=c[0];this.pm=c[1]}catch(b){this.am="";this.pm=""}this.getAbbreviatedWeekdayName=function(a){if(typeof a=="undefined"||a===null)return this.abday;if(!jsworld._isInteger(a)||a<0||a>6)throw"Error: Invalid weekday argument, must be an integer [0..6]";return this.abday[a]};this.getWeekdayName=function(a){if(typeof a=="undefined"||a===null)return this.day;if(!jsworld._isInteger(a)||a<0||a>6)throw"Error: Invalid weekday argument, must be an integer [0..6]";return this.day[a]};this.getAbbreviatedMonthName=function(a){if(typeof a=="undefined"||a===null)return this.abmon;if(!jsworld._isInteger(a)||a<0||a>11)throw"Error: Invalid month argument, must be an integer [0..11]";return this.abmon[a]};this.getMonthName=function(a){if(typeof a=="undefined"||a===null)return this.mon;if(!jsworld._isInteger(a)||a<0||a>11)throw"Error: Invalid month argument, must be an integer [0..11]";return this.mon[a]};this.getDecimalPoint=function(){return this.decimal_point};this.getCurrencySymbol=function(){return this.currency_symbol};this.getIntCurrencySymbol=function(){return this.int_curr_symbol.substring(0,3)};this.currencySymbolPrecedes=function(){if(this.p_cs_precedes==1)return true;else return false};this.intCurrencySymbolPrecedes=function(){if(this.int_p_cs_precedes==1)return true;else return false};this.getMonetaryDecimalPoint=function(){return this.mon_decimal_point};this.getFractionalDigits=function(){return this.frac_digits};this.getIntFractionalDigits=function(){return this.int_frac_digits}};jsworld.NumericFormatter=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.format=function(a,b){if(typeof a=="string")a=jsworld._trim(a);if(!jsworld._isNumber(a))throw"Error: The input is not a number";var c=parseFloat(a,10);var d=jsworld._getPrecision(b);if(d!=-1)c=Math.round(c*Math.pow(10,d))/Math.pow(10,d);var e=jsworld._splitNumber(String(c));var f;if(c===0)f="0";else f=jsworld._hasOption("^",b)?e.integer:jsworld._formatIntegerPart(e.integer,this.lc.grouping,this.lc.thousands_sep);var g=d!=-1?jsworld._formatFractionPart(e.fraction,d):e.fraction;var h=g.length?f+this.lc.decimal_point+g:f;if(jsworld._hasOption("~",b)||c===0){return h}else{if(jsworld._hasOption("+",b)||c<0){if(c>0)return"+"+h;else if(c<0)return"-"+h;else return h}else{return h}}}};jsworld.DateTimeFormatter=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance.";this.lc=a;this.formatDate=function(a){var b=null;if(typeof a=="string"){try{b=jsworld.parseIsoDate(a)}catch(c){b=jsworld.parseIsoDateTime(a)}}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.d_fmt)};this.formatTime=function(a){var b=null;if(typeof a=="string"){try{b=jsworld.parseIsoTime(a)}catch(c){b=jsworld.parseIsoDateTime(a)}}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.t_fmt)};this.formatDateTime=function(a){var b=null;if(typeof a=="string"){b=jsworld.parseIsoDateTime(a)}else if(a!==null&&typeof a=="object"){b=a}else{throw"Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"}return this._applyFormatting(b,this.lc.d_t_fmt)};this._applyFormatting=function(a,b){b=b.replace(/%%/g,"%");b=b.replace(/%a/g,this.lc.abday[a.getDay()]);b=b.replace(/%A/g,this.lc.day[a.getDay()]);b=b.replace(/%b/g,this.lc.abmon[a.getMonth()]);b=b.replace(/%B/g,this.lc.mon[a.getMonth()]);b=b.replace(/%d/g,jsworld._zeroPad(a.getDate(),2));b=b.replace(/%e/g,jsworld._spacePad(a.getDate(),2));b=b.replace(/%F/g,a.getFullYear()+"-"+jsworld._zeroPad(a.getMonth()+1,2)+"-"+jsworld._zeroPad(a.getDate(),2));b=b.replace(/%h/g,this.lc.abmon[a.getMonth()]);b=b.replace(/%H/g,jsworld._zeroPad(a.getHours(),2));b=b.replace(/%I/g,jsworld._zeroPad(this._hours12(a.getHours()),2));b=b.replace(/%k/g,a.getHours());b=b.replace(/%l/g,this._hours12(a.getHours()));b=b.replace(/%m/g,jsworld._zeroPad(a.getMonth()+1,2));b=b.replace(/%n/g,"\n");b=b.replace(/%M/g,jsworld._zeroPad(a.getMinutes(),2));b=b.replace(/%p/g,this._getAmPm(a.getHours()));b=b.replace(/%P/g,this._getAmPm(a.getHours()).toLocaleLowerCase());b=b.replace(/%R/g,jsworld._zeroPad(a.getHours(),2)+":"+jsworld._zeroPad(a.getMinutes(),2));b=b.replace(/%S/g,jsworld._zeroPad(a.getSeconds(),2));b=b.replace(/%T/g,jsworld._zeroPad(a.getHours(),2)+":"+jsworld._zeroPad(a.getMinutes(),2)+":"+jsworld._zeroPad(a.getSeconds(),2));b=b.replace(/%w/g,this.lc.day[a.getDay()]);b=b.replace(/%y/g,(new String(a.getFullYear())).substring(2));b=b.replace(/%Y/g,a.getFullYear());b=b.replace(/%Z/g,"");b=b.replace(/%[a-zA-Z]/g,"");return b};this._hours12=function(a){if(a===0)return 12;else if(a>12)return a-12;else return a};this._getAmPm=function(a){if(a===0||a>12)return this.lc.pm;else return this.lc.am}};jsworld.MonetaryFormatter=function(a,b,c){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.currencyFractionDigits={AFN:0,ALL:0,AMD:0,BHD:3,BIF:0,BYR:0,CLF:0,CLP:0,COP:0,CRC:0,DJF:0,GNF:0,GYD:0,HUF:0,IDR:0,IQD:0,IRR:0,ISK:0,JOD:3,JPY:0,KMF:0,KRW:0,KWD:3,LAK:0,LBP:0,LYD:3,MGA:0,MMK:0,MNT:0,MRO:0,MUR:0,OMR:3,PKR:0,PYG:0,RSD:0,RWF:0,SLL:0,SOS:0,STD:0,SYP:0,TND:3,TWD:0,TZS:0,UGX:0,UZS:0,VND:0,VUV:0,XAF:0,XOF:0,XPF:0,YER:0,ZMK:0};if(typeof b=="string"){this.currencyCode=b.toUpperCase();var d=this.currencyFractionDigits[this.currencyCode];if(typeof d!="number")d=2;this.lc.frac_digits=d;this.lc.int_frac_digits=d}else{this.currencyCode=this.lc.int_curr_symbol.substring(0,3).toUpperCase()}this.intSep=this.lc.int_curr_symbol.charAt(3);if(this.currencyCode==this.lc.int_curr_symbol.substring(0,3)){this.internationalFormatting=false;this.curSym=this.lc.currency_symbol}else{if(typeof c=="string"){this.curSym=c;this.internationalFormatting=false}else{this.internationalFormatting=true}}this.getCurrencySymbol=function(){return this.curSym};this.currencySymbolPrecedes=function(a){if(typeof a=="string"&&a=="i"){if(this.lc.int_p_cs_precedes==1)return true;else return false}else{if(this.internationalFormatting){if(this.lc.int_p_cs_precedes==1)return true;else return false}else{if(this.lc.p_cs_precedes==1)return true;else return false}}};this.getDecimalPoint=function(){return this.lc.mon_decimal_point};this.getFractionalDigits=function(a){if(typeof a=="string"&&a=="i"){return this.lc.int_frac_digits}else{if(this.internationalFormatting)return this.lc.int_frac_digits;else return this.lc.frac_digits}};this.format=function(a,b){var c;if(typeof a=="string"){a=jsworld._trim(a);c=parseFloat(a);if(typeof c!="number"||isNaN(c))throw"Error: Amount string not a number"}else if(typeof a=="number"){c=a}else{throw"Error: Amount not a number"}var d=jsworld._getPrecision(b);if(d==-1){if(this.internationalFormatting||jsworld._hasOption("i",b))d=this.lc.int_frac_digits;else d=this.lc.frac_digits}c=Math.round(c*Math.pow(10,d))/Math.pow(10,d);var e=jsworld._splitNumber(String(c));var f;if(c===0)f="0";else f=jsworld._hasOption("^",b)?e.integer:jsworld._formatIntegerPart(e.integer,this.lc.mon_grouping,this.lc.mon_thousands_sep);var g;if(d==-1){if(this.internationalFormatting||jsworld._hasOption("i",b))g=jsworld._formatFractionPart(e.fraction,this.lc.int_frac_digits);else g=jsworld._formatFractionPart(e.fraction,this.lc.frac_digits)}else{g=jsworld._formatFractionPart(e.fraction,d)}var h;if(this.lc.frac_digits>0||g.length)h=f+this.lc.mon_decimal_point+g;else h=f;if(jsworld._hasOption("~",b)){return h}else{var i=jsworld._hasOption("!",b)?true:false;var j=c<0?"-":"+";if(this.internationalFormatting||jsworld._hasOption("i",b)){if(i)return this._formatAsInternationalCurrencyWithNoSym(j,h);else return this._formatAsInternationalCurrency(j,h)}else{if(i)return this._formatAsLocalCurrencyWithNoSym(j,h);else return this._formatAsLocalCurrency(j,h)}}};this._formatAsLocalCurrency=function(a,b){if(a=="+"){if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return"("+b+this.curSym+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return"("+this.curSym+b+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return"("+b+" "+this.curSym+")"}else if(this.lc.p_sign_posn===0&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return"("+this.curSym+" "+b+")"}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b+" "+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+" "+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+" "+b+this.curSym}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+this.curSym+b}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.curSym+b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.curSym+" "+b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.curSym+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.curSym+b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+this.curSym+" "+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign+" "+this.curSym}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+this.curSym+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.curSym+this.lc.positive_sign+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.curSym+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.curSym+this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.curSym+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.curSym+" "+this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return"("+b+this.curSym+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return"("+this.curSym+b+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return"("+b+" "+this.curSym+")"}else if(this.lc.n_sign_posn===0&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return"("+this.curSym+" "+b+")"}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b+" "+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+" "+b+this.curSym}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+this.curSym+b}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.curSym+b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.curSym+" "+b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.curSym+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.curSym+b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+this.curSym+" "+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign+" "+this.curSym}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+this.curSym+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.curSym+this.lc.negative_sign+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.curSym+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.curSym+this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.curSym+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.curSym+" "+this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsInternationalCurrency=function(a,b){if(a=="+"){if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return"("+b+this.currencyCode+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return"("+this.currencyCode+b+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return"("+b+this.intSep+this.currencyCode+")"}else if(this.lc.int_p_sign_posn===0&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return"("+this.currencyCode+this.intSep+b+")"}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b+this.intSep+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+this.intSep+b+this.currencyCode}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.currencyCode+b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.intSep+b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.currencyCode+b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign+this.intSep+this.currencyCode}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.currencyCode+this.intSep+this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return"("+b+this.currencyCode+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return"("+this.currencyCode+b+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return"("+b+this.intSep+this.currencyCode+")"}else if(this.lc.int_n_sign_posn===0&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return"("+this.currencyCode+this.intSep+b+")"}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b+this.intSep+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+this.intSep+b+this.currencyCode}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.currencyCode+b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.intSep+b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.currencyCode+b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.currencyCode+this.intSep+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign+this.intSep+this.currencyCode}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+this.currencyCode+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.currencyCode+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.currencyCode+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.currencyCode+this.intSep+this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsLocalCurrencyWithNoSym=function(a,b){if(a=="+"){if(this.lc.p_sign_posn===0){return"("+b+")"}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===1&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===2&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===3&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===0&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===1&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+" "+b}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===0){return b+" "+this.lc.positive_sign}else if(this.lc.p_sign_posn===4&&this.lc.p_sep_by_space===2&&this.lc.p_cs_precedes===1){return this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.n_sign_posn===0){return"("+b+")"}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===1&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===2&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===3&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===0&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===1&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+" "+b}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===0){return b+" "+this.lc.negative_sign}else if(this.lc.n_sign_posn===4&&this.lc.n_sep_by_space===2&&this.lc.n_cs_precedes===1){return this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC MONETARY definition"};this._formatAsInternationalCurrencyWithNoSym=function(a,b){if(a=="+"){if(this.lc.int_p_sign_posn===0){return"("+b+")"}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===1&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===2&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===3&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===0){return b+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===0&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===1&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+this.intSep+b}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===0){return b+this.intSep+this.lc.positive_sign}else if(this.lc.int_p_sign_posn===4&&this.lc.int_p_sep_by_space===2&&this.lc.int_p_cs_precedes===1){return this.lc.positive_sign+b}}else if(a=="-"){if(this.lc.int_n_sign_posn===0){return"("+b+")"}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===1&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===2&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===3&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===0){return b+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===0&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===1&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+this.intSep+b}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===0){return b+this.intSep+this.lc.negative_sign}else if(this.lc.int_n_sign_posn===4&&this.lc.int_n_sep_by_space===2&&this.lc.int_n_cs_precedes===1){return this.lc.negative_sign+b}}throw"Error: Invalid POSIX LC_MONETARY definition"}};jsworld.NumericParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.parse=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=jsworld._trim(a);b=jsworld._stringReplaceAll(a,this.lc.thousands_sep,"");b=jsworld._stringReplaceAll(b,this.lc.decimal_point,".");if(jsworld._isNumber(b))return parseFloat(b,10);else throw"Parse error: Invalid number string"}};jsworld.DateTimeParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance.";this.lc=a;this.parseTime=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.t_fmt,a);var c=false;if(b.hour!==null&&b.minute!==null&&b.second!==null){c=true}else if(b.hourAmPm!==null&&b.am!==null&&b.minute!==null&&b.second!==null){if(b.am){b.hour=parseInt(b.hourAmPm,10)}else{if(b.hourAmPm==12)b.hour=0;else b.hour=parseInt(b.hourAmPm,10)+12}c=true}if(c)return jsworld._zeroPad(b.hour,2)+":"+jsworld._zeroPad(b.minute,2)+":"+jsworld._zeroPad(b.second,2);else throw"Parse error: Invalid/ambiguous time string"};this.parseDate=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.d_fmt,a);var c=false;if(b.year!==null&&b.month!==null&&b.day!==null){c=true}if(c)return jsworld._zeroPad(b.year,4)+"-"+jsworld._zeroPad(b.month,2)+"-"+jsworld._zeroPad(b.day,2);else throw"Parse error: Invalid date string"};this.parseDateTime=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._extractTokens(this.lc.d_t_fmt,a);var c=false;var d=false;if(b.hour!==null&&b.minute!==null&&b.second!==null){c=true}else if(b.hourAmPm!==null&&b.am!==null&&b.minute!==null&&b.second!==null){if(b.am){b.hour=parseInt(b.hourAmPm,10)}else{if(b.hourAmPm==12)b.hour=0;else b.hour=parseInt(b.hourAmPm,10)+12}c=true}if(b.year!==null&&b.month!==null&&b.day!==null){d=true}if(d&&c)return jsworld._zeroPad(b.year,4)+"-"+jsworld._zeroPad(b.month,2)+"-"+jsworld._zeroPad(b.day,2)+" "+jsworld._zeroPad(b.hour,2)+":"+jsworld._zeroPad(b.minute,2)+":"+jsworld._zeroPad(b.second,2);else throw"Parse error: Invalid/ambiguous date/time string"};this._extractTokens=function(a,b){var c={year:null,month:null,day:null,hour:null,hourAmPm:null,am:null,minute:null,second:null,weekday:null};while(a.length>0){if(a.charAt(0)=="%"&&a.charAt(1)!=""){var d=a.substring(0,2);if(d=="%%"){b=b.substring(1)}else if(d=="%a"){for(var e=0;e31)throw"Parse error: Unrecognised day of the month (%e)";b=b.substring(f.length)}else if(d=="%F"){if(/^\d\d\d\d/.test(b)){c.year=parseInt(b.substring(0,4),10);b=b.substring(4)}else{throw"Parse error: Unrecognised date (%F)"}if(jsworld._stringStartsWith(b,"-"))b=b.substring(1);else throw"Parse error: Unrecognised date (%F)";if(/^0[1-9]|1[0-2]/.test(b)){c.month=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised date (%F)";if(jsworld._stringStartsWith(b,"-"))b=b.substring(1);else throw"Parse error: Unrecognised date (%F)";if(/^0[1-9]|[1-2][0-9]|3[0-1]/.test(b)){c.day=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised date (%F)"}else if(d=="%H"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised hour (%H)"}else if(d=="%I"){if(/^0[1-9]|1[0-2]/.test(b)){c.hourAmPm=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised hour (%I)"}else if(d=="%k"){var g=b.match(/^(\d{1,2})/);c.hour=parseInt(g,10);if(isNaN(c.hour)||c.hour<0||c.hour>23)throw"Parse error: Unrecognised hour (%k)";b=b.substring(g.length)}else if(d=="%l"){var g=b.match(/^(\d{1,2})/);c.hourAmPm=parseInt(g,10);if(isNaN(c.hourAmPm)||c.hourAmPm<1||c.hourAmPm>12)throw"Parse error: Unrecognised hour (%l)";b=b.substring(g.length)}else if(d=="%m"){if(/^0[1-9]|1[0-2]/.test(b)){c.month=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised month (%m)"}else if(d=="%M"){if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised minute (%M)"}else if(d=="%n"){if(b.charAt(0)=="\n")b=b.substring(1);else throw"Parse error: Unrecognised new line (%n)"}else if(d=="%p"){if(jsworld._stringStartsWith(b,this.lc.am)){c.am=true;b=b.substring(this.lc.am.length)}else if(jsworld._stringStartsWith(b,this.lc.pm)){c.am=false;b=b.substring(this.lc.pm.length)}else throw"Parse error: Unrecognised AM/PM value (%p)"}else if(d=="%P"){if(jsworld._stringStartsWith(b,this.lc.am.toLowerCase())){c.am=true;b=b.substring(this.lc.am.length)}else if(jsworld._stringStartsWith(b,this.lc.pm.toLowerCase())){c.am=false;b=b.substring(this.lc.pm.length)}else throw"Parse error: Unrecognised AM/PM value (%P)"}else if(d=="%R"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%R)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%R)";if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%R)"}else if(d=="%S"){if(/^[0-5][0-9]/.test(b)){c.second=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised second (%S)"}else if(d=="%T"){if(/^[0-1][0-9]|2[0-3]/.test(b)){c.hour=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%T)";if(/^[0-5][0-9]/.test(b)){c.minute=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)";if(jsworld._stringStartsWith(b,":"))b=b.substring(1);else throw"Parse error: Unrecognised time (%T)";if(/^[0-5][0-9]/.test(b)){c.second=parseInt(b.substring(0,2),10);b=b.substring(2)}else throw"Parse error: Unrecognised time (%T)"}else if(d=="%w"){if(/^\d/.test(b)){c.weekday=parseInt(b.substring(0,1),10);b=b.substring(1)}else throw"Parse error: Unrecognised weekday number (%w)"}else if(d=="%y"){if(/^\d\d/.test(b)){var h=parseInt(b.substring(0,2),10);if(h>50)c.year=1900+h;else c.year=2e3+h;b=b.substring(2)}else throw"Parse error: Unrecognised year (%y)"}else if(d=="%Y"){if(/^\d\d\d\d/.test(b)){c.year=parseInt(b.substring(0,4),10);b=b.substring(4)}else throw"Parse error: Unrecognised year (%Y)"}else if(d=="%Z"){if(a.length===0)break}a=a.substring(2)}else{if(a.charAt(0)!=b.charAt(0))throw'Parse error: Unexpected symbol "'+b.charAt(0)+'" in date/time string';a=a.substring(1);b=b.substring(1)}}return c}};jsworld.MonetaryParser=function(a){if(typeof a!="object"||a._className!="jsworld.Locale")throw"Constructor error: You must provide a valid jsworld.Locale instance";this.lc=a;this.parse=function(a){if(typeof a!="string")throw"Parse error: Argument must be a string";var b=this._detectCurrencySymbolType(a);var c,d;if(b=="local"){c="local";d=a.replace(this.lc.getCurrencySymbol(),"")}else if(b=="int"){c="int";d=a.replace(this.lc.getIntCurrencySymbol(),"")}else if(b=="none"){c="local";d=a}else throw"Parse error: Internal assert failure";d=jsworld._stringReplaceAll(d,this.lc.mon_thousands_sep,"");d=d.replace(this.lc.mon_decimal_point,".");d=d.replace(/\s*/g,"");d=this._removeLocalNonNegativeSign(d,c);d=this._normaliseNegativeSign(d,c);if(jsworld._isNumber(d))return parseFloat(d,10);else throw"Parse error: Invalid currency amount string"};this._detectCurrencySymbolType=function(a){if(this.lc.getCurrencySymbol().length>this.lc.getIntCurrencySymbol().length){if(a.indexOf(this.lc.getCurrencySymbol())!=-1)return"local";else if(a.indexOf(this.lc.getIntCurrencySymbol())!=-1)return"int";else return"none"}else{if(a.indexOf(this.lc.getIntCurrencySymbol())!=-1)return"int";else if(a.indexOf(this.lc.getCurrencySymbol())!=-1)return"local";else return"none"}};this._removeLocalNonNegativeSign=function(a,b){a=a.replace(this.lc.positive_sign,"");if((b=="local"&&this.lc.p_sign_posn===0||b=="int"&&this.lc.int_p_sign_posn===0)&&/\(\d+\.?\d*\)/.test(a)){a=a.replace("(","");a=a.replace(")","")}return a};this._normaliseNegativeSign=function(a,b){a=a.replace(this.lc.negative_sign,"-");if(b=="local"&&this.lc.n_sign_posn===0||b=="int"&&this.lc.int_n_sign_posn===0){if(/^\(\d+\.?\d*\)$/.test(a)){a=a.replace("(","");a=a.replace(")","");return"-"+a}}if(b=="local"&&this.lc.n_sign_posn==2||b=="int"&&this.lc.int_n_sign_posn==2){if(/^\d+\.?\d*-$/.test(a)){a=a.replace("-","");return"-"+a}}if(b=="local"&&this.lc.n_cs_precedes===0&&this.lc.n_sign_posn==3||b=="local"&&this.lc.n_cs_precedes===0&&this.lc.n_sign_posn==4||b=="int"&&this.lc.int_n_cs_precedes===0&&this.lc.int_n_sign_posn==3||b=="int"&&this.lc.int_n_cs_precedes===0&&this.lc.int_n_sign_posn==4){if(/^\d+\.?\d*-$/.test(a)){a=a.replace("-","");return"-"+a}}return a}} + + +if(typeof POSIX_LC == "undefined") var POSIX_LC = {}; + +POSIX_LC.en_US = { + "decimal_point" : ".", + "thousands_sep" : ",", + "grouping" : "3", + "abday" : ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"], + "day" : ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], + "abmon" : ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], + "mon" : ["January","February","March","April","May","June","July","August","September","October","November","December"], + "d_fmt" : "%m/%e/%y", + "t_fmt" : "%I:%M:%S %p", + "d_t_fmt" : "%B %e, %Y %I:%M:%S %p %Z", + "am_pm" : ["AM","PM"], + "int_curr_symbol" : "USD ", + "currency_symbol" : "\u0024", + "mon_decimal_point" : ".", + "mon_thousands_sep" : ",", + "mon_grouping" : "3", + "positive_sign" : "", + "negative_sign" : "-", + "int_frac_digits" : 2, + "frac_digits" : 2, + "p_cs_precedes" : 1, + "n_cs_precedes" : 1, + "p_sep_by_space" : 0, + "n_sep_by_space" : 0, + "p_sign_posn" : 1, + "n_sign_posn" : 1, + "int_p_cs_precedes" : 1, + "int_n_cs_precedes" : 1, + "int_p_sep_by_space" : 0, + "int_n_sep_by_space" : 0, + "int_p_sign_posn" : 1, + "int_n_sign_posn" : 1 +} + +if(typeof POSIX_LC == "undefined") var POSIX_LC = {}; + +POSIX_LC.fr_FR = { + "decimal_point" : ",", + "thousands_sep" : "\u00a0", + "grouping" : "3", + "abday" : ["dim.","lun.","mar.", + "mer.","jeu.","ven.", + "sam."], + "day" : ["dimanche","lundi","mardi", + "mercredi","jeudi","vendredi", + "samedi"], + "abmon" : ["janv.","f\u00e9vr.","mars", + "avr.","mai","juin", + "juil.","ao\u00fbt","sept.", + "oct.","nov.","d\u00e9c."], + "mon" : ["janvier","f\u00e9vrier","mars", + "avril","mai","juin", + "juillet","ao\u00fbt","septembre", + "octobre","novembre","d\u00e9cembre"], + "d_fmt" : "%d/%m/%y", + "t_fmt" : "%H:%M:%S", + "d_t_fmt" : "%e %B %Y %H:%M:%S %Z", + "am_pm" : ["AM","PM"], + "int_curr_symbol" : "EUR ", + "currency_symbol" : "\u20ac", + "mon_decimal_point" : ",", + "mon_thousands_sep" : "\u00a0", + "mon_grouping" : "3", + "positive_sign" : "", + "negative_sign" : "-", + "int_frac_digits" : 2, + "frac_digits" : 2, + "p_cs_precedes" : 0, + "n_cs_precedes" : 0, + "p_sep_by_space" : 1, + "n_sep_by_space" : 1, + "p_sign_posn" : 1, + "n_sign_posn" : 1, + "int_p_cs_precedes" : 0, + "int_n_cs_precedes" : 0, + "int_p_sep_by_space" : 1, + "int_n_sep_by_space" : 1, + "int_p_sign_posn" : 1, + "int_n_sign_posn" : 1 +}; + +/** https://github.com/csnover/js-iso8601 */(function(n,f){var u=n.parse,c=[1,4,5,6,7,10,11];n.parse=function(t){var i,o,a=0;if(o=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(t)){for(var v=0,r;r=c[v];++v)o[r]=+o[r]||0;o[2]=(+o[2]||1)-1,o[3]=+o[3]||1,o[8]!=="Z"&&o[9]!==f&&(a=o[10]*60+o[11],o[9]==="+"&&(a=0-a)),i=n.UTC(o[1],o[2],o[3],o[4],o[5]+a,o[6],o[7])}else i=u?u(t):NaN;return i}})(Date) + +/*! + * geo-location-javascript v0.4.3 + * http://code.google.com/p/geo-location-javascript/ + * + * Copyright (c) 2009 Stan Wiechers + * Licensed under the MIT licenses. + * + * Revision: $Rev: 68 $: + * Author: $Author: whoisstan $: + * Date: $Date: 2010-02-15 13:42:19 +0100 (Mon, 15 Feb 2010) $: + */ +var geo_position_js=function() { + + var pub = {}; + var provider=null; + + pub.getCurrentPosition = function(successCallback,errorCallback,options) + { + provider.getCurrentPosition(successCallback, errorCallback,options); + } + + pub.init = function() + { + try + { + if (typeof(geo_position_js_simulator)!="undefined") + { + provider=geo_position_js_simulator; + } + else if (typeof(bondi)!="undefined" && typeof(bondi.geolocation)!="undefined") + { + provider=bondi.geolocation; + } + else if (typeof(navigator.geolocation)!="undefined") + { + provider=navigator.geolocation; + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + function _successCallback(p) + { + //for mozilla geode,it returns the coordinates slightly differently + if(typeof(p.latitude)!="undefined") + { + successCallback({timestamp:p.timestamp, coords: {latitude:p.latitude,longitude:p.longitude}}); + } + else + { + successCallback(p); + } + } + provider.getCurrentPosition(_successCallback,errorCallback,options); + } + } + else if(typeof(window.google)!="undefined" && typeof(google.gears)!="undefined") + { + provider=google.gears.factory.create('beta.geolocation'); + } + else if ( typeof(Mojo) !="undefined" && typeof(Mojo.Service.Request)!="Mojo.Service.Request") + { + provider=true; + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + + parameters={}; + if(options) + { + //http://developer.palm.com/index.php?option=com_content&view=article&id=1673#GPS-getCurrentPosition + if (options.enableHighAccuracy && options.enableHighAccuracy==true) + { + parameters.accuracy=1; + } + if (options.maximumAge) + { + parameters.maximumAge=options.maximumAge; + } + if (options.responseTime) + { + if(options.responseTime<5) + { + parameters.responseTime=1; + } + else if (options.responseTime<20) + { + parameters.responseTime=2; + } + else + { + parameters.timeout=3; + } + } + } + + + r=new Mojo.Service.Request('palm://com.palm.location', { + method:"getCurrentPosition", + parameters:parameters, + onSuccess: function(p){successCallback({timestamp:p.timestamp, coords: {latitude:p.latitude, longitude:p.longitude,heading:p.heading}});}, + onFailure: function(e){ + if (e.errorCode==1) + { + errorCallback({code:3,message:"Timeout"}); + } + else if (e.errorCode==2) + { + errorCallback({code:2,message:"Position Unavailable"}); + } + else + { + errorCallback({code:0,message:"Unknown Error: webOS-code"+errorCode}); + } + } + }); + } + + } + else if (typeof(device)!="undefined" && typeof(device.getServiceObject)!="undefined") + { + provider=device.getServiceObject("Service.Location", "ILocation"); + + //override default method implementation + pub.getCurrentPosition = function(successCallback, errorCallback, options) + { + function callback(transId, eventCode, result) { + if (eventCode == 4) + { + errorCallback({message:"Position unavailable", code:2}); + } + else + { + //no timestamp of location given? + successCallback({timestamp:null, coords: {latitude:result.ReturnValue.Latitude, longitude:result.ReturnValue.Longitude, altitude:result.ReturnValue.Altitude,heading:result.ReturnValue.Heading}}); + } + } + //location criteria + var criteria = new Object(); + criteria.LocationInformationClass = "BasicLocationInformation"; + //make the call + provider.ILocation.GetLocation(criteria,callback); + } + } + } + catch (e){ + alert("error="+e); + if(typeof(console)!="undefined") + { + console.log(e); + } + return false; + } + return provider!=null; + } + + + return pub; +}(); +// Couldn't get unminified version to work , go here for docs => https://github.com/iamnoah/writeCapture +(function(E,a){var j=a.document;function A(Q){var Z=j.createElement("div");j.body.insertBefore(Z,null);E.replaceWith(Z,'\n \n
      \n
      \n \n\n
      \n
      \n \n
      \n

      '); + __out.push(__sanitize(t('Invite Link'))); + __out.push(' '); + __out.push(__sanitize(USER.referral_url)); + __out.push('

      \n\n \n\n
      \n\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/login": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('
      \n\t

      '); + __out.push(__sanitize(t('Sign In'))); + __out.push('

      \n\t
      \n\t\t
      \n\n\t\t\t
      \n\t\t\t\t\n\t\t\t
      \n\t\t\t
      \n\t\t\t\t\n\t\t\t
      \n\n\t\t\t
      \n\n\t\t\t
      \n\t\t\t\t\n\t\t\t
      \n\t\t\t
      \n\t\t\t\t\n\t\t\t
      \n\n\t\t\t
      \n\n
      \n\n

      '); + __out.push(__sanitize(t('Forgot Password?'))); + __out.push('

      \n\n\t\t
      \n\t
      \n
      \n\n
      \n
      \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/modules/credit_card": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var printCard; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + if (this.cards === "new") { + __out.push('\n
      \n
      \n
      \n
      \n
      \n
      \n \n \n
      \n
      \n
      \n
      \n \n \n
      \n
      \n \n \n
      \n
      \n
      \n
      \n \n \n
      \n
      \n
      \n \n \n
      \n
      \n
      \n \n \n
      \n
      \n
      \n \n
      \n
      \n
      \n'); + } else { + __out.push('\n '); + printCard = __bind(function(card, index) { + var exp, style; + __out.push('\n
      \n '); + style = "background-position:-173px"; + __out.push('\n '); + if (card.get("card_type") === "Visa") { + style = "background-position:0px"; + } + __out.push('\n '); + if (card.get("card_type") === "MasterCard") { + style = "background-position:-42px"; + } + __out.push('\n '); + if (card.get("card_type") === "American Express") { + style = "background-position:-130px"; + } + __out.push('\n '); + if (card.get("card_type") === "Discover Card") { + style = "background-position:-85px"; + } + __out.push('\n
      \n
      \n ****'); + __out.push(__sanitize(card.get("card_number"))); + __out.push('\n \n '); + if (card.get("card_expiration")) { + __out.push('\n '); + __out.push(__sanitize(t('Expiry'))); + __out.push('\n '); + exp = card.get('card_expiration').split('-'); + __out.push('\n '); + __out.push(__sanitize("" + exp[0] + "-" + exp[1])); + __out.push('\n '); + } + __out.push('\n \n \n \n '); + if (card.get("default")) { + __out.push('\n ('); + __out.push(__sanitize(t('default card'))); + __out.push(')\n '); + } + __out.push('\n '); + if (this.cards.length > 1 && !card.get("default")) { + __out.push('\n '); + __out.push(__sanitize(t('make default'))); + __out.push('\n '); + } + __out.push('\n \n '); + __out.push(__sanitize(t('Edit'))); + __out.push('\n \n '); + if (this.cards.length > 1) { + __out.push('\n '); + __out.push(__sanitize(t('Delete'))); + __out.push('\n '); + } + __out.push('\n
      \n '); + _.each(this.cards.models, printCard); + __out.push('\n
      \n
      \n\n'); + } + __out.push('\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/modules/sub_header": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('
      \n
      '); + __out.push(__sanitize(this.heading)); + __out.push('
      \n
      \n '); + if (window.USER.first_name) { + __out.push('\n '); + __out.push(__sanitize(t('Hello Greeting', { + name: USER.first_name + }))); + __out.push('\n '); + } + __out.push('\n
      \n
      \n
      \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/promotions": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var promo, _i, _len, _ref; + __out.push(require('templates/clients/modules/sub_header').call(this, { + heading: t("Promotions") + })); + __out.push('\n\n
      \n
      \n
      \n \n \n
      \n
      \n \n \n\n \n
      \n '); + if (this.promos.length > 0) { + __out.push('\n
      \n

      '); + __out.push(__sanitize(t('Your Available Promotions'))); + __out.push('

      \n \n \n\n \n \n \n \n \n \n \n \n '); + _ref = this.promos; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + promo = _ref[_i]; + __out.push('\n \n \n \n \n \n \n '); + } + __out.push('\n \n
      '); + __out.push(__sanitize(t('Code'))); + __out.push(''); + __out.push(__sanitize(t('Details'))); + __out.push(''); + __out.push(__sanitize(t('Starts'))); + __out.push(''); + __out.push(__sanitize(t('Expires'))); + __out.push('
      '); + __out.push(__sanitize(promo.code)); + __out.push(''); + __out.push(__sanitize(promo.description)); + __out.push(''); + __out.push(__sanitize(app.helpers.formatDate(promo.starts_at, true, "America/Los_Angeles"))); + __out.push(''); + __out.push(__sanitize(app.helpers.formatDate(promo.ends_at, true, "America/Los_Angeles"))); + __out.push('
      \n
      \n '); + } else { + __out.push('\n\n

      '); + __out.push(__sanitize(t('No Active Promotions'))); + __out.push('

      \n '); + } + __out.push('\n\n
      \n
      \n
      \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/clients/request": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + var showFavoriteLocation; + showFavoriteLocation = function(location, index) { + var alphabet; + __out.push('\n '); + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + __out.push('\n
      \n '); + __out.push(__sanitize(location.nickname)); + return __out.push('\n
      \n \n \n \n \n \n \n \n \n \n \n \n \n \n
      \n

      '); + __out.push(__sanitize(t('Driver Name:'))); + __out.push('

      \n

      \n
      \n

      '); + __out.push(__sanitize(t('Driver #:'))); + __out.push('

      \n

      \n
      \n

      '); + __out.push(__sanitize(t('Pickup Address:'))); + __out.push('

      \n

      \n
      \n ');
+      __out.push(__sanitize(t('Add to Favorite Locations')));
+      __out.push('\n
      \n
      \n

      \n '); + __out.push(__sanitize(t('Nickname:'))); + __out.push('\n \n \n \n \n
      \n
      \n
      \n
      \n

      '); + __out.push(__sanitize(t('Your last trip'))); + __out.push('

      \n
      \n \n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n ');
+      __out.push(__sanitize(t('Star')));
+      __out.push('\n \n \n
      \n \n
      \n \n
      \n \n
      \n \n\n
      \n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "templates/shared/menu": function(exports, require, module) {module.exports = function(__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + __out.push('\n'); + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}}, "translations/en": function(exports, require, module) {(function() { + exports.translations = { + "Uber": "Uber", + "Sign Up": "Sign Up", + "Ride Request": "Ride Request", + "Invite Friends": "Invite Friends", + "Promotions": "Promotions", + "Billing": "Billing", + "Settings": "Settings", + "Forgot Password?": "Forgot Password?", + "Password Recovery": "Password Recovery", + "Login": "Login", + "Trip Detail": "Trip Detail", + "Password Reset": "Password Reset", + "Confirm Email": "Confirm Email", + "Request Ride": "Request Ride", + "Credit Card Number": "Credit Card Number", + "month": "month", + "01-Jan": "01-Jan", + "02-Feb": "02-Feb", + "03-Mar": "03-Mar", + "04-Apr": "04-Apr", + "05-May": "05-May", + "06-Jun": "06-Jun", + "07-Jul": "07-Jul", + "08-Aug": "08-Aug", + "09-Sep": "09-Sep", + "10-Oct": "10-Oct", + "11-Nov": "11-Nov", + "12-Dec": "12-Dec", + "year": "year", + "CVV": "CVV", + "Category": "Category", + "personal": "personal", + "business": "business", + "Default Credit Card": "Default Credit Card", + "Add Credit Card": "Add Credit Card", + "Expiry": "Expiry", + "default card": "default card", + "make default": "make default", + "Edit": "Edit", + "Delete": "Delete", + "Expiry Month": "Expiry Month", + "Expiry Year": "Expiry Year", + "Unable to Verify Card": "Unable to verify card at this time. Please try again later.", + "Credit Card Update Succeeded": "Your card has been successfully updated!", + "Credit Card Update Failed": "We couldn't save your changes. Please try again in a few minutes.", + "Credit Card Delete Succeeded": "Your card has been deleted!", + "Credit Card Delete Failed": "We were unable to delete your card. Please try again later.", + "Credit Card Update Category Succeeded": "Successfully changed card category!", + "Credit Card Update Category Failed": "We couldn't change your card category. Please try again in a few minutes.", + "Credit Card Update Default Succeeded": "Successfully changed default card!", + "Credit Card Update Default Failed": "We couldn't change your default card. Please try again in a few minutes.", + "Hello Greeting": "Hello, <%= name %>", + "Card Ending in": "Card Ending in", + "Trip Map": "Trip Map", + "Amount": "Amount: <%= amount %>", + "Last Attempt to Bill": "Last Attempt to Bill: <%= date %>", + "Charge": "Charge", + "Uber Credit Balance Note": "Your account has an UberCredit balance of <%= amount %>. When billing for trips, we'll deplete your UberCredit balance before applying charges to your credit card.", + "Please Add Credit Card": "Please add a credit card to bill your outstanding charges.", + "Credit Cards": "Credit Cards", + "add a new credit card": "add a new credit card", + "Account Balance": "Account Balance", + "Arrears": "Arrears", + "Billing Succeeded": "Your card was successfully billed.", + "Confirm Email Succeeded": "Successfully confirmed email token, redirecting to log in page...", + "Confirm Email Failed": "Unable to confirm email. Please contact support@uber.com if this problem persists.", + "Email Already Confirmed": "Your email address has already been confirmed, redirecting to log in page...", + "Credit Card Added": "Credit Card Added", + "No Credit Card": "No Credit Card", + "Mobile Number Confirmed": "Mobile Number Confirmed", + "No Confirmed Mobile": "No Confirmed Mobile", + "E-mail Address Confirmed": "E-mail Address Confirmed", + "No Confirmed E-mail": "No Confirmed E-mail", + 'Reply to sign up text': 'Reply "GO" to the text message you received at sign up.', + "Resend text message": "Resend text message", + "Click sign up link": "Click the link in the email you received at sign up.", + "Resend email": "Resend email", + "Add a credit card to ride": "Add a credit card and you'll be ready to ride Uber.", + "Your Most Recent Trip": "Your Most Recent Trip", + "details": "details", + "Your Trip History ": "Your Trip History ", + "Status": "Status", + "Here's how it works:": "Here's how it works:", + "Show all trips": "Show all trips", + "Set your location:": "Set your location:", + "App search for address": "iPhone/Android app: fix the pin or search for an address", + "SMS text address": "SMS: text your address to UBRCAB (827222)", + "Confirm pickup request": "Confirm your pickup request", + "Uber sends ETA": "Uber will send you an ETA (usually within 5-10 minutes)", + "Car arrives": "When your car is arriving, Uber will inform you again.", + "Ride to destination": "Hop in the car and tell the driver your destination.", + "Thank your driver": "That’s it! Please thank your driver but remember that your tip is included and no cash is necessary.", + "Trip started here": "Trip started here", + "Trip ended here": "Trip ended here", + "Sending Email": "Sending email...", + "Resend Email Succeeded": "We just sent the email. Please click on the confirmation link you recieve.", + "Resend Email Failed": "There was an error sending the email. Please contact support if the problem persists.", + "Resend Text Succeeded": 'We just sent the text message. Please reply "GO" to the message you recieve. It may take a few minutes for the message to reach you phone.', + "Resend Text Failed": "There was an error sending the text message. Please contact support if the problem persists.", + "Password Reset Error": "There was an error processing your password reset request.", + "New Password": "New Password", + "Forgot Password": "Forgot Password", + "Forgot Password Error": "Your email address could not be found. Please make sure to use the same email address you used when you signed up.", + "Forgot Password Success": "Please check your email for a link to reset your password.", + "Forgot Password Enter Email": 'Enter your email address and Uber will send you a link to reset your password. If you remember your password, you can sign in here.', + "Invite friends": "Invite friends", + "Give $ Get $": "Give $10, Get $10", + "Give $ Get $ Description": "Every friend you invite to Uber gets $10 of Uber credit. After someone you’ve invited takes his/her first ride, you get $10 of Uber credits too!", + "What are you waiting for?": "So, what are you waiting for? Invite away!", + "Tweet": "Tweet", + "Invite Link": "Email or IM this link to your friends:", + "Email Address": "Email Address", + "Reset Password": "Reset Password", + "Enter Promotion Code": "If you have a promotion code, enter it here:", + "Your Active Promotions": "Your Active Promotions", + "Code": "Code", + "Details": "Details", + "Trips Remaining": "Trips Remaining", + "Expires": "Expires", + "No Active Promotions": "There are no active promotions on your account.", + "Your Available Promotions": "Your Available Promotions", + "Where do you want us to pick you up?": "Where do you want us to pick you up?", + "Address to search": "Address to search", + "Search": "Search", + "Driver Name:": "Driver Name:", + "Driver #:": "Driver #:", + "Pickup Address:": "Pickup Address:", + "Add to Favorite Locations": "Add to Favorite Locations", + "Star": "Star", + "Nickname:": "Nickname:", + "Add": "Add", + "Your last trip": "Your last trip", + "Please rate your driver:": "Please rate your driver:", + "Comments: (optional)": "Comments: (optional)", + "Rate Trip": "Rate Trip", + "Pickup time:": "Pickup time:", + "Miles:": "Miles:", + "Trip time:": "Trip time:", + "Fare:": "Fare:", + "Favorite Locations": "Favorite Locations", + "Search Results": "Search Results", + "You have no favorite locations saved.": "You have no favorite locations saved.", + "Loading...": "Loading...", + "Request Pickup": "Request Pickup", + "Cancel Pickup": "Cancel Pickup", + "Requesting Closest Driver": "Requesting the closest driver to pick you up...", + "En Route": "You are currently en route...", + "Rate Last Trip": "Please rate your trip to make another request", + "Rate Before Submitting": "Please rate your trip before submitting the form", + "Address too short": "Address too short", + "or did you mean": "or did you mean", + "Search Address Failed": "Unable to find the given address. Please enter another address close to your location.", + "Sending pickup request...": "Sending pickup request...", + "Cancel Request Prompt": "Are you sure you want to cancel your request?", + "Cancel Request Arrived Prompt": 'Are you sure you want to cancel your request? Your driver has arrived so there is a $10 cancellation fee. It may help to call your driver now', + "Favorite Location Nickname Length Error": "Nickname has to be atleast 3 characters", + "Favorite Location Save Succeeded": "Location Saved!", + "Favorite Location Save Failed": "Unable to save your location. Please try again later.", + "Favorite Location Title": "Favorite Location <%= id %>", + "Search Location Title": "Search Location <%= id %>", + "ETA Message": "ETA: Around <%= minutes %> Minutes", + "Nearest Cab Message": "The closest driver is approximately <%= minutes %> minute(s) away", + "Arrival ETA Message": "Your Uber will arrive in about <%= minutes %> minute(s)", + "Arriving Now Message": "Your Uber is arriving now...", + "Rating Driver Failed": "Unable to contact server. Please try again later or email support if this issue persists.", + "Account Information": "Account Information", + "Mobile Phone Information": "Mobile Phone Information", + "settings": "settings", + "Information": "Information", + "Picture": "Picture", + "Change password": "Change password", + "Your current Picture": "Your current Picture", + "Your Favorite Locations": "Your Favorite Locations", + "You have no favorite locations saved.": "You have no favorite locations saved.", + "Purpose of Mobile": "We send text messages to your mobile phone to tell you when your driver is arriving. You can also request trips using text messages.", + "Country": "Country", + "Mobile Number": "Mobile Number", + "Submit": "Submit", + "Favorite Location": "Favorite Location", + "No Approximate Address": "Could not find an approximate address", + "Address:": "Address:", + "Information Update Succeeded": "Your information has been updated!", + "Information Update Failed": "We couldn't update your information. Please try again in few minutes or contact support if the problem persists.", + "Location Delete Succeeded": "Location deleted!", + "Location Delete Failed": "We were unable to delete your favorite location. Please try again later or contact support of the issue persists.", + "Location Edit Succeeded": "Changes Saved!", + "Location Edit Failed": "We couldn't save your changes. Please try again in a few minutes.", + "Picture Update Succeeded": "Your picture has been updated!", + "Picture Update Failed": "We couldn't change your picture. Please try again in a few minutes.", + "Personal Information": "Personal Information", + "Mobile Phone Number": "Mobile Phone Number", + "Payment Information": "Payment Information", + "Purpose of Credit Card": "We keep your credit card on file so that your trip go as fast as possible. You will not be charged until you take a trip.", + "Your card will not be charged until you take a trip.": "Your card will not be charged until you take a trip.", + "Credit Card Number": "Credit Card Number", + "Expiration Date": "Expiration Date", + "Promotion Code": "Promotion Code", + "Enter Promo Here": "If you have a code for a promotion, invitation or group deal, you can enter it here.", + "Promotion Code Input Label": "Promotion, Invite or Groupon Code (optional)", + "Terms and Conditions": "Terms and Conditions", + "HELP": "HELP", + "STOP": "STOP", + "Legal Information": "Legal Information", + "Sign Up Agreement": "By signing up, I agree to the Uber <%= terms_link %> and <%= privacy_link %> and understand that Uber is a request tool, not a transportation carrier.", + "Sign Up Agreement Error": "You must agree to the Uber Terms and Conditions and Privacy Policy to continue.", + "Message and Data Rates Disclosure": "Message and Data Rates May Apply. Reply <%= help_string %> to 827-222 for help. Reply <%= stop_string %> to 827-222 to stop texts. For additional assistance, visit support.uber.com or call (866) 576-1039. Supported Carriers: AT&T, Sprint, Verizon, and T-Mobile.", + "I Agree": "I agree to the Terms & Conditions and Privacy Policy", + "Security Code": "Security Code", + "Type of Card": "Type of Card", + "Personal": "Personal", + "Business": "Business", + "Code": "Code", + "Zip or Postal Code": "Zip or Postal Code", + "Your Trip": "Your Trip", + "Trip Info": "Trip Info", + "Request a fare review": "Request a fare review", + "Fare Review Submitted": "Your fare review has been submitted. We'll get back to you soon about your request. Sorry for any inconvenience this may have caused!", + "Fair Price Consideration": "We're committed to delivering Uber service at a fair price. Before requesting a fare review, please consider:", + "Your Fare Calculation": "Your Fare Calculation", + "Charges": "Charges", + "Discounts": "Discounts", + "Total Charge": "Total Charge", + "Uber pricing information": "Uber pricing information", + "Uber Pricing Information Message": "<%= learn_link %> is published on our website.", + "GPS Point Capture Disclosure": "Due to a finite number of GPS point captures, corners on your trip map may appear cut off or rounded. These minor inaccuracies result in a shorter measured distance, which always results in a cheaper trip.", + "Fare Review Note": "Please elaborate on why this trip requires a fare review. Your comments below will help us better establish the correct price for your trip:", + "Fare Review Error": "There was an error submitting the review. Please ensure that you have a message.", + "Sign In": "Sign In" + }; +}).call(this); +}, "translations/fr": function(exports, require, module) {(function() { + exports.translations = { + "Uber": "Uber", + "Sign Up": "Inscription", + "Ride Request": "Passer une Commande", + "Invite Friends": "Inviter vos Amis", + "Promotions": "Promotions", + "Billing": "Paiement", + "Settings": "Paramètres", + "Forgot Password?": "Mot de passe oublié ?", + "Password Recovery": "Récupération du mot de passe", + "Login": "Connexion", + "Trip Detail": "Détail de la Course", + "Password Reset": "Réinitialisation du mot de passe", + "Confirm Email": "Confirmation de l’e-mail", + "Request Ride": "Passer une Commande", + "Credit Card Number": "Numéro de Carte de Crédit", + "month": "mois", + "01-Jan": "01-Jan", + "02-Feb": "02-Fév", + "03-Mar": "03-Mar", + "04-Apr": "04-Avr", + "05-May": "05-Mai", + "06-Jun": "06-Juin", + "07-Jul": "07-Jui", + "08-Aug": "08-Aoû", + "09-Sep": "09-Sep", + "10-Oct": "10-Oct", + "11-Nov": "11-Nov", + "12-Dec": "12-Déc", + "year": "année", + "CVV": "Code de Sécurité", + "Category": "Type", + "personal": "personnel", + "business": "entreprise", + "Default Credit Card": "Carte par Défaut", + "Add Credit Card": "Ajouter une Carte", + "Expiry": "Expire", + "default card": "carte par défaut", + "make default": "choisir par défaut", + "Edit": "Modifier", + "Delete": "Supprimer", + "Expiry Month": "Mois d’Expiration", + "Expiry Year": "Année d’Expiration", + "Unable to Verify Card": "Impossible de vérifier la carte pour le moment. Merci de réessayer un peu plus tard.", + "Credit Card Update Succeeded": "Votre carte a été mise à jour avec succès !", + "Credit Card Update Failed": "Nous ne pouvons enregistrer vos changements. Merci de réessayer dans quelques minutes.", + "Credit Card Delete Succeeded": "Votre carte a été supprimée !", + "Credit Card Delete Failed": "Nous n’avons pas été en mesure de supprimer votre carte. Merci de réessayer plus tard.", + "Credit Card Update Category Succeeded": "Changement de catégorie de carte réussi !", + "Credit Card Update Category Failed": "Nous ne pouvons pas changer la catégorie de votre carte. Merci de réessayer dans quelques minutes.", + "Credit Card Update Default Succeeded": "Carte par défaut changée avec succès !", + "Credit Card Update Default Failed": "Nous ne pouvons pas changer votre carte par défaut. Merci de réessayer dans quelques minutes.", + "Hello Greeting": "Bonjour, <%= name %>", + "Card Ending in": "La carte expire dans", + "Trip Map": "Carte des Courses", + "Amount": "Montant: <%= amount %>", + "Last Attempt to Bill": "Dernière tentative de prélèvement : <%= date %>", + "Charge": "Débit", + "Uber Credit Balance Note": "Votre compte a un solde de <%= amount %> UberCredits. Lorsque nous facturons des courses, nous réduirons votre solde d’UberCredits avant de prélever votre carte de crédit.", + "Please Add Credit Card": "Merci d’ajouter une carte de crédit pour que nous puissions vous facturer.", + "Credit Cards": "Cartes de crédit", + "add a new credit card": "Ajouter une nouvelle carte de crédit", + "Account Balance": "Solde du compte", + "Arrears": "Arriérés", + "Billing Succeeded": "Votre carte a été correctement débitée.", + "Confirm Email Succeeded": "L’adresse e-mail a bien été validée, vous êtes redirigé vers le tableau de bord...", + "Confirm Email Failed": "Impossible de confirmer l’adresse e-mail. Merci de contacter support@uber.com si le problème persiste.", + "Credit Card Added": "Carte de crédit ajoutée", + "No Credit Card": "Pas de carte de crédit", + "Mobile Number Confirmed": "Numéro de téléphone confirmé", + "No Confirmed Mobile": "Pas de numéro de téléphone confirmé", + "E-mail Address Confirmed": "Adresse e-mail confirmée", + "No Confirmed E-mail": "Pas d’adresse e-mail confirmée", + 'Reply to sign up text': 'Répondre "GO" au SMS que vous avez reçu à l’inscription.', + "Resend text message": "Renvoyer le SMS", + "Click sign up link": "Cliquez sur le lien contenu dans l’e-mail reçu à l’inscription.", + "Resend email": "Renvoyer l’e-mail", + "Add a credit card to ride": "Ajouter une carte de crédit et vous serez prêt à voyager avec Uber.", + "Your Most Recent Trip": "Votre course la plus récente", + "details": "détails", + "Your Trip History": "Historique de votre trajet", + "Status": "Statut", + "Here's how it works:": "Voici comment ça marche :", + "Show all trips": "Montrer toutes les courses", + "Set your location:": "Définir votre position :", + "App search for address": "Application iPhone/Android : positionner la punaise ou rechercher une adresse", + "SMS text address": "SMS : envoyez votre adresse à UBRCAB (827222)", + "Confirm pickup request": "Validez la commande", + "Uber sends ETA": "Uber envoie un temps d’attente estimé (habituellement entre 5 et 10 minutes)", + "Car arrives": "Lorsque votre voiture arrive, Uber vous en informera encore..", + "Ride to destination": "Montez dans la voiture et donnez votre destination au chauffeur.", + "Thank your driver": "C’est tout ! Remerciez le chauffeur mais souvenez-vous que les pourboires sont compris et qu’il n’est pas nécessaire d’avoir du liquide sur soi.", + "Trip started here": "La course a commencé ici.", + "Trip ended here": "La course s’est terminée ici.", + "Sending Email": "Envoi de l’e-mail...", + "Resend Email Succeeded": "Nous venons d’envoyer l’e-mail. Merci de cliquer sur le lien de confirmation que vous avez reçu.", + "Resend Email Failed": "Il y a eu un problème lors de l’envoi de l’email. Merci de contacter le support si le problème persiste.", + "Resend Text Succeeded": 'Nous venons d’envoyer le SMS. Merci de répondre "GO" au message que vous avez reçu. Il se peut que cela prenne quelques minutes pour que le message arrive sur votre téléphone.', + "Resend Text Failed": "Il y a eu un problème lors de l’envoi du SMS. Merci de contacter le support si le problème persiste.", + "Password Reset Error": "Il y a eu une error lors de la réinitialisation de votre mot de passe.", + "New Password:": "Nouveau mot de passe:", + "Forgot Password Error": "Votre nom d’utilisateur / adresse email ne peut être trouvé. Merci d’utiliser la même qu’à l’inscription.", + "Forgot Password Success": "Merci de consulter votre boîte mail pour suivre la demande de ‘réinitialisation de mot de passe.", + "Forgot Password Enter Email": "Merci de saisir votre adresse email et nous vous enverrons un lien vous permettant de réinitialiser votre mot de passe :", + "Invite friends": "Inviter vos amis", + "Give $ Get $": "Donnez $10, Recevez $10", + "Give $ Get $ Description": "Chaque ami que vous invitez à Uber recevra $10 de crédits Uber. Dès lors qu’une personne que vous aurez invité aura utilisé Uber pour la première, vous recevrez $10 de crédits Uber également !", + "What are you waiting for?": "N’attendez plus ! Lancez les invitations !", + "Tweet": "Tweeter", + "Invite Link": "Envoyez ce lien par email ou messagerie instantanée à vos amis :", + "Enter Promotion Code": "Si vous avez un code promo, saisissez-le ici:", + "Your Active Promotions": "Vos Codes Promos Actifs", + "Code": "Code", + "Details": "Détails", + "Trips Remaining": "Courses restantes", + "Expires": "Expire", + "No Active Promotions": "Vous n’avez pas de code promo actif.", + "Your Available Promotions": "Votres Promos Disponibles", + "Where do you want us to pick you up?": "Où souhaitez-vous que nous vous prenions en charge ?", + "Address to search": "Adresse à rechercher", + "Search": "Chercher", + "Driver Name:": "Nom du chauffeur:", + "Driver #:": "# Chauffeur:", + "Pickup Address:": "Lieu de prise en charge:", + "Add to Favorite Locations": "Ajoutez aux Lieux Favoris", + "Star": "Étoiles", + "Nickname:": "Pseudo", + "Add": "Ajouter", + "Your last trip": "Votre dernière course", + "Please rate your driver:": "Merci de noter votre chauffeur :", + "Comments: (optional)": "Commentaires: (optionnel)", + "Rate Trip": "Notez votre course", + "Pickup time:": "Heure de Prise en Charge :", + "Miles:": "Kilomètres :", + "Trip time:": "Temps de course :", + "Fare:": "Tarif :", + "Favorite Locations": "Lieux Favoris", + "Search Results": "Résultats", + "You have no favorite locations saved.": "Vous n’avez pas de lieux de prise en charge favoris.", + "Loading...": "Chargement...", + "Request Pickup": "Commander ici", + "Cancel Pickup": "Annuler", + "Requesting Closest Driver": "Nous demandons au chauffeur le plus proche de vous prendre en charge...", + "En Route": "Vous êtes actuellement en route...", + "Rate Last Trip": "Merci de noter votre précédent trajet pour faire une autre course.", + "Rate Before Submitting": "Merci de noter votre trajet avant de le valider.", + "Address too short": "L’adresse est trop courte", + "or did you mean": "ou vouliez-vous dire", + "Search Address Failed": "Impossible de trouver l’adresse spécifiée. Merci de saisir une autre adresse proche de l’endroit où vous vous trouvez.", + "Sending pickup request...": "Envoi de la demande de prise en charge...", + "Cancel Request Prompt": "Voulez-vous vraiment annuler votre demande ?", + "Cancel Request Arrived Prompt": 'Voulez-vous vraiment annuler votre demande ? Votre chauffeur est arrivé, vous serez donc facturé de $10 de frais d’annulation. Il pourrait être utile que vous appeliez votre chauffeur maintenant.', + "Favorite Location Nickname Length Error": "Le pseudo doit faire au moins 3 caractères de long", + "Favorite Location Save Succeeded": "Adresse enregistrée !", + "Favorite Location Save Failed": "Impossible d’enregistrer votre adresse. Merci de réessayer ultérieurement.", + "Favorite Location Title": "Adresse favorie <%= id %>", + "Search Location Title": "Recherche d’adresse <%= id %>", + "ETA Message": "Temps d’attente estimé: environ <%= minutes %> minutes", + "Nearest Cab Message": "Le chauffeur le plus proche sera là dans <%= minutes %> minute(s)", + "Arrival ETA Message": "Votre chauffeur arrivera dans <%= minutes %> minute(s)", + "Arriving Now Message": "Votre chauffeur est en approche...", + "Rating Driver Failed": "Impossible de contacter le serveur. Merci de réessayer ultérieurement ou de contacter le support si le problème persiste.", + "settings": "Paramètres", + "Information": "Information", + "Picture": "Photo", + "Change password": "Modifier votre mot de passe", + "Your current Picture": "Votre photo", + "Your Favorite Locations": "Vos lieux favoris", + "You have no favorite locations saved.": "Vous n’avez pas de lieu favori", + "Account Information": "Informations Personnelles", + "Mobile Phone Information": "Informations de Mobile", + "Change Your Password": "Changez votre mot de passe.", + "Country": "Pays", + "Language": "Langue", + "Favorite Location": "Lieu favori", + "No Approximate Address": "Impossible de trouver une adresse même approximative", + "Address:": "Adresse :", + "Information Update Succeeded": "Vos informations ont été mises à jour !", + "Information Update Failed": "Nous n’avons pas pu mettre à jour vos informations. Merci de réessayer dans quelques instants ou de contacter le support si le problème persiste.", + "Location Delete Succeeded": "Adresse supprimée !", + "Location Delete Failed": "Nous n’avons pas pu supprimée votre adresse favorie. Merci de réessayer plus tard ou de contacter le support si le problème persiste.", + "Location Edit Succeeded": "Modifications sauvegardées !", + "Location Edit Failed": "Nous n’avons pas pu sauvegarder vos modifications. Merci de réessayer dans quelques minutes.", + "Picture Update Succeeded": "Votre photo a été mise à jour !", + "Picture Update Failed": "Nous n’avons pas pu mettre à jour votre photo. Merci de réessayer dans quelques instants.", + "Personal Information": "Informations Personnelles", + "Mobile Phone Number": "Numéro de Téléphone Portable", + "Payment Information": "Informations de Facturation", + "Your card will not be charged until you take a trip.": "Votre carte ne sera pas débitée avant votre premier trajet.", + "Card Number": "Numéro de Carte", + "Promotion Code Input Label": "Code promo, code d’invitation ou “deal” acheté en ligne (optionnel)", + "Terms and Conditions": "Conditions Générales", + "HELP": "HELP", + "STOP": "STOP", + "Sign Up Agreement": "En souscrivant, j’accepte les <%= terms_link %> et <%= privacy_link %> et comprends qu’Uber est un outil de commande de chauffeur, et non un transporteur.", + "Sign Up Agreement Error": "Vous devez accepter les Conditions Générales d’utilisation d’Uber Terms and Conditions et la Politique de Confidentialité pour continuer.", + "Message and Data Rates Disclosure": "Les frais d’envoi de SMS et de consommation de données peuvent s’appliquer. Répondez <%= help_string %> au 827-222 pour obtenir de l’aide. Répondez <%= stop_string %> au 827-222 pour ne plus recevoir de SMS. Pour plus d’aide, visitez support.uber.com ou appelez le (866) 576-1039. Opérateurs supportés: AT&T, Sprint, Verizon, T-Mobile, Orange, SFR et Bouygues Telecom.", + "Zip/Postal Code": "Code Postal", + "Expiration Date": "Date D'expiration", + "Security Code": "Code de Sécurité", + "Type of Card": "Type", + "Personal": "Personnel", + "Business": "Entreprise", + "Promotion Code": "Code Promo", + "Legal Information": "Mentions Légales", + "I Agree": "J'accepte.", + "Your Trip": "Votre Course", + "Trip Info": "Informations de la Course", + "Request a fare review": "Demander un contrôle du tarif", + "Fare Review Submitted": "Votre demande de contrôle du tarif a été soumis. Nous reviendrons vers vous rapidement concernant cette demande. Nous nous excusons pour les dérangements éventuellement occasionnés !", + "Fair Price Consideration": "Nous nous engageons à proposer Uber à un tarif juste. Avant de demander un contrôle du tarif, merci de prendre en compte :", + "Your Fare Calculation": "Calcul du Prix", + "Charges": "Coûts", + "Discounts": "Réductions", + "Total Charge": "Coût total", + "Uber pricing information": "Information sur les prix d’Uber", + "Uber Pricing Information Message": "<%= learn_link %> est disponible sur notre site web.", + "GPS Point Capture Disclosure": "A cause d’un nombre limité de coordonnées GPS sauvegardées, les angles de votre trajet sur la carte peuvent apparaître coupés ou arrondis. Ces légères incohérences débouchent sur des distances mesurées plus courtes, ce qui implique toujours un prix du trajet moins élevé.", + "Fare Review Note": "Merci de nous expliquer pourquoi le tarif de cette course nécessite d’être contrôlé. Vos commentaires ci-dessous nous aideront à établir un prix plus juste si nécessaire :", + "Fare Review Error": "Il y a eu une erreur lors de l’envoi de la demande. Assurez-vous d’avoir bien ajouté une description à votre demande." + }; +}).call(this); +}, "views/clients/billing": function(exports, require, module) {(function() { + var clientsBillingTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsBillingTemplate = require('templates/clients/billing'); + exports.ClientsBillingView = (function() { + __extends(ClientsBillingView, UberView); + function ClientsBillingView() { + ClientsBillingView.__super__.constructor.apply(this, arguments); + } + ClientsBillingView.prototype.id = 'billing_view'; + ClientsBillingView.prototype.className = 'view_container'; + ClientsBillingView.prototype.events = { + 'click a#add_card': 'addCard', + 'click .charge_arrear': 'chargeArrear' + }; + ClientsBillingView.prototype.render = function() { + this.RefreshUserInfo(__bind(function() { + var cards, newForm; + this.HideSpinner(); + $(this.el).html(clientsBillingTemplate()); + if (USER.payment_gateway.payment_profiles.length === 0) { + newForm = new app.views.clients.modules.creditcard; + $(this.el).find("#add_card_wrapper").html(newForm.render(0).el); + } else { + cards = new app.views.clients.modules.creditcard; + $("#cards").html(cards.render("all").el); + } + return this.delegateEvents(); + }, this)); + return this; + }; + ClientsBillingView.prototype.addCard = function(e) { + var newCard; + e.preventDefault(); + newCard = new app.views.clients.modules.creditcard; + $('#cards').append(newCard.render("new").el); + return $("a#add_card").hide(); + }; + ClientsBillingView.prototype.chargeArrear = function(e) { + var $el, arrearId, attrs, cardId, options, tryCharge; + e.preventDefault(); + $(".error_message").text(""); + $el = $(e.currentTarget); + arrearId = $el.attr('id'); + cardId = $el.parent().find('#card_to_charge').val(); + this.ShowSpinner('submit'); + tryCharge = new app.models.clientbills({ + id: arrearId + }); + attrs = { + payment_profile_id: cardId, + dataType: 'json' + }; + options = { + success: __bind(function(data, textStatus, jqXHR) { + $el.parent().find(".success_message").text(t("Billing Succeeded")); + $el.hide(); + return $el.parent().find('#card_to_charge').hide(); + }, this), + error: __bind(function(jqXHR, status, errorThrown) { + return $el.parent().find(".error_message").text(JSON.parse(status.responseText).error); + }, this), + complete: __bind(function() { + return this.HideSpinner(); + }, this) + }; + return tryCharge.save(attrs, options); + }; + return ClientsBillingView; + })(); +}).call(this); +}, "views/clients/confirm_email": function(exports, require, module) {(function() { + var clientsConfirmEmailTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsConfirmEmailTemplate = require('templates/clients/confirm_email'); + exports.ClientsConfirmEmailView = (function() { + __extends(ClientsConfirmEmailView, UberView); + function ClientsConfirmEmailView() { + ClientsConfirmEmailView.__super__.constructor.apply(this, arguments); + } + ClientsConfirmEmailView.prototype.id = 'confirm_email_view'; + ClientsConfirmEmailView.prototype.className = 'view_container'; + ClientsConfirmEmailView.prototype.render = function(token) { + var attrs; + $(this.el).html(clientsConfirmEmailTemplate()); + attrs = { + data: { + email_token: token + }, + success: __bind(function(data, textStatus, jqXHR) { + var show_dashboard; + this.HideSpinner(); + show_dashboard = function() { + return app.routers.clients.navigate('!/dashboard', true); + }; + if (data.status === 'OK') { + $('.success_message').show(); + return _.delay(show_dashboard, 3000); + } else if (data.status === 'ALREADY_COMFIRMED') { + $('.already_confirmed_message').show(); + return _.delay(show_dashboard, 3000); + } else { + return $('.error_message').show(); + } + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('.error_message').show(); + }, this), + complete: function(status) { + return $('#attempt_text').hide(); + }, + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/self" + }; + $.ajax(attrs); + this.ShowSpinner('submit'); + return this; + }; + return ClientsConfirmEmailView; + })(); +}).call(this); +}, "views/clients/dashboard": function(exports, require, module) {(function() { + var clientsDashboardTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsDashboardTemplate = require('templates/clients/dashboard'); + exports.ClientsDashboardView = (function() { + var displayFirstTrip; + __extends(ClientsDashboardView, UberView); + function ClientsDashboardView() { + this.showAllTrips = __bind(this.showAllTrips, this); + this.render = __bind(this.render, this); + ClientsDashboardView.__super__.constructor.apply(this, arguments); + } + ClientsDashboardView.prototype.id = 'dashboard_view'; + ClientsDashboardView.prototype.className = 'view_container'; + ClientsDashboardView.prototype.events = { + 'click a.confirmation': 'confirmationClick', + 'click #resend_email': 'resendEmail', + 'click #resend_mobile': 'resendMobile', + 'click #show_all_trips': 'showAllTrips' + }; + ClientsDashboardView.prototype.render = function() { + var displayPage, downloadTrips; + this.HideSpinner(); + displayPage = __bind(function() { + $(this.el).html(clientsDashboardTemplate()); + this.confirmationsSetup(); + return this.RequireMaps(__bind(function() { + if (USER.trips.models[0]) { + if (!USER.trips.models[0].get("points")) { + return USER.trips.models[0].fetch({ + data: { + relationships: 'points' + }, + success: __bind(function() { + this.CacheData("USERtrips", USER.trips); + return displayFirstTrip(); + }, this) + }); + } else { + return displayFirstTrip(); + } + } + }, this)); + }, this); + downloadTrips = __bind(function() { + return this.DownloadUserTrips(displayPage, false, 10); + }, this); + this.RefreshUserInfo(downloadTrips); + return this; + }; + displayFirstTrip = __bind(function() { + var bounds, endPos, map, myOptions, path, polyline, startPos; + myOptions = { + zoom: 12, + mapTypeId: google.maps.MapTypeId.ROADMAP, + zoomControl: false, + rotateControl: false, + panControl: false, + mapTypeControl: false, + scrollwheel: false + }; + if (USER.trips.length === 10) { + $("#show_all_trips").show(); + } + if (USER.trips.length > 0) { + map = new google.maps.Map(document.getElementById("trip_details_map"), myOptions); + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(USER.trips.models[0].get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + map.fitBounds(bounds); + startPos = new google.maps.Marker({ + position: _.first(path), + map: map, + title: t('Trip started here'), + icon: 'https://uber-static.s3.amazonaws.com/marker_start.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: map, + title: t('Trip ended here'), + icon: 'https://uber-static.s3.amazonaws.com/marker_end.png' + }); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + return polyline.setMap(map); + } + }, ClientsDashboardView); + ClientsDashboardView.prototype.confirmationsSetup = function() { + var blink, cardForm, element, _ref, _ref2, _ref3, _ref4, _ref5; + blink = function(element) { + var opacity; + opacity = 0.5; + if (element.css('opacity') === "0.5") { + opacity = 1.0; + } + return element.fadeTo(2000, opacity, function() { + return blink(element); + }); + }; + if (((_ref = window.USER) != null ? (_ref2 = _ref.payment_gateway) != null ? (_ref3 = _ref2.payment_profiles) != null ? _ref3.length : void 0 : void 0 : void 0) === 0) { + element = $('#confirmed_credit_card'); + cardForm = new app.views.clients.modules.creditcard; + $('#card.info').append(cardForm.render().el); + blink(element); + } + if (((_ref4 = window.USER) != null ? _ref4.confirm_email : void 0) === false) { + element = $('#confirmed_email'); + blink(element); + } + if ((((_ref5 = window.USER) != null ? _ref5.confirm_mobile : void 0) != null) === false) { + element = $('#confirmed_mobile'); + return blink(element); + } + }; + ClientsDashboardView.prototype.confirmationClick = function(e) { + e.preventDefault(); + $('.info').hide(); + $('#more_info').show(); + switch (e.currentTarget.id) { + case "card": + return $('#card.info').slideToggle(); + case "mobile": + return $('#mobile.info').slideToggle(); + case "email": + return $('#email.info').slideToggle(); + } + }; + ClientsDashboardView.prototype.resendEmail = function(e) { + var $el; + e.preventDefault(); + $el = $(e.currentTarget); + $el.removeAttr('href').prop({ + disabled: true + }); + $el.html(t("Sending Email")); + return $.ajax({ + type: 'GET', + url: API + '/users/request_confirm_email', + data: { + token: USER.token + }, + dataType: 'json', + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Resend Email Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.html(t("Resend Email Failed")); + }, this) + }); + }; + ClientsDashboardView.prototype.resendMobile = function(e) { + var $el; + e.preventDefault(); + $el = $(e.currentTarget); + $el.removeAttr('href').prop({ + disabled: true + }); + $el.html("Sending message..."); + return $.ajax({ + type: 'GET', + url: API + '/users/request_confirm_mobile', + data: { + token: USER.token + }, + dataType: 'json', + success: __bind(function(data, textStatus, jqXHR) { + return $el.html(t("Resend Text Succeeded")); + }, this), + error: __bind(function(jqXHR, textStatus, errorThrown) { + return $el.html(t("Resend Text Failed")); + }, this) + }); + }; + ClientsDashboardView.prototype.showAllTrips = function(e) { + e.preventDefault(); + $(e.currentTarget).hide(); + return this.DownloadUserTrips(this.render, true, 1000); + }; + return ClientsDashboardView; + }).call(this); +}).call(this); +}, "views/clients/forgot_password": function(exports, require, module) {(function() { + var clientsForgotPasswordTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + clientsForgotPasswordTemplate = require('templates/clients/forgot_password'); + exports.ClientsForgotPasswordView = (function() { + __extends(ClientsForgotPasswordView, UberView); + function ClientsForgotPasswordView() { + ClientsForgotPasswordView.__super__.constructor.apply(this, arguments); + } + ClientsForgotPasswordView.prototype.id = 'forgotpassword_view'; + ClientsForgotPasswordView.prototype.className = 'view_container modal_view_container'; + ClientsForgotPasswordView.prototype.events = { + "submit #password_reset": "passwordReset", + "click #password_reset_submit": "passwordReset", + "submit #forgot_password": "forgotPassword", + "click #forgot_password_submit": "forgotPassword" + }; + ClientsForgotPasswordView.prototype.render = function(token) { + this.HideSpinner(); + $(this.el).html(clientsForgotPasswordTemplate({ + token: token + })); + this.delegateEvents(); + return this; + }; + ClientsForgotPasswordView.prototype.forgotPassword = function(e) { + var attrs; + e.preventDefault(); + $('.success_message').hide(); + $(".error_message").hide(); + attrs = { + data: { + login: $("#login").val() + }, + success: __bind(function(data, textStatus, jqXHR) { + this.HideSpinner(); + $('.success_message').show(); + return $("#forgot_password").hide(); + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('.error_message').show(); + }, this), + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/forgot_password" + }; + $.ajax(attrs); + return this.ShowSpinner('submit'); + }; + ClientsForgotPasswordView.prototype.passwordReset = function(e) { + var attrs; + e.preventDefault(); + attrs = { + data: { + email_token: $("#token").val(), + password: $("#password").val() + }, + success: __bind(function(data, textStatus, jqXHR) { + this.HideSpinner(); + $.cookie('token', data.token); + amplify.store('USERjson', data); + app.refreshMenu(); + return location.hash = '!/dashboard'; + }, this), + error: __bind(function(e) { + this.HideSpinner(); + return $('#error_reset').show(); + }, this), + dataType: 'json', + type: 'PUT', + url: "" + API + "/users/self" + }; + $.ajax(attrs); + return this.ShowSpinner('submit'); + }; + return ClientsForgotPasswordView; + })(); +}).call(this); +}, "views/clients/invite": function(exports, require, module) {(function() { + var clientsInviteTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsInviteTemplate = require('templates/clients/invite'); + exports.ClientsInviteView = (function() { + __extends(ClientsInviteView, UberView); + function ClientsInviteView() { + ClientsInviteView.__super__.constructor.apply(this, arguments); + } + ClientsInviteView.prototype.id = 'invite_view'; + ClientsInviteView.prototype.className = 'view_container'; + ClientsInviteView.prototype.render = function() { + this.ReadUserInfo(); + this.HideSpinner(); + $(this.el).html(clientsInviteTemplate()); + console.log(screen); + return this; + }; + return ClientsInviteView; + })(); +}).call(this); +}, "views/clients/login": function(exports, require, module) {(function() { + var clientsLoginTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsLoginTemplate = require('templates/clients/login'); + exports.ClientsLoginView = (function() { + __extends(ClientsLoginView, UberView); + function ClientsLoginView() { + ClientsLoginView.__super__.constructor.apply(this, arguments); + } + ClientsLoginView.prototype.id = 'login_view'; + ClientsLoginView.prototype.className = 'view_container modal_view_container'; + ClientsLoginView.prototype.events = { + 'submit form': 'authenticate', + 'click button': 'authenticate' + }; + ClientsLoginView.prototype.initialize = function() { + _.bindAll(this, 'render'); + return this.render(); + }; + ClientsLoginView.prototype.render = function() { + this.HideSpinner(); + $(this.el).html(clientsLoginTemplate()); + this.delegateEvents(); + return this.place(); + }; + ClientsLoginView.prototype.authenticate = function(e) { + e.preventDefault(); + return $.ajax({ + type: 'POST', + url: API + '/auth/web_login/client', + data: { + login: $("#login").val(), + password: $("#password").val() + }, + dataType: 'json', + success: function(data, textStatus, jqXHR) { + $.cookie('user', JSON.stringify(data)); + $.cookie('token', data.token); + amplify.store('USERjson', data); + $('header').html(app.views.shared.menu.render().el); + return app.routers.clients.navigate('!/dashboard', true); + }, + error: function(jqXHR, textStatus, errorThrown) { + $.cookie('user', null); + $.cookie('token', null); + if (jqXHR.status === 403) { + $.cookie('redirected_user', JSON.stringify(JSON.parse(jqXHR.responseText).error_obj), { + domain: '.uber.com' + }); + window.location = 'http://partners.uber.com/'; + } + return $('.error_message').html(JSON.parse(jqXHR.responseText).error).hide().fadeIn(); + } + }); + }; + return ClientsLoginView; + })(); +}).call(this); +}, "views/clients/modules/credit_card": function(exports, require, module) {(function() { + var creditCardTemplate; + var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + creditCardTemplate = require('templates/clients/modules/credit_card'); + exports.CreditCardView = (function() { + __extends(CreditCardView, UberView); + function CreditCardView() { + CreditCardView.__super__.constructor.apply(this, arguments); + } + CreditCardView.prototype.id = 'creditcard_view'; + CreditCardView.prototype.className = 'module_container'; + CreditCardView.prototype.events = { + 'submit #credit_card_form': 'processNewCard', + 'click #new_card': 'processNewCard', + 'change #card_number': 'showCardType', + 'click .edit_card_show': 'showEditCard', + 'click .edit_card': 'editCard', + 'click .delete_card': 'deleteCard', + 'click .make_default': 'makeDefault', + 'change .use_case': 'saveUseCase' + }; + CreditCardView.prototype.initialize = function() { + return app.collections.paymentprofiles.bind("refresh", __bind(function() { + return this.RefreshUserInfo(__bind(function() { + this.render("all"); + return this.HideSpinner(); + }, this)); + }, this)); + }; + CreditCardView.prototype.render = function(cards) { + if (cards == null) { + cards = "new"; + } + if (cards === "all") { + app.collections.paymentprofiles.reset(USER.payment_gateway.payment_profiles); + cards = app.collections.paymentprofiles; + } + $(this.el).html(creditCardTemplate({ + cards: cards + })); + return this; + }; + CreditCardView.prototype.processNewCard = function(e) { + var $el, attrs, model, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $("#credit_card_form"); + $el.find('.error_message').html(""); + attrs = { + card_number: $el.find('#card_number').val(), + card_code: $el.find('#card_code').val(), + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + use_case: $el.find('#use_case').val(), + "default": $el.find('#default_check').prop("checked") + }; + options = { + statusCode: { + 200: __bind(function(e) { + this.HideSpinner(); + $('#cc_form_wrapper').hide(); + app.collections.paymentprofiles.trigger("refresh"); + $(this.el).remove(); + $("a#add_card").show(); + return $('section').html(app.views.clients.billing.render().el); + }, this), + 406: __bind(function(e) { + var error, errors, _i, _len, _ref, _results; + this.HideSpinner(); + errors = JSON.parse(e.responseText); + _ref = _.keys(errors); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + error = _ref[_i]; + _results.push(error === "top_of_form" ? $("#top_of_form").html(errors[error]) : $("#credit_card_form").find("#" + error).parent().find(".error_message").html(errors[error])); + } + return _results; + }, this), + 420: __bind(function(e) { + this.HideSpinner(); + return $("#top_of_form").html(t("Unable to Verify Card")); + }, this) + } + }; + this.ShowSpinner("submit"); + model = new app.models.paymentprofile; + model.save(attrs, options); + return app.collections.paymentprofiles.add(model); + }; + CreditCardView.prototype.showCardType = function(e) { + var $el, reAmerica, reDiscover, reMaster, reVisa, validCard; + reVisa = /^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/; + reMaster = /^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/; + reAmerica = /^6011-?\d{4}-?\d{4}-?\d{4}$/; + reDiscover = /^3[4,7]\d{13}$/; + $el = $("#card_logos"); + validCard = false; + if (e.currentTarget.value.match(reVisa)) { + validCard = true; + } else if (e.currentTarget.value.match(reMaster)) { + $el.css('background-position', "-60px"); + validCard = true; + } else if (e.currentTarget.value.match(reAmerica)) { + $el.css('background-position', "-120px"); + validCard = true; + } else if (e.currentTarget.value.match(reDiscover)) { + $el.css('background-position', "-180px"); + validCard = true; + } + if (validCard) { + $el.css('width', "60px"); + return $el.css('margin-left', "180px"); + } else { + $el.css('width', "250px"); + return $el.css('margin-left', "80px"); + } + }; + CreditCardView.prototype.showEditCard = function(e) { + var $el, id; + e.preventDefault(); + $el = $(e.currentTarget); + if ($el.html() === t("Edit")) { + id = $el.html(t("Cancel")).parents("tr").attr("id").substring(1); + return $("#e" + id).show(); + } else { + id = $el.html(t("Edit")).parents("tr").attr("id").substring(1); + return $("#e" + id).hide(); + } + }; + CreditCardView.prototype.editCard = function(e) { + var $el, attrs, id, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + $el.attr('disabled', 'disabled'); + this.ShowSpinner('submit'); + attrs = { + card_expiration_month: $el.find('#card_expiration_month').val(), + card_expiration_year: $el.find('#card_expiration_year').val(), + card_code: $el.find('#card_code').val() + }; + options = { + success: __bind(function(response) { + this.HideSpinner(); + this.ShowSuccess(t("Credit Card Update Succeeded")); + $("#e" + id).hide(); + $("#d" + id).find(".edit_card_show").html(t("Edit")); + return app.collections.paymentprofiles.trigger("refresh"); + }, this), + error: __bind(function(e) { + this.HideSpinner(); + this.ShowError(t("Credit Card Update Failed")); + return $el.removeAttr('disabled'); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + CreditCardView.prototype.deleteCard = function(e) { + var $el, id, options; + e.preventDefault(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + this.ClearGlobalStatus(); + this.ShowSpinner('submit'); + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Credit Card Delete Succeeded")); + $("form").hide(); + app.collections.paymentprofiles.trigger("refresh"); + return $('section').html(app.views.clients.billing.render().el); + }, this), + error: __bind(function(xhr, e) { + this.HideSpinner(); + return this.ShowError(t("Credit Card Delete Failed")); + }, this) + }; + return app.collections.paymentprofiles.models[id].destroy(options); + }; + CreditCardView.prototype.saveUseCase = function(e) { + var $el, attrs, id, options, use_case; + this.ClearGlobalStatus(); + $el = $(e.currentTarget); + use_case = $el.val(); + id = $el.parents("tr").attr("id").substring(1); + attrs = { + use_case: use_case + }; + options = { + success: __bind(function(response) { + return this.ShowSuccess(t("Credit Card Update Category Succeeded")); + }, this), + error: __bind(function(e) { + return this.ShowError(t("Credit Card Update Category Failed")); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + CreditCardView.prototype.makeDefault = function(e) { + var $el, attrs, id, options; + e.preventDefault(); + this.ClearGlobalStatus(); + $el = $(e.currentTarget).parents("td"); + id = $el.parents("tr").attr("id").substring(1); + attrs = { + "default": true + }; + options = { + success: __bind(function(response) { + this.ShowSuccess(t("Credit Card Update Default Succeeded")); + return app.collections.paymentprofiles.trigger("refresh"); + }, this), + error: __bind(function(e) { + return this.ShowError(t("Credit Card Update Default Failed")); + }, this) + }; + app.collections.paymentprofiles.models[id].set(attrs); + return app.collections.paymentprofiles.models[id].save({}, options); + }; + return CreditCardView; + })(); +}).call(this); +}, "views/clients/promotions": function(exports, require, module) {(function() { + var clientsPromotionsTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsPromotionsTemplate = require('templates/clients/promotions'); + exports.ClientsPromotionsView = (function() { + __extends(ClientsPromotionsView, UberView); + function ClientsPromotionsView() { + this.render = __bind(this.render, this); + ClientsPromotionsView.__super__.constructor.apply(this, arguments); + } + ClientsPromotionsView.prototype.id = 'promotions_view'; + ClientsPromotionsView.prototype.className = 'view_container'; + ClientsPromotionsView.prototype.events = { + 'submit form': 'submitPromo', + 'click button': 'submitPromo' + }; + ClientsPromotionsView.prototype.initialize = function() { + if (this.model) { + return this.RefreshUserInfo(this.render); + } + }; + ClientsPromotionsView.prototype.render = function() { + var renderTemplate; + this.ReadUserInfo(); + renderTemplate = __bind(function() { + $(this.el).html(clientsPromotionsTemplate({ + promos: window.USER.unexpired_client_promotions || [] + })); + return this.HideSpinner(); + }, this); + this.DownloadUserPromotions(renderTemplate); + return this; + }; + ClientsPromotionsView.prototype.submitPromo = function(e) { + var attrs, model, options, refreshTable; + e.preventDefault(); + this.ClearGlobalStatus(); + refreshTable = __bind(function() { + $('section').html(this.render().el); + return this.HideSpinner(); + }, this); + attrs = { + code: $('#code').val() + }; + options = { + success: __bind(function(response) { + this.HideSpinner(); + if (response.get('first_name')) { + return this.ShowSuccess("Your promotion has been applied in the form of an account credit. Click here to check your balance."); + } else { + this.ShowSuccess("Your promotion has successfully been applied"); + return this.RefreshUserInfo(this.render, true); + } + }, this), + statusCode: { + 400: __bind(function(e) { + this.ShowError(JSON.parse(e.responseText).error); + return this.HideSpinner(); + }, this) + } + }; + this.ShowSpinner("submit"); + model = new app.models.promotions; + return model.save(attrs, options); + }; + return ClientsPromotionsView; + })(); +}).call(this); +}, "views/clients/request": function(exports, require, module) {(function() { + var clientsRequestTemplate; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { + for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } + function ctor() { this.constructor = child; } + ctor.prototype = parent.prototype; + child.prototype = new ctor; + child.__super__ = parent.prototype; + return child; + }; + clientsRequestTemplate = require('templates/clients/request'); + exports.ClientsRequestView = (function() { + __extends(ClientsRequestView, UberView); + function ClientsRequestView() { + this.AjaxCall = __bind(this.AjaxCall, this); + this.AskDispatch = __bind(this.AskDispatch, this); + this.removeMarkers = __bind(this.removeMarkers, this); + this.displaySearchLoc = __bind(this.displaySearchLoc, this); + this.displayFavLoc = __bind(this.displayFavLoc, this); + this.showFavLoc = __bind(this.showFavLoc, this); + this.addToFavLoc = __bind(this.addToFavLoc, this); + this.removeCabs = __bind(this.removeCabs, this); + this.requestRide = __bind(this.requestRide, this); + this.rateTrip = __bind(this.rateTrip, this); + this.locationChange = __bind(this.locationChange, this); + this.panToLocation = __bind(this.panToLocation, this); + this.clickLocation = __bind(this.clickLocation, this); + this.searchLocation = __bind(this.searchLocation, this); + this.mouseoutLocation = __bind(this.mouseoutLocation, this); + this.mouseoverLocation = __bind(this.mouseoverLocation, this); + this.fetchTripDetails = __bind(this.fetchTripDetails, this); + this.submitRating = __bind(this.submitRating, this); + this.setStatus = __bind(this.setStatus, this); + this.initialize = __bind(this.initialize, this); + ClientsRequestView.__super__.constructor.apply(this, arguments); + } + ClientsRequestView.prototype.id = 'request_view'; + ClientsRequestView.prototype.className = 'view_container'; + ClientsRequestView.prototype.pollInterval = 2 * 1000; + ClientsRequestView.prototype.events = { + "submit #search_form": "searchAddress", + "click .locations_link": "locationLinkHandle", + "mouseover .location_row": "mouseoverLocation", + "mouseout .location_row": "mouseoutLocation", + "click .location_row": "clickLocation", + "click #search_location": "searchLocation", + "click #pickupHandle": "pickupHandle", + "click .stars": "rateTrip", + "submit #rating_form": "submitRating", + "click #addToFavButton": "showFavLoc", + "click #favLocNickname": "selectInputText", + "submit #favLoc_form": "addToFavLoc" + }; + ClientsRequestView.prototype.status = ""; + ClientsRequestView.prototype.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker.png"; + ClientsRequestView.prototype.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker.png"; + ClientsRequestView.prototype.initialize = function() { + var displayCabs; + displayCabs = __bind(function() { + return this.AskDispatch("NearestCab"); + }, this); + this.showCabs = _.throttle(displayCabs, this.pollInterval); + return this.numSearchToDisplay = 1; + }; + ClientsRequestView.prototype.setStatus = function(status) { + var autocomplete; + if (this.status === status) { + return; + } + try { + google.maps.event.trigger(this.map, 'resize'); + } catch (_e) {} + switch (status) { + case "init": + this.AskDispatch("StatusClient"); + this.status = "init"; + return this.ShowSpinner("load"); + case "ready": + this.HideSpinner(); + $(".panel").hide(); + $("#top_bar").fadeIn(); + $("#location_panel").fadeIn(); + $("#location_panel_control").fadeIn(); + $("#pickupHandle").attr("class", "button_green").fadeIn().find("span").html(t("Request Pickup")); + this.pickup_icon.setDraggable(true); + this.map.panTo(this.pickup_icon.getPosition()); + this.showCabs(); + try { + this.pickup_icon.setMap(this.map); + this.displayFavLoc(); + autocomplete = new google.maps.places.Autocomplete(document.getElementById('address'), { + types: ['geocode'] + }); + autocomplete.bindTo('bounds', this.map); + } catch (_e) {} + return this.status = "ready"; + case "searching": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#status_message").html(t("Requesting Closest Driver")); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "searching"; + case "waiting": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "waiting"; + case "arriving": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").attr("class", "button_red").fadeIn().find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.pickup_icon.setMap(this.map); + return this.status = "arriving"; + case "riding": + this.HideSpinner(); + this.removeMarkers(); + $(".panel").hide(); + $("#top_bar").fadeOut(); + $("#pickupHandle").fadeIn().attr("class", "button_red").find("span").html(t("Cancel Pickup")); + $("#waiting_riding").fadeIn(); + this.pickup_icon.setDraggable(false); + this.status = "riding"; + return $("#status_message").html(t("En Route")); + case "rate": + this.HideSpinner(); + $(".panel").hide(); + $("#pickupHandle").fadeOut(); + $("#trip_completed_panel").fadeIn(); + $('#status_message').html(t("Rate Last Trip")); + return this.status = "rate"; + } + }; + ClientsRequestView.prototype.render = function() { + this.ReadUserInfo(); + this.HideSpinner(); + this.ShowSpinner("load"); + $(this.el).html(clientsRequestTemplate()); + this.cabs = []; + this.RequireMaps(__bind(function() { + var center, myOptions, streetViewPano; + center = new google.maps.LatLng(37.7749295, -122.4194155); + this.markers = []; + this.pickup_icon = new google.maps.Marker({ + position: center, + draggable: true, + clickable: true, + icon: this.pickupMarker + }); + this.geocoder = new google.maps.Geocoder(); + myOptions = { + zoom: 12, + center: center, + mapTypeId: google.maps.MapTypeId.ROADMAP, + rotateControl: false, + rotateControl: false, + panControl: false + }; + this.map = new google.maps.Map($(this.el).find("#map_wrapper_right")[0], myOptions); + if (this.status === "ready") { + this.pickup_icon.setMap(this.map); + } + if (geo_position_js.init()) { + geo_position_js.getCurrentPosition(__bind(function(data) { + var location; + location = new google.maps.LatLng(data.coords.latitude, data.coords.longitude); + this.pickup_icon.setPosition(location); + this.map.panTo(location); + return this.map.setZoom(16); + }, this)); + } + this.setStatus("init"); + streetViewPano = this.map.getStreetView(); + google.maps.event.addListener(streetViewPano, 'visible_changed', __bind(function() { + if (streetViewPano.getVisible()) { + this.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker_large.png"; + this.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker_large.png"; + } else { + this.pickupMarker = "https://uber-static.s3.amazonaws.com/pickup_marker.png"; + this.cabMarker = "https://uber-static.s3.amazonaws.com/cab_marker.png"; + } + this.pickup_icon.setIcon(this.pickupMarker); + return _.each(this.cabs, __bind(function(cab) { + return cab.setIcon(this.cabMarker); + }, this)); + }, this)); + if (this.status === "ready") { + return this.displayFavLoc(); + } + }, this)); + return this; + }; + ClientsRequestView.prototype.submitRating = function(e) { + var $el, message, rating; + e.preventDefault(); + $el = $(e.currentTarget); + rating = 0; + _(5).times(function(num) { + if ($el.find(".stars#" + (num + 1)).attr("src") === "/web/img/star_active.png") { + return rating = num + 1; + } + }); + if (rating === 0) { + $("#status_message").html("").html(t("Rate Before Submitting")); + } else { + this.ShowSpinner("submit"); + this.AskDispatch("RatingDriver", { + rating: rating + }); + } + message = $el.find("#comments").val().toString(); + if (message.length > 5) { + return this.AskDispatch("Feedback", { + message: message + }); + } + }; + ClientsRequestView.prototype.fetchTripDetails = function(id) { + var trip; + trip = new app.models.trip({ + id: id + }); + return trip.fetch({ + data: { + relationships: 'points,driver,city' + }, + dataType: 'json', + success: __bind(function() { + var bounds, endPos, path, polyline, startPos; + bounds = new google.maps.LatLngBounds(); + path = []; + _.each(trip.get('points'), __bind(function(point) { + path.push(new google.maps.LatLng(point.lat, point.lng)); + return bounds.extend(_.last(path)); + }, this)); + startPos = new google.maps.Marker({ + position: _.first(path), + map: this.map, + title: t("Trip started here"), + icon: 'https://uber-static.s3.amazonaws.com/carstart.png' + }); + endPos = new google.maps.Marker({ + position: _.last(path), + map: this.map, + title: t("Trip ended here"), + icon: 'https://uber-static.s3.amazonaws.com/carstop.png' + }); + polyline = new google.maps.Polyline({ + path: path, + strokeColor: '#003F87', + strokeOpacity: 1, + strokeWeight: 5 + }); + polyline.setMap(this.map); + this.map.fitBounds(bounds); + $("#tripTime").html(app.helpers.parseDateTime(trip.get('pickup_local_time'), trip.get('city.timezone'))); + $("#tripDist").html(app.helpers.RoundNumber(trip.get('distance'), 2)); + $("#tripDur").html(app.helpers.FormatSeconds(trip.get('duration'))); + return $("#tripFare").html(app.helpers.FormatCurrency(trip.get('fare'))); + }, this) + }); + }; + ClientsRequestView.prototype.searchAddress = function(e) { + var $locationsDiv, address, alphabet, bounds, showResults; + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + try { + e.preventDefault(); + } catch (_e) {} + $('.error_message').html(""); + $locationsDiv = $("
      "); + address = $('#address').val(); + bounds = new google.maps.LatLngBounds(); + if (address.length < 5) { + $('#status_message').html(t("Address too short")).fadeIn(); + return false; + } + showResults = __bind(function(address, index) { + var first_cell, row, second_cell; + if (index < this.numSearchToDisplay) { + first_cell = "
      " + address.formatted_address + "
      " + (t('or did you mean')) + "
      " + address.formatted_address + "
      diff --git a/widgets/citrix/common/templates/Dialog.html b/widgets/citrix/common/templates/Dialog.html new file mode 100644 index 0000000..47e8c86 --- /dev/null +++ b/widgets/citrix/common/templates/Dialog.html @@ -0,0 +1,10 @@ +
      +
      + + + + +
      +
      +
      +
      \ No newline at end of file diff --git a/widgets/citrix/common/templates/FooterBarItem.html b/widgets/citrix/common/templates/FooterBarItem.html new file mode 100644 index 0000000..1ec7b6a --- /dev/null +++ b/widgets/citrix/common/templates/FooterBarItem.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/widgets/citrix/common/templates/ImageButton.html b/widgets/citrix/common/templates/ImageButton.html new file mode 100644 index 0000000..1594755 --- /dev/null +++ b/widgets/citrix/common/templates/ImageButton.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/widgets/citrix/common/templates/ImageItem.html b/widgets/citrix/common/templates/ImageItem.html new file mode 100644 index 0000000..5e3a909 --- /dev/null +++ b/widgets/citrix/common/templates/ImageItem.html @@ -0,0 +1,3 @@ +
    • + +
    • \ No newline at end of file diff --git a/widgets/citrix/common/templates/ImageSelect.html b/widgets/citrix/common/templates/ImageSelect.html new file mode 100644 index 0000000..257055f --- /dev/null +++ b/widgets/citrix/common/templates/ImageSelect.html @@ -0,0 +1 @@ +
        \ No newline at end of file diff --git a/widgets/citrix/common/templates/Keyboard.html b/widgets/citrix/common/templates/Keyboard.html new file mode 100755 index 0000000..f238424 --- /dev/null +++ b/widgets/citrix/common/templates/Keyboard.html @@ -0,0 +1,4 @@ +
        +
        +
        +
        \ No newline at end of file diff --git a/widgets/citrix/common/templates/Label.html b/widgets/citrix/common/templates/Label.html new file mode 100755 index 0000000..17628f2 --- /dev/null +++ b/widgets/citrix/common/templates/Label.html @@ -0,0 +1,3 @@ + diff --git a/widgets/citrix/common/templates/MenuBarItem.html b/widgets/citrix/common/templates/MenuBarItem.html new file mode 100644 index 0000000..fa4a319 --- /dev/null +++ b/widgets/citrix/common/templates/MenuBarItem.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/widgets/citrix/common/templates/OrderableHorizItem.html b/widgets/citrix/common/templates/OrderableHorizItem.html new file mode 100755 index 0000000..14be438 --- /dev/null +++ b/widgets/citrix/common/templates/OrderableHorizItem.html @@ -0,0 +1,5 @@ +
      • +
        +
      • \ No newline at end of file diff --git a/widgets/citrix/common/templates/OrderableList.html b/widgets/citrix/common/templates/OrderableList.html new file mode 100755 index 0000000..75a255d --- /dev/null +++ b/widgets/citrix/common/templates/OrderableList.html @@ -0,0 +1 @@ +
          \ No newline at end of file diff --git a/widgets/citrix/common/templates/ProgressBar.html b/widgets/citrix/common/templates/ProgressBar.html new file mode 100644 index 0000000..a99bf10 --- /dev/null +++ b/widgets/citrix/common/templates/ProgressBar.html @@ -0,0 +1,8 @@ +
           
          diff --git a/widgets/citrix/common/templates/Select.html b/widgets/citrix/common/templates/Select.html new file mode 100644 index 0000000..9e917ab --- /dev/null +++ b/widgets/citrix/common/templates/Select.html @@ -0,0 +1,14 @@ +
          diff --git a/widgets/citrix/common/templates/TabButton.html b/widgets/citrix/common/templates/TabButton.html new file mode 100644 index 0000000..9a87a1e --- /dev/null +++ b/widgets/citrix/common/templates/TabButton.html @@ -0,0 +1,14 @@ +
          +
          +
          +
          + + + + x +
          +
          +
          +
          diff --git a/widgets/citrix/common/templates/TabContainer.html b/widgets/citrix/common/templates/TabContainer.html new file mode 100644 index 0000000..c0912ce --- /dev/null +++ b/widgets/citrix/common/templates/TabContainer.html @@ -0,0 +1,7 @@ +
          +
          +
          +
          +
          +
          +
          \ No newline at end of file diff --git a/widgets/citrix/common/templates/Timeout.html b/widgets/citrix/common/templates/Timeout.html new file mode 100755 index 0000000..3b41592 --- /dev/null +++ b/widgets/citrix/common/templates/Timeout.html @@ -0,0 +1,9 @@ +
          + + + + +
          + + +
          \ No newline at end of file diff --git a/widgets/citrix/common/templates/WizardContainer.html b/widgets/citrix/common/templates/WizardContainer.html new file mode 100755 index 0000000..8c1d22b --- /dev/null +++ b/widgets/citrix/common/templates/WizardContainer.html @@ -0,0 +1,5 @@ +
          +
          +
          +
          +
          \ No newline at end of file diff --git a/widgets/citrix/common/templates/WizardNavigator.html b/widgets/citrix/common/templates/WizardNavigator.html new file mode 100755 index 0000000..481a7b7 --- /dev/null +++ b/widgets/citrix/common/templates/WizardNavigator.html @@ -0,0 +1,6 @@ +
          + + + + +
          \ No newline at end of file diff --git a/widgets/citrix/common/templates/WizardStepItem.html b/widgets/citrix/common/templates/WizardStepItem.html new file mode 100755 index 0000000..d8d866d --- /dev/null +++ b/widgets/citrix/common/templates/WizardStepItem.html @@ -0,0 +1 @@ +
        • ${label}
        • \ No newline at end of file diff --git a/widgets/citrix/common/templates/WizardSteps.html b/widgets/citrix/common/templates/WizardSteps.html new file mode 100755 index 0000000..b5e9540 --- /dev/null +++ b/widgets/citrix/common/templates/WizardSteps.html @@ -0,0 +1,6 @@ +
          +
          +
            +
              +
              +
              \ No newline at end of file diff --git a/widgets/citrix/common/templates/asmSelect.html b/widgets/citrix/common/templates/asmSelect.html new file mode 100644 index 0000000..898b6fa --- /dev/null +++ b/widgets/citrix/common/templates/asmSelect.html @@ -0,0 +1,4 @@ + + +
                +
                \ No newline at end of file diff --git a/widgets/citrix/common/templates/asmSelectNode.html b/widgets/citrix/common/templates/asmSelectNode.html new file mode 100755 index 0000000..03f00ad --- /dev/null +++ b/widgets/citrix/common/templates/asmSelectNode.html @@ -0,0 +1,5 @@ +
              1. +
                + ${item.label} +
                +
              2. \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/AlertPopup.css b/widgets/citrix/common/themes/tundra/AlertPopup.css new file mode 100644 index 0000000..b07490b --- /dev/null +++ b/widgets/citrix/common/themes/tundra/AlertPopup.css @@ -0,0 +1,19 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .alertPopup { + font-size: 12px; +} + +.tundra .alertPopup .alertIcon { + padding: 0; + margin: 8px; +} + +.tundra .alertPopup .alertMessage { + white-space: pre-line; + width: 400px; +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/Button.css b/widgets/citrix/common/themes/tundra/Button.css new file mode 100644 index 0000000..178bf56 --- /dev/null +++ b/widgets/citrix/common/themes/tundra/Button.css @@ -0,0 +1,115 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixButton { + position: relative; + cursor: pointer; + min-width: 72px; +} + +.tundra .imageButton { + height: 70px; +} + +.tundra .citrixButtonContents { + display: block; + text-align: center; +} + +.tundra .imageButton .citrixButtonContents { + max-width: 97px; +} + +.tundra .smallImageButton .imageButton .citrixButtonContents { + width: 75px; +} + +.tundra .citrixButtonText { + font-size: 12px; +} +.tundra .imageButton .citrixButtonText { + font-size: 11px; /* should be a minimum of 12px for ja and sc text */ +} +.tundra .dijitDisabled .citrixButtonText { + color: rgb(127, 127, 127); +} +.tundra .dijitButtonHover .citrixButtonText { + color: rgb(36, 60, 95); +} + +.tundra .citrixImageButton { + display: block; + margin: 5px auto; +} + +/* enabled state - inner */ +.tundra .citrixButtonCenter { + margin: 0 7px; + padding: 0 7px; + height: 23px; + line-height: 23px; + background: url("/images/buttons/075_ButtonNormalCenter_h32bit_1x23.png") repeat-x; +} +.tundra .citrixButtonLeft { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url("/images/buttons/075_ButtonNormalLeft_h32bit_7x23.png") no-repeat; +} +.tundra .citrixButtonRight { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url("/images/buttons/075_ButtonNormalRight_h32bit_7x23.png") no-repeat right; +} + +/* disabled state - inner */ +.tundra .dijitButtonDisabled .citrixButtonCenter { + background: url("/images/buttons/075_ButtonDisabledCenter_h32bit_1x23.png") repeat-x; +} +.tundra .dijitButtonDisabled .citrixButtonLeft { + background: url("/images/buttons/075_ButtonDisabledLeft_h32bit_7x23.png") no-repeat; +} +.tundra .dijitButtonDisabled .citrixButtonRight { + background: url("/images/buttons/075_ButtonDisabledRight_h32bit_7x23.png") no-repeat right; +} + +/* hover state - inner */ +.tundra .dijitButtonHover .citrixButtonCenter { + background: url("/images/buttons/075_ButtonHoverCenter_h32bit_1x23.png") repeat-x; +} +.tundra .dijitButtonHover .citrixButtonLeft { + background: url("/images/buttons/075_ButtonHoverLeft_h32bit_7x23.png") no-repeat; +} +.tundra .dijitButtonHover .citrixButtonRight { + background: url("/images/buttons/075_ButtonHoverRight_h32bit_7x23.png") no-repeat right; +} + +/* active state - inner (for when you are pressing a normal button */ +.tundra .dijitButtonActive .citrixButtonCenter { + background: url("/images/buttons/075_ButtonClickedCenter_h32bit_1x23.png") repeat-x; +} +.tundra .dijitButtonActive .citrixButtonLeft { + background: url("/images/buttons/075_ButtonClickedLeft_h32bit_7x23.png") no-repeat; +} +.tundra .dijitButtonActive .citrixButtonRight { + background: url("/images/buttons/075_ButtonClickedRight_h32bit_7x23.png") no-repeat right; +} + +/* focused state */ +.tundra .dijitButtonFocused .citrixButtonCenter { + background: url("/images/buttons/075_ButtonFocusedCenter_h32bit_1x23.png") repeat-x; +} +.tundra .dijitButtonFocused .citrixButtonLeft { + background: url("/images/buttons/075_ButtonFocusedLeft_h32bit_7x23.png") no-repeat; +} +.tundra .dijitButtonFocused .citrixButtonRight { + background: url("/images/buttons/075_ButtonFocusedRight_h32bit_7x23.png") no-repeat right; +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/Common.css b/widgets/citrix/common/themes/tundra/Common.css new file mode 100755 index 0000000..e277e77 --- /dev/null +++ b/widgets/citrix/common/themes/tundra/Common.css @@ -0,0 +1,371 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +body, img { + -webkit-user-select: none; + -moz-user-select: none; + -webkit-user-drag: none; +} + +*[tabindex]:focus { + outline: none; + /* border: $FOCUS_OUTLINE; */ +} + +.tundra .left { + float: left; +} + +.tundra .right { + float: right; +} + +.tundra .horizontalSeparator { + clear: both; + border-width: 1px 17px 0 17px; + -webkit-border-image: url(/images/borders/h_separator.png) 1 17 0 17 repeat; +} + +.tundra .verticalSeparator { + padding-right: 5px; + border-width: 0 1px 0 0; + -webkit-border-image: url(/images/borders/v_separator.png) 0 1 0 0 stretch; +} + +.tundra .alignBottom { + position: absolute; + bottom: 0; + width: 100%; +} + +.tundra .alertBox { + position: relative; + margin: 3px 10px; + text-align: left; + display: inline-block; + border-radius: 6px 6px; + -webkit-box-shadow: rgba(0, 0, 0, 0.45) 0 1px 5px; +} + +.tundra .alertBox > img, +.tundra .alertBox > span { + position: absolute; + top: 10px; + bottom: auto; + left: 10px; + right: auto; +} + +.tundra .alertBox > div, +.tundra .alertBox > ul { + padding: 10px 10px 10px 41px; + font-size: 12px; +} + +.tundra .blueBox { + border: 1px solid white; /*rgb(30, 120, 180);*/ + background: -webkit-gradient(radial, 50% 0, 0, 50% 0, 600, from(rgb(255, 255, 255)), to(rgb(30, 145, 210))); +} + +.tundra .orangeBox { + border: 1px solid rgb(245, 130, 40); + background: -webkit-gradient(radial, 50% 0, 0, 50% 0, 600, from(rgb(255, 255, 255)), to(rgb(245, 175, 120))); +} + +.tundra .yellowBox { + border: 1px solid rgb(250, 240, 40); + background: -webkit-gradient(radial, 50% 0, 0, 50% 0, 600, from(rgb(255, 255, 255)), to(rgb(250, 240, 140))); +} + +.tundra .redBox { + border: 1px solid rgb(250, 80, 80); + background: -webkit-gradient(radial, 50% 0, 0, 50% 0, 600, from(rgb(255, 255, 255)), to(rgb(250, 80, 80))); +} + +.tundra .dojoDndTarget { + padding: 5px 0 0 0; + margin: 0; +} + +.tundra .dndItem { + list-style: none; +} + +.tundra .dndItem.dojoDndItemBefore { + padding-top: 10px; + border: none; +} + +.tundra .dndItem.dojoDndItemAfter { + padding-bottom: 10px; + border: none; +} + +.tundra .dndWrapper { + background: #DDD; + border: 1px solid #BBB; + margin: 0 0 -1px 0; + line-height: 10px; +} + +.tundra .dndText { + cursor: move; + padding: 5px; + display: inline-block; +} + +.tundra .dndDelete { + float: right; + display: inline-block; + width: 19px; + height: 18px; + background: url(/images/sprites/buttonIcons.png) no-repeat 0 0; +} + +.tundra .dndItemFocused .dndDelete { + background-position: -19px 0; +} + +.tundra .dojoDndAvatarHeader { + display: none; +} + +.tundra .dojoDndAvatar .dojoDndAvatarItem { + background-color: rgb(245, 130, 130); +} + +.tundra .dojoDndAvatarCanDrop .dojoDndAvatarItem { + background-color: rgb(150, 230, 140); +} + +.dijitDialogUnderlay { + background-color: rgb(72, 72, 72); + opacity: 0.6; +} + +.tundra table.dijitInline { + display: inline-block; +} + +.tundra .dijitError { + border-color: rgb(245, 130, 40) !important; + background-color: rgb(255, 240, 230); + background-image: none; +} + +.tundra .dijitValidationIcon { + background: url(/images/sprites/alertIcons.png) no-repeat 0 0; + width: 16px; + height: 16px; + background-position-x: -80px; + background-position-y: -48px; + border: 0 !important; +} + +.tundra .dijitArrowButtonInner { + background: url(/images/sprites/arrowIcons.png) no-repeat 0 0; + width: 10px; + max-height: 10px; + margin: 0; + padding-top: 2px; +} + +.tundra .dijitDisabled .dijitArrowButtonInner { + visibility: hidden; +} + +.tundra .dijitUpArrowButton .dijitArrowButtonInner { + background-position: 0 0; +} + +.tundra .dijitDownArrowButton .dijitArrowButtonInner { + background-position: 0 -10px; +} + +.tundra .dijitSelect .dijitButtonNode, +.tundra .dijitTextBoxDisabled { + background: white url("/widgets/dijit/themes/tundra/images/validationInputBg.png") repeat-x top left; +} + +.tundra .dijitTextBoxDisabled .dijitButtonNode { + background: none; +} + +.tundra .citrixTabPaneField .dijitTextBox, +.tundra .citrixTabPaneField .dijitSpinner, +.tundra .citrixTabPaneField .dijitTextArea, +.tundra .citrixTabPaneField .dijitSelect { + margin: 0 0 0 -5px !important; +} + +.tundra .dijitTextArea { + font-size: 13px; + color: #333; +} + +.tundra .dijitSelect, +.tundra .dijitTextBox { + border: solid 2px lightGrey; + -webkit-border-radius: 4px; + height: 19px; +} + +.tundra .dijitSelect .dijitValidationContainer { + display: none; +} + +.tundra .dijitButtonNode { + border: none; + background: none; +} + +.tundra .dijitSelectFocused, +.tundra .dijitTextBoxFocused, +.tundra .dijitSelectHover, +.tundra .dijitSelectHover TD, +.tundra .dijitSelectActive, +.tundra .dijitSelectOpened, +.tundra .dijitSelectActive TD, +.tundra .dijitSelectOpened TD, +.tundra .dijitSelectFocused TD, +.tundra .dijitTextBoxFocused .dijitButtonNode { + border-color: rgb(140, 186, 235) !important; + outline: none; + color: inherit; + background: none; +} + +.tundra .dijitTextArea { + resize: none; + padding: 1px 3px; +} + +.tundra .dijitSelect td.dijitStretch { + width: 100%; +} + +.tundra .dijitSpinner .dijitSpinnerButtonInner { + margin: 2px 0; +} + +.tundra .dijitSelectMenu { + font-size: 13px; + border: 1px solid rgb(140, 186, 235) !important; +} + +.tundra .dijitMenuItemHover, +.tundra .dijitMenuItemSelected { + background-color: rgb(50, 90, 170); +} + +.tundra .dijitSelect .dijitButtonNode .dijitArrowButtonInner { + margin: 4px 2px 0 0; +} + +.tundra .dijitTextBox .dijitInputField { + padding: 1px 2px; +} + +.tundra .dijitDisabled .dijitButtonText { + color: rgb(154, 154, 154); +} + +.tundra .dijitFocusedLabel { + background-color: rgb(195, 215, 235); + outline: none; +} + +.tundra .dijitCheckBoxFocused:not(.dijitChecked) { + background-position: -80px; +} + +.tundra .dijitRadioFocused:not(.dijitChecked) { + background-position: -176px; +} + +.tundra .citrixTooltipAnchor span { + border-bottom: 1px dashed; + cursor: pointer; +} + +.tundra .citrixTooltipAnchor.dijitHover, +.tundra .citrixTooltipAnchor.dijitFocused { + color: #A0CFE5; +} + +/* midori doesn't honour opacity for radios and checkboxes, but will understand this */ +.tundra .dijitCheckBoxInput { + -webkit-appearance: none; +} + +.tundra .citrixTable { + border: solid 1px #CCC; + border-spacing: 0px; + color: black; + width: 100%; + margin-bottom: 10px; +} + +.tundra .citrixTable>thead>tr, +.tundra .citrixTable>tfoot>tr { + text-align: left; + background: -webkit-gradient(linear, left top, left bottom, from(white), to(rgb(231, 231, 231))); +} + +.tundra .citrixTable>thead>tr>th { + border-bottom: solid 1px rgb(204, 204, 204); +} + +.tundra .citrixTable>tfoot>tr>td { + border-top: solid 1px rgb(204, 204, 204); +} + +.tundra .citrixTable>thead>tr>td, +.tundra .citrixTable>tbody>tr>td, +.tundra .citrixTable>tfoot>tr>td { + height: 35px; +} + +.tundra .citrixTable>thead>tr>th, +.tundra .citrixTable>thead>tr>td, +.tundra .citrixTable>tbody>tr>th, +.tundra .citrixTable>tbody>tr>td, +.tundra .citrixTable>tfoot>tr>th, +.tundra .citrixTable>tfoot>tr>td { + padding: 5px; + border-left: solid 1px white; + border-right: solid 1px rgb(232, 232, 232); +} + +.tundra .citrixTable>thead>tr>th:first-child, +.tundra .citrixTable>thead>tr>td:first-child, +.tundra .citrixTable>tbody>tr>th:first-child, +.tundra .citrixTable>tbody>tr>td:first-child, +.tundra .citrixTable>tfoot>tr>th:first-child, +.tundra .citrixTable>tfoot>tr>td:first-child { + border-left: none; +} + +.tundra .citrixTable>thead>tr>th:last-child, +.tundra .citrixTable>thead>tr>td:last-child, +.tundra .citrixTable>tbody>tr>th:last-child, +.tundra .citrixTable>tbody>tr>td:last-child, +.tundra .citrixTable>tfoot>tr>th:last-child, +.tundra .citrixTable>tfoot>tr>td:last-child { + border-right: none; +} + +.tundra .citrixTable>tbody>tr:nth-child(2n) { + background-color: rgb(245, 245, 245); +} + +.tundra .citrixTable .noWrap { + white-space: nowrap; +} + +.tundra .citrixTable .centreAlign { + text-align: center; +} diff --git a/widgets/citrix/common/themes/tundra/Dialog.css b/widgets/citrix/common/themes/tundra/Dialog.css new file mode 100644 index 0000000..f9d1239 --- /dev/null +++ b/widgets/citrix/common/themes/tundra/Dialog.css @@ -0,0 +1,69 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixDialog { +} + +.tundra .citrixDialogError { + background-color: transparent; +} + +.tundra .citrixDialog .center { + text-align: center; +} + +.tundra .citrixDialogTitleBar { + height: 34px; + border-width: 0 11px; + -webkit-border-image: url(/images/borders/system_dialog.png) 0 11 55 11 stretch; +} + +.tundra .citrixDialogTitleIcon { + max-height: 30px; + float: left; + padding-top: 5px; +} + +.tundra .citrixDialogTitle { + font-size: 14px; + line-height: 38px; + font-weight: bold; + padding: 0px 10px; + white-space: pre; +} + +.tundra .citrixDialogCloseIcon { + cursor: pointer; + background: url("/images/buttons/075_CloseWindow_h32bit_16x16.png") no-repeat right top; + position: absolute; + right: 12px; + top: 10px; + height: 16px; + width: 16px; +} + +.tundra .citrixDialogPaneContent { + padding: 10px; + border-width: 0 11px; + -webkit-border-image: url(/images/borders/system_dialog.png) 34 11 54 11 repeat; +} + +.tundra .citrixDialogCloseIconHover { +} + +.tundra .citrixDialogFooterContent { + height: 54px; + border-width: 0 11px; + -webkit-border-image: url(/images/borders/system_dialog.png) 35 11 0 11 stretch; +} + +.tundra .citrixDialogFooter { + padding: 8px 0 0 0; +} + +.tundra .citrixDialogFooterItem { + margin: 12px 0 0 5px; +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/Keyboard.css b/widgets/citrix/common/themes/tundra/Keyboard.css new file mode 100755 index 0000000..44a17cc --- /dev/null +++ b/widgets/citrix/common/themes/tundra/Keyboard.css @@ -0,0 +1,74 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .keyboardWrapper { + font-size: 18px; + color: white; + text-align: center; + background: -webkit-gradient(linear, left top, left bottom, from(rgb(70, 70, 70)), to(rgb(40, 40, 40))); +} + +.tundra .closeKeyboard { + height: 21px; + width: 21px; + background-image: url(/images/buttons/keyboardClose.png); + position: absolute; + top: 8px; + right: 10px; + cursor: pointer; +} + +.tundra .keyboardContainer { + width: 800px; + margin: 0 auto; + padding: 8px 0; +} + +.tundra .keyboardRowWrapper { + display: table; + width: 100%; +} + +.tundra .keyboardRowWrapper:last-child { + width: auto; + margin: 0 auto; +} + +.tundra .keyboardRow { + display: table-row; +} + +.tundra .keyboardKey { + display: table-cell; + height: 56px; + width: 54px; + vertical-align: middle; + white-space: pre; +} + +.tundra .keyboardKey>span { + display: block; + height: 50px; + width: auto; + line-height: 50px; + margin: 3px 2px; + border-radius: 5px; + background-color: #646464; + -webkit-box-shadow: rgba(0,0,0,0.45) 0px 1px 3px; +} + +.tundra .keyboardKey.space { + width: 400px; +} + +.tundra .keyboardKey.controlKey { + width: auto; +} + +.tundra .keyboardKey>span.keyHover, +.tundra .keyboardKey>span.keyPressed { + background-color: #8CBAEB; +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/Menu.css b/widgets/citrix/common/themes/tundra/Menu.css new file mode 100644 index 0000000..06f8ede --- /dev/null +++ b/widgets/citrix/common/themes/tundra/Menu.css @@ -0,0 +1,116 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixMenuBar { + border: 0; + height: 72px; + background: url(/images/frame/topBarBG.png) repeat-x; + -webkit-box-shadow: rgba(0, 0, 0, 0.45) 0 2px 10px; + text-align: right; +} + +.tundra .citrixMenuBarItem { + margin: 0; + cursor: pointer; +} + +.tundra .citrixMenuBarItemContainer { + min-width: 72px; + max-width: 100px; + height: 50px; + text-align: center; +} + +.tundra .citrixMenuBarItemIcon { + display: inline-block; +} + +.tundra .citrixMenuBarItemText { + height: 20px; + line-height: 12px; + font-size: 11px; /* should be a minimum of 12px for ja and sc text */ + position: relative; + top: -19px; + padding: 0 5px; + text-transform: capitalize; +} + +.tundra .citrixMenuBarItemSplitter { + width: 2px; + height: 72px; + background: url(/images/frame/topBarDivider.png) no-repeat; +} + +.tundra .citrixMenuBarAlert { + margin-top: 8px; +} + +.tundra .citrixMenuPopup { + z-index: 3000 !important; + border-width: 10px 10px 10px 11px; + -webkit-border-image: url(/images/borders/l_menu_border.png) 10 10 10 11 repeat; +} + +.tundra .citrixMenuPopupRight { + z-index: 3000 !important; + border-width: 10px 11px 10px 10px; + -webkit-border-image: url(/images/borders/r_menu_border.png) 10 11 10 10 repeat; +} + +.tundra .citrixMenuPopup .dijitMenu { + border: 0; + background-color: #E8E8E8; +} + +.tundra .citrixMenuPopup .dijitMenuItem { + font-size: 12px; + border: 1px solid #E8E8E8; + line-height: 22px; +} + +.tundra .citrixMenuPopup .dijitMenuItemHover, +.tundra .citrixMenuPopup .dijitMenuItemSelected { + color: #333; + background: -webkit-gradient(linear, left top, left bottom, from(rgb(236, 243, 252)), to(rgb(147, 191, 238))); +} + +.tundra .citrixMenuPopup .dijitMenuSeparatorTop { + border: 0; + height: 2px; + background: url(/images/backgrounds/menu_horiz_splitter.png) repeat-x; +} + +.tundra .citrixFooterBar { + border: 0; + height: 35px; + overflow: hidden; + background: url(/images/frame/footer_bar_bgslice.png) repeat-x; +} + +.tundra .citrixFooterBarItemLeft { + padding-left: 10px; + float: left; +} + +.tundra .citrixFooterBarItemRight { + padding-right: 10px; + float: right; +} + +.tundra .citrixFooterBarItemContainer { + height: 35px; +} + +.tundra .citrixFooterBarItemIcon { + float: left; +} + +.tundra .citrixFooterBarItemText { + line-height: 35px; + font-size: 11px; /* should be a minimum of 12px for ja and sc text */ + padding-left: 5px; + float: right; +} diff --git a/widgets/citrix/common/themes/tundra/OrderableList.css b/widgets/citrix/common/themes/tundra/OrderableList.css new file mode 100755 index 0000000..68cf221 --- /dev/null +++ b/widgets/citrix/common/themes/tundra/OrderableList.css @@ -0,0 +1,48 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixDecreaseButton, .tundra .citrixIncreaseButton { +} + +.tundra .citrixDecreaseButton { +} + +.tundra .citrixIncreaseButton { +} + +.tundra .citrixInfo { + width: 95px; + height: 55px; + text-align: center; + line-height: 45px; + background: url("/images/icons/gpu.png") no-repeat center center; + border: solid 2px lightGrey; + border-radius: 4px 4px; + padding: 5px; +} + +.tundra .citrixOrderableHorizItem { + padding: 5px; +} + +.tundra .citrixOrderableHorizItem .text { + background-color: rgba(255, 255, 255, 0.7); +} + +.tundra .citrixOrderableHorizItem:first-child .citrixDecreaseButton { + display: none; +} + +.tundra .citrixOrderableHorizItem:last-child .citrixIncreaseButton { + display: none; +} + +.tundra .citrixOrderableList { + overflow-x: auto; + overflow-y: hidden; + white-space: nowrap; + text-align: center; +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/ProgressBar.css b/widgets/citrix/common/themes/tundra/ProgressBar.css new file mode 100644 index 0000000..c401001 --- /dev/null +++ b/widgets/citrix/common/themes/tundra/ProgressBar.css @@ -0,0 +1,56 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixProgressBar { + margin:2px 0 2px 0; +} + +.tundra .citrixProgressBarEmpty{ + /* outer container and background of the bar that's not finished yet*/ + position:relative;overflow:hidden; + border: 1px solid rgb(140, 160, 190); + z-index: 0; /* establish a stacking context for this progress bar */ + background: #fff; + height: 14px; +} + +.tundra .citrixProgressBarFull { + /* outer container for background of bar that is finished */ + position:absolute; + overflow:hidden; + z-index: -1; + top:0; + width:100%; + border-right:1px solid rgb(140, 160, 190); +} + +.tundra .citrixProgressBarTile{ + /* inner container for finished portion when in 'tile' (image) mode */ + position:absolute; + overflow:hidden; + top:0; + left:0; + bottom:0; + right:0; + margin:0; + padding:0; + width:auto; + height:auto; + background-color: #aaa; + background-attachment: fixed; + background:#f0f0f0 url(/images/backgrounds/progressBarAnim.gif) repeat-x center center; +} + +.tundra .citrixProgressBarLabel { + /* Set to a color that contrasts with both the "Empty" and "Full" parts. */ + display:block; + position:static; + width:100%; + text-align:center; + background-color:transparent !important; + color: rgb(40, 60, 75); + line-height: 12px; +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/TabContainer.css b/widgets/citrix/common/themes/tundra/TabContainer.css new file mode 100755 index 0000000..06ee7d4 --- /dev/null +++ b/widgets/citrix/common/themes/tundra/TabContainer.css @@ -0,0 +1,233 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixTabWrapper { + position: absolute; + width: 190px; + border-width: 10px; + -webkit-border-image: url(/images/borders/system_panel.png) 10 repeat; +} + +.tundra .citrixTabWrapper.dijitFocused { + -webkit-border-image: url(/images/borders/system_panel_focus.png) 10 repeat; +} + +.tundra .citrixTabContainerLeft-tabs { + left: -7px !important; + position: relative !important; + overflow: visible !important; + height: auto !important; +} + +.tundra .citrixTabPaneWrapper { + border: none; + margin: 0; + padding: 5px; + font-size: 13px; +} + +.tundra .citrixTab { + cursor: pointer; + line-height: 30px; + width: 202px; + padding: 1px 1px 0; + border-width: 1px 0; + -webkit-border-image: url(/images/borders/h_separator.png) 0 0 1 0 stretch; +} + +.tundra .citrixTab:last-child { + border-width: 1px 0 0 0; +} + +/* hovered tab */ +.tundra .citrixTabHover { + background: url(/images/dialog_panel/highlight_mid.png) no-repeat 1px 0; + opacity: 0.5; +} + +/* selected */ +.tundra .citrixTabChecked { + background: url(/images/dialog_panel/highlight_mid.png) no-repeat 1px 0; + font-weight: bold; +} + +.tundra .citrixTabInner { + padding: 0 18px; +} + +.tundra .citrixTab .tabLabel { + display: inline-block; + font-size: 14px; +} + +.tundra .citrixTabSpacer { + width: 20px; +} + +.tundra .citrixDialogPaneContent h1 { + font-size: 13px; + line-height: 29px; + font-weight: normal; + padding: 0; + color: rgb(23, 123, 173); + margin: 0; +} + +.tundra .citrixDialogPaneContent p { + font-size: 12px; + line-height: 14px; + margin: 8px 0; +} + +.tundra .citrixTabPaneColumn { + border: 0; + margin: 0; + padding: 0; +} + +.tundra .citrixTabPaneColumn.single { + clear: both; +} + +.tundra .citrixTabPaneHR { + clear: both; +} + +.tundra .citrixTabPaneField { + display: table-row; + height: 35px; + font-size: 13px; +} + +.tundra .citrixTabPaneField.spacer { + height: 20px; +} + +.tundra .citrixTabPaneField .value, +.tundra .citrixTabPaneField .valueContainer { + margin-top: 3px; + display: inline-block; + word-wrap: break-word; +} + +.tundra .citrixTabPaneField .value { + white-space: pre-wrap; +} + +.tundra .citrixTabPaneField .value.textArea { + overflow-y: auto; + width: 333px; + height: 91px; +} + +.tundra .citrixTabPaneField .dijitButton { + margin: 0 10px; +} + +.tundra .citrixTabPaneField .citrixProgressBar { + width: 200px; +} + +.tundra .citrixTabPaneField.radio { + height: 25px; +} + +.tundra .citrixTabPaneField.radioInfo { + height: auto; +} + +.tundra .citrixTabPaneField.radioInfo label { + padding-left: 21px; + padding-bottom: 2px; + font-size: 12px; +} + +.tundra .citrixTabPaneField.radio .dijitRadio, +.tundra .citrixTabPaneField.radio .dijitCheckBox { + padding-top: 5px; +} + +.tundra .citrixTabPaneField.radio label.inline { + display: inline-block; + padding-left: 5px; +} + +.tundra .citrixTabPaneField label { + display: table-cell; + vertical-align: top; + color: #666; + padding: 3px 15px 0 15px; + min-width: 90px; +} + +.tundra .citrixTabPaneField .grey { + color: rgb(142, 142, 142); +} + +.tundra .citrixTabPaneField .editTip { + padding-left: 10px; +} + +.tundra .citrixTabPaneField.radio .editTip { + padding-left: 21px; + max-width: 450px; +} + +.tundra .citrixTabPaneField.radio .specalPony { + margin: 0 2px 10px 21px; +} + +.tundra .citrixTabPaneField .instruction { + padding: 5px 0 20px; + max-width: 330px; +} + +.tundra .citrix.dijitTextBox { + width: 335px !important; +} + +.tundra .citrix.dijitSelect { + width: 250px !important; +} + +.tundra .citrix.dijitSelect .dijitSelectLabel { + max-width: 230px; + overflow: hidden; + padding-bottom: 1px; +} + +.tundra .citrix.dijitTextArea { + width: 330px !important; + height: 90px; +} + +.tundra .citrix.dijitSpinner { + width: 65px !important; +} + +.tundra .citrix.dijitSpinner.longNumber { + width: 200px !important; +} + +.tundra .dijitContentPane { + overflow: hidden; +} + +.tundra .citrixTabPaneColumn.double { + float: left; + width: 50%; +} + +.tundra .citrixTabPaneColumn.double .value { +} + +.tundra .citrixTabPaneColumn.double .citrix.dijitTextBox, +.tundra .citrixTabPaneColumn.double .citrix.dijitTextArea, +.tundra .citrixTabPaneColumn.double .citrix.dijitSelect { +} + +.tundra .citrixTabPaneColumn.double .citrix.dijitSelect .dijitSelectLabel { +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/Wizard.css b/widgets/citrix/common/themes/tundra/Wizard.css new file mode 100755 index 0000000..0f54a72 --- /dev/null +++ b/widgets/citrix/common/themes/tundra/Wizard.css @@ -0,0 +1,143 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixWizard { + width: 738px; + margin: -10px -15px; +} + +.tundra .citrixWizardSpacer { + border-width: 1px 0; + -webkit-border-image: url(/images/borders/h_separator.png) 0 0 1 0 stretch; + margin: 0px; +} + +.tundra .citrixWizardStepsWrapper { + background: url(/images/backgrounds/wizard_step_bg.png) repeat-x 0 0; + font-size: 12px; + padding: 10px 0; + position: relative; +} + +.tundra .citrixWizardSteps { + overflow: auto; + display: table; + margin: 0px auto; + text-align: center; + position: relative; +} + +.tundra .citrixWizardSteps ul { + display: table-row; + margin: 0; + padding: 0; + position: relative; +} + +.tundra .citrixWizardStepItem, .tundra .citrixWizardStepMarker { + display: table-cell; + text-align: center; + vertical-align: top; + width: 120px; + position: relative; + color: rgb(153, 153, 153); +} + +.tundra .citrixWizardStepMarker .citrixWizardStepLabel { + position: relative; + height: 22px; +} + +.tundra .citrixWizardStepMarker .citrixWizardStepLabel div:first-child { + position: absolute; + top: 9px; + right: 0; + left: 0; + background-color: rgb(204, 204, 204); + height: 4px; +} + +.tundra .citrixWizardStepMarker .citrixWizardStepLabel div:last-child { + position: absolute; + height: 100%; + width: 100%; + background-repeat: no-repeat; + background-image: url(/images/icons/greyOrb.png); + background-position: 50% 50%; +} + +.tundra .citrixWizardStepMarker.citrixWizardStepSelected .citrixWizardStepLabel div:last-child { + background-image: url(/images/icons/blueOrb.png); +} + +.tundra .citrixWizardStepMarker:first-child .citrixWizardStepLabel div:first-child { + left: 65px; +} + +.tundra .citrixWizardStepMarker:last-child .citrixWizardStepLabel div:first-child { + right: 65px; +} + +.tundra .citrixWizardStepItem .citrixWizardStepLabel { + padding-bottom: 4px; +} + +.tundra .citrixWizardStepItem.citrixWizardStepSelected { + color: rgb(23, 123, 173); + font-weight: bold; +} + +.tundra .citrixWizardContainer { + display: table; + width: 100%; + position:relative; +} + +.tundra .citrixWizard .dijitContentPane { + min-height: 285px; + overflow: hidden; + position: relative; + font-size: 13px; + padding: 20px 5px 5px; +} + +.tundra .citrixWizard .alignBottom { + margin-left: -5px; + margin-bottom: 5px; +} + +.tundra .citrixWizard .dijitContentPane.dijitVisible { + text-align: center; +} + +.tundra .citrixWizard .citrixTabPaneColumn { + text-align: left; +} + +.tundra .citrixWizard .citrixTabPaneColumn.single { + display: inline-block; +} + +.tundra .citrixWizard .citrixTabPaneColumn.double .citrix.dijitTextBox, +.tundra .citrixWizard .citrixTabPaneColumn.double .citrix.dijitSelect { + width: 220px !important; +} + +.tundra .citrixWizard .citrixTabPaneColumn.double .citrix.dijitSelect .dijitSelectLabel { + max-width: 200px !important; +} + +.tundra .citrixWizard .citrixTabPaneColumn.double .citrix.dijitTextArea { + width: 214px !important; +} + +.tundra .citrixWizard .citrixTabPaneField.smallHeight { + height: 25px; +} + +.tundra .citrixWizard .citrixTabPaneField .value { + margin-bottom: 5px; +} \ No newline at end of file diff --git a/widgets/citrix/common/themes/tundra/tundra.css b/widgets/citrix/common/themes/tundra/tundra.css new file mode 100644 index 0000000..2c295ea --- /dev/null +++ b/widgets/citrix/common/themes/tundra/tundra.css @@ -0,0 +1,15 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +@import url("AlertPopup.css"); +@import url("Button.css"); +@import url("Common.css"); +@import url("Keyboard.css"); +@import url("Menu.css"); +@import url("OrderableList.css"); +@import url("ProgressBar.css"); +@import url("TabContainer.css"); +@import url("Wizard.css"); \ No newline at end of file diff --git a/widgets/citrix/package.json b/widgets/citrix/package.json new file mode 100644 index 0000000..76918fc --- /dev/null +++ b/widgets/citrix/package.json @@ -0,0 +1,10 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +{ + "name": "citrix", + "dojoBuild": "citrix.profile.js" +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/AddDisk.js b/widgets/citrix/sync-wui/AddDisk.js new file mode 100644 index 0000000..f58908e --- /dev/null +++ b/widgets/citrix/sync-wui/AddDisk.js @@ -0,0 +1,93 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!citrix/sync-wui/nls/AddDisk", + "dojo/text!citrix/sync-wui/templates/AddDisk.html", + // Mixins + "citrix/common/ValidationDialog", + "citrix/common/_CitrixWidgetMixin", + "citrix/common/_CitrixTooltipMixin", + "citrix/sync-wui/_ModelMixin", + // Required in template + "citrix/sync-wui/Button", + "citrix/common/ValidationTextBox", + "citrix/common/NumberSpinner", + "citrix/common/Select" +], +function(dojo, declare, nls, template, dialog, _citrixWidgetMixin, _citrixTooltipMixin, _modelMixin) { +return declare("citrix.sync-wui.AddDisk", [dialog, _citrixWidgetMixin, _citrixTooltipMixin, _modelMixin], { + + templateString: template, + widgetsInTemplate: true, + destroyOnHide: true, + + constructor: function(args) { + this.saveFunction = "add_disk"; + this.saveParams = { + disk_name: null, + disk_type: null, + file_path: null, + file_size: null, + file_hash: null, + encryption_key: null, + shared: null, + read_only: null + }; + }, + + postMixInProperties: function() { + dojo.mixin(this, nls); + this.inherited(arguments); + }, + + postCreate: function() { + this.inherited(arguments); + this.startup(); + this.set("title", nls.TITLE); + + this.typeNode.set("value", "v"); + this.readonlyNode.set("value", false); + this.sharedNode.set("value", false); + }, + + onTypeChange: function(newValue) { + this._setEnabled(this.keyNode, newValue == 'v'); + this._setEnabled(this.readonlyNode, newValue == 'v'); + if (newValue == 'i') { + this.keyNode.set("value", ""); + this.readonlyNode.set("value", true); + } + }, + + onROChange: function(newValue) { + this._setEnabled(this.sharedNode, newValue == false); + if (newValue == true) { + this.sharedNode.set("value", false); + } + }, + + save: function() { + this.saveParams.disk_name = this.nameNode.get("value"); + this.saveParams.file_path = this.pathNode.get("value"); + this.saveParams.file_hash = this.hashNode.get("value"); + this.saveParams.file_size = this.sizeNode.get("value"); + this.saveParams.disk_type = this.typeNode.get("value"); + this.saveParams.encryption_key = this.keyNode.get("value"); + this.saveParams.read_only = this.readonlyNode.get("value"); + this.saveParams.shared = this.sharedNode.get("value"); + + var finish = dojo.hitch(this, function() { + this.onExecute(); + }); + + this.saveData(finish); + } +}); +}); diff --git a/widgets/citrix/sync-wui/AlertDialog.js b/widgets/citrix/sync-wui/AlertDialog.js new file mode 100644 index 0000000..6be4f7e --- /dev/null +++ b/widgets/citrix/sync-wui/AlertDialog.js @@ -0,0 +1,237 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!citrix/sync-wui/nls/Alerts", + "dojo/i18n!citrix/sync-wui/nls/AlertDialog", + "dojo/text!citrix/sync-wui/templates/AlertDialog.html", + // Mixins + "citrix/common/Dialog", + // Required in template + "citrix/common/CheckBox", + "citrix/sync-wui/Button" +], +function(dojo, declare, alertsNls, alertDialogNls, template, dialog) { +return declare("citrix.xenclient.AlertDialog", [dialog], { + + templateString: template, + widgetsInTemplate: true, + _alertSource: null, + _onContinue: null, + _onClose: null, + _showAgainProperty: "", + _msgQueue: [], + _getPrefix: "get_", + _setPrefix: "set_", + + _defaultArguments: { + headerText: "", + continueText: "", + closeText: "", + iconClass: "", + showAgainProperty: "" + }, + + _errorArguments: { + headerText: "ERROR", + continueText: "CLOSE_ACTION", + iconClass: "alertIcon alertIconLarge alertIconError" + }, + + _warningArguments: { + headerText: "WARNING", + continueText: "CONTINUE_ACTION", + iconClass: "alertIcon alertIconLarge alertIconWarning" + }, + + _tipArguments: { + headerText: "TIP", + continueText: "CONTINUE_ACTION", + iconClass: "alertIcon alertIconLarge alertIconInformation" + }, + + _informationArguments: { + headerText: "INFORMATION", + continueText: "CONTINUE_ACTION", + iconClass: "alertIcon alertIconLarge alertIconInformation" + }, + + _securityArguments: { + headerText: "SECURITY", + continueText: "CONTINUE_ACTION", + iconClass: "shieldIcon shieldIconLarge shieldIconRed" + }, + + _confirmationArguments: { + headerText: "WARNING", + continueText: "OK_ACTION", + closeText: "CANCEL_ACTION", + iconClass: "alertIcon alertIconLarge alertIconWarning" + }, + + postMixInProperties: function() { + this._alertSource = alertsNls; + dojo.mixin(this, alertDialogNls); + this.inherited(arguments); + }, + + showError: function(error, sourceHint, overrideArguments, onContinue) { + var args = this._mergeArguments(this._errorArguments, overrideArguments); + var message = error; + if (typeof(message) == "object") { + // Assume it's an rpc error object, go get the error + message = this._getErrorMessage(error, sourceHint); + } + this._showDialog(message, args, onContinue); + }, + + showWarning: function(message, onContinue, overrideArguments) { + var args = this._mergeArguments(this._warningArguments, overrideArguments); + this._showDialog(message, args, onContinue); + }, + + showTip: function(message, onContinue, overrideArguments) { + var args = this._mergeArguments(this._tipArguments, overrideArguments); + this._showDialog(message, args, onContinue); + }, + + showInformation: function(message, onContinue, overrideArguments) { + var args = this._mergeArguments(this._informationArguments, overrideArguments); + this._showDialog(message, args, onContinue); + }, + + showSecurity: function(message, onContinue, overrideArguments) { + var args = this._mergeArguments(this._securityArguments, overrideArguments); + this._showDialog(message, args, onContinue); + }, + + showConfirmation: function(message, onContinue, overrideArguments, onClose) { + var args = this._mergeArguments(this._confirmationArguments, overrideArguments); + this._showDialog(message, args, onContinue, onClose); + }, + + onExecute: function() { + this.inherited(arguments); + if (typeof(this._onContinue) == "function") { + this._onContinue(); + } + }, + + onCancel: function() { + this.inherited(arguments); + if (typeof(this._onClose) == "function") { + this._onClose(); + } + }, + + afterHide: function() { + this.inherited(arguments); + /*if (this._showAgainProperty != "") { + var _showAgainProp = this._showAgainProperty; + if(XUICache.Host[this._setPrefix + this._showAgainProperty]) { + _showAgainProp = this._setPrefix + this._showAgainProperty; + } + if(typeof(XUICache.Host[_showAgainProp]) === "function") { + XUICache.Host.save(this._showAgainProperty, XUICache.Host[_showAgainProp](!this.notAgainNode.get("checked"))); + } else { + XUICache.Host.save(this._showAgainProperty, !this.notAgainNode.get("checked")); + } + }*/ + if (this._msgQueue.length > 0) { + var args = this._msgQueue.shift(); + this._showDialog(args.message, args.args, args.onContinue, args.onClose, true); + } + }, + + _showDialog: function(message, args, onContinue, onClose, fromQueue) { + if (!fromQueue && this.open) { + // Queue up the message + this._msgQueue.push({ message: message, args: args, onContinue: onContinue, onClose: onClose }); + return; + } + /*var _showAgain; + var _showAgainProp = args.showAgainProperty; + if(XUICache.Host[this._getPrefix + args.showAgainProperty]) { + _showAgainProp = this._getPrefix + args.showAgainProperty; + } + if(typeof(XUICache.Host[_showAgainProp]) === "function") { + _showAgain = XUICache.Host[_showAgainProp](); + } else { + _showAgain = XUICache.Host[_showAgainProp]; + } + if (_showAgainProp != "" && !_showAgain) { + // Dialog has been previously hidden + if (typeof(onContinue) == "function") { + onContinue(); + } + return; + }*/ + this._onContinue = onContinue || null; + this._onClose = onClose || null; + //this._showAgainProperty = args.showAgainProperty || ""; + this._bindDijit(message, args); + this.show(); + }, + + _mergeArguments: function() { + var mergedArgs = {}; + var argArrays = [].slice.call(arguments); + argArrays.unshift(this._defaultArguments); + for (var i = 0; i < argArrays.length; i++) { + var mergeIn = argArrays[i]; + for (var key in mergeIn) { + mergedArgs[key] = mergeIn[key]; + } + } + return mergedArgs; + }, + + _getErrorMessage: function(error, sourceHint) { + var message = ""; + if (error.code && sourceHint) { + dojo.some(Object.keys(sourceHint), function(key) { + if (sourceHint[key] == error.code) { + message = this._alertSource[key]; + return true; + } + return false; + }, this); + if (message != "") { + return message; + } + } + // Set a default message and code + message = error.message || this._alertSource.UNKNOWN; + var code = error.code || this._alertSource.UNKNOWN; + return this.UNKNOWN_ERROR_MASK.format(this._alertSource.UNKNOWN_ERROR, code, message); + }, + + _getText: function(text, source) { + source = source || this; + if (text != "" && source[text]) { + return source[text]; + } + return text; + }, + + _bindDijit: function(message, args) { + this.messageNode.innerHTML = message; + this.set("title", this._getText(args.headerText)); + this.continueButton.set("label", this._getText(args.continueText)); + this.closeButton.set("label", this._getText(args.closeText)); + this.notAgainNode.set("checked", false); + this.iconNode.className = "left " + args.iconClass; + this._setDisplay(".notAgain", args.showAgainProperty != ""); + this._setDisplay(this.continueButton, args.continueText != ""); + this._setDisplay(this.closeButton, args.closeText != ""); + this.set("canExecute", args.continueText != ""); + this.set("canCancel", args.closeText != ""); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/AlertPopup.js b/widgets/citrix/sync-wui/AlertPopup.js new file mode 100644 index 0000000..2129d3b --- /dev/null +++ b/widgets/citrix/sync-wui/AlertPopup.js @@ -0,0 +1,81 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/text!citrix/sync-wui/templates/AlertPopup.html", + // Mixins + "citrix/common/AlertPopup" +], +function(dojo, declare, template, alertPopup) { +return declare("citrix.xenclient.AlertPopup", [alertPopup], { + + templateString: template, + + _defaultArguments: { + headerText: "", + iconClass: "" + }, + + _errorArguments: { + iconClass: "alertIcon alertIconLarge alertIconError" + }, + + _warningArguments: { + iconClass: "alertIcon alertIconLarge alertIconWarning" + }, + + _informationArguments: { + iconClass: "alertIcon alertIconLarge alertIconInformation" + }, + + postCreate: function() { + this.inherited(arguments); + this.subscribe(XUtils.publishTopic, this._messageHandler); + }, + + showPopup: function(data) { + if (typeof(data) === "string") { + data = [data]; + } + + var overrideArguments = {}; + var extra = data[1]; + if (extra) { + if (this[extra]) { + // Specifiying specific argument object above + overrideArguments = this[extra]; + } else { + overrideArguments.headerText = extra; + } + } + + var args = this._mergeArguments(this._warningArguments, overrideArguments); + this._showDialog(data[0], args); + }, + + _bindDijit: function(message, args) { + this.set("title", args.headerText); + this.messageNode.innerHTML = message; + this.iconNode.className = "left " + args.iconClass; + }, + + _messageHandler: function(message) { + switch(message.type) { + case XenConstants.TopicTypes.UI_SHOW_POPUP: { + this.showPopup(message.data); + break; + } + case XenConstants.TopicTypes.UI_HIDE_POPUP: { + this.hide(); + break; + } + } + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/BooleanSelect.js b/widgets/citrix/sync-wui/BooleanSelect.js new file mode 100755 index 0000000..fd655c3 --- /dev/null +++ b/widgets/citrix/sync-wui/BooleanSelect.js @@ -0,0 +1,29 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!citrix/sync-wui/nls/BooleanSelect", + // Mixins + "citrix/common/Select" +], +function(dojo, declare, nls, select) { + +return declare("citrix.sync-wui.BooleanSelect", [select], { + + postCreate: function() { + this.inherited(arguments); + + this.set("options", [ + {label: nls.TRUE, value: "true"}, + {label: nls.FALSE, value: "false"}, + {label: nls.NOT_SET, value: ""} + ]); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/Button.js b/widgets/citrix/sync-wui/Button.js new file mode 100644 index 0000000..072ac66 --- /dev/null +++ b/widgets/citrix/sync-wui/Button.js @@ -0,0 +1,30 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/text!citrix/sync-wui/templates/Button.html", + // Mixins + "citrix/common/Button" +], +function(dojo, declare, template, button) { +return declare("citrix.sync-wui.Button", [button], { + + templateString: template, + + _onClick: function(/*Event*/ e){ + var ok = this.inherited(arguments); + if(ok){ + e.preventDefault(); + e.stopPropagation(); + } + return ok; + } + +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/ComboButton.js b/widgets/citrix/sync-wui/ComboButton.js new file mode 100644 index 0000000..9342de2 --- /dev/null +++ b/widgets/citrix/sync-wui/ComboButton.js @@ -0,0 +1,26 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/text!citrix/sync-wui/templates/ComboButton.html", + // Mixins + "citrix/common/ComboButton" +], +function(dojo, declare, template, comboButton) { +return declare("citrix.sync-wui.ComboButton", [comboButton], { + + templateString: template, + + cssStateNodes: { + "buttonNode": "dijitButtonNode", + "_popupStateNode": "dijitDownArrowButton" + } + +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/DeviceExpando.js b/widgets/citrix/sync-wui/DeviceExpando.js new file mode 100644 index 0000000..89264b5 --- /dev/null +++ b/widgets/citrix/sync-wui/DeviceExpando.js @@ -0,0 +1,65 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/dom-class", + // Resources + "dojo/i18n!./nls/DeviceWidget", + // Mixins + "citrix/common/ContentPane", + // Used in code + "citrix/sync-wui/DevicePlatform", + "citrix/sync-wui/VMSelect" + // Used in template +], +function(dojo, declare, domClass, nls, contentPane, devicePlatform, vmSelect) { +return declare("citrix.sync-wui.DeviceExpando", [contentPane], { + + device_uuid: "", + _expanded: false, + onRouting: null, + + postCreate: function() { + this.inherited(arguments); + domClass.add(this.domNode, "expandoContent"); + this.on("click", function(e) { + // prevent clicking the background of the expando bubbling up to the row (de)select event + e.preventDefault(); + e.stopPropagation(); + }); + }, + + expand: function() { + this.isVisible = true; + if(!this._expanded) { + var platform = new devicePlatform({device_uuid: this.device_uuid, onRouting: this.onRouting}); + var userselect = new vmSelect({userVMs: true, device_uuid: this.device_uuid, itemClass: "citrixImageItem citrixImage45"}); + var serviceselect = new vmSelect({userVMs: false, device_uuid: this.device_uuid, itemClass: "citrixImageItem citrixImage45"}); + this.addChild(platform); + this.addChild(userselect); + this.addChild(serviceselect); + platform.startup(); + userselect.startup(); + serviceselect.startup(); + this._expanded = true; + } else { + dojo.forEach(this.getChildren(), function(child) { + child.getData(); + }); + } + }, + + collapse: function() { + this.isVisible = false; + }, + + startup: function() { + this.inherited(arguments); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/DeviceGrid.js b/widgets/citrix/sync-wui/DeviceGrid.js new file mode 100644 index 0000000..b2956d1 --- /dev/null +++ b/widgets/citrix/sync-wui/DeviceGrid.js @@ -0,0 +1,55 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/array", + // Resources + "dojo/i18n!./nls/DeviceWidget", + // Mixins + "citrix/common/_TopicMixin", + "citrix/sync-wui/GridTemplate" +], +function(dojo, declare, array, nls, _topicMixin, gridTemplate) { +return declare("citrix.sync-wui.DeviceGrid", [gridTemplate, _topicMixin], { + + getGridProperties: function() { + return { + allowSelectAll: true, + selectionMode: "multiple", + dataFunction: "list_devices_ui", + idProperty: "device_uuid", + filterParam: "device_name_contains", + + columns: [ + { + label: nls.NAME, + field: "device_name" + } + ], + + dataParams: { + device_name_contains: null + } + }; + }, + + postCreate: function() { + this.inherited(arguments); + this.subscribe(XUtils.publishTopic, dojo.hitch(this, this._messageHandler)); + }, + + _messageHandler: function(message) { + switch(message.type) { + case "add_device": + case "devices_removed": + this._bindDijitRetainPosition(); + break; + } + } +}); +}); diff --git a/widgets/citrix/sync-wui/DevicePlatform.js b/widgets/citrix/sync-wui/DevicePlatform.js new file mode 100644 index 0000000..1b3efcf --- /dev/null +++ b/widgets/citrix/sync-wui/DevicePlatform.js @@ -0,0 +1,53 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!./nls/DeviceWidget", + "dojo/text!./templates/DevicePlatform.html", + // Mixins + "dijit/_Widget", + "dijit/_Templated", + "dijit/_Contained", + "citrix/common/_BoundContainerMixin", + "citrix/sync-wui/_ModelMixin", + // Used in template + "citrix/common/BoundWidget" +], +function(dojo, declare, nls, template, _widget, _templated, _contained, _boundContainer, _model) { +return declare("citrix.sync-wui.DevicePlatform", [_widget, _templated, _contained, _boundContainer, _model], { + + templateString: template, + widgetsInTemplate: true, + + device_uuid: "", + dataFunction: "get_report", + onRouting: null, + + constructor: function(args) { + this.device_uuid = args.device_uuid; + this.dataParams = { + device_uuid: this.device_uuid + }; + }, + + postMixInProperties: function() { + dojo.mixin(this, nls); + }, + + _bindDijit: function() { + this.bind(this.model, this.domNode); + }, + + onConfigClick: function(event) { + if(this.onRouting) { + this.onRouting(this.device_uuid); + } + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/DeviceWidget.js b/widgets/citrix/sync-wui/DeviceWidget.js new file mode 100644 index 0000000..5bfa512 --- /dev/null +++ b/widgets/citrix/sync-wui/DeviceWidget.js @@ -0,0 +1,402 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/lang", + "dojo/_base/array", + "dojo/_base/event", + "dojo/date/locale", + "dojo/aspect", + "dojo/query", + "dijit/registry", + // Resources + "dojo/i18n!./nls/DeviceWidget", + "dojo/text!./templates/DeviceWidget.html", + // Mixins + "citrix/sync-wui/DeviceGrid", + "citrix/common/_RouterMixin", + // Used in code + "citrix/common/Grid", + "put-selector/put", + "citrix/sync-wui/DeviceExpando", + "citrix/sync-wui/Settings", + "citrix/sync-wui/Button", + "citrix/sync-wui/ImageButton", + "citrix/sync-wui/GridDialog", + // Used in template + "citrix/sync-wui/RepoGrid", + "citrix/common/Select" +], +function(dojo, declare, lang, array, event, localeDate, aspect, query, registry, nls, template, deviceGrid, _routerMixin, grid, put, deviceExpando, settings, button, imageButton, gridDialog) { +return declare("citrix.sync-wui.DeviceWidget", [deviceGrid, _routerMixin], { + + templateString: template, + expandedNode: null, + expandedId: null, + routerBase: "computers", + + getGridProperties: function() { + var props = this.inherited(arguments); + + props.columns.unshift({ + label: " ", + className: "expandoButtonCell", + renderCell: dojo.hitch(this, function(object, value, node, options) { + var button = new imageButton({label: nls.EXPAND, showLabel: false, iconClass: "expandoIcon"}); + button.onClick = dojo.hitch(this, function() { + this._expandNode(this.grid.row(object).element); + }); + node.appendChild(button.domNode); + }) + }); + + props.columns.push({ + label: nls.REPORT_TIME, + field: "report_time", + formatter: function(value) { + if(value != null) { + value = localeDate.format(new Date(value)); + } else { + value = ""; + } + return value; + } + }); + + props.columns.push({ + label: nls.ACTIONS, + field: "actions", + sortable: false, + renderCell: dojo.hitch(this, function(object, value, node, options) { + var dupeButton = new button({label: nls.DUPLICATE_DEVICE}); + dupeButton.onClick = dojo.hitch(this, function(e) { + this._onDuplicateDevice(object.device_uuid); + event.stop(e); + }); + node.appendChild(dupeButton.domNode); + }) + }); + + props.renderRow = dojo.hitch(this, function(obj, options) { + var div = put("div.collapsed", grid.prototype.renderRow.apply(this.grid, arguments)); + var widget = new deviceExpando({device_uuid: obj.device_uuid, onRouting: dojo.hitch(this, this.route)}); + put(div, "div.expando", widget.domNode); + widget.startup(); + if(obj.device_uuid == this.expandedId) { + this._expandNode(div, true, obj.device_uuid); + this.grid.select(obj.device_uuid); + } + return div; + }); + + props.removeRow = function(rowElement, justCleanup) { + var expandoDiv = rowElement.children && rowElement.children.length == 2 ? rowElement.children[1] : null; + var widgetDiv = expandoDiv && expandoDiv.children.length == 1 ? expandoDiv.children[0] : null; + if(widgetDiv) { + registry.byNode(widgetDiv).destroyRecursive(); + } + this.inherited(arguments); + }; + + return props; + }, + + postMixInProperties: function() { + this.inherited(arguments); + dojo.mixin(this, nls); + }, + + postCreate: function() { + this.inherited(arguments); + + this.grid.on("dgrid-select, dgrid-deselect", dojo.hitch(this, this._updateButtons)); + this._updateButtons(); + }, + + _expandNode: function(node, rendering, device_uuid) { + if(!node) {return;} + var collapsed = node.className.indexOf("collapsed") >= 0; + + var expandoContent = dijit.byNode(query(".expandoContent", node)[0]); + + // if clicked row wasn't expanded, collapse any previously-expanded row + if(collapsed && this.expandedNode && !rendering) { + put(this.expandedNode, ".collapsed"); + dijit.byNode(query(".expandoContent", this.expandedNode)[0]).collapse(); + } + + expandoContent.expand(); + + // toggle state of node which was clicked + put(node, (collapsed ? "!" : ".") + "collapsed"); + + // if the row clicked was previously expanded, nothing is expanded now + this.expandedNode = collapsed ? node : null; + this.expandedId = collapsed ? (!rendering ? this.grid.row(node).id : device_uuid) : null; + + this._updateButtons(); + }, + + _onAddDevice: function(event) { + new settings({isNew: true}).show(); + }, + + _onEditDevices: function() { + this._editDevices(false); + }, + + _onEditDevicesFromTemplate: function() { + this._editDevices(true); + }, + + _editDevices: function(fromExisting) { + var device_uuids = []; + for(var rowid in this._selectedData) { + device_uuids.push(this._selectedData[rowid].device_uuid); + } + if(device_uuids.length == 0) { + return; + } + var result = function(data) { + var device_uuid; + if(data && data.length == 1) { + device_uuid = data[0].device_uuid; + } + new settings({isNew: true, isDuplicate: fromExisting && device_uuid, isSaveToDevices: true, device_uuid: device_uuid, device_uuid_array: device_uuids}).show(); + } + if(fromExisting) { + new gridDialog({ gridTemplateName: "citrix.sync-wui.DeviceGrid", finishFn: result, selectionMode: "single", title: nls.SELECT_DEVICE_TITLE }).show(); + } else { + result(); + } + }, + + _onRemove: function() { + var device_uuids = []; + for(var rowid in this._selectedData) { + device_uuids.push(this._selectedData[rowid].device_uuid); + } + if(device_uuids.length == 0) { + return; + } + XenClient.Utils.messageBox.showConfirmation(nls.REMOVE_DEVICE_WARNING, dojo.hitch(this, function() { + var wait = new XUtils.AsyncWait(dojo.hitch(this, function() { + this.grid.clearSelection(); + XUtils.publish("devices_removed", device_uuids); + })); + array.forEach(device_uuids, function(id) { + var success = wait.addCallback(); + socket.data_access("remove_device", {device_uuid: id, cascade: true}, success, success); + }, this); + wait.finish(); + }), { continueText: "REMOVE_ACTION" }); + }, + + _onAssignRepo: function() { + var device_uuids = []; + for(var rowid in this._selectedData) { + device_uuids.push(this._selectedData[rowid].device_uuid); + } + if(device_uuids.length == 0) { + return; + } + + var result = dojo.hitch(this, function(data) { + if(data.length == 0) { + return; + } + array.forEach(device_uuids, function(id) { + socket.data_access("modify_device_repo", {device_uuid: id, repo_uuid: data[0].repo_uuid}); + }, this); + }); + new gridDialog({ gridTemplateName: "citrix.sync-wui.RepoGrid", finishFn: result, selectionMode: "single", title: nls.SELECT_REPO_TITLE }).show(); + }, + + _onAssignServices: function() { + this._assignVMs(false); + }, + + _onAssignVMs: function() { + this._assignVMs(true); + }, + + _assignVMs: function(userVm) { + if(Object.keys(this._selectedData).length == 0) { + return; + } + var result = dojo.hitch(this, function(data) { + if(data.length == 0) { + return; + } + var wait = new XUtils.AsyncWait(function() { + XUtils.publish("vm_instances_assigned"); + }); + for(var device_uuid in this._selectedData) { + array.forEach(data, function(item) { + var success = wait.addCallback(); + socket.data_access("add_vm_instance", {device_uuid: device_uuid, vm_uuid: item.vm_uuid, vm_instance_name: item.vm_name}, success, + dojo.hitch(this, function(error) { + if(error.code == "20060") { + // need to readd vm instance instead of add + // first need to get existing instance id + socket.data_access("list_vm_instances", {vm_instance_name: "", device_uuid: device_uuid, vm_uuid: "", disk_uuid: "", removed: true}, function(vmInstances) { + array.some(vmInstances, function(instance, i) { + if(instance.vm_uuid == item.vm_uuid) { + socket.data_access("readd_vm_instance", {vm_instance_uuid: instance.vm_instance_uuid}, success, success); + return true; + } + }); + }, success); + } else if(success) { + success(); + } + }) + ); + }); + }; + wait.finish(); + }); + new gridDialog({ gridTemplateName: userVm ? "citrix.sync-wui.VMGrid" : "citrix.sync-wui.ServiceVMGrid", finishFn: result, selectionMode: "multiple", title: userVm ? nls.SELECT_VMS_TO_ASSIGN : nls.SELECT_SERVICES_TO_ASSIGN }).show(); + }, + + _onLockAllInstances: function() { + this._instanceAction(true, "lock"); + }, + + _onLockSelectedInstances: function() { + this._instanceAction(false, "lock"); + }, + + _onUnlockAllInstances: function() { + this._instanceAction(true, "unlock"); + }, + + _onunlockSelectedInstances: function() { + this._instanceAction(false, "unlock"); + }, + + _onRemoveAllInstances: function() { + this._instanceAction(true, "remove"); + }, + + _onRemoveSelectedInstances: function() { + this._instanceAction(false, "remove"); + }, + + _onRemoveAllServiceInstances: function() { + this._instanceAction(true, "remove", true); + }, + + _onRemoveSelectedServiceInstances: function() { + this._instanceAction(false, "remove", true); + }, + + _instanceAction: function(all, action, services) { + // do "action" on vm instances on selected devices. + // boolean all: true = all vm instances, false = instances of selected vms + // string action: "remove", "lock" or "unlock" + + if(Object.keys(this._selectedData).length == 0) { + return; + } + // result needs to know what VM instances we are actioning - an array of vm_instance_uuids + var result = dojo.hitch(this, function(data) { + if(data.length == 0) { + return; + } + var wait = new XUtils.AsyncWait(function() { + XUtils.publish("vm_instances_" + (action + (action.endsWith("e") ? "d" : "ed")), data); + }); + array.forEach(data, function(item) { + var waitCb = wait.addCallback(); + socket.data_access(action + "_vm_instance", {vm_instance_uuid: item}, waitCb, waitCb); + }); + wait.finish(); + }); + var vm_instance_uuids = []; + var vm_uuids = []; + var getInstanceIds = dojo.hitch(this, function() { + var wait = new XUtils.AsyncWait(function() { + result(vm_instance_uuids); + }); + for(var device_uuid in this._selectedData) { + var waitCb = wait.addCallback(); + socket.data_access("list_vm_insts_for_device_ui", + {device_uuid: device_uuid, config: ["vm:type", "vm:image-path"]}, + function(listData) { + array.forEach(listData, function(item) { + if((all || vm_uuids.contains(item.vm_uuid)) && ["svm", "pvm", null].contains(item.config_value_1)) { + vm_instance_uuids.push(item.vm_instance_uuid); + } + }); + waitCb(); + }, waitCb + ); + }; + wait.finish(); + }); + if(all) { + getInstanceIds(); + } else { + // need to select a vm whose instances we want to lock, and pass them to result + var vmResults = function(data) { + array.forEach(data, function(item) { + vm_uuids.push(item.vm_uuid); + }); + getInstanceIds(); + }; + new gridDialog({ gridTemplateName: "citrix.sync-wui.VMGrid", finishFn: vmResults, selectionMode: "multiple", title: nls.SELECT_TITLE_LOCK_VMS }).show(); + } + }, + + _bindDijit: function() { + this.inherited(arguments); + this._updateButtons(); + }, + + _bindDijitRetainPosition: function(args) { + this._retainPositionArgs = args; + this.inherited(arguments); + }, + + _bindDijitRetainPositionFinish: function() { + if(this.expandedId) { + var id = this.expandedId; + if(this.grid.row(id).data) { + this.grid.select(id); + } else if(this._retainPositionArgs) { + this.grid.select(id = this._retainPositionArgs.device_uuid); + } + this.expandedId = id; + var node = this.grid.row(id).element; + if(node) { + this._expandNode(node); + } + } + delete this._retainPositionArgs; + this._updateButtons(); + }, + + _updateButtons: function() { + this._setEnabled(this.actionButton, Object.keys(this._selectedData).length > 0); + }, + + onRoute: function(value) { + var popup = new settings({device_uuid: value}); + var handle = aspect.after(popup, "onHide", dojo.hitch(this, function() { + this.route(""); + handle.remove(); + })); + popup.show(); + }, + + _onDuplicateDevice: function(device_uuid) { + new settings({isNew: true, isDuplicate: true, device_uuid: device_uuid}).show(); + } +}); +}); diff --git a/widgets/citrix/sync-wui/DiskGrid.js b/widgets/citrix/sync-wui/DiskGrid.js new file mode 100644 index 0000000..124b625 --- /dev/null +++ b/widgets/citrix/sync-wui/DiskGrid.js @@ -0,0 +1,62 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!./nls/DiskWidget", + // Mixins + "citrix/common/_TopicMixin", + "citrix/sync-wui/GridTemplate" +], +function(dojo, declare, nls, _topicMixin, gridTemplate) { +return declare("citrix.sync-wui.DiskGrid", [gridTemplate, _topicMixin], { + + getGridProperties: function() { + return { + dataFunction: "list_disks_ui", + idProperty: "disk_uuid", + filterParam: "disk_name_contains", + + columns: { + disk_name: nls.NAME, + file_path: nls.PATH, + disk_type: { + label: nls.TYPE, + formatter: function(type) { + return (type == "v") ? "VHD" : (type == "i") ? "ISO" : type; + } + }, + read_only: { + label: nls.PERSISTENT, + formatter: function(readonly) { + return (readonly == "t") ? nls.FALSE : (readonly == "f") ? nls.TRUE : readonly; + } + } + }, + + dataParams: { + disk_name_contains: "" + } + }; + }, + + postCreate: function() { + this.inherited(arguments); + this.subscribe(XUtils.publishTopic, dojo.hitch(this, this._messageHandler)); + }, + + _messageHandler: function(message) { + switch(message.type) { + case "add_disk": + case "remove_disk": + this._bindDijitRetainPosition(); + break; + } + } +}); +}); diff --git a/widgets/citrix/sync-wui/DiskWidget.js b/widgets/citrix/sync-wui/DiskWidget.js new file mode 100644 index 0000000..92b6830 --- /dev/null +++ b/widgets/citrix/sync-wui/DiskWidget.js @@ -0,0 +1,88 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/string", + // Resources + "dojo/i18n!./nls/DiskWidget", + "dojo/text!./templates/DiskWidget.html", + // Mixins + "citrix/sync-wui/DiskGrid", + // Used in code + "citrix/sync-wui/AddDisk", + // Used in template + "citrix/sync-wui/Button" +], +function (dojo, declare, string, nls, template, diskGrid, addDisk) { +return declare("citrix.sync-wui.DiskWidget", diskGrid, { + + templateString: template, + popup: null, + + getGridProperties: function() { + var props = this.inherited(arguments); + + props.columns.shared = { + label: nls.SHARED, + formatter: function(shared) { + return (shared == "t") ? nls.TRUE : (shared == "f") ? nls.FALSE : shared; + } + }; + + props.columns.num_vms = nls.NUM_VMS; + + return props; + }, + + postMixInProperties: function() { + this.inherited(arguments); + dojo.mixin(this, nls); + }, + + postCreate: function() { + this.inherited(arguments); + this.grid.on(".dgrid-row:click", dojo.hitch(this, this._updateButtons)); + this._updateButtons(); + }, + + _onAddDisk: function(event) { + if (this.popup) { + this.popup.hide(); + } + this.popup = new addDisk(); + this.popup.show(); + }, + + _onRemoveDisk: function(event) { + var row = this.grid.row(Object.keys(this.grid.selection)[0]); + var disk_uuid = row.data.disk_uuid; + XenClient.Utils.messageBox.showConfirmation(nls.REMOVE_DISK_WARNING, dojo.hitch(this, function() { + socket.data_access("remove_disk", { disk_uuid: disk_uuid }, dojo.hitch(this, function(data) { + this._bindDijit(); + })); + }), { continueText: "REMOVE_ACTION" }); + }, + + _bindDijit: function() { + this.inherited(arguments); + this._updateButtons(); + }, + + _updateButtons: function(event) { + var selection = this.grid.selection; + if (event && Object.keys(this.grid.selection).length == 1) { + if (this.grid.row(event).data.num_vms == 0) { + this._setEnabled(".diskButton", true); + return; + } + } + + this._setEnabled(".diskButton", false); + } +}); +}); diff --git a/widgets/citrix/sync-wui/GridDialog.js b/widgets/citrix/sync-wui/GridDialog.js new file mode 100644 index 0000000..d312578 --- /dev/null +++ b/widgets/citrix/sync-wui/GridDialog.js @@ -0,0 +1,64 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/lang", + "dojo/_base/array", + // Resources + "dojo/i18n!citrix/sync-wui/nls/GridDialog", + "dojo/text!citrix/sync-wui/templates/GridDialog.html", + // Mixins + "citrix/common/Dialog" +], +function (dojo, declare, lang, array, nls, template, dialog) { +return declare("citrix.sync-wui.GridDialog", [dialog], { + + templateString: template, + canExecute: true, + destroyOnHide: true, + + finishFn: null, + gridTemplateName: "", + gridTemplateWidget: null, + + postMixInProperties: function() { + this.inherited(arguments); + dojo.mixin(this, nls); + }, + + postCreate: function () { + this.inherited(arguments); + + var object = lang.getObject(this.gridTemplateName); + if(object) { + this.gridTemplateWidget = new object(); + var grid = this.gridTemplateWidget.grid; + grid.selectionMode = this.selectionMode; + if(this.query) { + grid.query = this.query; + } + grid.deselectOnRefresh = false; + this.addChild(this.gridTemplateWidget); + this.gridTemplateWidget.startup(); + } + }, + + onExecute: function() { + if(this.finishFn) { + var data = []; + for(var row in this.gridTemplateWidget._selectedData) { + if(this.gridTemplateWidget._selectedData.hasOwnProperty(row)){ + data.push(this.gridTemplateWidget._selectedData[row]); + } + } + this.finishFn(data); + } + this.inherited(arguments); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/GridTemplate.js b/widgets/citrix/sync-wui/GridTemplate.js new file mode 100644 index 0000000..5fc9031 --- /dev/null +++ b/widgets/citrix/sync-wui/GridTemplate.js @@ -0,0 +1,136 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/array", + "dojo/aspect", + "dojo/string", + // Resources + "dojo/i18n!./nls/GridDialog", + "dojo/text!./templates/GridTemplate.html", + // Mixins + "dijit/_Widget", + "dijit/_Templated", + "dijit/_Container", + "citrix/common/_CitrixWidgetMixin", + // Used in code + "citrix/sync-wui/StoreGrid", + // Used in template + "citrix/sync-wui/Button", + "citrix/common/ValidationTextBox", + "citrix/common/Select", + "citrix/sync-wui/ComboButton" +], +function(dojo, declare, array, aspect, string, nls, template, _widget, _templated, _container, _citrixWidgetMixin, storeGrid) { +return declare("citrix.sync-wui.GridTemplate", [_widget, _templated, _container, _citrixWidgetMixin], { + + templateString: template, + widgetsInTemplate: true, + grid: null, + _selectedData: null, + + getGridProperties: function() { + return {}; + }, + + postMixInProperties: function() { + this.inherited(arguments); + dojo.mixin(this, nls); + }, + + postCreate: function() { + this.inherited(arguments); + this.grid = new storeGrid(this.getGridProperties()); + this.addChild(this.grid); + this.grid.deselectOnRefresh = false; + this._selectedData = {}; + this.grid.on("dgrid-select", dojo.hitch(this, function(event) { + array.forEach(event.rows, function(row) { + this._selectedData[row.id] = row.data; + }, this); + })); + this.grid.on("dgrid-deselect", dojo.hitch(this, function(event){ + array.forEach(event.rows, function(row) { + delete this._selectedData[row.id]; + }, this); + })); + this.grid.selectAll = dojo.hitch(this, this.selectAll); + aspect.before(this.grid, "clearSelection", dojo.hitch(this, this.clearSelection)); + }, + + resize: function() { + // for some reason grid.resize is needed before and after base resize (just 1 will result in incorrect settings), + // otherwise table header layout is screwed up + this.grid.resize(); + this.inherited(arguments); + this.grid.resize(); + }, + + startup: function() { + this.inherited(arguments); + this.grid.startup(); + }, + + _bindDijit: function(retainPosition, onFinish) { + this.grid._bindDijit(retainPosition, onFinish); + }, + + _bindDijitRetainPosition: function() { + this._bindDijit(true, dojo.hitch(this, this._bindDijitRetainPositionFinish)); + }, + + _bindDijitRetainPositionFinish: function() { + + }, + + _onSearch: function(event) { + this.grid.set("filterParam", string.trim(this.filterNode.get("value"))); + this.grid.query = this.query; + }, + + _onClear: function(event) { + this.grid.set("filterParam", ""); + this.grid.query = this.query; + this.filterNode.set("value", ""); + }, + + _onShowSelected: function(event) { + this.grid.set("filterParam", ""); + this.grid.query = dojo.hitch(this, function(item, index, items) { + return this._selectedData[item[this.grid.idProperty]]; + }); + }, + + _onSelectNone: function(event) { + this.grid.clearSelection(); + }, + + _onSelectAll: function(event) { + this.grid.selectAll(); + }, + + clearSelection: function(exceptId, dontResetLastSelected){ + for(var id in this._selectedData) { + if(this._selectedData.hasOwnProperty(id) && exceptId !== id) { + delete this._selectedData[id]; + } + } + }, + selectAll: function(){ + // this is so that only those rows that are part of the current filter are selected, and when the filter changes + // the grid doesn't continue to select all + this.grid.allSelected = false; + + var data = this.grid.store.query(this.grid.query); + array.forEach(data, function(item) { + this.grid.select(item[this.grid.idProperty]); + this._selectedData[item[this.grid.idProperty]] = item; + }, this); + } +}); +}); diff --git a/widgets/citrix/sync-wui/ImageButton.js b/widgets/citrix/sync-wui/ImageButton.js new file mode 100644 index 0000000..dbbaf33 --- /dev/null +++ b/widgets/citrix/sync-wui/ImageButton.js @@ -0,0 +1,22 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/text!citrix/sync-wui/templates/ImageButton.html", + // Mixins + "citrix/sync-wui/Button" +], +function(dojo, declare, template, button) { +return declare("citrix.sync-wui.ImageButton", [button], { + + templateString: template + + +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/ImageSelect.js b/widgets/citrix/sync-wui/ImageSelect.js new file mode 100644 index 0000000..2e31ed6 --- /dev/null +++ b/widgets/citrix/sync-wui/ImageSelect.js @@ -0,0 +1,61 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/array", + // Mixins + "citrix/common/ImageSelect" +], +function(dojo, declare, array, imageSelect) { +return declare("citrix.sync-wui.ImageSelect", [imageSelect], { + + setSelected: function(image) { + var value; + array.some(Object.keys(this.source), function(key) { + if(image == this.source[key]) { + value = key; + return true; + } + return false; + }, this); + this._handleOnChange(value); + this.value = value; + this._updateChildren(); + }, + + _updateChildren: function() { + if (this.source != null) { + var keys = Object.keys(this.source); + var baseClass = (this.itemClassSmall != "" && keys.length > this.classThreshold) ? this.itemClassSmall : this.itemClass; + array.forEach(keys, function(srcItem) { + if (!array.some(this.getChildren(), function(item) { return this.source[srcItem] == item.image; }, this)) { + // image doesn't exist in the children already, so add + var imageItem = new this.imageWidget({image: this.source[srcItem], baseClass: baseClass}); + this.addChild(imageItem); + } + }, this); + } + array.forEach(this.getChildren(), function(item) { + item.set("selected", (item.image == this.source[this.value])); + item.set("disabled", !this.editing); + }, this); + }, + + findChild: function() { + // want to return the child matching the current value + var result = null; + dojo.some(this.getChildren(), function(child){ + if(child[this.valueProperty] == this.source[this.value]) { + result = child; + return true; + } + }, this); + return result; + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/MainTabButton.js b/widgets/citrix/sync-wui/MainTabButton.js new file mode 100644 index 0000000..7ede582 --- /dev/null +++ b/widgets/citrix/sync-wui/MainTabButton.js @@ -0,0 +1,21 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/text!citrix/sync-wui/templates/MainTabButton.html", + // Mixins + "dijit/layout/TabController" +], +function(dojo, declare, template, tabController) { +return declare("citrix.sync-wui.MainTabButton", tabController.TabButton, { + + templateString: template + //baseClass: "citrixMainTab" +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/MainTabContainer.js b/widgets/citrix/sync-wui/MainTabContainer.js new file mode 100644 index 0000000..b10f88a --- /dev/null +++ b/widgets/citrix/sync-wui/MainTabContainer.js @@ -0,0 +1,24 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/text!citrix/sync-wui/templates/MainTabContainer.html", + // Mixins + "citrix/common/RouterTabContainer", + // Required in code + "citrix/sync-wui/MainTabController" +], +function(dojo, declare, template, tabContainer) { +return declare("citrix.sync-wui.MainTabContainer", tabContainer, { + + templateString: template, + controllerWidget: "citrix.sync-wui.MainTabController" + +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/MainTabController.js b/widgets/citrix/sync-wui/MainTabController.js new file mode 100644 index 0000000..6928dcc --- /dev/null +++ b/widgets/citrix/sync-wui/MainTabController.js @@ -0,0 +1,20 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Mixins + "dijit/layout/TabController", + // Required in code + "citrix/sync-wui/MainTabButton" +], +function(dojo, declare, tabController, tabButton) { +return declare("citrix.sync-wui.MainTabController", tabController, { + + buttonWidget: tabButton +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/RepoGrid.js b/widgets/citrix/sync-wui/RepoGrid.js new file mode 100644 index 0000000..a4d7cc4 --- /dev/null +++ b/widgets/citrix/sync-wui/RepoGrid.js @@ -0,0 +1,45 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!./nls/RepoGrid", + // Mixins + "citrix/sync-wui/GridTemplate" +], +function(dojo, declare, nls, gridTemplate) { +return declare("citrix.sync-wui.RepoGrid", [gridTemplate], { + + getGridProperties: function() { + return { + dataFunction: "list_repos", + idProperty: "repo_uuid", + + columns: { + release: nls.RELEASE, + build: nls.BUILD, + file_path: nls.FILE_PATH + }, + + dataParams: { + release: "", + build: "", + file_path: "", + file_hash: "", + unused: false + } + }; + }, + + postCreate: function() { + this.inherited(arguments); + this._setDisplay(".gridHeader", false); + this._setClass(".gridBody", "fullHeight"); + } +}); +}); diff --git a/widgets/citrix/sync-wui/ServiceVMGrid.js b/widgets/citrix/sync-wui/ServiceVMGrid.js new file mode 100644 index 0000000..70cf9c0 --- /dev/null +++ b/widgets/citrix/sync-wui/ServiceVMGrid.js @@ -0,0 +1,28 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!./nls/VMWidget", + // Mixins + "citrix/sync-wui/VMGrid" +], +function(dojo, declare, nls, vmGrid) { +return declare("citrix.sync-wui.ServiceVMGrid", [vmGrid], { + + getGridProperties: function() { + var props = this.inherited(arguments); + props.columns.vm_name = nls.SERVICE_NAME; + return props; + }, + + query: function(item, index, items) { + return !["svm", "pvm", null].contains(item.config_value_1); + } +}); +}); diff --git a/widgets/citrix/sync-wui/ServiceVMWidget.js b/widgets/citrix/sync-wui/ServiceVMWidget.js new file mode 100644 index 0000000..02543ea --- /dev/null +++ b/widgets/citrix/sync-wui/ServiceVMWidget.js @@ -0,0 +1,25 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Mixins + "citrix/sync-wui/VMWidget", + "citrix/sync-wui/ServiceVMGrid" +], +function (dojo, declare, vmWidget, serviceVmGrid) { +return declare("citrix.sync-wui.ServiceVMWidget", [vmWidget, serviceVmGrid], { + + routerBase: "servicevms", + vm_type: "", + + postCreate: function() { + this.inherited(arguments); + this.addVMButton.set("label", this.ADD_SERVICE); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/Settings.js b/widgets/citrix/sync-wui/Settings.js new file mode 100755 index 0000000..1f5d836 --- /dev/null +++ b/widgets/citrix/sync-wui/Settings.js @@ -0,0 +1,252 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/array", + "dojo/_base/lang", + "dojo/query", + "dojo/NodeList-traverse", + "dojo/string", + // Resources + "dojo/i18n!citrix/sync-wui/nls/Settings", + "dojo/text!citrix/sync-wui/templates/Settings.html", + // Mixins + "citrix/common/ValidationDialog", + "citrix/common/_BoundContainerMixin", + "citrix/common/_CitrixTooltipMixin", + "citrix/sync-wui/_ConfigMixin", + "citrix/sync-wui/_ModelMixin", + // Used in code + "citrix/sync-wui/GridDialog", + // Required in template + "citrix/common/TabContainer", + "citrix/common/ContentPane", + "citrix/sync-wui/ImageSelect", + "citrix/sync-wui/Button", + "citrix/common/ValidationTextBox", + "citrix/common/Label", + "citrix/common/Select", + "citrix/sync-wui/BooleanSelect", + "citrix/common/Repeater", + "citrix/common/BoundWidget", + "citrix/common/EditableWidget" +], +function(dojo, declare, array, lang, query, traverse, string, nls, template, dialog, _boundContainerMixin, _citrixTooltipMixin, _configMixin, _modelMixin, gridDialog) { +return declare("citrix.sync-wui.Settings", [dialog, _boundContainerMixin, _citrixTooltipMixin, _configMixin, _modelMixin], { + + templateString: template, + widgetsInTemplate: true, + destroyOnHide: true, + + isNew: false, // by default, we're modifying existing + isDuplicate: false, + isSaveToDevices: false, + + constructor: function(args) { + this.isNew = args.isNew ? true : false; + this.isDuplicate = args.isDuplicate ? true : false; + this.isSaveToDevices = args.isSaveToDevices ? true : false; + this.device_uuid = args.device_uuid; + this.device_uuid_array = args.device_uuid_array; + this._setDataFunctions(); + }, + + _setDataFunctions: function() { + if(!this.isNew) { + // config modification + this.configDataFunction = "get_device_config", + this.configSaveFunction = "modify_device_config", + this.configDataParams = {device_uuid: this.device_uuid}; + this.configSaveParams = { + device_uuid: this.device_uuid, + config: [], + replace: false + }; + // model modification + this.dataFunction = "get_device"; + this.dataParams = {device_uuid: this.device_uuid}; + this.saveFunction = "modify_device_repo"; + this.saveParams = { + device_uuid: this.device_uuid, + repo_uuid: "" + }; + } else { + // model and config creation + this.configDataFunction = this.isDuplicate ? "get_device_config" : "", + this.configSaveFunction = this.isSaveToDevices ? "modify_device_config" : "", + this.configDataParams = this.isDuplicate ? {device_uuid: this.device_uuid} : {}; + this.configSaveParams = this.isSaveToDevices ? {device_uuid: null, config: [], replace: false} : {}; + this.dataFunction = this.isDuplicate ? "get_device" : ""; + this.dataParams = this.isDuplicate ? {device_uuid: this.device_uuid} : {}; + this.saveFunction = this.isSaveToDevices ? "" : "add_device"; + this.saveParams = this.isSaveToDevices ? {} : {device_name: "", repo_uuid: "", config: []}; + } + }, + + postMixInProperties: function() { + dojo.mixin(this, nls); + this.inherited(arguments); + }, + + postCreate: function() { + this.inherited(arguments); + this.startup(); + + if(this.isNew) { + this.nameNode.edit(); + // this creates a set of default bindings which are not undefined, as that causes issues when controlling the save button + if(!this.isDuplicate) { + dojo.mixin(this, this.unbind(this.tabContainer.containerNode)); + } + this._setCustomsArray(); + this._bindDijit(); + } + + if(this.isSaveToDevices) { + this.tabContainer.removeChild(this.detailsTab); + } + + this._updateRepoSelect(); + + this.connect(this.tabContainer.tablist, "onSelectChild", "_onTabChange"); + this.connect(this.wallpaperSelect, "setSelected", "_onWallpaperSelect"); + }, + + save: function(toDevices) { + var values = this.unbind(this.tabContainer.containerNode); + + if(values.config._customs) { + array.forEach(values.config._customs, function(item, i) { + lang.setObject(item.id, item.value, values.config); + }, this); + } + delete values.config._customs; // important otherwise you'll get config lines with daemon = "_custom" + + if(this.isNew && !this.isSaveToDevices) { + this.saveParams.device_name = values.model.device_name; + this.saveParams.repo_uuid = values.model.repo_uuid; + this.saveParams.config = this._formatConfigObjectsIntoStringList(values.config); + var finishSave = dojo.hitch(this, function(data) { + this.isNew = false; + this.isDuplicate = false; + this.device_uuid = data; + this._setDataFunctions(); + this.nameNode.cancel(); + this.getConfigData(); + }); + this.saveData(finishSave); + } else if(this.isSaveToDevices) { + var saveTo = dojo.hitch(this, function() { + array.forEach(this.device_uuid_array, function(item) { + this.configSaveParams.device_uuid = item; + this.saveConfigData(values.config); + }, this); + this.detailsTab.destroyRecursive(); + this.onCancel(); + }); + XenClient.Utils.messageBox.showConfirmation(nls.REPLACE_CONFIG_MESSAGE, saveTo, { continueText: "CONTINUE_ACTION" }); + } else { + this.saveParams.device_uuid = this.device_uuid; + this.saveParams.repo_uuid = values.model.repo_uuid; + this.saveData(); + this.saveConfigData(values.config); + } + this._setEnabled(this.saveButton, false); + }, + + _onWallpaperSelect: function() { + this.wallpaperText.set("value", this.wallpaperSelect.get("value")); + }, + + _onWallpaperTextChange: function(newVal) { + if(this.wallpaperSelect.get("value") != newVal) { + this.wallpaperSelect.set("value", newVal); + } + }, + + _onTabChange: function(tab) { + this._setDisplay(this.saveButton, tab.saveable); + }, + + _bindDijit: function() { + if(this._destroyed) { + return; + } + this.wallpaperSelect.set("source", XenClient.Utils.Wallpapers); + this._enableAddConfigButton(); + + this._updateRepoSelect(); + + this.bind({model: this.model, config: this.config}, this.tabContainer.containerNode); + this.set("title", this.model.device_name); + + if(this.isDuplicate) { + this._setEnabled(this.saveButton, true); + } + this._setDisplay(this.device_uuidNode, !this.isDuplicate); + + this.inherited(arguments); + }, + + _updateRepoSelect: function() { + var options = []; + socket.data_access("list_repos", {release: "", build: "", file_path: "", file_hash: "", unused: false}, dojo.hitch(this, function(data) { + options = array.map(data, function(item, i) { + return {label: "Release: " + item.release + "; Build: " + item.build, value: item.repo_uuid}; + }); + options.unshift({label: this.NONE_SET, value: ""}); + this.repoNode.set("options", options); + this.repoNode.set("value", this.model.repo_uuid); + })); + }, + + _enableAddConfigButton: function() { + this._setEnabled(this.ConfigAddButton, !(this.newConfigDaemon.get("value") == "" || this.newConfigProperty.get("value") == "" || this.newConfigValue.get("value") == "")); + }, + + onAddConfig: function(event) { + var daemon = string.trim(this.newConfigDaemon.get("value")); + var property = string.trim(this.newConfigProperty.get("value")); + var value = string.trim(this.newConfigValue.get("value")); + if(daemon != "" && property != "" && value != "") { + lang.setObject("config." + daemon + "." + property, value, this); + this._setCustomsArray(); + this.customRepeater.set("value", this.config._customs); + this._setEnabled(this.saveButton, true); + this.newConfigDaemon.set("value", ""); + this.newConfigProperty.set("value", ""); + this.newConfigValue.set("value", ""); + this._enableAddConfigButton(); + } + }, + + onDeleteConfig: function(event) { + var templateNode = query.NodeList(event.target).parents("tr").first()[0]; + var daemon = templateNode.getAttribute("daemon"); + var property = templateNode.getAttribute("property"); + var o = lang.getObject("config." + daemon + "." + property, false, this); + if(o !== undefined) { + this.config[daemon][property] = ""; + this._setCustomsArray(); + this.customRepeater.set("value", this.config._customs); + this._setEnabled(this.saveButton, true); + } + }, + + onCustomConfigKeyUp: function(event) { + this._enableAddConfigButton(); + }, + + onSaveToDevices: function(event) { + if (!this.validate()) { + return; + } + + } +}); +}); diff --git a/widgets/citrix/sync-wui/StoreGrid.js b/widgets/citrix/sync-wui/StoreGrid.js new file mode 100644 index 0000000..acd1474 --- /dev/null +++ b/widgets/citrix/sync-wui/StoreGrid.js @@ -0,0 +1,18 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Mixins + "citrix/common/Grid", + "citrix/sync-wui/_StoreMixin" +], +function (dojo, declare, grid, _storeMixin) { +return declare("citrix.sync-wui.StoreGrid", [grid, _storeMixin], { + +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/UnseenDeviceWidget.js b/widgets/citrix/sync-wui/UnseenDeviceWidget.js new file mode 100644 index 0000000..d331a20 --- /dev/null +++ b/widgets/citrix/sync-wui/UnseenDeviceWidget.js @@ -0,0 +1,80 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/date/locale", + // Resources + "dojo/i18n!./nls/UnseenDeviceWidget", + // Mixins + "citrix/common/_TopicMixin", + "citrix/sync-wui/StoreGrid" +], +function (dojo, declare, localeDate, nls, _topicMixin, grid) { +return declare("citrix.sync-wui.UnseenDeviceWidget", [grid, _topicMixin], { + + days: 3, + dataFunction: "list_devices_ui", + idProperty: "composite_key", + + constructor: function(args) { + this.days = args.days || this.days; + + this.dataParams = { + device_name_contains: null + }; + + this.columns = { + device_name: nls.HEADING.format(this.days), + user_name: nls.USER_NAME, + report_time: { + label: nls.REPORT_TIME, + formatter: function(value) { + if(value != null) { + value = localeDate.format(new Date(value)); + } + return value; + } + } + }; + + var milliseconds = 1000*60*60*24*this.days; + this.query = function(item, index, items) { + if (item.report_time) { + var seen = new Date(item.report_time) + var now = new Date(); + var diff = now - seen; + if (diff > milliseconds) { + return true; + } + } + return false; + } + }, + + postCreate: function() { + this.inherited(arguments); + this.subscribe(XUtils.publishTopic, dojo.hitch(this, this._messageHandler)); + }, + + _setStoreData: function(data) { + dojo.forEach(data, function(item, i) { + item.composite_key = item.device_uuid + (item.user_uuid ? "|" + item.user_uuid : ""); + }); + this.store.data = data; + }, + + _messageHandler: function(message) { + switch(message.type) { + case "add_device": + case "devices_removed": + this._bindDijit(true); + break; + } + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/VMDetails.js b/widgets/citrix/sync-wui/VMDetails.js new file mode 100644 index 0000000..f4368b6 --- /dev/null +++ b/widgets/citrix/sync-wui/VMDetails.js @@ -0,0 +1,407 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/array", + "dojo/_base/lang", + "dojo/query", + "dojo/NodeList-traverse", + "dojo/string", + "dojo/router", + // Resources + "dojo/i18n!./nls/VMDetails", + "dojo/text!./templates/VMDetails.html", + // Mixins + "citrix/common/ValidationDialog", + "citrix/common/_BoundContainerMixin", + "citrix/common/_EditableMixin", + "citrix/common/_CitrixTooltipMixin", + "citrix/sync-wui/_ConfigMixin", + "citrix/sync-wui/_ModelMixin", + // Used in code + "citrix/sync-wui/GridDialog", + // Required in template + "citrix/common/TabContainer", + "citrix/common/ContentPane", + "citrix/sync-wui/ImageSelect", + "citrix/sync-wui/Button", + "citrix/common/NumberTextBox", + "citrix/common/ValidationTextBox", + "citrix/common/ValidationTextarea", + "citrix/common/Label", + "citrix/common/Select", + "citrix/sync-wui/BooleanSelect", + "citrix/common/Repeater", + "citrix/common/BoundWidget", + "citrix/common/EditableWidget", + "citrix/common/asmSelect" +], +function(dojo, declare, array, lang, query, traverse, string, router, nls, template, dialog, _boundContainerMixin, _editableMixin, _citrixTooltipMixin, _configMixin, _modelMixin, gridDialog) { +return declare("citrix.sync-wui.VMDetails", [dialog, _boundContainerMixin, _editableMixin, _citrixTooltipMixin, _configMixin, _modelMixin], { + + templateString: template, + widgetsInTemplate: true, + destroyOnHide: true, + isNew: false, + isDuplicate: false, + + constructor: function(args) { + this.isNew = args.isNew; + this.isDuplicate = args.isDuplicate; + this.vm_uuid = args.vm_uuid; + this.vm_type = args.vm_type || ""; + this._setDataFunctions(); + }, + + postMixInProperties: function() { + this.inherited(arguments); + dojo.mixin(this, nls); + }, + + postCreate: function() { + this.inherited(arguments); + this.startup(); + + if (this.isNew || this.isDuplicate) { + var options = dojo.map(new Array(9), function(key, index) { + index += 1; + var label = this.SWITCHER_KEY_MASK.format(index); + return { label: label, value: index }; + }, this); + options.unshift({ label: this.NOT_INCLUDED, value: '' }); + + this.slotSelect.set("options", options); + this.connect(this.image_path, "setSelected", "_onVmIconSelect"); + + this.edit(); + } + + if (this.isNew) { + // this creates a set of default bindings which are not undefined, as that causes issues when controlling the save button + var bindings = this.unbind(this.tabContainer.containerNode); + bindings.config.vm.type = this.vm_type; + + if (this._isUserVM()) { + bindings.config.vm.cd = "xc-tools.iso"; + bindings.config.vm.boot = ["d", "c"]; + } + + dojo.mixin(this, bindings); + this._setCustomsArray(); + this.model.disks = []; + this._bindDijit(); + } + }, + + getDisks: function() { + this.model.disks = [] + + if(this.vm_uuid) { + socket.data_access("list_disks", { + disk_name: "", + disk_type: "", + file_path: "", + file_hash: "", + vm_uuid: this.vm_uuid, + vm_instance_uuid: "", + unused: false + }, dojo.hitch(this, function(data) { + if(data) { + this.model.disks = data; + } + this._bindDijit(); + })); + } + }, + + getDataFinishFn: function() { + if (this.isDuplicate) { + this.model.vm_uuid = ''; + this.saveButton.set("disabled", false); + } + if (this.config.vm && this.config.vm.boot) { + this.config.vm.boot = this.config.vm.boot.split(""); + } + this.getDisks(); + }, + + edit: function() { + this.inherited(arguments); + this._descendantAction("edit"); + this._updateButtons(); + }, + + cancel: function() { + this.inherited(arguments); + this._updateButtons(); + this._descendantAction("cancel"); + }, + + save: function() { + this.inherited(arguments); + var values = this.unbind(this.tabContainer.containerNode); + + if (values.config._customs) { + array.forEach(values.config._customs, function(item, i) { + lang.setObject(item.id, item.value, values.config); + }, this); + } + delete values.config._customs; // important otherwise you'll get config lines with daemon = "_custom" + + array.forEach(this.networkArray, function(item, i) { + lang.setObject(item.id, item.value, values.config); + }, this); + + this.saveParams.vm_name = values.model.vm_name; + + dojo.forEach(values.model.disks, function(disk) { + this.saveParams.disk_uuids.push(disk.disk_uuid); + }, this); + + this.saveParams.config = this._formatConfigObjectsIntoStringList(values.config); + var finish = dojo.hitch(this, function(data) { + this.isNew = false; + this.isDuplicate = false; + this.vm_uuid = data; + this._setDataFunctions(); + this.getConfigData(); + this.cancel(); + }); + this.saveData(finish); + }, + + onAddConfig: function(event) { + var daemon = string.trim(this.newConfigDaemon.get("value")); + var property = string.trim(this.newConfigProperty.get("value")); + var value = string.trim(this.newConfigValue.get("value")); + if(daemon != "" && property != "" && value != "") { + lang.setObject("config." + daemon + "." + property, value, this); + this._setCustomsArray(); + this.customRepeater.set("value", this.config._customs); + this._setEnabled(this.saveButton, true); + this.newConfigDaemon.set("value", ""); + this.newConfigProperty.set("value", ""); + this.newConfigValue.set("value", ""); + this._enableAddConfigButton(); + } + }, + + onDeleteConfig: function(event) { + var templateNode = query.NodeList(event.target).parents("tr").first()[0]; + var daemon = templateNode.getAttribute("daemon"); + var property = templateNode.getAttribute("property"); + var o = lang.getObject("config." + daemon + "." + property, false, this); + if(o !== undefined) { + this.config[daemon][property] = ""; + this._setCustomsArray(); + this.customRepeater.set("value", this.config._customs); + this._setEnabled(this.saveButton, true); + } + }, + + onCustomConfigKeyUp: function(event) { + this._enableAddConfigButton(); + }, + + _setNetworkArray: function() { + this.networkArray = []; + array.forEach(Object.keys(this.config), function(daemon) { + if(!daemon.startsWith("nic/")) { + return; + } + array.forEach(Object.keys(this.config[daemon]), function(property) { + this.networkArray.push({ + id: daemon + "." + property, + daemon: daemon, + property: property, + value: this.config[daemon][property], + className: this.config[daemon][property] == "" ? "removedConfig" : "" + }); + }, this); + }, this); + }, + + onAddNetwork: function(event) { + var daemon = "nic/" + string.trim(this.newNetwork.get("value")); + var property = string.trim(this.newNetworkProperty.get("value")); + var bool = ["wireless-driver", "enabled"].contains(property); + var value = ["wireless-driver", "enabled"].contains(property) ? string.trim(this.newBoolNetworkValue.get("value")) : string.trim(this.newNetworkValue.get("value")); + if(daemon != "" && property != "" && value != "") { + lang.setObject("config." + daemon + "." + property, value, this); + this._setNetworkArray(); + this.networkRepeater.set("value", this.networkArray); + this._setEnabled(this.saveButton, true); + this.newNetwork.set("value", ""); + this.newNetworkProperty.set("value", ""); + this.newNetworkValue.set("value", ""); + this.newBoolNetworkValue.set("value", ""); + this._enableAddNetworkButton(); + } + }, + + onDeleteNetwork: function(event) { + var templateNode = query.NodeList(event.target).parents("tr").first()[0]; + var daemon = templateNode.getAttribute("daemon"); + var property = templateNode.getAttribute("property"); + var o = lang.getObject("config." + daemon + "." + property, false, this); + if(o !== undefined) { + this.config[daemon][property] = ""; + this._setNetworkArray(); + this.networkRepeater.set("value", this.networkArray); + this._setEnabled(this.saveButton, true); + } + }, + + onNetworkKeyUp: function(event) { + this._enableAddNetworkButton(); + }, + + onNewNetworkPropertyChange: function(newVal) { + var bool = ["wireless-driver", "enabled"].contains(newVal); + this._setDisplay(this.newNetworkValue, !bool); + this._setDisplay(this.newBoolNetworkValue, bool); + this.newNetworkValue.set("value", ""); + this.newBoolNetworkValue.set("value", ""); + this._enableAddNetworkButton(); + }, + + onDiskAdd: function() { + var result = dojo.hitch(this, function(data) { + if(data.length > 0) { + dojo.forEach(data, function(disk) { + var exists = false; + dojo.some(this.model.disks, function(currentDisk) { + if (currentDisk.disk_uuid == disk.disk_uuid) { + exists = true; + return true; + } + return false; + }, this); + + if (!exists) { + this.model.disks.unshift(disk); + } + }, this); + this.diskRepeater.set("value", this.model.disks); + } + }); + new gridDialog({ gridTemplateName: "citrix.sync-wui.DiskGrid", finishFn: result, selectionMode: "multiple", title: nls.SELECT_DISK_TITLE }).show(); + }, + + onDiskDelete: function(event) { + var disk_uuid = this._getDeviceID(event.target); + dojo.some(this.model.disks, function(disk, i) { + if (disk.disk_uuid == disk_uuid) { + this.model.disks.splice(i, 1); + this.diskRepeater.set("value", this.model.disks); + return true; + } + return false; + }, this); + }, + + _getDeviceID: function(node) { + return new dojo.NodeList(node).parents("tr").first()[0].getAttribute("deviceId"); + }, + + _enableAddConfigButton: function() { + this._setEnabled(this.ConfigAddButton, !(this.newConfigDaemon.get("value") == "" || this.newConfigProperty.get("value") == "" || this.newConfigValue.get("value") == "")); + }, + + _enableAddNetworkButton: function() { + this._setEnabled(this.NetworkAddButton, !(this.newNetwork.get("value") == "" || this.newNetworkProperty.get("value") == "" || (this.newNetworkValue.get("value") == "" && this.newBoolNetworkValue.get("value") == ""))); + }, + + _descendantAction: function(action) { + dojo.forEach(this.getDescendants(), function(widget){ + if(widget[action] && typeof(widget[action]) == "function") { + widget[action](); + } + }); + }, + + _updateButtons: function() { + this._setDisplay(this.saveButton, this.isNew || this.isDuplicate); + this._setDisplay(".diskAction", this.isNew || this.isDuplicate); + this._setDisplay(".customAction", this.isNew || this.isDuplicate); + }, + + _setDataFunctions: function() { + // config modification + this.configDataFunction = "", + this.configSaveFunction = "", + this.configDataParams = {}; + this.configSaveParams = {}; + // model modification + this.dataFunction = ""; + this.dataParams = {}; + this.saveFunction = ""; + this.saveParams = {}; + + if (this.isNew || this.isDuplicate) { + this.saveFunction = "add_vm"; + this.saveParams.disk_uuids = []; + } + + if (this.isNew) { + this.saveParams.vm_name = ""; + this.saveParams.config = []; + } else { + this.configDataFunction = "get_vm_config", + this.configDataParams = { vm_uuid: this.vm_uuid }; + this.dataFunction = "get_vm"; + this.dataParams = { vm_uuid: this.vm_uuid }; + } + }, + + _onVmIconSelect: function() { + this.vmIconText.set("value", this.image_path.get("value")); + }, + + _onVmIconTextChange: function(newVal) { + if(this.image_path.get("value") != newVal) { + this.image_path.set("value", newVal); + } + }, + + _isUserVM: function() { + if (["svm", "pvm", null].contains(this.vm_type)) { + return true; + } + if (this.config.vm && (!this.config.vm.type || (this.config.vm.type == "svm" || this.config.vm.type == "pvm"))) { + return true; + } + return false; + }, + + _bindDijit: function() { + this.image_path.set("source", XenClient.Utils.VmIcons); + this._enableAddConfigButton(); + this._enableAddNetworkButton(); + this.onNewNetworkPropertyChange(); + this.bind({model: this.model, config: this.config}, this.tabContainer.containerNode); + this._setNetworkArray(); + this.networkRepeater.set("value", this.networkArray); + + var hasImage = false; + if (this.config.vm && this.config.vm["image-path"]) { + var path = this.config.vm["image-path"]; + if (XenClient.Utils.VmIcons[path]) { + this.imageNode.domNode.src = XenClient.Utils.VmIcons[path]; + hasImage = true; + } + this.image_path.set("value", path); + } + this._setVisible(this.imageNode, hasImage); + this.set("title", this.model.vm_name); + this._updateButtons(); + this._setDisplay(".userVmOnly", this._isUserVM()); + this.inherited(arguments); + } +}); +}); diff --git a/widgets/citrix/sync-wui/VMGrid.js b/widgets/citrix/sync-wui/VMGrid.js new file mode 100644 index 0000000..7c658f4 --- /dev/null +++ b/widgets/citrix/sync-wui/VMGrid.js @@ -0,0 +1,73 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Resources + "dojo/i18n!./nls/VMWidget", + // Mixins + "citrix/common/_TopicMixin", + "citrix/sync-wui/GridTemplate" +], +function(dojo, declare, nls, _topicMixin, gridTemplate) { +return declare("citrix.sync-wui.VMGrid", [gridTemplate, _topicMixin], { + + getGridProperties: function() { + return { + allowSelectAll: true, + selectionMode: "multiple", + dataFunction: "list_vms_ui", + idProperty: "vm_uuid", + filterParam: "vm_name_contains", + + columns: { + icon: { + label: " ", + field: "config_value_2", + className: "field-icon", + formatter: function(path) { + if (path) { + if (XenClient.Utils.VmIcons[path]) { + return ""; + } + } + return ""; + } + }, + vm_name: nls.NAME + }, + + dataParams: { + vm_name_contains: null, + config: ["vm:type", "vm:image-path"] + } + }; + }, + + postCreate: function() { + this.inherited(arguments); + this.grid.query = this.query; + this.subscribe(XUtils.publishTopic, dojo.hitch(this, this._messageHandler)); + }, + + query: function(item, index, items) { + return ["svm", "pvm", null].contains(item.config_value_1); + }, + + _messageHandler: function(message) { + switch(message.type) { + case "vms_removed": + case "add_vm": + case "devices_removed": + case "vm_instances_removed": + case "vm_instances_assigned": + this._bindDijitRetainPosition(); + break; + } + } +}); +}); diff --git a/widgets/citrix/sync-wui/VMItem.js b/widgets/citrix/sync-wui/VMItem.js new file mode 100644 index 0000000..0a2038f --- /dev/null +++ b/widgets/citrix/sync-wui/VMItem.js @@ -0,0 +1,42 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/dom-class", + // Resources + "dojo/text!citrix/sync-wui/templates/VMItem.html", + // Mixins + "citrix/common/ImageItem" +], +function(dojo, declare, domclass, template, imageItem) { +return declare("citrix.sync-wui.VMItem", [imageItem], { + + templateString: template, + vm_instance_name: "", + vm_instance_uuid: "", + + _setVm_instance_nameAttr: function(value) { + this.nameNode.innerHTML = value; + }, + + _setImageAttr: function(value) { + if(value && value !== "") { + this.imageNode.src = value; + this.image = value; + this.imageNode.style.visibility = "visible"; + } else { + this.imageNode.style.visibility = "hidden"; + } + }, + + _setLockedAttr: function(value) { + this.locked = value == true ? true : false; + domclass.toggle(this.containerNode, "locked", this.locked); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/VMSelect.js b/widgets/citrix/sync-wui/VMSelect.js new file mode 100644 index 0000000..632145c --- /dev/null +++ b/widgets/citrix/sync-wui/VMSelect.js @@ -0,0 +1,188 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/array", + // Resources + "dojo/i18n!./nls/DeviceWidget", + "dojo/text!./templates/VMSelect.html", + // Mixins + "dijit/_Contained", + "citrix/common/_TopicMixin", + "citrix/common/ImageSelect", + "citrix/sync-wui/_StoreMixin", + "citrix/common/_CitrixWidgetMixin", + // Used in code + "citrix/sync-wui/VMItem", + "citrix/sync-wui/GridDialog", + // Used in template + "citrix/sync-wui/Button" +], +function(dojo, declare, array, nls, template, _contained, _topicMixin, imageSelect, _storeMixin, _citrixWidgetMixin, vmItem, gridDialog) { +return declare("citrix.sync-wui.VMSelect", [_contained, imageSelect, _storeMixin, _citrixWidgetMixin, _topicMixin], { + + templateString: template, + widgetsInTemplate: true, + + imageWidget: vmItem, + valueProperty: "vm_instance_uuid", + + userVMs: true, + device_uuid: "", + dataFunction: "list_vm_insts_for_device_ui", + idProperty: "vm_instance_uuid", + + constructor: function(args) { + this.device_uuid = args.device_uuid; + this.dataParams = { + device_uuid: this.device_uuid, + config: ["vm:type", "vm:image-path"] + }; + }, + + postMixInProperties: function() { + dojo.mixin(this, nls); + this.VM_SELECT_TITLE = this.userVMs ? nls.VM_SELECT_TITLE_USER : nls.VM_SELECT_TITLE_SERVICE; + }, + + postCreate: function() { + this.inherited(arguments); + this.subscribe(XUtils.publishTopic, dojo.hitch(this, this._messageHandler)); + }, + + _messageHandler: function(message) { + if(!this.getParent().isVisible) { + // no need to update if we're not visible, and when the parent expands us, it instructs us to getData anyway + return; + } + switch(message.type) { + case "vms_removed": + case "vm_instances_assigned": + this.getData(); + break; + case "vm_instances_locked": + case "vm_instances_unlocked": + case "vm_instances_removed": + array.some(this.getChildren(), function(widget) { + if (message.data && message.data.contains(widget.vm_instance_uuid)) { + this.getData(); + return true; + } + }, this); + break; + } + }, + + _setStoreData: function(data) { + this.store.data = array.filter(data, function(item) { + return ["svm", "pvm", null].contains(item.config_value_1) == this.userVMs; + }, this); + this.refresh(); + }, + + refresh: function() { + this.set("editing", true); + this.set("source", this.store.data); + }, + + _updateWidgets: function() { + var child = this.findChild(); + this._setEnabled(this.RemoveButton, this.value); + this._setEnabled(this.LockButton, this.value); + this.LockButton.set("label", child && child.locked ? nls.UNLOCK_BUTTON : nls.LOCK_BUTTON); + }, + + _updateChildren: function() { + if (this.source != null && this.source) { + var baseClass = (this.itemClassSmall != "" && this.source.length > this.classThreshold) ? this.itemClassSmall : this.itemClass; + array.forEach(this.source, function(srcItem) { + if (!array.some(this.getChildren(), function(item) { return srcItem.vm_instance_uuid == item.vm_instance_uuid; })) { + // doesn't exist in the children already, so add + var widget = new this.imageWidget({ + vm_instance_uuid: srcItem.vm_instance_uuid, + vm_instance_name: srcItem.vm_instance_name, + image: XenClient.Utils.VmIcons[srcItem.config_value_2], + locked: srcItem.locked == "t", + baseClass: baseClass + }); + this.addChild(widget); + widget.startup(); + } + }, this); + } + array.forEach(this.getChildren(), function(item) { + if(array.some(this.source, function(dataItem) { + var match = dataItem.vm_instance_uuid == item.vm_instance_uuid; + if(match) { + item.set("locked", dataItem.locked == "t"); + } + return match; + })) { + item.set("selected", (item.vm_instance_uuid == this.value)); + item.set("disabled", !this.editing); + } else { + if(item.vm_instance_uuid == this.value) { + this._handleOnChange(""); + this.value = ""; + } + this.removeChild(item); + item.destroyRecursive(); + } + }, this); + this._updateWidgets(); + }, + + _onAddClick: function(event) { + var result = dojo.hitch(this, function(data) { + var wait = new XenClient.Utils.AsyncWait(dojo.hitch(this, this._bindDijit)); + var getData = function(device_uuid, vm_uuid, name, success) { + socket.data_access("add_vm_instance", {device_uuid: device_uuid, vm_uuid: vm_uuid, vm_instance_name: name}, success, + dojo.hitch(this, function(error) { + if(error.code == "20060") { + // need to readd vm instance instead of add + // first need to get existing instance id + socket.data_access("list_vm_instances", {vm_instance_name: "", device_uuid: device_uuid, vm_uuid: "", disk_uuid: "", removed: true}, function(data) { + array.some(data, function(item, i) { + if(item.vm_uuid == vm_uuid) { + socket.data_access("readd_vm_instance", {vm_instance_uuid: item.vm_instance_uuid}, success); + return true; + } + }); + }, success); + } else if(success) { + success(); + } + }) + ); + } + array.forEach(data, function(item, i) { + getData(this.device_uuid, item.vm_uuid, item.vm_name, wait.addCallback()); + }, this); + wait.finish(); + }); + new gridDialog({ gridTemplateName: this.userVMs ? "citrix.sync-wui.VMGrid" : "citrix.sync-wui.ServiceVMGrid", finishFn: result, selectionMode: "multiple", title: this.userVMs? nls.SELECT_VM_TITLE : nls.SELECT_SERVICE_TITLE }).show(); + }, + + _onRemoveClick: function(event) { + XenClient.Utils.messageBox.showConfirmation(nls.REMOVE_VM_INSTANCE_WARNING, dojo.hitch(this, function() { + socket.data_access("remove_vm_instance", {vm_instance_uuid: this.value}, dojo.hitch(this, function(data) { + this._bindDijit(); + })); + }), { continueText: "REMOVE_ACTION" }); + }, + + _onLockClick: function(event) { + var child = this.findChild(); + if(child) { + socket.data_access((child.locked ? "unlock" : "lock") + "_vm_instance", {vm_instance_uuid: this.value}, dojo.hitch(this, function(data) { + this._bindDijit(); + })); + } + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/VMWidget.js b/widgets/citrix/sync-wui/VMWidget.js new file mode 100644 index 0000000..b1a6deb --- /dev/null +++ b/widgets/citrix/sync-wui/VMWidget.js @@ -0,0 +1,161 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/string", + "dojo/aspect", + "dojo/_base/array", + // Resources + "dojo/i18n!./nls/VMWidget", + "dojo/text!./templates/VMWidget.html", + // Mixins + "citrix/sync-wui/VMGrid", + "citrix/common/_RouterMixin", + // Used in code + "citrix/sync-wui/VMDetails", + "citrix/sync-wui/Button", + "citrix/sync-wui/GridDialog" +], +function (dojo, declare, string, aspect, array, nls, template, vmGrid, _routerMixin, vmDetails, button, gridDialog) { +return declare("citrix.sync-wui.VMWidget", [vmGrid, _routerMixin], { + + templateString: template, + routerBase: "uservms", + vm_type: "svm", + popup: null, + + getGridProperties: function() { + var props = this.inherited(arguments); + var self = this; + + props.columns.num_vm_instances = nls.NUM_VM_INSTANCES; + + props.columns.actions = { + label: nls.ACTIONS, + sortable: false, + renderCell: function(object, value, node, options) { + var assignButton = new button({label: nls.ASSIGN_VM}); + assignButton.onClick = function() { + self.onAssign(object[props.idProperty], object.vm_name); + }; + var detailsButton = new button({label: nls.VIEW_VM}); + detailsButton.onClick = function() { + self.route(object[props.idProperty]); + }; + var dupeButton = new button({label: nls.DUPLICATE_VM}); + dupeButton.onClick = function() { + self._onDuplicateVM(object[props.idProperty]); + }; + node.appendChild(assignButton.domNode); + node.appendChild(detailsButton.domNode); + node.appendChild(dupeButton.domNode); + } + }; + + return props; + }, + + postMixInProperties: function() { + this.inherited(arguments); + dojo.mixin(this, nls); + }, + + postCreate: function() { + this.inherited(arguments); + + this.grid.on(".dgrid-content .dgrid-cell:click", dojo.hitch(this, this._updateButtons)); + this._updateButtons(); + }, + + onAssign: function(uuid, name) { + var result = dojo.hitch(this, function(data) { + var finish = dojo.hitch(this, function() { + XUtils.publish("vm_instances_assigned"); + }); + var wait = new XenClient.Utils.AsyncWait(finish); + var getData = function(device_uuid, vm_uuid, name, success) { + socket.data_access("add_vm_instance", { device_uuid: device_uuid, vm_uuid: vm_uuid, vm_instance_name: name }, success, + dojo.hitch(this, function(error) { + if(error.code == "20060") { + // need to readd vm instance instead of add + // first need to get existing instance id + socket.data_access("list_vm_instances", {vm_instance_name: "", device_uuid: device_uuid, vm_uuid: "", disk_uuid: "", removed: true}, function(data) { + array.some(data, function(item, i) { + if(item.vm_uuid == vm_uuid) { + socket.data_access("readd_vm_instance", {vm_instance_uuid: item.vm_instance_uuid}, success); + return true; + } + }); + }); + } + }) + ); + }; + dojo.forEach(data, function(device) { + getData(device.device_uuid, uuid, name, wait.addCallback()); + }, this); + wait.finish(); + }); + new gridDialog({ gridTemplateName: "citrix.sync-wui.DeviceGrid", finishFn: result, selectionMode: "multiple", title: nls.SELECT_DEVICE_TITLE }).show(); + }, + + onRoute: function(uuid) { + this._openVM({ vm_uuid: uuid }); + }, + + _onAddVM: function(event) { + this._openVM({ isNew: true, vm_type: this.vm_type }); + }, + + _onDuplicateVM: function(uuid) { + this._openVM({ vm_uuid: uuid, isDuplicate: true }); + }, + + _openVM: function(props) { + if (this.popup) { + this.popup.hide(); + } + this.popup = new vmDetails(props); + var handle = aspect.after(this.popup, "onHide", dojo.hitch(this, function() { + this.route(""); + handle.remove(); + })); + this.popup.show(); + }, + + _onRemoveVM: function(event) { + XenClient.Utils.messageBox.showConfirmation(this.routerbase == "servicevms" ? nls.REMOVE_SERVICE_WARNING : nls.REMOVE_VM_WARNING, dojo.hitch(this, function() { + var wait = new XUtils.AsyncWait(dojo.hitch(this, function() { + this.grid.clearSelection(); + XUtils.publish("vms_removed"); + })); + for(var vm_uuid in this._selectedData) { + var success = wait.addCallback(); + socket.data_access("remove_vm", { vm_uuid: vm_uuid, cascade: true }, success, success); + } + wait.finish(); + }), { continueText: "REMOVE_ACTION" }); + + var row = this.grid.row(Object.keys(this.grid.selection)[0]); + var vm_uuid = row.data.vm_uuid; + }, + + _bindDijit: function() { + this.inherited(arguments); + this._updateButtons(); + }, + + _updateButtons: function() { + this._setEnabled(".vmButton", Object.keys(this.grid.selection).length > 0); + }, + + _bindDijitRetainPositionFinish: function() { + this._updateButtons(); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/_ConfigMixin.js b/widgets/citrix/sync-wui/_ConfigMixin.js new file mode 100644 index 0000000..8bfeb19 --- /dev/null +++ b/widgets/citrix/sync-wui/_ConfigMixin.js @@ -0,0 +1,86 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dojo/_base/array", + "dojo/query" +], +function(dojo, declare, array, query) { +return declare("citrix.sync-wui._ConfigMixin", null, { + + configDataFunction: "", + configSaveFunction: "", + + constructor: function() { + this.configDataParams = {}; + this.configSaveParams = {}; + this.config = {}; + }, + + postCreate: function() { + this.inherited(arguments); + this.getConfigData(); + }, + + getConfigData: function() { + if(this.configDataFunction != "") { + socket.data_access(this.configDataFunction, this.configDataParams, dojo.hitch(this, function(data) { + this.config = {}; + array.forEach(data, function(item) { + if(!this.config[item.daemon]) { + this.config[item.daemon] = {}; + } + this.config[item.daemon][item.key] = item.value; + }, this); + this._setCustomsArray(); + this._bindDijit(); + })); + } + }, + + _setCustomsArray: function() { + this.config._customs = []; + array.forEach(Object.keys(this.config), function(daemon) { + if(daemon == "_customs") { + return; + } + array.forEach(Object.keys(this.config[daemon]), function(property) { + if(!daemon.startsWith("nic/") && query("[name = 'config." + daemon + "." + property + "']").length == 0) { + this.config._customs.push({ + id: daemon + "." + property, + daemon: daemon, + property: property, + value: this.config[daemon][property], + className: this.config[daemon][property] == "" ? "removedConfig" : "" + }); + } + }, this); + }, this); + }, + + saveConfigData: function(configObject) { + if(this.configSaveFunction != "") { + this.configSaveParams.config = this._formatConfigObjectsIntoStringList(configObject); + socket.data_access(this.configSaveFunction, this.configSaveParams, dojo.hitch(this, function(data){ + this.getConfigData(); + })); + } + }, + + _formatConfigObjectsIntoStringList: function(configObject) { + var configlist = []; + array.forEach(Object.keys(configObject), function(daemonkey) { + array.forEach(Object.keys(configObject[daemonkey]), function(key) { + var value = daemonkey + ":" + key + ":" + configObject[daemonkey][key]; + configlist.push(value); + }, this); + }, this); + return configlist; + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/_ModelMixin.js b/widgets/citrix/sync-wui/_ModelMixin.js new file mode 100644 index 0000000..d6acf25 --- /dev/null +++ b/widgets/citrix/sync-wui/_ModelMixin.js @@ -0,0 +1,56 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + "dijit" +], +function(dojo, declare, dijit) { +return declare("citrix.sync-wui._ModelMixin", null, { + + dataFunction: "", + saveFunction: "", + getDataFinishFn: null, + + constructor: function() { + this.dataParams = {}; + this.saveParams = {}; + this.model = {}; + }, + + postCreate: function() { + this.inherited(arguments); + this.getData(); + }, + + getData: function() { + if(this.dataFunction != "") { + socket.data_access(this.dataFunction, this.dataParams, dojo.hitch(this, function(data) { + this.model = data[0]; + if(this.getDataFinishFn) { + this.getDataFinishFn(); + } else { + this._bindDijit(); + } + })); + } + }, + + saveData: function(finish) { + if(this.saveFunction != "") { + socket.data_access(this.saveFunction, this.saveParams, dojo.hitch(this, function(data) { + if(finish) { + finish(data); + } + this.getData(); + }), function(error) { + XenClient.Utils.messageBox.showError("[{0}] {1}".format(error.code, error.message)); + }); + } + } +}); +}); diff --git a/widgets/citrix/sync-wui/_StoreMixin.js b/widgets/citrix/sync-wui/_StoreMixin.js new file mode 100644 index 0000000..81ad090 --- /dev/null +++ b/widgets/citrix/sync-wui/_StoreMixin.js @@ -0,0 +1,69 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define([ + "dojo", + "dojo/_base/declare", + // Used in code + "dojo/store/Memory", + "dojo/store/Observable" +], +function (dojo, declare, memory, observable) { +return declare("citrix.sync-wui._StoreMixin", null, { + + dataFunction: "", + idProperty: "", + filterParam: "", + + constructor: function() { + this.dataParams = {}; + }, + + postCreate: function () { + this.inherited(arguments); + this.store = observable(new memory()); + this._bindDijit(); + }, + + _setStoreData: function(data) { + this.store.data = data; + }, + + getData: function (retainPosition, onFinish) { + if (this.dataFunction != "") { + socket.data_access(this.dataFunction, this.dataParams, dojo.hitch(this, function (data) { + this._setStoreData(data); + + if (this.idProperty != "") { + this.store.idProperty = this.idProperty; + } + + if(retainPosition) { + var pos = this.getScrollPosition(); + this.refresh(); + this.scrollTo(pos); + } else { + this.refresh(); + } + if(onFinish) { + onFinish(); + } + })); + } + }, + + _setFilterParam: function (value) { + if (this.filterParam && this.dataParams[this.filterParam] !== undefined) { + this.dataParams[this.filterParam] = value == "" ? null : value; + } + this._bindDijit(); + }, + + _bindDijit: function (retainPosition, onFinish) { + this.getData(retainPosition, onFinish); + } +}); +}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/AddDisk.js b/widgets/citrix/sync-wui/nls/AddDisk.js new file mode 100644 index 0000000..2c4194c --- /dev/null +++ b/widgets/citrix/sync-wui/nls/AddDisk.js @@ -0,0 +1,25 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define({root: { + ADD_BUTTON: "Add", + CANCEL_BUTTON: "Cancel", + NAME_LABEL: "Name:", + NAME_VALERROR: "Name is required", + PATH_LABEL: "File Path:", + PATH_VALERROR: "File path is required", + HASH_LABEL: "File Hash:", + HASH_VALERROR: "File hash must be 64 lowercase HEX characters", + SIZE_LABEL: "File Size:", + TYPE_LABEL: "Type:", + KEY_LABEL: "Encryption Key:", + KEY_VALERROR: "Encryption key must be 64 or 128 lowercase HEX characters", + READONLY_LABEL: "Persistent:", + SHARED_LABEL: "Shared:", + TRUE: "True", + FALSE: "False", + TITLE: "Add Disk" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/AlertDialog.js b/widgets/citrix/sync-wui/nls/AlertDialog.js new file mode 100644 index 0000000..b2daf80 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/AlertDialog.js @@ -0,0 +1,24 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + // Layout + INFORMATION: "Information", + SECURITY: "Security", + WARNING: "Warning", + ERROR: "Error", + TIP: "Did you know?", + DO_NOT_SHOW_AGAIN: "Don't show me this again.", + UNKNOWN_ERROR_MASK: "{0}

                Error code: {1}

                Message: {2}", + // Buttons + OK_ACTION: "OK", + CANCEL_ACTION: "Cancel", + CLOSE_ACTION: "Close", + NO_ACTION: "No", + CONTINUE_ACTION: "Continue", + ACCEPT_ACTION: "Accept", + REMOVE_ACTION: "Remove" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/Alerts.js b/widgets/citrix/sync-wui/nls/Alerts.js new file mode 100755 index 0000000..1918c76 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/Alerts.js @@ -0,0 +1,12 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + // Default message in AlertDialog + UNKNOWN_ERROR: "Unknown internal error", + UNKNOWN: "Unknown" + // Alerts +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/BooleanSelect.js b/widgets/citrix/sync-wui/nls/BooleanSelect.js new file mode 100644 index 0000000..c849a75 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/BooleanSelect.js @@ -0,0 +1,11 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + TRUE: "True", + FALSE: "False", + NOT_SET: "Not Included" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/DeviceWidget.js b/widgets/citrix/sync-wui/nls/DeviceWidget.js new file mode 100644 index 0000000..cb57c58 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/DeviceWidget.js @@ -0,0 +1,49 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + NAME: "Computer Name", + USER_NAME: "User", + REPORT_TIME: "Last Sync", + RELEASE_LABEL: "Release:", + BUILD_LABEL: "Build No.:", + ADD_BUTTON: "Add", + REMOVE_BUTTON: "Remove", + LOCK_BUTTON: "Lock", + UNLOCK_BUTTON: "Unlock", + REMOVE_ACTION: "Remove", + LOCK_ALL_INSTANCES_ACTION: "Lock All VM Instances", + LOCK_INSTANCES_ACTION: "Lock Selected VM Instances", + UNLOCK_ALL_INSTANCES_ACTION: "Unlock All VM Instances", + UNLOCK_INSTANCES_ACTION: "Unlock Selected VM Instances", + REMOVE_ALL_INSTANCES_ACTION: "Remove All VM Instances", + REMOVE_INSTANCES_ACTION: "Remove Selected VM Instances", + REMOVE_ALL_SERVICE_INSTANCES_ACTION: "Remove All Service Instances", + REMOVE_SERVICE_INSTANCES_ACTION: "Remove Selected Service Instances", + EDIT_CONFIGURATION_ACTION: "Edit Configuration", + EDIT_CONFIGURATION_FROM_EXISTING_ACTION: "Edit Configuration from Existing", + ASSIGN_REPO: "Assign Repo", + ASSIGN_USERVMS: "Assign VMs", + ASSIGN_SERVICEVMS: "Assign Services", + SELECT_ACTION: "--- SELECT ACTION ---", + SELECT_REPO_TITLE: "Select Repo", + SELECT_TITLE_LOCK_VMS: "Select VMs to lock", + SELECT_VMS_TO_ASSIGN: "Select VMs to assign", + SELECT_SERVICES_TO_ASSIGN: "Select Services to assign", + CONFIG_BUTTON: "Configure", + DUPLICATE_DEVICE: "Duplicate Configuration", + ACTIONS: "Actions", + NONE_SET: "None found", + ADD_DEVICE: "Connect a new computer", + VM_SELECT_TITLE_USER: "Assigned VMs", + VM_SELECT_TITLE_SERVICE: "Assigned Services", + REMOVE_DEVICE_WARNING: "Are you sure you want to remove these computers? This action cannot be undone, and will remove any data relating to them.", + REMOVE_VM_INSTANCE_WARNING: "Are you sure you want to remove this instance?", + SELECT_VM_TITLE: "Select VMs", + SELECT_SERVICE_TITLE: "Select Services", + SELECT_DEVICE_TITLE: "Select a Device", + EXPAND: "Expand Computer" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/DiskWidget.js b/widgets/citrix/sync-wui/nls/DiskWidget.js new file mode 100644 index 0000000..b173225 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/DiskWidget.js @@ -0,0 +1,19 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define({root: { + NAME: "Disk Name", + TYPE: "Disk Type", + PATH: "File Path", + PERSISTENT: "Persistent", + SHARED: "Shared", + NUM_VMS: "VM Count", + ADD_DISK: "Add a new Disk", + REMOVE_BUTTON: "Remove", + REMOVE_DISK_WARNING: "Are you sure you want to remove this disk? This action cannot be undone.", + TRUE: "True", + FALSE: "False" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/GridDialog.js b/widgets/citrix/sync-wui/nls/GridDialog.js new file mode 100755 index 0000000..13f69cd --- /dev/null +++ b/widgets/citrix/sync-wui/nls/GridDialog.js @@ -0,0 +1,15 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + SEARCH_BUTTON: "Filter", + ADD_BUTTON: "Use Selection", + CANCEL_BUTTON: "Cancel", + CLEAR_FILTER_BUTTON: "Clear Filter", + SHOW_SELECTED_BUTTON: "View Selected Rows", + SELECT_ALL_BUTTON: "Select All", + SELECT_NONE_BUTTON: "Select None" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/RepoGrid.js b/widgets/citrix/sync-wui/nls/RepoGrid.js new file mode 100755 index 0000000..0835cd5 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/RepoGrid.js @@ -0,0 +1,11 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + RELEASE: "Release", + BUILD: "Build", + FILE_PATH: "File Path" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/Settings.js b/widgets/citrix/sync-wui/nls/Settings.js new file mode 100755 index 0000000..604dce4 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/Settings.js @@ -0,0 +1,65 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + // Layout + SYS_INFO_HEADER: "Settings", + DETAILS: "Details", + NAME_LABEL: "Name:", + NAME_VALERROR: "Name is required", + UUID_LABEL: "UUID:", + REPO_LABEL: "Repository:", + USER_LABEL: "User:", + SELECT_USER_ACTION: "Select User", + REMOVE_USER_ACTION: "Remove User", + REMOVE_USER_WARNING: "Are you sure you want to remove this user from the computer?", + ADD_USER_ERROR: "There was an error adding this user.", + NONE_SET: "None Set", + SYS_INFO_WALLPAPER_TITLE: "Wallpaper", + WALLPAPER_HEADER: "Select a wallpaper for the client", + WALLPAPER_OVERRIDE_HEADER: "To use a wallpaper not listed enter the client filepath below", + WALLPAPER_TEXT_LABEL: "Wallpaper Path:", + CLOSE_ACTION: "Cancel", + SAVE_ACTION: "Save", + NOT_SET: "Not Included", + CONFIGURATION: "Xenmgr Config", + AUTOSTART_LABEL: "Autostart:", + VM_CREATION_ALLOWED_LABEL: "VM Creation Allowed:", + VM_DELETION_ALLOWED_LABEL: "VM Deletion Allowed:", + OTA_UPGRADES_ALLOWED_LABEL: "OTA Upgrades Allowed:", + CONNECT_REMOTE_DESKTOP_ALLOWED_LABEL: "Allow Remote Desktop", + MEASURE_FAIL_ACTION_LABEL: "Measure Fail Action:", + NOTHING_ACTION: "Nothing", + SLEEP_ACTION: "Sleep", + HIBERNATE_ACTION: "Hibernate", + REBOOT_ACTION: "Reboot", + SHUTDOWN_ACTION: "Shutdown", + FORCE_SHUTDOWN_ACTION: "Force Shutdown", + V4V_FIREWALL_LABEL: "V4V Firewall Enabled:", + ENABLE_SSH_LABEL: "External SSH Enabled:", + ENABLE_V4V_SSH_LABEL: "Internal (v4v) SSH Enabled:", + ENABLE_DOM0_NETWORKING_LABEL: "Dom0 Networking Enabled:", + CUSTOM: "Custom", + ADD_ACTION: "Add", + DAEMON: "Daemon", + PROPERTY: "Property", + VALUE: "Value", + REMOVE_ACTION: "Remove", + UI_CONFIG: "UI Config", + LANGUAGE_LABEL: "Language", + SHOW_MSG_ON_VM_START_LABEL: "Show VM Start Message:", + SHOW_MSG_ON_VM_START_TOOLS_WARNING_LABEL: "Show VM Start Tools Warning:", + SHOW_MSG_ON_NO_DISK_LABEL: "Show Message on Deleting Last Disk", + SHOW_MBOOT_WARNING_LABEL: "Show Measured Boot Warning", + SHOW_TOOLS_WARNING_LABEL: "Show Tools Warning", + MODIFY_SETTINGS_LABEL: "Can Modify Settings:", + MODIFY_SERVICES_LABEL: "Can Modify Service:", + NO_CUSTOM_FIELDS: "No custom fields defined", + MODIFY_ADVANCED_VM_SETTINGS_LABEL: "Can Modify VM:", + SELECT_USER_TITLE: "Select User", + REPLACE_CONFIG_MESSAGE: "This will overwrite the existing computer configurations. Do you wish to continue?", + REPLACE_ACTION: "Replace" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/UnseenDeviceWidget.js b/widgets/citrix/sync-wui/nls/UnseenDeviceWidget.js new file mode 100644 index 0000000..c971943 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/UnseenDeviceWidget.js @@ -0,0 +1,11 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + HEADING: "Computers not seen in the past {0} days", + REPORT_TIME: "Last Sync", + USER_NAME: "User" +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/nls/VMDetails.js b/widgets/citrix/sync-wui/nls/VMDetails.js new file mode 100644 index 0000000..1613bca --- /dev/null +++ b/widgets/citrix/sync-wui/nls/VMDetails.js @@ -0,0 +1,124 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define ({root: { + // General + NOT_INCLUDED: "Not Included", + NOT_INCLUDED_VALUE: "[Not Included]", + NONE_SET: "None Set", + // Form + CLOSE: "Close", + DELETE: "Delete VM", + SAVE: "Save", + // Tabs + GENERAL: "General", + HARDWARE: "Hardware", + NETWORKS: "Networks", + DISKS: "Disks", + ICON: "Icon", + ADVANCED: "Advanced", + CUSTOM: "Custom", + // General Tab + UUID_LABEL: "UUID:", + NAME_LABEL: "Name:", + TEMPLATE_LABEL: "Template Name:", + NAME_VALERROR: "Name is required. It cannot exceed 60 characters and can contain letters, digits, spaces and some punctuation (including underscore, dash, brackets, period, apostrophe, and comma).", + TYPE_LABEL: "Type:", + SWITCHER_KEY_LABEL: "Switcher Key:", + SWITCHER_KEY_MASK: "CTRL + {0}", + AUTOBOOT_LABEL: "Autoboot:", + AUTOBOOT_TOOLTIP: "Start this VM when the host starts.", + POWER_BEHAVIOUR_LABEL: "VM Power Controls Host:", + POWER_BEHAVIOUR_TOOLTIP: "When the VM shuts down, hibernates or sleeps, the host device will do the same.", + POWER_BEHAVIOUR_ENABLED: "Power control affects VM and host", + POWER_BEHAVIOUR_DISABLED: "Power control affects VM only", + HIDDEN_UI_LABEL: "Hide VM from UI:", + HIDDEN_SWITCHER_LABEL: "Hide VM from Switcher Bar:", + COMMUNICATION_LABEL: "Seamless Application Support:", + COMMUNICATION_TOOLTIP: "Controls virtual machine firewall settings so that seamless applications will work.", + DESCRIPTION_LABEL: "Description:", + DESCRIPTION_VALERROR: "Description should not exceed 200 characters and can contain letters, digits, whitespace and some punctuation (including underscore, dash, brackets, period, apostrophe, and comma).", + // Hardware Tab + MEMORY_LABEL: "Memory (MB):", + CORES_LABEL: "Cores Per Socket:", + VCPUS_LABEL: "vCPUs:", + THREED_GFX_LABEL: "3D Graphics (experimental):", + VIRTUAL_CD_LABEL: "Virtual CD:", + TOOLS_CD: "XenClient Guest Tools", + USB_GRAB_LABEL: "Auto-assign USB Devices:", + USB_GRAB_TOOLTIP: "When you start this VM, any free USB devices will be assigned to it.", + MOUSE_LEFT_LABEL: "Seamless Mouse Left:", + MOUSE_RIGHT_LABEL: "Seamless Mouse Right:", + BOOT_PRIORITY_LABEL: "Boot Priority:", + SHUTDOWN_PRIORITY_LABEL: "Shutdown Priority:", + BOOT_ORDER_LABEL: "Boot Order:", + BOOT_TITLE: "Add a boot option...", + CD_DRIVE: "CD Drive", + HARD_DRIVE: "Hard Drive", + NETWORK: "Network", + // Network Tab + NETWORK_DAEMON: "Network", + // Disks Tab + DISK_NAME_TH: "Disk Name", + DISK_PATH_TH: "File Path", + DISK_TYPE_TH: "Disk Type", + PERSISTENT_TH: "Persistent", + PERSISTENT_TOOLTIP: "This setting indicates if changes to the data on the disk remain across VM boots.", + ACTIONS: "Actions", + ADD_DISK: "Add Disk", + REMOVE_DISK: "Remove", + NO_DISKS: "No virtual disks", + SELECT_DISK_TITLE: "Select Disks", + // Icon Tab + ICON_OVERRIDE_HEADER: "To use a vm icon not listed enter the client filepath below", + ICON_TEXT_LABEL: "VM Icon Path:", + // Advanced Tab + POLICY_TITLE: "Isolation Policies", + HARDWARE_TITLE: "Hardware Compatibility", + VIRTUAL_TITLE: "Virtual Compatibility", + OTHER_TITLE: "Other", + STUB_DOMAIN_LABEL: "Stub Domain:", + MODIFY_VM_LABEL: "Modify VM Settings:", + PRINT_SCREEN_LABEL: "Print Screen:", + NETWORKING_WIRED_LABEL: "Wired Network Access:", + NETWORKING_WIRELESS_LABEL: "Wireless Network Access:", + CD_ACCESS_LABEL: "CD Reading:", + CD_RECORDING_LABEL: "CD Writing:", + AUDIO_ACCESS_LABEL: "Audio Playback:", + AUDIO_RECORDING_LABEL: "Audio Recording:", + USB_ENABLED_LABEL: "USB Enabled:", + USB_CONTROL_LABEL: "USB Control:", + ACPI_PT_LABEL: "Allow OEM Windows Install:", + ACPI_PT_TOOLTIP: "Expose the host ACPI SLIC table to the VM to unlock OEM editions of Windows.", + SMBIOS_PT_LABEL: "Expose Physical Hardware Information:", + SMBIOS_PT_TOOLTIP: "Expose the SMBIOS record types describing host hardware to the VM instead of those supplied by XenClient.", + OEM_ACPI_PT_LABEL: "Expose Physical OEM Hardware:", + OEM_ACPI_PT_TOOLTIP: "Expose the OEM hardware directly to the VM. Only one VM can have this enabled.", + AMT_PT_LABEL: "Intel AMT Passthrough:", + AMT_PT_TOOLTIP: "Enabling the Intel Active Management Technology (AMT) Management Engine (ME) provides hardware-based network management that allows out-of-band access to XenClient devices for administration purposes. Only one VM can have this enabled.", + APIC_LABEL: "APIC", + VIRIDIAN_LABEL: "Emulate Microsoft Hyper-V:", + HVM_LABEL: "Hardware Virtual Machine", + HVM_EXTRA_LABEL: "HVM Extra:", + XENVM_EXTRA_LABEL: "XenVM Extra:", + COMMAND_LINE_LABEL: "Command Line:", + KERNEL_LABEL: "Kernel Path:", + KERNEL_EXTRACT_LABEL: "Kernel Extraction Path:", + INIT_RD_LABEL: "Initial Ramdisk:", + INIT_RD_EXTRACT_LABEL: "Initial Ramdisk Extraction Path:", + SOUND_LABEL: "Sound:", + MEASURED_LABEL: "Measured:", + TRACK_LABEL: "Track Dependencies:", + S3_WAKE_LABEL: "Auto S3 Wake:", + S3_MODE_LABEL: "S3 Mode:", + // Custom Tab + ADD_ACTION: "Add", + DAEMON: "Daemon", + PROPERTY: "Property", + VALUE: "Value", + REMOVE_ACTION: "Remove", + NO_CUSTOM_FIELDS: "No custom fields defined" +}}); diff --git a/widgets/citrix/sync-wui/nls/VMWidget.js b/widgets/citrix/sync-wui/nls/VMWidget.js new file mode 100644 index 0000000..c1904e3 --- /dev/null +++ b/widgets/citrix/sync-wui/nls/VMWidget.js @@ -0,0 +1,21 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +define({root: { + NAME: "VM Name", + SERVICE_NAME: "Service Name", + NUM_VM_INSTANCES: "Number of Instances", + ACTIONS: "Actions", + ADD_VM: "Create a new VM", + ADD_SERVICE: "Create a new Service", + ASSIGN_VM: "Assign", + SELECT_DEVICE_TITLE: "Select Computers", + VIEW_VM: "View", + DUPLICATE_VM: "Duplicate", + REMOVE_BUTTON: "Remove", + REMOVE_VM_WARNING: "Are you sure you want to remove these VMs? This action cannot be undone.", + REMOVE_SERVICE_WARNING: "Are you sure you want to remove these Services? This action cannot be undone." +}}); \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/AddDisk.html b/widgets/citrix/sync-wui/templates/AddDisk.html new file mode 100644 index 0000000..82ba119 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/AddDisk.html @@ -0,0 +1,46 @@ +
                +
                + + + + +
                +
                +
                + + +
                +
                + + +
                +
                + + +
                +
                + + +
                +
                + + +
                +
                + + +
                +
                + + +
                +
                + + +
                +
                +
                + + +
                +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/AlertDialog.html b/widgets/citrix/sync-wui/templates/AlertDialog.html new file mode 100644 index 0000000..dc5c198 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/AlertDialog.html @@ -0,0 +1,20 @@ +
                +
                + + + + +
                +
                +
                +
                +
                +
                + + + + + + +
                +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/AlertPopup.html b/widgets/citrix/sync-wui/templates/AlertPopup.html new file mode 100644 index 0000000..df26974 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/AlertPopup.html @@ -0,0 +1,13 @@ +
                +
                + + + + +
                +
                +
                +
                +
                +
                +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/Button.html b/widgets/citrix/sync-wui/templates/Button.html new file mode 100644 index 0000000..7e65ca2 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/Button.html @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/ComboButton.html b/widgets/citrix/sync-wui/templates/ComboButton.html new file mode 100644 index 0000000..7fa4e28 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/ComboButton.html @@ -0,0 +1,33 @@ + + +   + + diff --git a/widgets/citrix/sync-wui/templates/DevicePlatform.html b/widgets/citrix/sync-wui/templates/DevicePlatform.html new file mode 100644 index 0000000..3db6a70 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/DevicePlatform.html @@ -0,0 +1,13 @@ +
                +

                Platform settings

                +
                +   +
                +
                +   +
                +
                + +
                + +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/DeviceWidget.html b/widgets/citrix/sync-wui/templates/DeviceWidget.html new file mode 100644 index 0000000..6c91b8a --- /dev/null +++ b/widgets/citrix/sync-wui/templates/DeviceWidget.html @@ -0,0 +1,36 @@ +
                +
                + + +
                + + +
                +
                +
                +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/DiskWidget.html b/widgets/citrix/sync-wui/templates/DiskWidget.html new file mode 100644 index 0000000..fc31190 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/DiskWidget.html @@ -0,0 +1,16 @@ +
                +
                + + +
                + + +
                +
                +
                +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/GridDialog.html b/widgets/citrix/sync-wui/templates/GridDialog.html new file mode 100644 index 0000000..3e8a1f5 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/GridDialog.html @@ -0,0 +1,13 @@ +
                +
                + + + + +
                +
                +
                + + +
                +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/GridTemplate.html b/widgets/citrix/sync-wui/templates/GridTemplate.html new file mode 100644 index 0000000..cdc74d8 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/GridTemplate.html @@ -0,0 +1,17 @@ +
                +
                +
                + + +
                +
                +
                +
                \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/ImageButton.html b/widgets/citrix/sync-wui/templates/ImageButton.html new file mode 100644 index 0000000..be2cc06 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/ImageButton.html @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/MainTabButton.html b/widgets/citrix/sync-wui/templates/MainTabButton.html new file mode 100644 index 0000000..36fba35 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/MainTabButton.html @@ -0,0 +1,8 @@ + diff --git a/widgets/citrix/sync-wui/templates/MainTabContainer.html b/widgets/citrix/sync-wui/templates/MainTabContainer.html new file mode 100644 index 0000000..b9c7bc2 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/MainTabContainer.html @@ -0,0 +1,5 @@ +
                +
                +
                +
                +
                diff --git a/widgets/citrix/sync-wui/templates/Settings.html b/widgets/citrix/sync-wui/templates/Settings.html new file mode 100755 index 0000000..2257079 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/Settings.html @@ -0,0 +1,183 @@ +
                +
                + ${SYS_INFO_HEADER} + + + +
                +
                +
                +
                +

                ${DETAILS}

                +
                + + +
                +
                + + +
                +
                + + +
                +
                +
                +

                ${SYS_INFO_WALLPAPER_TITLE}

                +

                ${WALLPAPER_HEADER}

                + +
                  +

                  ${WALLPAPER_OVERRIDE_HEADER}

                  +
                  + + +
                  +
                  +
                  +

                  ${CONFIGURATION}

                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  +
                  +

                  ${UI_CONFIG}

                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  +
                  +

                  ${CUSTOM}

                  +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  + +
                  +
                  + + + + + + + + + + + + + + + + + + + + +
                  ${DAEMON}${PROPERTY}${VALUE}
                  + + + + + + + +
                  ${NO_CUSTOM_FIELDS}
                  +
                  +
                  +
                  +
                  +
                  + + + + +
                  +
                  \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/VMDetails.html b/widgets/citrix/sync-wui/templates/VMDetails.html new file mode 100755 index 0000000..7ebcc68 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/VMDetails.html @@ -0,0 +1,406 @@ +
                  +
                  + + + + + +
                  +
                  +
                  +
                  +

                  ${GENERAL}

                  +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  +
                  +
                  +

                  ${HARDWARE}

                  +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  + + +
                  +
                  +
                  +
                  +

                  ${NETWORKS}

                  +
                  +
                  + + nic/ + +
                  +
                  + + +
                  +
                  + + + +
                  + +
                  +
                  + + + + + + + + + + + + + + + + + + + + +
                  ${NETWORK_DAEMON}${PROPERTY}${VALUE}
                  + + + + + + + +
                  ${NO_CUSTOM_FIELDS}
                  +
                  +
                  +
                  +

                  ${DISKS}

                  +
                  + + + + + + + + + + + + + + + + + + + + + + + + + +
                  ${DISK_NAME_TH}${DISK_PATH_TH}${DISK_TYPE_TH}${ACTIONS}
                  + + + + + + + +
                  ${NO_DISKS}
                  + +
                  +
                  +
                  +
                  +

                  ${ICON}

                  +
                    +

                    ${ICON_OVERRIDE_HEADER}

                    +
                    + + +
                    +
                    +
                    +

                    ${ADVANCED}

                    +
                    +

                    ${POLICY_TITLE}

                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +

                    ${HARDWARE_TITLE}

                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +

                    ${VIRTUAL_TITLE}

                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +

                    ${OTHER_TITLE}

                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    +
                    +
                    +
                    +

                    ${CUSTOM}

                    +
                    +
                    + + +
                    +
                    + + +
                    +
                    + + +
                    + +
                    +
                    + + + + + + + + + + + + + + + + + + + + +
                    ${DAEMON}${PROPERTY}${VALUE}
                    + + + + + + + +
                    ${NO_CUSTOM_FIELDS}
                    +
                    +
                    +
                    +
                    +
                    + + + + + + +
                    +
                    diff --git a/widgets/citrix/sync-wui/templates/VMItem.html b/widgets/citrix/sync-wui/templates/VMItem.html new file mode 100644 index 0000000..c820c0d --- /dev/null +++ b/widgets/citrix/sync-wui/templates/VMItem.html @@ -0,0 +1,5 @@ +
                  • + + + +
                  • \ No newline at end of file diff --git a/widgets/citrix/sync-wui/templates/VMSelect.html b/widgets/citrix/sync-wui/templates/VMSelect.html new file mode 100644 index 0000000..57bd986 --- /dev/null +++ b/widgets/citrix/sync-wui/templates/VMSelect.html @@ -0,0 +1,10 @@ +
                    +

                    ${VM_SELECT_TITLE}

                    +
                      +
                      + + + +
                      + +
                      diff --git a/widgets/citrix/sync-wui/templates/VMWidget.html b/widgets/citrix/sync-wui/templates/VMWidget.html new file mode 100644 index 0000000..8de616a --- /dev/null +++ b/widgets/citrix/sync-wui/templates/VMWidget.html @@ -0,0 +1,19 @@ +
                      +
                      + + +
                      + + +
                      +
                      +
                      +
                      \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/AlertDialog.css b/widgets/citrix/sync-wui/themes/tundra/AlertDialog.css new file mode 100644 index 0000000..706e31e --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/AlertDialog.css @@ -0,0 +1,39 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .alertDialog { + font-size: 12px; +} + +.tundra .alertDialog .alertMessage { + font-size: 12px; + padding: 10px 0px 10px 70px; + min-width: 270px; + max-width: 400px; + min-height: 30px; + white-space: pre-line; +} + +.tundra .alertDialog .notAgainCheckbox { + margin: 15px 0 15px 10px; +} + +.tundra .alertDialog .ctrlText { + font-size: 20px; + font-weight: bold; + margin-top: 25px; +} + +.tundra .alertDialog .ctrlButton { + font-family: 'Courier New', Courier, monospace; + font-size: 14px; + padding: 5px; + border-width: 15px 18px; + -webkit-border-image: url(/images/buttons/ctrlButton.png) 15 18 15 18 stretch; + display: inline-block; + min-width: 10px; + margin: 0 10px; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/AlertIcons.css b/widgets/citrix/sync-wui/themes/tundra/AlertIcons.css new file mode 100644 index 0000000..766f2dd --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/AlertIcons.css @@ -0,0 +1,40 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .alertIcon { + background: url(/images/sprites/alertIcons.png) no-repeat 0 0; + text-align: center; +} + +.tundra .alertIconLarge { + background-position-x: 0px; + width: 48px; + height: 48px; +} + +.tundra .alertIconMedium { + background-position-x: -48px; + width: 32px; + height: 32px; +} + +.tundra .alertIconSmall { + background-position-x: -80px; + width: 16px; + height: 16px; +} + +.tundra .alertIconInformation { + background-position-y: 0px; +} + +.tundra .alertIconWarning { + background-position-y: -48px; +} + +.tundra .alertIconError { + background-position-y: -96px; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/AppLayout.css b/widgets/citrix/sync-wui/themes/tundra/AppLayout.css new file mode 100644 index 0000000..1057135 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/AppLayout.css @@ -0,0 +1,121 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +/************************************************ +APP LAYOUT STYLES +************************************************/ + +html, body { + height: 100%; + overflow: hidden; +} + +#app-screen-wrapper{ + height: 100%; + background: url("/images/symphony/HeaderGradient_desktopandApp_655x113.png") no-repeat top left #002F11; +} +#app-screen{ + background: url("/images/symphony/Brand_OverlaySquarePattern_4x4.png") repeat 0 0 transparent; + height: 100%; +} + +#app-header{ + color:#fff; + margin-right:20px; +} + +#app-header .title{ + display:block; + float:left; + width:303px; + height:60px; + background: url("/images/symphony/wordmark_xc_logo_small.png") no-repeat center left; + overflow:hidden; + text-indent:-99999px; + margin:0 0 0 25px; + padding:0; +} + +#app-header .header-links{ + display:block; + width:600px; + float:right; + padding:0; + font-size:13px; + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + height:60px; + line-height::60px; +} + +#app-header .header-links ul{ + display:block; + text-align:right; + padding:0; + margin:0; +} + +#app-header .header-links ul li{ + display:block; + float:left; + margin:0 auto; + padding:0; + height:63px; + line-height:63px; +} + +#app-header .header-links a{ + text-decoration:none; + color:#fff; +} + +#app-header .header-links a span{ + text-decoration:none; + padding:8px; +} + +#app-header .header-links a:link span{ + background:none; + color:#fff; +} +#app-header .header-links a:visited span{ + background:none; + color:#fff; +} +#app-header .header-links a:hover span{ + background:#fff; + color:#666; +} +#app-header .header-links a:active span{ + background:none; + color:#fff; +} + +#app-header .header-links .log-on{ + width:70px; + text-align:right; +} +#app-header .header-links .display-name{ + width:430px; + text-align:right; +} +#app-header .header-links .log-off{ + width:70px; + text-align:right; + background: url("/images/symphony/header_sep.png") no-repeat 0 50%; + margin-left:20px; +} + +#app-content{ + color:#666; + text-align:center; + height: 100%; + /*position: fixed; + top: 0px; + bottom: 0px; + left: 0px; + right: 0px;*/ +} + diff --git a/widgets/citrix/sync-wui/themes/tundra/ArrowIcons.css b/widgets/citrix/sync-wui/themes/tundra/ArrowIcons.css new file mode 100644 index 0000000..1dc3255 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/ArrowIcons.css @@ -0,0 +1,22 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .arrowIcon { + background: url(/images/sprites/arrowIcons.png) no-repeat 0 0; + width: 10px; + height: 10px; +} + +.tundra .arrowIcon:hover { + cursor: pointer; +} + +.tundra .citrixArrowIconFocused .arrowIcon { background-position-x: -10px; } + +.tundra .arrowIconUp { background-position-y: 0px; } +.tundra .arrowIconDown { background-position-y: -10px; } +.tundra .arrowIconLeft { background-position-y: -20px; } +.tundra .arrowIconRight { background-position-y: -30px; } \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/Assets.css b/widgets/citrix/sync-wui/themes/tundra/Assets.css new file mode 100644 index 0000000..7ca7e92 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/Assets.css @@ -0,0 +1,195 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + + +/**** BUTTONS ****************************************************/ + +.gridButtons{ + padding:0; + text-align:left; +} + +.blue-button { + -moz-box-shadow:inset 0px 1px 0px 0px #cbdb9e; + -webkit-box-shadow:inset 0px 1px 0px 0px #cbdb9e; + box-shadow:inset 0px 1px 0px 0px #cbdb9e; + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #aab63e), color-stop(1, #346712) ); + background:-moz-linear-gradient( center top, #aab63e 5%, #346712 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#aab63e', endColorstr='#346712'); + background-color:#aab63e; + -moz-border-radius:1px; + -webkit-border-radius:1px; + border-radius:1px; + border:1px solid #000; + display:inline-block; + color:#ffffff; + font-size:13px; + font-weight:bold; + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + padding:8px 20px; + text-decoration:none; + text-shadow:1px 1px 0px #484f3c; +} +.blue-button:hover { + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #346712), color-stop(1, #aab63e) ); + background:-moz-linear-gradient( center top, #346712 5%, #aab63e 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#346712', endColorstr='#aab63e'); + background-color:#346712; +} +.blue-button:active { + position:relative; + top:1px; +} + +.tundra .imageButton { + height: auto; +} + +.tundra .imageButton>span { + cursor: pointer; +} + +.symphony-button>span, +.symphony-button.dijitButtonHover>span, +.dijitComboButtonHover .symphony-button .dijitButtonNodeHover, +.dijitComboButtonHover .symphony-button .dijitDownArrowButtonHover { + -moz-box-shadow:inset 0px 1px 0px 0px #ffffff; + -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff; + box-shadow:inset 0px 1px 0px 0px #ffffff; + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ffffff), color-stop(1, #e5e5e5) ); + background:-moz-linear-gradient( center top, #ffffff 5%, #e5e5e5 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e5e5e5'); + background-color:#ffffff; + -moz-border-radius:2px; + -webkit-border-radius:2px; + border-radius:2px; + border:1px solid #999999; + display:inline-block; + color:#333333; + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + padding:8px 20px; + text-decoration:none; + cursor:pointer; +} + +.symphony-button.dijitButtonFocused>span, +.dijitComboButtonFocused .symphony-button .dijitButtonNodeFocused, +.dijitComboButtonFocused .symphony-button .dijitDownArrowButtonFocused { + -moz-box-shadow: 0px 0px 7px 1px #4197c5; + -webkit-box-shadow: 0px 0px 7px 1px #4197c5; + box-shadow: 0px 0px 7px 1px #4197c5; + border:1px solid #38578f; +} + +.symphony-button.dijitButtonActive>span, +.dijitComboButtonActive .symphony-button .dijitButtonNodeActive, +.dijitComboButtonActive .symphony-button .dijitDownArrowButtonActive { + -moz-box-shadow:inset 0px 0px 7px 1px #999799; + -webkit-box-shadow:inset 0px 0px 7px 1px #999799; + box-shadow:inset 0px 0px 7px 1px #999799; + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #e1e2e3), color-stop(1, #f0f0f0) ); + background:-moz-linear-gradient( center top, #e1e2e3 5%, #f0f0f0 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e1e2e3', endColorstr='#f0f0f0'); + background-color:#e1e2e3; + border:1px solid #5f5f5f; +} + +.symphony-button.dijitButtonDisabled>span, +.dijitComboButtonDisabled .symphony-button>span { + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ffffff), color-stop(1, #efefef) ); + background:-moz-linear-gradient( center top, #ffffff 5%, #efefef 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#efefef'); + background-color:#ffffff; + -moz-border-radius:2px; + -webkit-border-radius:2px; + border-radius:2px; + border:1px solid #dbdbdb; + display:inline-block; + color:#adadad; + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + padding:8px 20px; + text-decoration:none; +} + +.dijitComboButton .symphony-button .dijitArrowButton { + margin-left: -2px; + padding: 8px; +} + +.dijitComboButton .symphony-button .dijitDownArrowButton { + padding:1px 0px 0px; + display: block; +} + +.dj_chrome .dijitComboButton .symphony-button .dijitDownArrowButton { + padding:0px; + display: block; +} + +.dijitComboButton .symphony-button .dijitDownArrowButton .dijitArrowButtonInner { + margin:2px 0px 3px; +} + +/**** TABLES ****************************************************/ + +.tundra .dgrid{ + height: 100%; +} + +.tundra .dgrid-cell{ + padding:10px; + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + color:#666; + border:none; +} + +.tundra th.dgrid-cell{ + border-bottom:1px solid #BFBFBF; + border-right:1px solid #BFBFBF; + font-weight:bold; + color:#666; + -moz-box-shadow:inset 0px 1px 0px 0px #ffffff; + -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff; + box-shadow:inset 0px 1px 0px 0px #ffffff; + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ffffff), color-stop(1, #e5e5e5) ); + background:-moz-linear-gradient( center top, #ffffff 5%, #e5e5e5 100% ); + /* No IE filter background because it breaks the lines between cells in the header */ +} + +.tundra .dgrid-row:hover{ + background:#E6F1F7; +} + +.tundra .dgrid-row-even{ + background:#fff; +} + +.tundra .dgrid-row-odd{ + background:#F5F5F5; +} + +.tundra .dgrid-row-table { + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} + +.tundra .dgrid-selected .dgrid-row-table{ + border-top:1px solid #156BB8; + border-bottom:1px solid #156BB8; + background:#4A94D0; + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #4A94D0), color-stop(1, #156BB8) ); + background:-moz-linear-gradient( center top, #4A94D0 5%, #156BB8 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#4A94D0', endColorstr='#156BB8'); + background-color:#4A94D0; +} + +.tundra .dgrid-selected .dgrid-row-table .dgrid-cell{ + color:#fff; + font-weight:bold; +} + + + diff --git a/widgets/citrix/sync-wui/themes/tundra/BadgeIcons.css b/widgets/citrix/sync-wui/themes/tundra/BadgeIcons.css new file mode 100755 index 0000000..4ae49d9 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/BadgeIcons.css @@ -0,0 +1,21 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .badgeIcon { + background: url(/images/sprites/badgeIcons.png) no-repeat 0 0; + width: 24px; + height: 20px; + text-align: center; + display: inline-block; +} + +/* Firefox doesn't understand background-position-y */ +.tundra .badgeIconManaged { background-position: 0 0; } +.tundra .badgeIconThreed { background-position: 0 -20px; } +.tundra .badgeIconPublish { background-position: 0 -40px; } +.tundra .badgeIconNative { background-position: 0 -60px; } +.tundra .badgeIconEncrypted { background-position: 0 -80px; } +.tundra .badgeIconDiskReset { background-position: 0 -100px; } \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/Common.css b/widgets/citrix/sync-wui/themes/tundra/Common.css new file mode 100644 index 0000000..aaab97e --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/Common.css @@ -0,0 +1,61 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +/************************************************ +GENERAL STYLES +************************************************/ + +body { + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + margin: 0; + } + +.hero{ + width:704px; + height:878px; + position:absolute; + top:49px; + left:79px; + background: url("/images/symphony/DesktopAppsHeroImage_704x878.png") no-repeat scroll 0px 0px transparent +} + +.title { + font-size: 28px; + color: #fff; + padding: 10px; +} + +.user { + font-size: 14px; + color: #ddd; + padding: 10px; +} + +.brand_footer { + background: url("/images/symphony/BrandCorridorBackground_800x93.png") repeat-x; + height: 93px; + position: fixed; + bottom: 0; + width: 100%; + z-index: 10; +} + + +/************************************************ +MISC. +************************************************/ + +/* new clearfix */ +.clearfix:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +* html .clearfix { zoom: 1; } /* IE6 */ +*:first-child+html .clearfix { zoom: 1; } /* IE7 */ \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/DeviceGrid.css b/widgets/citrix/sync-wui/themes/tundra/DeviceGrid.css new file mode 100644 index 0000000..7c5db0a --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/DeviceGrid.css @@ -0,0 +1,96 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .dgrid .field-select { + width: 50px; +} + +.tundra .dgrid .field-report_time { + width: 200px; +} + +.tundra .dgrid .expandoButtonCell { + padding: 0px; + width: 45px; +} + +.tundra .dgrid .expandoButtonCell .imageButton .dijitStretch { + width: 40px; + height: 30px; + display: block; +} + +.tundra .dgrid .expandoButtonCell .imageButton .expandoIcon { + margin: 7px 12px; +} + +.tundra .expando { + display: block; + min-height: 100px; +} + +.tundra .collapsed .expando { + display: none; +} + +.tundra .expandoContent>div { + margin: 10px; + border: 1px black solid; + padding: 3px; + height: 200px; + float: left; +} + +.tundra .expandoContent .citrixImageSelect { + padding: 0; + clear: both; +} + +.tundra .expandoContent .citrixImageItem { + padding-bottom:5px; + border:none; + border-bottom:5px solid #fff; + -webkit-border-radius: 0px; + border-radius: 0px; + width: 60px; + float: left; + position: relative; +} + +.tundra .expandoContent .citrixImageItem img { + display: block; + margin: auto; +} + +.tundra .expandoContent .citrixImageItem .lockIcon { + display: none; +} + +.tundra .expandoContent .locked.citrixImageItem .lockIcon { + display: block; + position: absolute; + top: 10px; + left: 21px; + width: 20px; + height: 20px; + background: url(/images/icons/lock.png) no-repeat 0 0; +} + +.tundra .expandoContent .locked.citrixImageItem img { + opacity: 0.5; +} + +.tundra .expandoContent .citrixImageItem.dijitHover { + border-bottom-color: #ccc; +} + +.tundra .expandoContent .citrixImageItem.dijitSelected { + border-bottom-color: #4A94D0; +} + +.tundra .deviceWidgetContainer .dgrid .field-actions { + width: 200px; +} diff --git a/widgets/citrix/sync-wui/themes/tundra/Dialog.css b/widgets/citrix/sync-wui/themes/tundra/Dialog.css new file mode 100644 index 0000000..846d47b --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/Dialog.css @@ -0,0 +1,125 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .dijitDialogUnderlay{ + opacity:0.7; + background:#000; +} + +.tundra .citrixDialog { + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + background: #ffffff; /* Old browsers */ + background: -moz-radial-gradient(center, ellipse cover, #ffffff 60%, #daedf2 95%, #c1eaf6 100%); /* FF3.6+ */ + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(60%,#ffffff), color-stop(95%,#daedf2), color-stop(100%,#c1eaf6)); + /* Chrome,Safari4+ */ + background: -webkit-radial-gradient(center, ellipse cover, #ffffff 60%,#daedf2 95%,#c1eaf6 100%); /* Chrome10+,Safari5.1+ */ + background: -o-radial-gradient(center, ellipse cover, #ffffff 60%,#daedf2 95%,#c1eaf6 100%); /* Opera 12+ */ + background: -ms-radial-gradient(center, ellipse cover, #ffffff 60%,#daedf2 95%,#c1eaf6 100%); /* IE10+ */ + background: radial-gradient(ellipse at center, #ffffff 60%,#daedf2 95%,#c1eaf6 100%); /* W3C */ + /* Because IE only does horizontal radius, dialogs look better with just a plain white background */ + padding:30px; + color:#999; +} + +.tundra .citrixGridDialog { + height: 600px; + width: 900px; +} + +.tundra .citrixGridDialog .citrixDialogPaneContent { + height: 520px; +} + +.tundra .citrixGridDialog .citrixDialogFooterContent { + float: right; +} + +.tundra .citrixDialogError { + /*background-color: transparent; - zcc commented out because in IE8 the dialog forms inline a filter attribute with an opacity one which ignores the css above so we need there to be a background colour*/ +} + +.tundra .citrixDialog .center { + text-align: center; +} + +.tundra .citrixDialogTitleBar { + background:none; + color:#474747; + padding-bottom:20px; +} + +.tundra .citrixDialogTitleIcon { + max-height: 30px; + float: left; + padding-top: 5px; +} + +.tundra .citrixDialogTitle { + font-size: 14px; + font-weight:bold; + line-height: 38px; + padding: 0 10px; +} + + +.tundra .citrixDialogCloseIcon { + cursor: pointer; + background: url("/images/icons/close_dialog.png") no-repeat center center; + height: 11px; + width: 11px; + display:block; + float:right; +} +.tundra .citrixDialogCloseIconHover { +} + + +.tundra .citrixDialogPaneContent { + padding: 0px; + background:none; +} + +.tundra .vmDetailActions{ + padding:0; + margin:0; + height:auto; + width:auto; +} + +.tundra .citrixDialogPaneContent h2{ + margin:0 0 15px 0; + padding:0 0 10px 0; + border-bottom:1px solid #eee; + color:#474747; + +} + +.tundra .citrixDialogFooterContent { + background:none; + padding-top:10px; +} + +.tundra .citrixDialogFooter { +} + +.tundra .citrixDialogFooterItem { +} + +.tundra .dijitselect, .tundra .dijittextbox{ + height:26px; + line-height:26px; + border-radius:0; + border:1px solid #999; + background:fff; +} + +.tundra .citrixTabPaneField{ + margin-bottom:10px; +} + +.tundra .citrixTabPaneField label{ + padding-top:15px; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/DiskGrid.css b/widgets/citrix/sync-wui/themes/tundra/DiskGrid.css new file mode 100644 index 0000000..b86d5d8 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/DiskGrid.css @@ -0,0 +1,16 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .dgrid .field-disk_type, +.tundra .dgrid .field-read_only, +.tundra .dgrid .field-shared, +.tundra .dgrid .field-num_vms { + width: 110px; +} + +.tundra .dgrid-content .field-num_vms { + text-align: center; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/Expando.css b/widgets/citrix/sync-wui/themes/tundra/Expando.css new file mode 100644 index 0000000..fc4f8fd --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/Expando.css @@ -0,0 +1,62 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .expando{ + padding:10px 20px; + background:#F5F5F5; + -moz-box-shadow:inset 0 0 10px #999; + -webkit-box-shadow:inset 0 0 10px #999; + box-shadow:inset 0 0 10px #999; + border-bottom:5px solid #4A94D0; +} + + + +.tundra .expandoContent>div{ + padding:10px; + -moz-border-radius:5px; + -webkit-border-radius:5px; + border-radius:5px; + border:1px solid #dbdbdb; + background:#fff; + color:#474747; + height:auto; + +} + +.tundra .expandoContent .ExpandoPlatformSettings{ + text-align:left; + margin:10px; +} + +.tundra .expandoContent .ExpandoPlatformSettings label{ + color:#666; +} + +.tundra .expandoContent .ExpandoPlatformSettings .settingDetail{ + margin:5px 0; +} + +.tundra .expandoContent h2{ + margin:0 0 15px 0; + padding:0; + text-align:left; +} + +.tundra .expandoContent .actions{ + text-align:left; + margin: 20px 0 10px 0; +} + +.tundra .imageButton .expandoIcon { + background: url(/images/backgrounds/expanded.png) no-repeat 0 0; + height: 16px; + width: 16px; +} + +.tundra .collapsed .expandoIcon { + background: url(/images/backgrounds/collapsed.png) no-repeat 0 0; +} diff --git a/widgets/citrix/sync-wui/themes/tundra/Grid.css b/widgets/citrix/sync-wui/themes/tundra/Grid.css new file mode 100644 index 0000000..766a38a --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/Grid.css @@ -0,0 +1,49 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .gridContainer { + display: block; + height: 100%; + width: 100%; + position: relative; +} + +.tundra .gridHeader { + position: absolute; + top: 0px; + left: 0px; + right: 0px; +} + +.tundra .gridHeader.gridButtons .dijitSelect { + width: 200px; + margin-bottom: 19px; +} + +.tundra .gridBody { + position: absolute; + top: 55px; + bottom: 0px; + left: 0px; + right: 0px; +} + +.tundra .gridBody.fullHeight { + top: 0px; +} + +.tundra .gridHeader .symphony-button.dijitButton, +.tundra .gridHeader .dijitComboButton { + margin: 2px 2px 20px; +} + +.tundra .gridHeader .filterBox { + float:right; +} + +.tundra .gridHeader .filterBox .dijitTextBox { + margin-bottom: 19px; +} diff --git a/widgets/citrix/sync-wui/themes/tundra/ImageSelect.css b/widgets/citrix/sync-wui/themes/tundra/ImageSelect.css new file mode 100644 index 0000000..c60e9c4 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/ImageSelect.css @@ -0,0 +1,53 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixImageSelect { + margin: 0; + /*padding: 0; We want to keep the list style left pad */ + overflow: auto; +} + +.tundra .citrixImageItem { + border: 10px solid transparent; +} + +.tundra .citrixImageItem img { + opacity: 1.0; + vertical-align: middle; +} + +.tundra .dijitDisabled.citrixImageItem img { + opacity: 0.5; +} + +.tundra .dijitSelected.citrixImageItem img { + opacity: 1.0; +} + +.tundra .citrixImage100 img { + width: 100px; +} + +.tundra .citrixImage85 img { + width: 85px; +} + +.tundra .citrixImage75 img { + width: 75px; +} + +.tundra .citrixImage65 img { + width: 65px; +} + +.tundra .citrixImage55 img { + width: 55px; +} + +.tundra .citrixImage45 img { + width: 45px; + height: 45px; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/Logon.css b/widgets/citrix/sync-wui/themes/tundra/Logon.css new file mode 100644 index 0000000..f4e2d5c --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/Logon.css @@ -0,0 +1,135 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + + +/************************************************ +LOGON SCREEN STYLES +************************************************/ + +#logon-screen-wrapper{ + background: url("/images/symphony/DesktopAndAppsGradient_1255x900.jpg") no-repeat scroll center top #004E1C; /* Old browsers */ + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyYWRpYWxHcmFkaWVudCBpZD0iZyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjQjNDMDM0Ii8+PHN0b3Agb2Zmc2V0PSIwLjM3IiBzdG9wLWNvbG9yPSIjNjlBMzMwIi8+PHN0b3Agb2Zmc2V0PSIwLjcyIiBzdG9wLWNvbG9yPSIjMjc3ODE4Ii8+PHN0b3Agb2Zmc2V0PSIwLjgyIiBzdG9wLWNvbG9yPSIjMjA2OTE4Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA0RTFDIi8+PC9yYWRpYWxHcmFkaWVudD48cmVjdCB4PSIwJSIgeT0iMCUiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiMwMDRFMUMiIC8+PHJlY3QgeD0iMCUiIHk9Ii00MCUiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjE0MCUiIGZpbGw9InVybCgjZykiIC8+PC9zdmc+); + background: -moz-radial-gradient(50% 30%, ellipse farthest-side, #B3C034 0%, #69A330 37%, #277818 72%, #206918 82%, #004E1C 100%); /* FF3.6+ */ + background: -webkit-gradient(radial, 50% 30%, 0px, 50% 30%, 314, color-stop(0%,#B3C034), color-stop(37%,#69A330), color-stop(72%,#277818), color-stop(82%,#206918), color-stop(100%,#004E1C)); /* Chrome,Safari4+ */ + background: -webkit-radial-gradient(50% 30%, ellipse farthest-side, #B3C034 0%,#69A330 37%,#277818 72%,#206918 82%,#004E1C 100%); /* Chrome10+,Safari5.1+ */ + background: -o-radial-gradient(50% 30%, ellipse farthest-side, #B3C034 0%,#69A330 37%,#277818 72%,#206918 82%,#004E1C 100%); /* Opera 12+ */ + background: -ms-radial-gradient(50% 30%, ellipse farthest-side, #B3C034 0%,#69A330 37%,#277818 72%,#206918 82%,#004E1C 100%); /* IE10+ */ + background: radial-gradient(50% 30%, ellipse at center, #B3C034 0%,#69A330 37%,#277818 72%,#206918 82%,#004E1C 100%); /* W3C */ + width: 100%; + min-width: 800px; + height: 100%; + min-height: 800px; + overflow: hidden; + margin: 0; + padding: 0; + +} + +#logon-screen{ + width: 100%; + height:100%; + background: url("/images/symphony/Brand_OverlaySquarePattern_4x4.png") repeat 0 0 transparent; + +} + +.stripe { + position:absolute; + top:41%; + margin-top: -155px; + vertical-align:center; + width:100%; + padding-bottom:20px; + padding-top:30px; + display:block; + z-index:20; + background: rgba(2,27,10,0.8); +} + + +.logon { + margin:60px auto; + width:700px; + color: #fff; + +} + +.logon .logo { + width: 343px; + margin:-4px 35px 0 0; + float:left; +} + +.logon .logo .logo-xc{ + display:block; + width:343px; + height:101px; + background: url("/images/symphony/wordmark_xc_logo.png") no-repeat top left; + overflow:hidden; + text-indent:-99999px; +} + +.logon form{ + width: 295px; + float:left; + margin-left:25px; + text-align:left; +} + +.logon h1{ + margin:0; + padding:0; +} + +.logon .field { + padding-bottom: 10px; +} + +.logon label { + display: block; + font-size: 12px; + line-height: 25px; + width:200px; + padding:0; +} + + +.logon .value input { + border: 1px solid #ccc; + width: 200px; + font-size:13px; + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + height:27px; +} + + +.logon .textinput input:focus{ + border:1px solid #15508c; +} + + +.logon .value button { + margin-left:-1px; +} + +.logon a:link { + color:#0467D1; +} + +.logon .message{ + display:block; + background: url("/images/icons/logon-error.jpg") no-repeat center left #FCF1E9; + color:#E67125; + padding-left:40px; + -moz-border-radius:2px; + -webkit-border-radius:2px; + border-radius:3px; + border:1px solid #E67125; + height:40px; + line-height:40px; + font-weight:bold; + +} diff --git a/widgets/citrix/sync-wui/themes/tundra/MainTabContainer.css b/widgets/citrix/sync-wui/themes/tundra/MainTabContainer.css new file mode 100644 index 0000000..4fb48fe --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/MainTabContainer.css @@ -0,0 +1,119 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + + + + +/************************************************ +MAIN APP TAB STYLES +************************************************/ + +.tundra .citrixMainTab{ + position: absolute; + top: 60px; + bottom: 0px; + left: 0px; + right: 0px; +} + + +#app-content .citrixMainTab .dijitTabController { + margin:0 25px; + padding-left:10px; + padding-right:10px; + + border-top:1px solid #666; + border-left:1px solid #666; + border-right:1px solid #666; + border-bottom:none; + + -webkit-border-top-left-radius: 6px; + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topleft: 6px; + -moz-border-radius-topright: 6px; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + + background: #474747; /* Old browsers */ + background: -moz-linear-gradient(top, #474747 0%, #202020 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#474747), color-stop(100%,#202020)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #474747 0%,#202020 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #474747 0%,#202020 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #474747 0%,#202020 100%); /* IE10+ */ + background: linear-gradient(to bottom, #474747 0%,#202020 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#474747', endColorstr='#202020',GradientType=0 ); /* IE6-9 */ + + +} + +.tundra .citrixMainTab .dijitTabPaneWrapper{ + border-top:3px solid #3A7224; + background:#EAEAEA; +} + +.tundra .citrixMainTab .dijitTabPane{ + background:#fff; + margin-left:25px; + margin-right:25px; + padding:20px 25px; +} + +.tundra .citrixMainTabButton { + margin:10px 10px 0 10px; + padding:15px 25px 25px 25px; + float:left; + -webkit-border-top-left-radius: 6px; + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topleft: 6px; + -moz-border-radius-topright: 6px; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + + background:none; + border:1px; + border-style:solid; + border-color:transparent; + border-bottom:none; + color:#fff; + font-weight:bold; + font-size:14px; + + +} +.tundra .citrixMainTabButton:hover { + border:1px solid #242424; + border-bottom:none; + background: #535353; /* Old browsers */ + background: -moz-linear-gradient(top, #535353 0%, #2e2e2e 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#535353), color-stop(100%,#2e2e2e)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #535353 0%,#2e2e2e 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #535353 0%,#2e2e2e 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #535353 0%,#2e2e2e 100%); /* IE10+ */ + background: linear-gradient(to bottom, #535353 0%,#2e2e2e 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#535353', endColorstr='#2e2e2e',GradientType=0 ); /* IE6-9 */ + + +} + +.tundra .citrixMainTabButton.dijitChecked{ + background:#fff; + color:#333; + border:1px solid #fff; + border-bottom:none; + margin:10px 10px 0 10px; + padding:15px 25px 25px 25px; +} + +.tundra .citrixMainTabButton.dijitChecked:hover{ + background:#fff; + color:#333; + border:1px solid #fff; + border-bottom:none; + margin:10px 10px 0 10px; + padding:15px 25px 25px 25px; +} + + diff --git a/widgets/citrix/sync-wui/themes/tundra/MediaWizard.css b/widgets/citrix/sync-wui/themes/tundra/MediaWizard.css new file mode 100755 index 0000000..611f4c0 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/MediaWizard.css @@ -0,0 +1,11 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .citrixWizard .citrixImageSelect { + width: 700px; + padding: 0px; + height: 285px; +} diff --git a/widgets/citrix/sync-wui/themes/tundra/Settings.css b/widgets/citrix/sync-wui/themes/tundra/Settings.css new file mode 100755 index 0000000..58b86a0 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/Settings.css @@ -0,0 +1,144 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .settingsTabContainer { + width: 900px; + height: 600px; +} + +.tundra .settingsTabContainer .citrix.dijitSelect { + width: 200px !important; +} + +.tundra .settingsTabContainer .citrixTabPaneField{ + margin-bottom:50px; +} + +.tundra .settingsTabContainer .citrixTabPaneField label { + min-width: 150px; + line-height:20px; +} + +.tundra .settingsTabContainer .citrixImageSelect{ + height:auto; + margin:20px 0; + padding:0; + max-height: 420px; +} + +.tundra .settingsTabContainer .citrixImageSelect .citrixImageItem{ + -webkit-border-image:none; + border:5px solid #eee; + margin:0 20px 20px 0; + -webkit-border-radius: 3px; + border-radius: 3px; +} + +.tundra .settingsTabContainer .citrixImageSelect .citrixImageItemHover{ + border:5px solid #ccc; + cursor:pointer; +} + + +.tundra .settingsTabContainer .citrixImageSelect .citrixImageItemSelected{ + border:5px solid #4A94D0; +} + +.tundra .citrixDialogPaneContent .wallpaperOverrideTitle{ + margin-bottom:20px; +} + +.tundra .citrixTabPaneField.wallpaperOverride label{ + padding-left:0; +} + + +.tundra .customDataEntry{ + + padding-bottom:10px; + margin-bottom:20px; + border-bottom:1px solid #ddd; +} + +.tundra .customDataTable{ + + height:340px; + width:100%; + overflow-y:auto; +} + +.tundra .speedContainer { + line-height: 20px; + display: inline-block; + text-align: center; + margin: 0 5px; +} + +.tundra .speedRadio { + display: block; +} + +.tundra .gpuPlacementFooter { + font-size: 14px; + margin: -15px 15px 0; + background: white; +} + +.tundra .actionButton { + padding: 10px 0; +} + +.tundra .updateButton { + margin: 0; +} + +.tundra .update { + height: 33px; + overflow: hidden; +} + +.tundra .updateText { + float: left; + width: 280px; +} + +.tundra .update .dijitTextBox { + width: 230px; +} + +.tundra .updateProgressDetails { + width: 230px; + display: inline-block; +} + +.tundra .nativeItem { + padding: 3px 0 0 17px; +} + +.tundra .nativeItem label { + margin-left: 2px; +} + +.tundra .customValueRow .dijitTextBox.citrix { + width: 180px !important; +} + +.tundra .removedConfig { + display: none; +} + +.tundra .selectUserButton.symphony-button.dijitButton { + margin: 0px; +} + +.tundra .selectUserButton.symphony-button > span, +.tundra .removeUserButton.symphony-button > span { + padding: 5px 20px; +} + +.tundra .settingsTabContainer .citrixTabPaneField .value { + margin-left: -5px; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/TabContainer.css b/widgets/citrix/sync-wui/themes/tundra/TabContainer.css new file mode 100644 index 0000000..cdd8a74 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/TabContainer.css @@ -0,0 +1,252 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +/************************************************ +DIALOG TAB STYLES +************************************************/ + +.tundra .dijitContentPane{ + background:none; + margin:0; + padding:0; +} + +.tundra .citrixTabWrapper { + border:none; + border-bottom:1px solid #eee; + padding:0; + margin:0; + color:#474747; + font-size:13px; + font-family: 'Segoe UI',Helvetica Neue,Helvetica, Arial, sans-serif; + +} + +.tundra .citrixTabWrapper.dijitFocused { + padding:0; + margin:0; + border:0; +} + + + +.tundra .citrixDialog .dijitContentPane { + padding:0px 20px 10px 20px; + +} + +.tundra .citrixTabPaneWrapper { + border: none; + margin: 0; + padding: 0; + font-size: 13px; +} + +.tundra .citrixTab { + cursor: pointer; + background:none; + border: none; + border-top:1px solid #eee; + font-size:13px; + padding:3px 0; + +} + + + +/* hovered tab */ +.tundra .citrixTabHover { + color:#000; + background:#E6F1F7; +} + +/* selected */ +.tundra .citrixTabChecked { + border: none; + border-top:1px solid #156BB8; + border-bottom:1px solid #156BB8; + background:#4A94D0; + background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #4A94D0), color-stop(1, #156BB8) ); + background:-moz-linear-gradient( center top, #4A94D0 5%, #156BB8 100% ); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#4A94D0', endColorstr='#156BB8'); + background-color:#4A94D0; + color:#fff; + font-weight: bold; +} + +.tundra .citrixTabInner { + padding: 0 20px; +} + +.tundra .citrixTab .tabLabel { + font-size: 13px; +} + +.tundra .citrixTabSpacer { + width:5px; +} + +.tundra .citrixDialogPaneContent h1 { + font-size: 13px; + line-height: 29px; + font-weight: normal; + padding: 0; + color: rgb(23, 123, 173); + margin: 0; +} + +.tundra .citrixDialogPaneContent p { + font-size: 13px; + line-height: 14px; + margin: 8px 0; +} + +.tundra .citrixTabPaneColumn { + border: 0; + margin: 0; + padding: 0; +} + +.tundra .citrixTabPaneColumn.single { + clear: both; + + +} + + +.tundra .citrixTabPaneHR { + clear: both; +} + +.tundra .citrixTabPaneField { + display: table-row; + height: 40px; + font-size: 13px; +} + +.tundra .citrixTabPaneField.spacer { + height: 20px; +} + +.tundra .citrixTabPaneField .value, +.tundra .citrixTabPaneField .valueContainer { + margin-top: 3px; + display: inline-block; + word-wrap: break-word; +} + +.tundra .citrixTabPaneField .value { + white-space: pre-wrap; +} + +.tundra .citrixTabPaneField .value.textArea { + overflow-y: auto; + width: 333px; + height: 91px; +} + +.tundra .citrixTabPaneField .dijitButton { + margin: 0 10px; +} + +.tundra .citrixTabPaneField .citrixProgressBar { + width: 200px; +} + +.tundra .citrixTabPaneField.radio { + height: 25px; +} + +.tundra .citrixTabPaneField.radioInfo { + height: auto; +} + +.tundra .citrixTabPaneField.radioInfo label { + padding-left: 21px; + padding-bottom: 2px; + font-size: 13px; +} + +.tundra .citrixTabPaneField.radio .dijitRadio, +.tundra .citrixTabPaneField.radio .dijitCheckBox { + padding-top: 5px; +} + +.tundra .citrixTabPaneField.radio label.inline { + display: inline-block; + padding-left: 5px; +} + +.tundra .citrixTabPaneField label { + display: table-cell; + vertical-align: top; + color: #666; + padding: 3px 15px 0 15px; + min-width: 90px; +} + +.tundra .citrixTabPaneField .grey { + color: rgb(142, 142, 142); +} + +.tundra .citrixTabPaneField .editTip { + padding-left: 10px; +} + +.tundra .citrixTabPaneField.radio .editTip { + padding-left: 21px; + max-width: 450px; +} + +.tundra .citrixTabPaneField.radio .specalPony { + margin: 0 2px 10px 21px; +} + +.tundra .citrixTabPaneField .instruction { + padding: 5px 0 20px; +} + +.tundra .citrix.dijitTextBox { + width: 335px !important; +} + +.tundra .citrix.dijitSelect { + width: 250px !important; +} + +.tundra .citrix.dijitSelect .dijitSelectLabel { +} + +.tundra .citrix.dijitTextArea { + width: 330px !important; + height: 90px; +} + +.tundra .citrix.dijitSpinner { + width: 65px !important; +} + +.tundra .citrix.dijitSpinner.longNumber { + width: 200px !important; +} + + + +.tundra .citrixTabPaneColumn.double { + float: left; + width: 50%; +} + +.tundra .citrixTabPaneColumn.double .value { +} + +.tundra .citrixTabPaneColumn.double .citrix.dijitTextBox, +.tundra .citrixTabPaneColumn.double .citrix.dijitTextArea, +.tundra .citrixTabPaneColumn.double .citrix.dijitSelect { +} + +.tundra .citrixTabPaneColumn.double .citrix.dijitSelect .dijitSelectLabel { +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/TooltipIcons.css b/widgets/citrix/sync-wui/themes/tundra/TooltipIcons.css new file mode 100644 index 0000000..73cf041 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/TooltipIcons.css @@ -0,0 +1,57 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.dj_webkit .tundra .dijitTooltip { + max-width: 400px; +} + +.dj_webkit .tundra .dijitTooltipContainer { + border: 1px solid #666; + background: rgb(244, 244, 244); + padding: 15px; + -webkit-box-shadow: 3px 3px 3px #CCC; + -webkit-border-radius: 5px; +} + +.dj_webkit .tundra .citrixTooltip .dijitTooltipContainer { + border: 1px solid rgb(187, 191, 196); + word-wrap: break-word; + -webkit-box-shadow: rgba(0, 0, 0, 0.45) 0 1px 3px; + background: rgb(246, 247, 249) url(/images/backgrounds/075_NotificationCenter_h32bit_1x90.png) repeat-x 0 bottom; +} + +.tundra .dijitTooltipAbove .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 0; +} + +.tundra .dijitTooltipBelow .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 -16px; +} + +.tundra .dijitTooltipLeft .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 -32px; +} + +.tundra .dijitTooltipRight .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 -48px; +} + +.tundra .dijitTooltipAbove.citrixTooltip .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 -64px; + bottom: 2px; +} + +.tundra .dijitTooltipBelow.citrixTooltip .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 -80px; +} + +.tundra .dijitTooltipLeft.citrixTooltip .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 -96px; +} + +.tundra .dijitTooltipRight.citrixTooltip .dijitTooltipConnector { + background: url(/images/sprites/tooltipIcons.png) no-repeat 0 -112px; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/VMDetails.css b/widgets/citrix/sync-wui/themes/tundra/VMDetails.css new file mode 100755 index 0000000..2068c8f --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/VMDetails.css @@ -0,0 +1,141 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +/* preload images */ +body:after { + display: none; + content: url(/images/borders/system_dialog.png) url(/images/borders/system_panel.png) url(/images/borders/system_panel_focus.png); +} + +.tundra .vmDetails .scrollableTab { + max-height: 530px; + overflow-y: auto; +} + +.tundra .vmDetails .scrollableTabSpace { + max-height: 530px; + overflow-y: auto; +} + +.tundra .vmDetails .alertContainer { + text-align: center; + margin-bottom: 8px; +} + +.tundra .detailsTabContainer { + width: 900px; + height: 600px; +} + +.tundra .detailsTabContainer .citrixTabPaneField label { + min-width: 150px; + max-width: 190px; +} + +.tundra .detailsTabContainer .citrixImageSelect{ + height:auto; + margin:20px 0; + padding:0; + max-height: 420px; +} + +.tundra .detailsTabContainer .citrixImageSelect .citrixImageItem{ + -webkit-border-image:none; + border:5px solid #eee; + margin:0 20px 20px 0; + -webkit-border-radius: 3px; + border-radius: 3px; +} + +.tundra .detailsTabContainer .citrixImageSelect .citrixImageItemHover{ + border:5px solid #ccc; + cursor:pointer; +} + + +.tundra .detailsTabContainer .citrixImageSelect .citrixImageItemSelected{ + border:5px solid #4A94D0; +} + +.tundra .vmDetailActions { + height: 65px; + display: inline-block; +} + +.tundra .vmTransferDetails { + font-size: 13px; +} + +.tundra .vmTransferSeparator { + padding-bottom: 10px; +} + +.tundra .vmTransferTitle { + font-size: 14px; + position: absolute; + margin: -25px 0 0 22px; + background: white; +} + +.tundra .vmTransferText { + text-transform: capitalize; +} + +.tundra .vmTransferInfo { + padding-left: 10px; +} + +.tundra .citrixTabPaneField .citrixProgressBar.vmProgressDetails { + width: 420px; + display: inline-block; +} + +.tundra .vmConnectedDevices thead tr th:nth-child(1) { + min-width: 200px; +} + +.tundra .vmConnectedDevices .deviceName { + width: 249px; + word-wrap: break-word; + display: inline-block; + white-space: pre; +} + +.tundra .vmConnectedDevices .deviceRow +.tundra .vmNics .nicRow { + padding-left: 10px; +} + +.tundra .versionDescription { + height: 30px; + overflow: auto; +} + +.tundra .vmDevices { + overflow: auto; +} + +.tundra .vmDevices .citrix.dijitTextBox { + width: 250px !important; + margin: 0 0 0 -5px !important; +} + +.tundra .vmDetails .citrixTabPaneField .editTip { + padding-top: 2px; + position: absolute; +} + +.tundra .vmDisks .dijitSelect { + width: 110px !important; +} + +.tundra .networkEntry .networkNumber { + width: 50px; +} + +.tundra .networkEntry .nic { + margin-right: 10px; +} \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/VMGrid.css b/widgets/citrix/sync-wui/themes/tundra/VMGrid.css new file mode 100644 index 0000000..c1b4cf4 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/VMGrid.css @@ -0,0 +1,46 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .dgrid .field-icon { + width: 50px; +} + +.tundra .dgrid .field-num_vm_instances { + width: 200px; +} + +.tundra .vmWidgetContainer .dgrid .field-actions { + width: 330px; +} + +.tundra .dgrid-content .field-icon { + padding: 2px 10px; +} + +.tundra .dgrid-content .field-num_vm_instances, +.tundra .dgrid-content .field-actions { + text-align: center; +} + +.tundra .dgrid-content .field-actions { + padding: 2px 10px; +} + +.tundra .dgrid-content .symphony-button > span { + padding: 5px 20px; +} + +.tundra .dgrid-content .vmGridImage { + height: 30px; +} + +/* +.tundra .gridBody .imagebutton { + height: 10px; + margin-top: -14; + min-width: 0; +} +*/ \ No newline at end of file diff --git a/widgets/citrix/sync-wui/themes/tundra/VMIcons.css b/widgets/citrix/sync-wui/themes/tundra/VMIcons.css new file mode 100644 index 0000000..29f4068 --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/VMIcons.css @@ -0,0 +1,38 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +.tundra .vmIcon { + background: url(/images/sprites/vmIcons.png) no-repeat 0 0; + width: 28px; + height: 28px; + text-align: center; +} +.tundra .dijitButtonHover .vmIcon { background-position-x: -28px; } +.tundra .dijitButtonActive .vmIcon { background-position-x: -56px; } +.tundra .dijitButtonFocused .vmIcon { background-position-x: -84px; } +.tundra .dijitButtonDisabled .vmIcon { background-position-x: -112px; } + +.tundra .vmIconStart { background-position-y: 0px; } +.tundra .vmIconStop { background-position-y: -28px; } +.tundra .vmIconForceStop { background-position-y: -56px; } +.tundra .vmIconReboot { background-position-y: -84px; } +.tundra .vmIconHibernate { background-position-y: -112px; } +.tundra .vmIconSleep { background-position-y: -140px; } +.tundra .vmIconLock { background-position-y: -168px; } +.tundra .vmIconDetails { background-position-y: -196px; } +.tundra .vmIconAdd { background-position-y: -224px; } +.tundra .vmIconDelete { background-position-y: -252px; } +.tundra .vmIconBackup { background-position-y: -280px; } +.tundra .vmIconCheck { background-position-y: -308px; } +.tundra .vmIconUpload { background-position-y: -336px; } +.tundra .vmIconPause { background-position-y: -364px; } +.tundra .vmIconResume { background-position-y: -392px; } +.tundra .vmIconRetry { background-position-y: -420px; } +.tundra .vmIconCancel { background-position-y: -448px; } +.tundra .vmIconConnect { background-position-y: -476px; } +.tundra .vmIconDisconnect { background-position-y: -504px; } +.tundra .vmIconWake { background-position-y: -532px; } +.tundra .vmIconSwitch { background-position-y: -560px; } diff --git a/widgets/citrix/sync-wui/themes/tundra/tundra.css b/widgets/citrix/sync-wui/themes/tundra/tundra.css new file mode 100644 index 0000000..f8a748c --- /dev/null +++ b/widgets/citrix/sync-wui/themes/tundra/tundra.css @@ -0,0 +1,29 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +@import url("AlertDialog.css"); +@import url("AlertIcons.css"); +@import url("ArrowIcons.css"); +@import url("BadgeIcons.css"); +@import url("Common.css"); +@import url("DeviceGrid.css"); +@import url("DiskGrid.css"); +@import url("Grid.css"); +@import url("ImageSelect.css"); +@import url("MainTabContainer.css"); +@import url("MediaWizard.css"); +@import url("Settings.css"); +@import url("TooltipIcons.css"); +@import url("VMDetails.css"); +@import url("VMIcons.css"); +@import url("VMGrid.css"); + +@import url("Logon.css"); +@import url("AppLayout.css"); +@import url("Assets.css"); +@import url("Dialog.css"); +@import url("TabContainer.css"); +@import url("Expando.css"); \ No newline at end of file diff --git a/widgets/dgrid.js b/widgets/dgrid.js new file mode 100644 index 0000000..478d15a --- /dev/null +++ b/widgets/dgrid.js @@ -0,0 +1 @@ +define(["dgrid/./OnDemandGrid"], function(main){return main;}); \ No newline at end of file diff --git a/widgets/dgrid/CHANGES.md b/widgets/dgrid/CHANGES.md new file mode 100644 index 0000000..0aca09d --- /dev/null +++ b/widgets/dgrid/CHANGES.md @@ -0,0 +1,272 @@ +This document outlines changes since 0.3.0. For older changelogs, see the +[dgrid wiki](https://github.com/SitePen/dgrid/wiki). + +# master (0.3.3-pre) + +## Breaking changes + +* The `Keyboard` module's `dgrid-cellfocusin` and `dgrid-cellfocusout` events + now report either a `row` or `cell` object, depending on whether + `cellNavigation` is `false` or `true`, respectively. (Formerly these events + always contained a `cell` property pointing to the DOM node that fired the event.) +* Several mixin and extension modules have had their `declare` hierarchies + simplified under the expectation that they will always be mixed in as + documented, and never be instantiated directly. To be clear, this will not + break any code that is written as prescribed by the documentation. + +## Significant changes + +* All custom events fired by dgrid components now report the following properties: + * `grid`: The dgrid instance which fired the event. + * `parentType`: If the event was fired in direct response to another event, + this property reflects the type of the originating event. If the event + was fired due to a direct API call, `parentType` will not be defined. +* The `ColumnReorder` extension now fires a `dgrid-columnreorder` event when + a column is reordered via drag'n'drop. Note that this event always reports + a `parentType` of `"dnd"` (there is no way to trigger this event directly + from an API call). +* The `Pagination` extension now exposes and references its i18n strings via the + `i18nPagination` instance property, allowing these strings to be overridden. + (#225) + +## Other changes and fixes + +### General/Core + +* Fixed an issue with the `up` and `down` methods in `List` and the `left` and + `right` methods in `Grid`, which could cause them to attempt to traverse + outside the list/grid in question. +* Fixed an issue in the observer code in `List` which could cause an updated + row to render out-of-sequence when `tree` is used. (#154) +* Fixed an issue that could cause old IE to throw errors due to an undefined + parameter to `insertBefore`. (#308) +* The `_StoreMixin` module now shows/hides a node displaying `noDataMessage` in + reaction to the last row being removed or first row being added. (#229) +* The `OnDemandList` module now adheres more strictly to the `maxRowsPerPage` + property. To accommodate this, the default has been increased from `100` to + `250`. (#280) +* The `OnDemandList` module's default value for `farOffRemoval` has been + lowered from `10000` to `2000`. +* The `loadingMessage` property (referenced by `OnDemandList` and the `Pagination` + extension) now supports HTML strings, like `noDataMessage` (#312) +* The CSS for one of the `util/has-css3` module's tests has had its class renamed + to prevent conflicting with users of Modernizr. (#313) + +### Mixins + +* The `Selection` mixin in single-selection mode now properly allows reselecting + a row that was deselected immediately prior. (#295) + +### Extensions + +* The `ColumnHider` extension will now resize its popup element and enable + scrolling within it, in cases where its height would otherwise exceed the + that of the parent grid. (#311) +* The `Pagination` extension now supports `noDataMessage` like `OnDemandList`. (#180) + +# 0.3.2 + +## Breaking changes + +### GridFromHtml and OnDemandGrid + +The `GridFromHtml` module no longer automatically mixes in the `OnDemandGrid` +module, mixing in only `Grid` instead, in order to support the option of using +alternative store-backed mechanisms such as the `Pagination` extension. +This may cause existing code which relied on `GridFromHtml` and loaded from a +store to break. Such cases will now need to mix in `OnDemandList` manually +(they don't need to mix in `OnDemandGrid`, since `Grid` is still inherited by +`GridFromHtml`). + +There are a couple of ways to deal with this. In Dojo 1.8, when parsing dgrid +instances declaratively, the new `data-dojo-mixins` attribute can be used to +mix `OnDemandList` into `GridFromHtml`: + +```html + + ... +
                      +``` + +In the case of Dojo 1.7, `dojo/parser` doesn't understand module IDs, and so a +global reference to the dgrid components used is needed. Changing such code to +mix in `OnDemandList` involves nothing more than an additional use of `declare`: + +```html + + ... +
                      +... + +``` + +## Significant changes + +### General/Core + +* All dgrid components now have `scrollTo` and `getScrollPosition` methods, + either inheriting from `TouchScroll` (see below) or implemented in `List` + based on `scrollTop` and `scrollLeft`. Updates have been made to dgrid + components where necessary to leverage these methods. +* All dgrid components now respond to `set("showFooter")` consistently with + `set("showHeader")`. (#284) +* It is now possible to initialize or later set CSS classes on a dgrid component's + top DOM node via `"class"` or `className`. (#183) +* `_StoreMixin` (used by `OnDemandList` and the `Pagination` extension) now + includes a reference to the grid instance in emitted `dgrid-error` events + (via a `grid` property on the `error` object). +* The `TouchScroll` module has undergone significant changes and improvements: + * uses CSS3 `translate3d` to take advantage of hardware acceleration + * a `util/has-css3` module has been added with has-feature tests to + detect CSS3 features to be used by `TouchScroll` + * implements increased tension and bounce-back beyond edges + * displays scrollbars as appropriate while scrolling + * implements `scrollTo` and `getScrollPosition` methods to allow manipulation + and retrieval of scroll information based on CSS transformations + * allows configuring how many touches are necessary to activate scrolling, + via the `touchesToScroll` property + +### Mixins + +* The `ColumnSet` mixin now defines a `styleColumnSet` method, which is + analogous to Grid's `styleColumn` method, but instead adds a style rule for + the class on nodes containing the entire columnset contents for a row. +* The `Keyboard` mixin now defines `focus` and `focusHeader` methods, for + programmatically focusing a row or cell (depending on the value of the + `cellNavigation` setting). (#130) + +### Column Plugins + +* The `tree` column plugin now supports a `collapseOnRefresh` property in the + column definition; if set to `true`, it will cause all parent rows to render + collapsed whenever the grid is refreshed, rather than remembering their + previous state. +* The `tree` column plugin now supports a `allowDuplicates` property in the + column definition; this can be set to `true` to allow for cases where the same + item may appear under multiple parents in the tree. Note however that it + limits the capabilities of the `row` method to the top level only. (#147) + +### Extensions + +* A `CompoundColumns` extension has been added, which allows defining column + structures which include additional spanning header cells describing the + contents beneath. +* The `ColumnHider` extension has undergone some refactoring to make it more + extensible and to provide a public API for toggling the hidden state of a + column, via the `toggleColumnHiddenState(columnId)` method. +* The `ColumnReorder` extension has been refactored to allow reordering of + columns within the same subrow or columnset in more complex column structures, + in addition to the previous ability to reorder columns in simple single-row + structures. +* The `DnD` extension now properly supports touch devices when used with Dojo 1.8. +* The `DnD` extension now supports specifying a `getObjectDndType` function, for + customizing the DnD type reported for each item rendered. + +## Other changes and fixes + +### General/Core + +* Several accessibility issues have been addressed, including fixes to the + roles reported by certain elements, and labels added to the Pagination + extension's controls. (Partly attributed to #273) +* `Grid`: calls to `cell` with a falsy `columnId` value now work properly. (#198) +* Fixed an issue with dgrid instances not reacting correctly to window resize. +* Fixed an issue affecting odd/even row classes on in-place updates. (#269) +* Fixed a rendering issue involving confusion of preload node dimensions. (#161) +* Fixed an issue causing `tree` level indentation to render improperly when used + with the `Pagination` extension. +* Fixed a deprecated API call in `_StoreMixin`. (#272) +* Improved logic in `OnDemandList` to properly account for lists with displays + which tile items using `display: inline-block`. + +### Mixins + +* The `ColumnSet` mixin now behaves properly when calling + `set("columnSets", ...)`. (#202) +* The non-standard `colsetid` attribute assigned to nodes by the `ColumnSet` + mixin has been replaced with the `data-dgrid-column-set-id` attribute. +* The `Selection` mixin will now properly reset `_lastSelected` when + `clearSelection` is called. (#175) +* The `Selection` mixin will now wait until `mouseup` when handling mouse events + on targets that are already selected. (#251) + +### Column Plugins + +* The `expand` method defined by the `tree` column plugin will no longer + be called at all in reaction to events on rows which report no children. + +### Extensions + +* The `ColumnHider` extension now supports setting `hidden` and `unhidable` + together, resulting in the column being hidden and not being present in the + popup menu (but it can still be shown programmatically). (#199) +* The `ColumnHider` extension now behaves appropriately for columns with no + `label` defined. (#244) +* A number of protected members in the `ColumnHider` extension have been renamed: + * `_toggleColumnState` has been replaced by `_setColumnHiddenState` and the + public API `toggleColumnHiddenState` mentioned above + * `_toggleHiderMenu` has been renamed to `_toggleColumnHiderMenu` + * `_columnStyleRules` has been renamed to `_columnHiderRules` +* An issue with the `ColumnResizer` extension which could cause distortion of + width values on the first resize has been fixed. (#291) +* The `DnD` extension can now drag non-root tree items *in Dojo 1.8 only* by + passing `allowNested: true` to the source via `dndParams`. (#68) +* The `DnD` extension now behaves better with regard to synchronizing with + dgrid's `Selection` mixin, and also with regard to dragging when some selected + nodes are no longer in the DOM. (#185, #246) +* The `DnD` extension now adds CSS to adequately override spurious styles which + can leak in from dijit.css in Dojo 1.8. (#255) + +# 0.3.1 + +## Significant changes + +* Column plugins can now define the following functions on column definitions, + providing more opportune timing for initialization and tear-down: + * `init`, which will be executed at the time the grid's column configuration + is (re-)applied + * `destroy`, which will be executed when the grid is destroyed, as well as + before a new column configuration is applied +* The `tree` plugin now supports the following column definition properties: + * `shouldExpand(row, level, previouslyExpanded)`, a function providing for + conditional automatic expansion of parent rows (#141) + * `indentWidth`, an integer specifying the size (in pixels) of each level's + indent (note that the default is now `9`, though it was previously `19`) + * `renderExpando()`, a function which can replace the default logic for + rendering the expando node (the arrow next to the content of each cell) +* The `editor` plugin now augments the grid instance with an `edit(cell)` method + which can be used to programmatically activate the editor in a given cell. +* A `util/mouse` module has been added, which exposes simulated events for + the mouse entering and leaving grid rows and cells. (#165) +* A `package.js` has been added in order to streamline the build process. + `package.json` has been updated to reflect the presence of `package.js` and + reference the latest versions of xstyle and put-selector, each of which now + have a `package.js` of their own. + +## Other Fixes + +* Mouse events for expanding/collapsing rows in tree grids should be a bit more + reliable. (#112) +* Rows expanded in a tree grid which has been started up but is currently hidden + will now be rendered properly when re-shown. (#140) +* The `tree` and `editor` plugins can now both be used on the same column, by + wrapping `editor` with `tree`. (#144) +* `sortable` now defaults to `false` for columns where `field` is `"_item"` + or entirely unspecified (in which case there's nothing to sort by anyway). + (#149) +* The `Pagination` extension now behaves appropriately with empty result sets. + (#173) +* The `ColumnHider` extension now iterates over `subRows` rather than `columns`, + making it a bit more reliable in general. (#164) +* A couple of issues with the `DijitRegistry` extension were identified and + fixed. (#146, thanks jdohert) \ No newline at end of file diff --git a/widgets/dgrid/CellSelection.js b/widgets/dgrid/CellSelection.js new file mode 100644 index 0000000..4484110 --- /dev/null +++ b/widgets/dgrid/CellSelection.js @@ -0,0 +1,127 @@ +define(["dojo/_base/declare", "./Selection", "dojo/on", "put-selector/put", "dojo/has"], function(declare, Selection, listen, put, has){ +return declare(Selection, { + // summary: + // Add cell level selection capabilities to a grid. The grid will have a selection property and + // fire "dgrid-select" and "dgrid-deselect" events. + + // ensure we don't select when an individual cell is not identifiable + selectionDelegate: ".dgrid-cell", + + select: function(cell, toCell, value){ + var i, id; + if(value === undefined){ + // default to true + value = true; + } + if(typeof cell != "object" || !("element" in cell)){ + cell = this.cell(cell); + }else if(!cell.row){ + // it is row, with the value being a hash + for(id in value){ + this.select(this.cell(cell.id, id), null, value[id]); + } + return; + } + if(this.allowSelect(cell)){ + var selection = this.selection, + rowId = cell.row.id, + previousRow = selection[rowId]; + if(!cell.column){ + for(i in this.columns){ + this.select(this.cell(rowId, i), null, value); + } + return; + } + var previous = previousRow && previousRow[cell.column.id]; + if(value === null){ + // indicates a toggle + value = !previous; + } + var element = cell.element; + previousRow = previousRow || {}; + previousRow[cell.column.id] = value; + this.selection[rowId] = previousRow; + + // Check for all-false objects to see if it can be deleted. + // This prevents build-up of unnecessary iterations later. + var hasSelected = false; + for(i in previousRow){ + if(previousRow[i] === true){ + hasSelected = true; + break; + } + } + if(!hasSelected){ delete this.selection[rowId]; } + + if(element){ + // add or remove classes as appropriate + if(value){ + put(element, ".dgrid-selected.ui-state-active"); + }else{ + put(element, "!dgrid-selected!ui-state-active"); + } + } + if(value != previous && element){ + this._selectionEventQueue(value, "cells").push(cell); + } + if(toCell){ + // a range + if(!toCell.element){ + toCell = this.cell(toCell); + } + var toElement = toCell.element; + var fromElement = cell.element; + // find if it is earlier or later in the DOM + var traverser = (toElement && (toElement.compareDocumentPosition ? + toElement.compareDocumentPosition(fromElement) == 2 : + toElement.sourceIndex > fromElement.sourceIndex)) ? "nextSibling" : "previousSibling"; + // now we determine which columns are in the range + var idFrom = cell.column.id, idTo = toCell.column.id, started, columnIds = []; + for(id in this.columns){ + if(started){ + columnIds.push(id); + } + if(id == idFrom && (idFrom = columnIds) || // once found, we mark it off so we don't hit it again + id == idTo && (idTo = columnIds)){ + columnIds.push(id); + if(started || // last id, we are done + (idFrom == columnIds && id == idTo)){ // the ids are the same, we are done + break; + } + started = true; + } + } + // now we iterate over rows + var row = cell.row, nextNode = row.element; + toElement = toCell.row.element; + do{ + // looping through each row.. + // and now loop through each column to be selected + for(i = 0; i < columnIds.length; i++){ + cell = this.cell(nextNode, columnIds[i]); + this.select(cell); + } + if(nextNode == toElement){ + break; + } + }while((nextNode = cell.row.element[traverser])); + } + } + }, + isSelected: function(object, columnId){ + if(!object){ + return false; + } + if(!object.element){ + object = this.cell(object, columnId); + } + + return this.selection[object.row.id] && !!this.selection[object.row.id][object.column.id]; + }, + clearSelection: function(exceptId){ + // disable exceptId in cell selection, since it would require double parameters + exceptId = false; + this.inherited(arguments); + } +}); +}); diff --git a/widgets/dgrid/ColumnSet.js b/widgets/dgrid/ColumnSet.js new file mode 100644 index 0000000..c091126 --- /dev/null +++ b/widgets/dgrid/ColumnSet.js @@ -0,0 +1,173 @@ +define(["dojo/_base/kernel", "dojo/_base/declare", "dojo/_base/Deferred", "dojo/on", "dojo/aspect", "dojo/query", "dojo/has", "put-selector/put", "xstyle/has-class", "./Grid", "dojo/_base/sniff", "xstyle/css!./css/columnset.css"], +function(kernel, declare, Deferred, listen, aspect, query, has, put, hasClass, Grid){ + var colsetidAttr = "data-dgrid-column-set-id"; + + hasClass("safari", "ie-7"); + + function positionScrollers(grid){ + var domNode = grid.domNode, + scrollers = grid._columnSetScrollers, + scrollerContents = grid._columnSetScrollerContents, + columnSets = grid.columnSets, + left = 0, scrollerWidth = 0, + i, l, columnSetElement, contentWidth; + for(i = 0, l = columnSets.length; i < l; i++){ + // iterate through the columnSets + left += scrollerWidth; + columnSetElement = query('.dgrid-column-set[' + colsetidAttr + '="' + i +'"]', domNode)[0]; + scrollerWidth = columnSetElement.offsetWidth; + contentWidth = columnSetElement.firstChild.offsetWidth; + scrollerContents[i].style.width = contentWidth + "px"; + scrollers[i].style.width = scrollerWidth + "px"; + scrollers[i].style.overflowX = contentWidth > scrollerWidth ? "scroll" : "auto"; // IE seems to need it be set explicitly + scrollers[i].style.left = left + "px"; + } + } + function adjustScrollLeft(grid, row){ + var scrollLefts = grid._columnSetScrollLefts; + function doAdjustScrollLeft(){ + query(".dgrid-column-set", row).forEach(function(element){ + element.scrollLeft = scrollLefts[element.getAttribute(colsetidAttr)]; + }); + } + if(has("ie") < 8 || has("quirks")){ + setTimeout(doAdjustScrollLeft, 1); + }else{ + doAdjustScrollLeft(); + } + } + + return declare(null, { + // summary: + // Provides column sets to isolate horizontal scroll of sets of + // columns from each other. This mainly serves the purpose of allowing for + // column locking. + + columnSets: [], + createRowCells: function(tag, each){ + var row = put("table.dgrid-row-table"); + var tr = put(row, "tbody tr"); + for(var i = 0, l = this.columnSets.length; i < l; i++){ + // iterate through the columnSets + var cell = put(tr, tag + ".dgrid-column-set-cell.dgrid-column-set-" + i + + " div.dgrid-column-set[" + colsetidAttr + "=" + i + "]"); + cell.appendChild(this.inherited(arguments, [tag, each, this.columnSets[i]])); + } + return row; + }, + renderArray: function(){ + var grid = this, + rows = this.inherited(arguments); + + Deferred.when(rows, function(rows){ + for(var i = 0; i < rows.length; i++){ + adjustScrollLeft(grid, rows[i]); + } + }); + return rows; + }, + renderHeader: function(){ + // summary: + // Setup the headers for the grid + this.inherited(arguments); + this.bodyNode.style.bottom = "17px"; + + var columnSets = this.columnSets, + domNode = this.domNode, + scrollers = this._columnSetScrollers, + scrollerContents = this._columnSetScrollerContents = {}, + scrollLefts = this._columnSetScrollLefts = {}, + grid = this, + i, l; + + function onScroll(){ + var scrollLeft = this.scrollLeft; + var colSetId = this.getAttribute(colsetidAttr); + if(scrollLefts[colSetId] != scrollLeft){ + scrollLefts[colSetId] = scrollLeft; + query('.dgrid-column-set[' + colsetidAttr + '="' + colSetId + '"],.dgrid-column-set-scroller[' + colsetidAttr + '="' + colSetId + '"]', domNode). + forEach(function(element){ + element.scrollLeft = scrollLeft; + }); + } + } + + function putScroller(columnSet, i){ + // function called for each columnSet + var scroller = scrollers[i] = + put(domNode, "div.dgrid-column-set-scroller.dgrid-scrollbar-height.dgrid-column-set-scroller-" + i + + "[" + colsetidAttr + "=" + i +"]"); + scrollerContents[i] = put(scroller, "div.dgrid-column-set-scroller-content"); + listen(scroller, "scroll", onScroll); + } + + function reposition(){ + positionScrollers(grid); + } + + if (scrollers) { + // this isn't the first time; destroy existing scroller nodes first + for(i in scrollers){ + put("!", scrollers[i]); + } + } else { + // first-time-only operations: hook up event/aspected handlers + aspect.after(this, "resize", reposition, true); + aspect.after(this, "styleColumn", reposition, true); + listen(domNode, ".dgrid-column-set:dgrid-cellfocusin", onScroll); + } + + // reset to new object to be populated in loop below + scrollers = this._columnSetScrollers = {}; + + for(i = 0, l = columnSets.length; i < l; i++){ + putScroller(columnSets[i], i); + } + + positionScrollers(this); + }, + + styleColumnSet: function(colsetId, css){ + // summary: + // Dynamically creates a stylesheet rule to alter a columnset's style. + + var rule = this.addCssRule("#" + this.domNode.id + " .dgrid-column-set-" + colsetId, css); + positionScrollers(this); + return rule; + }, + + _destroyColumns: function(){ + var columnSetsLength = this.columnSets.length, + i, j, k, subRowsLength, len, columnSet, subRow, column; + for(i = 0; i < columnSetsLength; i++){ + columnSet = this.columnSets[i]; + for(j = 0, subRowsLength = columnSet.length; j < subRowsLength; j++){ + subRow = columnSet[j]; + for(k = 0, len = subRow.length; k < len; k++){ + column = subRow[k]; + if(typeof column.destroy === "function"){ column.destroy(); } + } + } + } + }, + configStructure: function(){ + this.columns = {}; + for(var i = 0, l = this.columnSets.length; i < l; i++){ + // iterate through the columnSets + var columnSet = this.columnSets[i]; + for(var j = 0; j < columnSet.length; j++){ + columnSet[j] = this._configColumns(i + '-' + j + '-', columnSet[j]); + } + } + }, + _setColumnSets: function(columnSets){ + this._destroyColumns(); + this.columnSets = columnSets; + this._updateColumns(); + }, + setColumnSets: function(columnSets){ + kernel.deprecated("setColumnSets(...)", 'use set("columnSets", ...) instead', "dgrid 1.0"); + this.set("columnSets", columnSets); + } + }); +}); diff --git a/widgets/dgrid/Grid.js b/widgets/dgrid/Grid.js new file mode 100644 index 0000000..3cb9db5 --- /dev/null +++ b/widgets/dgrid/Grid.js @@ -0,0 +1,440 @@ +define(["dojo/_base/kernel", "dojo/_base/declare", "dojo/on", "dojo/has", "put-selector/put", "./List", "dojo/_base/sniff"], +function(kernel, declare, listen, has, put, List){ + var contentBoxSizing = has("ie") < 8 && !has("quirks"); + var invalidClassChars = /[^\._a-zA-Z0-9-]/g; + function appendIfNode(parent, subNode){ + if(subNode && subNode.nodeType){ + parent.appendChild(subNode); + } + } + + var Grid = declare(List, { + columns: null, + // cellNavigation: Boolean + // This indicates that focus is at the cell level. This may be set to false to cause + // focus to be at the row level, which is useful if you want only want row-level + // navigation. + cellNavigation: true, + tabableHeader: true, + showHeader: true, + column: function(target){ + // summary: + // Get the column object by node, or event, or a columnId + if(typeof target == "string"){ + return this.columns[target]; + }else{ + return this.cell(target).column; + } + }, + listType: "grid", + cell: function(target, columnId){ + // summary: + // Get the cell object by node, or event, id, plus a columnId + + if(target.row && target.row instanceof this._Row){ return target; } + + if(target.target && target.target.nodeType){ + // event + target = target.target; + } + var element; + if(target.nodeType){ + var object; + do{ + if(this._rowIdToObject[target.id]){ + break; + } + var colId = target.columnId; + if(colId){ + columnId = colId; + element = target; + break; + } + target = target.parentNode; + }while(target && target != this.domNode); + } + if(!element && typeof columnId != "undefined"){ + var row = this.row(target), + rowElement = row.element; + if(rowElement){ + var elements = rowElement.getElementsByTagName("td"); + for(var i = 0; i < elements.length; i++){ + if(elements[i].columnId == columnId){ + element = elements[i]; + break; + } + } + } + } + if(target != null){ + return { + row: row || this.row(target), + column: columnId && this.column(columnId), + element: element + }; + } + }, + _columnsCss: function(rule){ + // This is an attempt at integration with xstyle, will probably change + rule.fullSelector = function(){ + return this.parent.fullSelector() + " .dgrid-cell"; + }; + for(var i = 0;i < rule.children.length;i++){ + var child = rule.children[i]; + child.field = child.className = child.selector.substring(1); + } + return rule.children; + }, + createRowCells: function(tag, each, subRows){ + // summary: + // Generates the grid for each row (used by renderHeader and and renderRow) + var row = put("table.dgrid-row-table[role=presentation]"), + cellNavigation = this.cellNavigation, + // IE < 9 needs an explicit tbody; other browsers do not + tbody = (has("ie") < 9 || has("quirks")) ? put(row, "tbody") : row, + tr, + si, sl, i, l, // iterators + subRow, column, id, extraClassName, cell, innerCell, colSpan, rowSpan; // used inside loops + + // Allow specification of custom/specific subRows, falling back to + // those defined on the instance. + subRows = subRows || this.subRows; + + for(si = 0, sl = subRows.length; si < sl; si++){ + subRow = subRows[si]; + // for single-subrow cases in modern browsers, TR can be skipped + // http://jsperf.com/table-without-trs + tr = (sl == 1 && !has("ie")) ? tbody : put(tbody, "tr"); + if(subRow.className){ + put(tr, "." + subRow.className); + } + + for(i = 0, l = subRow.length; i < l; i++){ + // iterate through the columns + column = subRow[i]; + id = column.id; + extraClassName = column.className || (column.field && "field-" + column.field); + cell = put(tag + ( + ".dgrid-cell.dgrid-cell-padding" + + (id ? ".dgrid-column-" + id : "") + + (extraClassName ? "." + extraClassName : "") + ).replace(invalidClassChars,"-") + + "[role=" + (tag === "th" ? "columnheader" : "gridcell") + "]"); + cell.columnId = id; + if(contentBoxSizing){ + // The browser (IE7-) does not support box-sizing: border-box, so we emulate it with a padding div + innerCell = put(cell, "!dgrid-cell-padding div.dgrid-cell-padding");// remove the dgrid-cell-padding, and create a child with that class + cell.contents = innerCell; + }else{ + innerCell = cell; + } + colSpan = column.colSpan; + if(colSpan){ + cell.colSpan = colSpan; + } + rowSpan = column.rowSpan; + if(rowSpan){ + cell.rowSpan = rowSpan; + } + each(innerCell, column); + // add the td to the tr at the end for better performance + tr.appendChild(cell); + } + } + return row; + }, + left: function(cell, steps){ + return this.cell(this._move(cell, -(steps || 1), "dgrid-cell")); + }, + right: function(cell, steps){ + return this.cell(this._move(cell, steps || 1, "dgrid-cell")); + }, + renderRow: function(object, options){ + var row = this.createRowCells("td", function(td, column){ + var data = object; + // we support the field, get, and formatter properties like the DataGrid + if(column.get){ + data = column.get(object); + }else if("field" in column && column.field != "_item"){ + data = data[column.field]; + } + if(column.formatter){ + td.innerHTML = column.formatter(data); + }else if(column.renderCell){ + // A column can provide a renderCell method to do its own DOM manipulation, + // event handling, etc. + appendIfNode(td, column.renderCell(object, data, td, options)); + }else if(data != null){ + td.appendChild(document.createTextNode(data)); + } + }, options && options.subRows); + // row gets a wrapper div for a couple reasons: + // 1. So that one can set a fixed height on rows (heights can't be set on 's AFAICT) + // 2. So that outline style can be set on a row when it is focused, and Safari's outline style is broken on
                      + return put("div[role=row]>", row); + }, + renderHeader: function(){ + // summary: + // Setup the headers for the grid + var + grid = this, + columns = this.columns, + headerNode = this.headerNode, + i = headerNode.childNodes.length; + + headerNode.setAttribute("role", "row"); + + // clear out existing header in case we're resetting + while(i--){ + put(headerNode.childNodes[i], "!"); + } + + var row = this.createRowCells("th", function(th, column){ + var contentNode = column.headerNode = th; + if(contentBoxSizing){ + // we're interested in the th, but we're passed the inner div + th = th.parentNode; + } + var field = column.field; + if(field){ + th.field = field; + } + // allow for custom header content manipulation + if(column.renderHeaderCell){ + appendIfNode(contentNode, column.renderHeaderCell(contentNode)); + }else if(column.label || column.field){ + contentNode.appendChild(document.createTextNode(column.label || column.field)); + } + if(column.sortable !== false && field && field != "_item"){ + th.sortable = true; + th.className += " dgrid-sortable"; + } + }, this.subRows && this.subRows.headerRows); + this._rowIdToObject[row.id = this.id + "-header"] = this.columns; + headerNode.appendChild(row); + // if it columns are sortable, resort on clicks + listen(row, "click,keydown", function(event){ + // respond to click, space keypress, or enter keypress + if(event.type == "click" || event.keyCode == 32 /* space bar */ || (!has("opera") && event.keyCode == 13) /* enter */){ + var target = event.target, + field, descending, parentNode, sort; + do{ + if(target.sortable){ + // stash node subject to DOM manipulations, + // to be referenced then removed by sort() + grid._sortNode = target; + + field = target.field || target.columnId; + + // if the click is on the same column as the active sort, + // reverse sort direction + descending = (sort = grid._sort[0]) && sort.attribute == field && + !sort.descending; + + return grid.set("sort", field, descending); + } + }while((target = target.parentNode) && target != headerNode); + } + }); + }, + + resize: function(){ + // extension of List.resize to allow accounting for + // column sizes larger than actual grid area + var + headerTableNode = this.headerNode.firstChild, + contentNode = this.contentNode, + width; + + this.inherited(arguments); + + if(!has("ie") || (has("ie") > 7 && !has("quirks"))){ + // Force contentNode width to match up with header width. + // (Old IEs don't have a problem due to how they layout.) + + contentNode.style.width = ""; // reset first + + if(contentNode && headerTableNode){ + if((width = headerTableNode.offsetWidth) != contentNode.offsetWidth){ + // update size of content node if necessary (to match size of rows) + // (if headerTableNode can't be found, there isn't much we can do) + contentNode.style.width = width + "px"; + } + } + } + }, + + destroy: function(){ + // Run _destroyColumns first to perform any column plugin tear-down logic. + this._destroyColumns(); + this.inherited(arguments); + }, + + _setSort: function(property, descending){ + // summary: + // Extension of List.js sort to update sort arrow in UI + + this.inherited(arguments); // normalize _sort first + + // clean up UI from any previous sort + if(this._lastSortedArrow){ + // remove the sort classes from parent node + put(this._lastSortedArrow, "= tmp){ + // First, push info for the set we just finished: + // (i is still the active row index from the for loop) + columnsets[currcg][i] = groupColumns; + + // Now, time to move on to the next columnset for this row. + currcol -= tmp; + currcg++; + groupColumns = []; + } + } + + // no need for ColumnSet unless there's >1 colgroup + if(cglen < 2){ return false; } + + // read span from each colgroup (defaults to 1) + for(i = 0; i < cglen; i++){ + // store number of cells this column spans + tmp = getNum(colgroups[i], "span") || 1; + cgspans[i] = tmp; + // add nested array to return value to be populated for this set + columnsets[i] = []; + // initialize inner rowspan-tracking array for each + rowspans[i] = []; + for(j = 0; j < tmp; j++){ + rowspans[i][j] = 0; + } + } + + for(i = 0; i < trslen; i++){ + currcol = currcg = 0; + groupColumns = []; + tr = trs[i]; + ths = tr.getElementsByTagName("th"), thslen = ths.length; + for(j = 0; j < thslen; j++){ + // account for space occupied by previous rowSpans + while(rowspans[currcg][currcol]){ + // decrement rowspan "leftover" for next iteration + rowspans[currcg][currcol]--; + // skip past this cell for now, and try again w/ updated currcg/col + incCurrcol(1); + } + + // store cell info + tmp = getCol(ths[j]); + groupColumns.push(tmp); + + // if this cell has rowspan, keep that in mind for future iterations + rowspans[currcg][currcol] = tmp.rowSpan ? tmp.rowSpan - 1 : 0; + + // increment currcol/currcg appropriately, accounting for cell colSpan + incCurrcol(tmp.colSpan || 1); + } + // At the end of processing each row, there is a chance that the last + // column set didn't get pushed yet (specifically if there are trailing + // rowspans - since rowspan "debt" gets iterated at the beginning of each + // iteration, not the end). In that case, push the last one now. + if(groupColumns.length){ + columnsets[currcg][i] = groupColumns; + } + } + if(tr){ + domNode.removeChild(tr.parentNode); + } + return columnsets; + } + return declare([GridFromHtml, ColumnSet], { + configStructure: function(){ + // summary: + // Configure subRows based on HTML originally in srcNodeRef + + var tmp; + if(!this._checkedTrs){ + tmp = getColumnSetsFromDom(this.srcNodeRef); + if(tmp){ + this.columnSets = tmp; + this._checkedTrs = true; + }else{ + // no reason to worry about ColumnSets, let GridFromHtml do the job + return this.inherited(arguments); + } + } + return this.inherited(arguments); + } + }); +}); \ No newline at end of file diff --git a/widgets/dgrid/Keyboard.js b/widgets/dgrid/Keyboard.js new file mode 100644 index 0000000..bb57019 --- /dev/null +++ b/widgets/dgrid/Keyboard.js @@ -0,0 +1,233 @@ +define([ + "dojo/_base/declare", + "dojo/aspect", + "dojo/on", + "./List", + "dojo/_base/lang", + "dojo/has", + "put-selector/put", + "dojo/_base/Deferred", + "dojo/_base/sniff" +], function(declare, aspect, on, List, lang, has, put, Deferred){ + +var delegatingInputTypes = { + checkbox: 1, + radio: 1, + button: 1 + }, + hasGridCellClass = /\bdgrid-cell\b/, + hasGridRowClass = /\bdgrid-row\b/; + +has.add("dom-contains", function(){ + return !!document.createElement("a").contains; +}); + +function contains(parent, node){ + // summary: + // Checks to see if an element is contained by another element. + + if(has("dom-contains")){ + return parent.contains(node); + }else{ + return parent.compareDocumentPosition(node) & 8 /* DOCUMENT_POSITION_CONTAINS */; + } +} + +return declare(null, { + // summary: + // Add keyboard navigation capability to a grid/list + pageSkip: 10, + tabIndex: 0, + + postCreate: function(){ + this.inherited(arguments); + var grid = this; + + function handledEvent(event){ + // text boxes and other inputs that can use direction keys should be ignored and not affect cell/row navigation + var target = event.target; + return target.type && (!delegatingInputTypes[target.type] || event.keyCode == 32); + } + + function navigateArea(areaNode){ + var isFocusableClass = grid.cellNavigation ? hasGridCellClass : hasGridRowClass, + cellFocusedElement = areaNode, + next; + + function focusOnCell(element, event, dontFocus){ + var cellOrRowType = grid.cellNavigation ? "cell" : "row", + cell = grid[cellOrRowType](element); + + element = cell && cell.element; + if(!element){ return; } + event = lang.mixin({ grid: grid }, event); + if(event.type){ + event.parentType = event.type; + } + if(!event.bubbles){ + // IE doesn't always have a bubbles property already true. + // Opera throws if you try to set it to true if it is already true. + event.bubbles = true; + } + // clean up previously-focused element + // remove the class name and the tabIndex attribute + put(cellFocusedElement, "!dgrid-focus[!tabIndex]"); + if(cellFocusedElement){ + if(has("ie") < 8){ + // clean up after workaround below (for non-input cases) + cellFocusedElement.style.position = ""; + } + + // Expose object representing focused cell or row losing focus, via + // event.cell or event.row; which is set depends on cellNavigation. + event[cellOrRowType] = grid[cellOrRowType](cellFocusedElement); + on.emit(element, "dgrid-cellfocusout", event); + } + cellFocusedElement = element; + + // Expose object representing focused cell or row gaining focus, via + // event.cell or event.row; which is set depends on cellNavigation. + // Note that yes, the same event object is being reused; on.emit + // performs a shallow copy of properties into a new event object. + event[cellOrRowType] = cell; + + if(!dontFocus){ + if(has("ie") < 8){ + // setting the position to relative magically makes the outline + // work properly for focusing later on with old IE. + // (can't be done a priori with CSS or screws up the entire table) + element.style.position = "relative"; + } + element.tabIndex = grid.tabIndex; + element.focus(); + } + put(element, ".dgrid-focus"); + on.emit(cellFocusedElement, "dgrid-cellfocusin", event); + } + + while((next = cellFocusedElement.firstChild) && !isFocusableClass.test(next.className)){ + cellFocusedElement = next; + } + if(next){ cellFocusedElement = next; } + + if(areaNode === grid.contentNode){ + aspect.after(grid, "renderArray", function(ret){ + // summary: + // Ensures the first element of a grid is always keyboard selectable after data has been + // retrieved if there is not already a valid focused element. + + return Deferred.when(ret, function(ret){ + // do not update the focused element if we already have a valid one + if(isFocusableClass.test(cellFocusedElement.className) && contains(areaNode, cellFocusedElement)){ + return ret; + } + + // ensure that the focused element is actually a grid cell, not a + // dgrid-preload or dgrid-content element, which should not be focusable, + // even when data is loaded asynchronously + for(var i = 0, elements = areaNode.getElementsByTagName("*"), element; (element = elements[i]); ++i){ + if(isFocusableClass.test(element.className)){ + cellFocusedElement = element; + break; + } + } + + cellFocusedElement.tabIndex = grid.tabIndex; + + return ret; + }); + }); + }else if(isFocusableClass.test(cellFocusedElement.className)){ + cellFocusedElement.tabIndex = grid.tabIndex; + } + + on(areaNode, "mousedown", function(event){ + if(!handledEvent(event)){ + focusOnCell(event.target, event); + } + }); + + on(areaNode, "keydown", function(event){ + // For now, don't squash browser-specific functionalities by letting + // ALT and META function as they would natively + if(event.metaKey || event.altKey) { + return; + } + + var focusedElement = event.target; + var keyCode = event.keyCode; + if(handledEvent(event)){ + // text boxes and other inputs that can use direction keys should be ignored and not affect cell/row navigation + return; + } + var move = { + 32: 0, // space bar + 33: -grid.pageSkip, // page up + 34: grid.pageSkip,// page down + 37: -1, // left + 38: -1, // up + 39: 1, // right + 40: 1, // down + 35: 10000, //end + 36: -10000 // home + }[keyCode]; + if(isNaN(move)){ + return; + } + var nextSibling, columnId, cell = grid.cell(cellFocusedElement); + var orientation; + if(keyCode == 37 || keyCode == 39){ + // horizontal movement (left and right keys) + if(!grid.cellNavigation){ + return; // do nothing for row-only navigation + } + orientation = "right"; + }else{ + // other keys are vertical + orientation = "down"; + columnId = cell && cell.column && cell.column.id; + cell = grid.row(cellFocusedElement); + } + if(move){ + cell = cell && grid[orientation](cell, move, true); + } + var nextFocus = cell && cell.element; + if(nextFocus){ + if(columnId){ + nextFocus = grid.cell(nextFocus, columnId).element; + } + if(grid.cellNavigation){ + var inputs = nextFocus.getElementsByTagName("input"); + var inputFocused; + for(var i = 0;i < inputs.length; i++){ + var input = inputs[i]; + if((input.tabIndex != -1 || "lastValue" in input) && !input.disabled){ + // focusing here requires the same workaround for IE<8, + // though here we can get away with doing it all at once. + if(has("ie") < 8){ input.style.position = "relative"; } + input.focus(); + if(has("ie") < 8){ input.style.position = ""; } + inputFocused = true; + break; + } + } + } + focusOnCell(nextFocus, event, inputFocused); + } + event.preventDefault(); + }); + + return function(target){ + target = target || cellFocusedElement; + focusOnCell(target, { target: target }); + } + } + + if(this.tabableHeader){ + this.focusHeader = navigateArea(this.headerNode); + } + + this.focus = navigateArea(this.contentNode); + } +}); +}); diff --git a/widgets/dgrid/LICENSE b/widgets/dgrid/LICENSE new file mode 100644 index 0000000..ff3d504 --- /dev/null +++ b/widgets/dgrid/LICENSE @@ -0,0 +1,190 @@ +dgrid is available under *either* the terms of the modified BSD license *or* the +Academic Free License version 2.1. As a recipient of dgrid, you may choose which +license to receive this code under. + +The text of the AFL and BSD licenses is reproduced below. + +------------------------------------------------------------------------------- +The "New" BSD License: +********************** + +Copyright (c) 2010-2011, The Dojo Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the Dojo Foundation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +The Academic Free License, v. 2.1: +********************************** + +This Academic Free License (the "License") applies to any original work of +authorship (the "Original Work") whose owner (the "Licensor") has placed the +following notice immediately following the copyright notice for the Original +Work: + +Licensed under the Academic Free License version 2.1 + +1) Grant of Copyright License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license to do the +following: + +a) to reproduce the Original Work in copies; + +b) to prepare derivative works ("Derivative Works") based upon the Original +Work; + +c) to distribute copies of the Original Work and Derivative Works to the +public; + +d) to perform the Original Work publicly; and + +e) to display the Original Work publicly. + +2) Grant of Patent License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license, under patent +claims owned or controlled by the Licensor that are embodied in the Original +Work as furnished by the Licensor, to make, use, sell and offer for sale the +Original Work and Derivative Works. + +3) Grant of Source Code License. The term "Source Code" means the preferred +form of the Original Work for making modifications to it and all available +documentation describing how to modify the Original Work. Licensor hereby +agrees to provide a machine-readable copy of the Source Code of the Original +Work along with each copy of the Original Work that Licensor distributes. +Licensor reserves the right to satisfy this obligation by placing a +machine-readable copy of the Source Code in an information repository +reasonably calculated to permit inexpensive and convenient access by You for as +long as Licensor continues to distribute the Original Work, and by publishing +the address of that information repository in a notice immediately following +the copyright notice that applies to the Original Work. + +4) Exclusions From License Grant. Neither the names of Licensor, nor the names +of any contributors to the Original Work, nor any of their trademarks or +service marks, may be used to endorse or promote products derived from this +Original Work without express prior written permission of the Licensor. Nothing +in this License shall be deemed to grant any rights to trademarks, copyrights, +patents, trade secrets or any other intellectual property of Licensor except as +expressly stated herein. No patent license is granted to make, use, sell or +offer to sell embodiments of any patent claims other than the licensed claims +defined in Section 2. No right is granted to the trademarks of Licensor even if +such marks are included in the Original Work. Nothing in this License shall be +interpreted to prohibit Licensor from licensing under different terms from this +License any Original Work that Licensor otherwise would have a right to +license. + +5) This section intentionally omitted. + +6) Attribution Rights. You must retain, in the Source Code of any Derivative +Works that You create, all copyright, patent or trademark notices from the +Source Code of the Original Work, as well as any notices of licensing and any +descriptive text identified therein as an "Attribution Notice." You must cause +the Source Code for any Derivative Works that You create to carry a prominent +Attribution Notice reasonably calculated to inform recipients that You have +modified the Original Work. + +7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that +the copyright in and to the Original Work and the patent rights granted herein +by Licensor are owned by the Licensor or are sublicensed to You under the terms +of this License with the permission of the contributor(s) of those copyrights +and patent rights. Except as expressly stated in the immediately proceeding +sentence, the Original Work is provided under this License on an "AS IS" BASIS +and WITHOUT WARRANTY, either express or implied, including, without limitation, +the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. +This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No +license to Original Work is granted hereunder except under this disclaimer. + +8) Limitation of Liability. Under no circumstances and under no legal theory, +whether in tort (including negligence), contract, or otherwise, shall the +Licensor be liable to any person for any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License +or the use of the Original Work including, without limitation, damages for loss +of goodwill, work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses. This limitation of liability shall not +apply to liability for death or personal injury resulting from Licensor's +negligence to the extent applicable law prohibits such limitation. Some +jurisdictions do not allow the exclusion or limitation of incidental or +consequential damages, so this exclusion and limitation may not apply to You. + +9) Acceptance and Termination. If You distribute copies of the Original Work or +a Derivative Work, You must make a reasonable effort under the circumstances to +obtain the express assent of recipients to the terms of this License. Nothing +else but this License (or another written agreement between Licensor and You) +grants You permission to create Derivative Works based upon the Original Work +or to exercise any of the rights granted in Section 1 herein, and any attempt +to do so except under the terms of this License (or another written agreement +between Licensor and You) is expressly prohibited by U.S. copyright law, the +equivalent laws of other countries, and by international treaty. Therefore, by +exercising any of the rights granted to You in Section 1 herein, You indicate +Your acceptance of this License and all of its terms and conditions. + +10) Termination for Patent Action. This License shall terminate automatically +and You may no longer exercise any of the rights granted to You by this License +as of the date You commence an action, including a cross-claim or counterclaim, +against Licensor or any licensee alleging that the Original Work infringes a +patent. This termination provision shall not apply for an action alleging +patent infringement by combinations of the Original Work with other software or +hardware. + +11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this +License may be brought only in the courts of a jurisdiction wherein the +Licensor resides or in which Licensor conducts its primary business, and under +the laws of that jurisdiction excluding its conflict-of-law provisions. The +application of the United Nations Convention on Contracts for the International +Sale of Goods is expressly excluded. Any use of the Original Work outside the +scope of this License or after its termination shall be subject to the +requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et +seq., the equivalent laws of other countries, and international treaty. This +section shall survive the termination of this License. + +12) Attorneys Fees. In any action to enforce the terms of this License or +seeking damages relating thereto, the prevailing party shall be entitled to +recover its costs and expenses, including, without limitation, reasonable +attorneys' fees and costs incurred in connection with such action, including +any appeal of such action. This section shall survive the termination of this +License. + +13) Miscellaneous. This License represents the complete agreement concerning +the subject matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent necessary to +make it enforceable. + +14) Definition of "You" in This License. "You" throughout this License, whether +in upper or lower case, means an individual or a legal entity exercising rights +under, and complying with all of the terms of, this License. For legal +entities, "You" includes any entity that controls, is controlled by, or is +under common control with you. For purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (ii) ownership of fifty percent +(50%) or more of the outstanding shares, or (iii) beneficial ownership of such +entity. + +15) Right to Use. You may use the Original Work in all ways not otherwise +restricted or conditioned by this License or by law, and Licensor promises not +to interfere with or be responsible for such uses by You. + +This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. +Permission is hereby granted to copy and distribute this license without +modification. This license may not be modified without the express written +permission of its copyright owner. diff --git a/widgets/dgrid/List.js b/widgets/dgrid/List.js new file mode 100644 index 0000000..77a5726 --- /dev/null +++ b/widgets/dgrid/List.js @@ -0,0 +1,846 @@ +define(["dojo/_base/array","dojo/_base/kernel", "dojo/_base/declare", "dojo/on", "dojo/has", "./util/misc", "dojo/has!touch?./TouchScroll", "xstyle/has-class", "put-selector/put", "dojo/_base/sniff", "xstyle/css!./css/dgrid.css"], +function(arrayUtil, kernel, declare, listen, has, miscUtil, TouchScroll, hasClass, put){ + // Add user agent/feature CSS classes + hasClass("mozilla", "opera", "webkit", "ie", "ie-6", "ie-6-7", "quirks", "no-quirks", "touch"); + + var scrollbarWidth; + + // establish an extra stylesheet which addCssRule calls will use, + // plus an array to track actual indices in stylesheet for removal + var + extraSheet = put(document.getElementsByTagName("head")[0], "style"), + extraRules = [], + oddClass = "dgrid-row-odd", + evenClass = "dgrid-row-even"; + // keep reference to actual StyleSheet object (.styleSheet for IE < 9) + extraSheet = extraSheet.sheet || extraSheet.styleSheet; + + // functions for adding and removing extra style rules. + // addExtraRule is exposed on the List prototype as addCssRule. + function addExtraRule(selector, css){ + var index = extraRules.length; + extraRules[index] = (extraSheet.cssRules || extraSheet.rules).length; + extraSheet.addRule ? + extraSheet.addRule(selector, css) : + extraSheet.insertRule(selector + '{' + css + '}', extraRules[index]); + return { + remove: function(){ removeExtraRule(index); } + }; + } + function removeExtraRule(index){ + var + realIndex = extraRules[index], + i, l = extraRules.length; + if (realIndex === undefined) { return; } // already removed + + // remove rule indicated in internal array at index + extraSheet.deleteRule ? + extraSheet.deleteRule(realIndex) : + extraSheet.removeRule(realIndex); // IE < 9 + + // Clear internal array item representing rule that was just deleted. + // NOTE: we do NOT splice, since the point of this array is specifically + // to negotiate the splicing that occurs in the stylesheet itself! + extraRules[index] = undefined; + + // Then update array items as necessary to downshift remaining rule indices. + // Can start at index, since array is sparse but strictly increasing. + for(i = index; i < l; i++){ + if(extraRules[i] > realIndex){ extraRules[i]--; } + } + } + + function byId(id){ + return document.getElementById(id); + } + + // var and function for autogenerating ID when one isn't provided + var autogen = 0; + function generateId(){ + return "dgrid_" + autogen++; + } + + // common functions for class and className setters/getters + // (these are run in instance context) + var spaceRx = / +/g; + function setClass(cls){ + // Format input appropriately for use with put... + var putClass = cls ? "." + cls.replace(spaceRx, ".") : ""; + + // Remove any old classes, and add new ones. + if(this._class){ + putClass = "!" + this._class.replace(spaceRx, "!") + putClass; + } + put(this.domNode, putClass); + + // Store for later retrieval/removal. + this._class = cls; + } + function getClass(){ + return this._class; + } + + // window resize event handler, run in context of List instance + var winResizeHandler = has("ie") < 7 && !has("quirks") ? function(){ + // IE6 triggers window.resize on any element resize; + // avoid useless calls (and infinite loop if height: auto). + // The measurement logic here is based on dojo/window logic. + var root, w, h, dims; + + if(!this._started){ return; } // no sense calling resize yet + + root = document.documentElement; + w = root.clientWidth; + h = root.clientHeight; + dims = this._prevWinDims || []; + if(dims[0] !== w || dims[1] !== h){ + this.resize(); + this._prevWinDims = [w, h]; + } + } : + function(){ + if(this._started){ this.resize(); } + }; + + return declare(TouchScroll ? TouchScroll : null, { + tabableHeader: false, + // showHeader: Boolean + // Whether to render header (sub)rows. + showHeader: false, + // showFooter: Boolean + // Whether to render footer area. Extensions which display content + // in the footer area should set this to true. + showFooter: false, + // maintainOddEven: Boolean + // Indicates whether to maintain the odd/even classes when new rows are inserted. + // This can be disabled to improve insertion performance if odd/even styling is not employed. + maintainOddEven: true, + + postscript: function(params, srcNodeRef){ + // perform setup and invoke create in postScript to allow descendants to + // perform logic before create/postCreate happen (a la dijit/_WidgetBase) + var grid = this; + + (this._Row = function(id, object, element){ + this.id = id; + this.data = object; + this.element = element; + }).prototype.remove = function(){ + grid.removeRow(this.element); + }; + + if(srcNodeRef){ + // normalize srcNodeRef and store on instance during create process. + // Doing this in postscript is a bit earlier than dijit would do it, + // but allows subclasses to access it pre-normalized during create. + this.srcNodeRef = srcNodeRef = + srcNodeRef.nodeType ? srcNodeRef : byId(srcNodeRef); + } + this.create(params, srcNodeRef); + }, + listType: "list", + + create: function(params, srcNodeRef){ + var domNode = this.domNode = srcNodeRef || put("div"), + cls; + + if(params){ + this.params = params; + declare.safeMixin(this, params); + + // Check for initial class or className in params or on domNode + cls = params["class"] || params.className || domNode.className; + + // handle sort param - TODO: revise @ 1.0 when _sort -> sort + this._sort = params.sort || []; + delete this.sort; // ensure back-compat method isn't shadowed + }else{ + this._sort = []; + } + + // ensure arrays and hashes are initialized + this.observers = []; + this._listeners = []; + this._rowIdToObject = {}; + + this.postMixInProperties && this.postMixInProperties(); + + // Apply id to widget and domNode, + // from incoming node, widget params, or autogenerated. + this.id = domNode.id = domNode.id || this.id || generateId(); + + // Perform initial rendering, and apply classes if any were specified. + this.buildRendering(); + if(cls){ setClass.call(this, cls); } + + this.postCreate && this.postCreate(); + + // remove srcNodeRef instance property post-create + delete this.srcNodeRef; + // to preserve "it just works" behavior, call startup if we're visible + if(this.domNode.offsetHeight){ + this.startup(); + } + }, + buildRendering: function(){ + var domNode = this.domNode, + self = this, + headerNode, spacerNode, bodyNode, footerNode, isRTL; + + // Detect RTL on html/body nodes; taken from dojo/dom-geometry + isRTL = this.isRTL = (document.body.dir || document.documentElement.dir || + document.body.style.direction).toLowerCase() == "rtl"; + + // Clear out className (any pre-applied classes will be re-applied via the + // class / className setter), then apply standard classes/attributes + domNode.className = ""; + + put(domNode, "[role=grid].ui-widget.dgrid.dgrid-" + this.listType); + + // Place header node (initially hidden if showHeader is false). + headerNode = this.headerNode = put(domNode, + "div.dgrid-header.dgrid-header-row.ui-widget-header" + + (this.showHeader ? "" : ".dgrid-header-hidden")); + if(has("quirks") || has("ie") < 8){ + spacerNode = put(domNode, "div.dgrid-spacer"); + } + bodyNode = this.bodyNode = put(domNode, "div.dgrid-scroller"); + + // firefox 4 until at least 10 adds overflow: auto elements to the tab index by default for some + // reason; force them to be not tabbable + bodyNode.tabIndex = -1; + + this.headerScrollNode = put(domNode, "div.dgrid-header-scroll.dgrid-scrollbar-width.ui-widget-header"); + + // Place footer node (initially hidden if showFooter is false). + footerNode = this.footerNode = put("div.dgrid-footer" + + (this.showFooter ? "" : ".dgrid-footer-hidden")); + put(domNode, footerNode); + + if(isRTL){ + domNode.className += " dgrid-rtl" + (has("webkit") ? "" : " dgrid-rtl-nonwebkit"); + } + + listen(bodyNode, "scroll", function(event){ + if(self.showHeader){ + // keep the header aligned with the body + headerNode.scrollLeft = event.scrollLeft || bodyNode.scrollLeft; + } + // re-fire, since browsers are not consistent about propagation here + event.stopPropagation(); + listen.emit(domNode, "scroll", {scrollTarget: bodyNode}); + }); + this.configStructure(); + this.renderHeader(); + + this.contentNode = this.touchNode = put(this.bodyNode, "div.dgrid-content.ui-widget-content"); + // add window resize handler, with reference for later removal if needed + this._listeners.push(this._resizeHandle = listen(window, "resize", + miscUtil.throttleDelayed(winResizeHandler, this))); + }, + startup: function(){ + // summary: + // Called automatically after postCreate if the component is already + // visible; otherwise, should be called manually once placed. + + this.inherited(arguments); + if(this._started){ return; } // prevent double-triggering + this._started = true; + this.resize(); + // apply sort (and refresh) now that we're ready to render + this.set("sort", this._sort); + }, + + configStructure: function(){ + // does nothing in List, this is more of a hook for the Grid + }, + resize: function(){ + var + bodyNode = this.bodyNode, + headerNode = this.headerNode, + footerNode = this.footerNode, + headerHeight = headerNode.offsetHeight, + footerHeight = this.showFooter ? footerNode.offsetHeight : 0, + quirks = has("quirks") || has("ie") < 7; + + this.headerScrollNode.style.height = bodyNode.style.marginTop = headerHeight + "px"; + bodyNode.style.marginBottom = footerHeight + "px"; + + if(quirks){ + // in IE6 and quirks mode, the "bottom" CSS property is ignored. + // We guard against negative values in case of issues with external CSS. + bodyNode.style.height = ""; // reset first + bodyNode.style.height = + Math.max((this.domNode.offsetHeight - headerHeight - footerHeight), 0) + "px"; + if (footerHeight) { + // Work around additional glitch where IE 6 / quirks fails to update + // the position of the bottom-aligned footer; this jogs its memory. + footerNode.style.bottom = '1px'; + setTimeout(function(){ footerNode.style.bottom = ''; }, 0); + } + } + + if(!scrollbarWidth){ + // Measure the browser's scrollbar width using a DIV we'll delete right away + var scrollDiv = put(document.body, "div.dgrid-scrollbar-measure"); + scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; + put(scrollDiv, "!"); + + // avoid crazy issues in IE7 only, with certain widgets inside + if(has("ie") === 7){ scrollbarWidth++; } + + // add rules that can be used where scrollbar width/height is needed + this.addCssRule(".dgrid-scrollbar-width", "width: " + scrollbarWidth + "px"); + this.addCssRule(".dgrid-scrollbar-height", "height: " + scrollbarWidth + "px"); + + if(scrollbarWidth != 17 && !quirks){ + // for modern browsers, we can perform a one-time operation which adds + // a rule to account for scrollbar width in all grid headers. + this.addCssRule(".dgrid-header", "right: " + scrollbarWidth + "px"); + // add another for RTL grids + this.addCssRule(".dgrid-rtl-nonwebkit .dgrid-header", "left: " + scrollbarWidth + "px"); + } + } + + if(quirks){ + // old IE doesn't support left + right + width:auto; set width directly + headerNode.style.width = bodyNode.clientWidth + "px"; + setTimeout(function(){ + // sync up (after the browser catches up with the new width) + headerNode.scrollLeft = bodyNode.scrollLeft; + }, 0); + } + }, + addCssRule: addExtraRule, + on: function(eventType, listener){ + // delegate events to the domNode + var signal = listen(this.domNode, eventType, listener); + if(!has("dom-addeventlistener")){ + this._listeners.push(signal); + } + return signal; + }, + + cleanup: function(){ + // summary: + // Clears out all rows currently in the list. + + var observers = this.observers, + i; + for(i in this._rowIdToObject){ + if(this._rowIdToObject[i] != this.columns){ + var rowElement = byId(i); + if(rowElement){ + this.removeRow(rowElement, true); + } + } + } + // remove any store observers + for(i = 0;i < observers.length; i++){ + var observer = observers[i]; + observer && observer.cancel(); + } + this.observers = []; + this.preload = null; + }, + destroy: function(){ + // summary: + // Destroys this grid + + // remove any event listeners + if(this._listeners){ // guard against accidental subsequent calls to destroy + for(var i = this._listeners.length; i--;){ + this._listeners[i].remove(); + } + delete this._listeners; + } + + this.cleanup(); + // destroy DOM + put("!", this.domNode); + }, + refresh: function(){ + // summary: + // refreshes the contents of the grid + this.cleanup(); + this._rowIdToObject = {}; + this._autoId = 0; + + // make sure all the content has been removed so it can be recreated + this.contentNode.innerHTML = ""; + // Ensure scroll position always resets (especially for TouchScroll). + this.scrollTo({ x: 0, y: 0 }); + }, + + newRow: function(object, before, to, options){ + if(!before || before.parentNode){ + var i = options.start + to; + var row = this.insertRow(object, before ? before.parentNode : this.contentNode, before, i, options); + put(row, ".ui-state-highlight"); + setTimeout(function(){ + put(row, "!ui-state-highlight"); + }, 250); + return row; + } + }, + adjustRowIndices: function(firstRow){ + if(this.maintainOddEven){ + // this traverses through rows to maintain odd/even classes on the rows when indexes shift; + var next = firstRow; + var rowIndex = next.rowIndex; + do{ + if(next.rowIndex > -1){ + // skip non-numeric, non-rows + if((next.className + ' ').indexOf("dgrid-row ") > -1){ + put(next, '.' + (rowIndex % 2 == 1 ? oddClass : evenClass) + '!' + (rowIndex % 2 == 0 ? oddClass : evenClass)); + } + next.rowIndex = rowIndex++; + } + }while((next = next.nextSibling) && next.rowIndex != rowIndex); + } + }, + renderArray: function(results, beforeNode, options){ + // summary: + // This renders an array or collection of objects as rows in the grid, before the + // given node. This will listen for changes in the collection if an observe method + // is available (as it should be if it comes from an Observable data store). + options = options || {}; + var self = this, + start = options.start || 0, + row, rows, container; + + if(!beforeNode){ + this._lastCollection = results; + } + if(results.observe){ + // observe the results for changes + var observerIndex = this.observers.push(results.observe(function(object, from, to){ + var firstRow, nextNode; + // a change in the data took place + if(from > -1 && rows[from]){ + // remove from old slot + row = rows.splice(from, 1)[0]; + // check to make the sure the node is still there before we try to remove it, (in case it was moved to a different place in the DOM) + if(row.parentNode == container){ + firstRow = row.nextSibling; + if(firstRow){ // it's possible for this to have been already removed if it is in overlapping query results + if(from != to){ // if from and to are identical, it is an in-place update and we don't want to alter the rowIndex at all + firstRow.rowIndex--; // adjust the rowIndex so adjustRowIndices has the right starting point + } + } + self.removeRow(row); // now remove + } + // the removal of rows could cause us to need to page in more items + if(self._processScroll){ + self._processScroll(); + } + } + if(to > -1){ + // Add to new slot (either before an existing row, or at the end) + // First determine the DOM node that this should be placed before. + nextNode = rows[to]; + if(!nextNode){ + nextNode = rows[to - 1]; + if(nextNode){ + // Make sure to skip connected nodes, so we don't accidentally + // insert a row in between a parent and its children. + nextNode = (nextNode.connected || nextNode).nextSibling; + } + } + row = self.newRow(object, nextNode, to, options); + + if(row){ + row.observerIndex = observerIndex; + rows.splice(to, 0, row); + if(!firstRow || to < from){ + // the inserted row is first, so we update firstRow to point to it + var previous = row.previousSibling; + // if we are not in sync with the previous row, roll the firstRow back one so adjustRowIndices can sync everything back up. + firstRow = !previous || previous.rowIndex + 1 == row.rowIndex || row.rowIndex == 0 ? + row : previous; + } + } + options.count++; + } + from != to && firstRow && self.adjustRowIndices(firstRow); + }, true)) - 1; + } + var rowsFragment = document.createDocumentFragment(); + // now render the results + if(results.map){ + rows = results.map(mapEach, console.error); + if(rows.then){ + return rows.then(whenDone); + } + }else{ + rows = []; + for(var i = 0, l = results.length; i < l; i++){ + rows[i] = mapEach(results[i]); + } + } + var lastRow; + function mapEach(object){ + lastRow = self.insertRow(object, rowsFragment, null, start++, options); + lastRow.observerIndex = observerIndex; + return lastRow; + } + function whenDone(resolvedRows){ + container = beforeNode ? beforeNode.parentNode : self.contentNode; + if(container){ + container.insertBefore(rowsFragment, beforeNode || null); + lastRow = resolvedRows[resolvedRows.length - 1]; + lastRow && self.adjustRowIndices(lastRow); + } + return (rows = resolvedRows); + } + return whenDone(rows); + }, + + renderHeader: function(){ + // no-op in a plain list + }, + + _autoId: 0, + insertRow: function(object, parent, beforeNode, i, options){ + // summary: + // Creates a single row in the grid. + + // Include parentId within row identifier if one was specified in options. + // (This is used by tree to allow the same object to appear under + // multiple parents.) + var parentId = options.parentId, + id = this.id + "-row-" + (parentId ? parentId + "-" : "") + + ((this.store && this.store.getIdentity) ? + this.store.getIdentity(object) : this._autoId++), + row = byId(id), + previousRow = row && row.previousSibling; + + if(!row || // we must create a row if it doesn't exist, or if it previously belonged to a different container + (beforeNode && row.parentNode != beforeNode.parentNode)){ + if(row){// if it existed elsewhere in the DOM, we will remove it, so we can recreate it + this.removeRow(row); + } + row = this.renderRow(object, options); + row.className = (row.className || "") + " ui-state-default dgrid-row " + (i % 2 == 1 ? oddClass : evenClass); + // get the row id for easy retrieval + this._rowIdToObject[row.id = id] = object; + } + parent.insertBefore(row, beforeNode || null); + if(previousRow){ + // in this case, we are pulling the row from another location in the grid, and we need to readjust the rowIndices from the point it was removed + this.adjustRowIndices(previousRow); + } + row.rowIndex = i; + return row; + }, + renderRow: function(value, options){ + // summary: + // Responsible for returning the DOM for a single row in the grid. + + return put("div", "" + value); + }, + removeRow: function(rowElement, justCleanup){ + // summary: + // Simply deletes the node in a plain List. + // Column plugins may aspect this to implement their own cleanup routines. + // rowElement: Object|DOMNode + // Object or element representing the row to be removed. + // justCleanup: Boolean + // If true, the row element will not be removed from the DOM; this can + // be used by extensions/plugins in cases where the DOM will be + // massively cleaned up at a later point in time. + + rowElement = rowElement.element || rowElement; + delete this._rowIdToObject[rowElement.id]; + if(!justCleanup){ + put(rowElement, "!"); + } + }, + + row: function(target){ + // summary: + // Get the row object by id, object, node, or event + var id; + + if(target instanceof this._Row){ return target; } // no-op; already a row + + if(target.target && target.target.nodeType){ + // event + target = target.target; + } + if(target.nodeType){ + var object; + do{ + var rowId = target.id; + if((object = this._rowIdToObject[rowId])){ + return new this._Row(rowId.substring(this.id.length + 5), object, target); + } + target = target.parentNode; + }while(target && target != this.domNode); + return; + } + if(typeof target == "object"){ + // assume target represents a store item + id = this.store.getIdentity(target); + }else{ + // assume target is a row ID + id = target; + target = this._rowIdToObject[this.id + "-row-" + id]; + } + return new this._Row(id, target, byId(this.id + "-row-" + id)); + }, + cell: function(target){ + // this doesn't do much in a plain list + return { + row: this.row(target) + }; + }, + + _move: function(item, steps, targetClass, visible){ + var nextSibling, current, element; + // Start at the element indicated by the provided row or cell object. + element = current = item.element; + steps = steps || 1; + + do{ + // Outer loop: move in the appropriate direction. + if((nextSibling = current[steps < 0 ? "previousSibling" : "nextSibling"])){ + do{ + // Inner loop: advance, and dig into children if applicable. + current = nextSibling; + if(current && (current.className + " ").indexOf(targetClass + " ") > -1){ + // Element with the appropriate class name; count step, stop digging. + element = current; + steps += steps < 0 ? 1 : -1; + break; + } + // If the next sibling isn't a match, drill down to search, unless + // visible is true and children are hidden. + }while((nextSibling = (!visible || !current.hidden) && current[steps < 0 ? "lastChild" : "firstChild"])); + }else{ + current = current.parentNode; + if(current === this.bodyNode || current === this.headerNode){ + // Break out if we step out of the navigation area entirely. + break; + } + } + }while(steps); + // Return the final element we arrived at, which might still be the + // starting element if we couldn't navigate further in that direction. + return element; + }, + + up: function(row, steps, visible){ + // summary: + // Returns the row that is the given number of steps (1 by default) + // above the row represented by the given object. + // row: + // The row to navigate upward from. + // steps: + // Number of steps to navigate up from the given row; default is 1. + // visible: + // If true, rows that are currently hidden (i.e. children of + // collapsed tree rows) will not be counted in the traversal. + // returns: + // A row object representing the appropriate row. If the top of the + // list is reached before the given number of steps, the first row will + // be returned. + return this.row(this._move(row, -(steps || 1), "dgrid-row", visible)); + }, + down: function(row, steps, visible){ + // summary: + // Returns the row that is the given number of steps (1 by default) + // below the row represented by the given object. + // row: + // The row to navigate downward from. + // steps: + // Number of steps to navigate down from the given row; default is 1. + // visible: + // If true, rows that are currently hidden (i.e. children of + // collapsed tree rows) will not be counted in the traversal. + // returns: + // A row object representing the appropriate row. If the bottom of the + // list is reached before the given number of steps, the last row will + // be returned. + return this.row(this._move(row, steps || 1, "dgrid-row", visible)); + }, + + scrollTo: has("touch") ? function(){ + // If TouchScroll is the superclass, defer to its implementation. + return this.inherited(arguments); + } : function(options){ + // No TouchScroll; simple implementation which sets scrollLeft/Top. + if(typeof options.x !== "undefined"){ + this.bodyNode.scrollLeft = options.x; + } + if(typeof options.y !== "undefined"){ + this.bodyNode.scrollTop = options.y; + } + }, + + getScrollPosition: has("touch") ? function(){ + // If TouchScroll is the superclass, defer to its implementation. + return this.inherited(arguments); + } : function(){ + // No TouchScroll; return based on scrollLeft/Top. + return { + x: this.bodyNode.scrollLeft, + y: this.bodyNode.scrollTop + }; + }, + + get: function(/*String*/ name /*, ... */){ + // summary: + // Get a property on a List instance. + // name: + // The property to get. + // returns: + // The property value on this List instance. + // description: + // Get a named property on a List object. The property may + // potentially be retrieved via a getter method in subclasses. In the base class + // this just retrieves the object's property. + + var fn = "_get" + name.charAt(0).toUpperCase() + name.slice(1); + + if(typeof this[fn] === "function"){ + return this[fn].apply(this, [].slice.call(arguments, 1)); + } + + // Alert users that try to use Dijit-style getter/setters so they don’t get confused + // if they try to use them and it does not work + if(!has("dojo-built") && typeof this[fn + "Attr"] === "function"){ + console.warn("dgrid: Use " + fn + " instead of " + fn + "Attr for getting " + name); + } + + return this[name]; + }, + + set: function(/*String*/ name, /*Object*/ value /*, ... */){ + // summary: + // Set a property on a List instance + // name: + // The property to set. + // value: + // The value to set in the property. + // returns: + // The function returns this List instance. + // description: + // Sets named properties on a List object. + // A programmatic setter may be defined in subclasses. + // + // set() may also be called with a hash of name/value pairs, ex: + // | myObj.set({ + // | foo: "Howdy", + // | bar: 3 + // | }) + // This is equivalent to calling set(foo, "Howdy") and set(bar, 3) + + if(typeof name === "object"){ + for(var k in name){ + this.set(k, name[k]); + } + }else{ + var fn = "_set" + name.charAt(0).toUpperCase() + name.slice(1); + + if(typeof this[fn] === "function"){ + this[fn].apply(this, [].slice.call(arguments, 1)); + }else{ + // Alert users that try to use Dijit-style getter/setters so they don’t get confused + // if they try to use them and it does not work + if(!has("dojo-built") && typeof this[fn + "Attr"] === "function"){ + console.warn("dgrid: Use " + fn + " instead of " + fn + "Attr for setting " + name); + } + + this[name] = value; + } + } + + return this; + }, + + // Accept both class and className programmatically to set domNode class. + _getClass: getClass, + _setClass: setClass, + _getClassName: getClass, + _setClassName: setClass, + + _setSort: function(property, descending){ + // summary: + // Sort the content + // property: String|Array + // String specifying field to sort by, or actual array of objects + // with attribute and descending properties + // descending: boolean + // In the case where property is a string, this argument + // specifies whether to sort ascending (false) or descending (true) + + this._sort = typeof property != "string" ? property : + [{attribute: property, descending: descending}]; + + this.refresh(); + + if(this._lastCollection){ + if(property.length){ + // if an array was passed in, flatten to just first sort attribute + // for default array sort logic + if(typeof property != "string"){ + descending = property[0].descending; + property = property[0].attribute; + } + + this._lastCollection.sort(function(a,b){ + var aVal = a[property], bVal = b[property]; + // fall back undefined values to "" for more consistent behavior + if(aVal === undefined){ aVal = ""; } + if(bVal === undefined){ bVal = ""; } + return aVal == bVal ? 0 : (aVal > bVal == !descending ? 1 : -1); + }); + } + this.renderArray(this._lastCollection); + } + }, + // TODO: remove the following two (and rename _sort to sort) in 1.0 + sort: function(property, descending){ + kernel.deprecated("sort(...)", 'use set("sort", ...) instead', "dgrid 1.0"); + this.set("sort", property, descending); + }, + _getSort: function(){ + return this._sort; + }, + + _setShowHeader: function(show){ + // this is in List rather than just in Grid, primarily for two reasons: + // (1) just in case someone *does* want to show a header in a List + // (2) helps address IE < 8 header display issue in List + + var headerNode = this.headerNode; + + this.showHeader = show; + + // add/remove class which has styles for "hiding" header + put(headerNode, (show ? "!" : ".") + "dgrid-header-hidden"); + + this.renderHeader(); + this.resize(); // resize to account for (dis)appearance of header + + if(show){ + // Update scroll position of header to make sure it's in sync. + headerNode.scrollLeft = this.getScrollPosition().x; + } + }, + setShowHeader: function(show){ + kernel.deprecated("setShowHeader(...)", 'use set("showHeader", ...) instead', "dgrid 1.0"); + this.set("showHeader", show); + }, + + _setShowFooter: function(show){ + this.showFooter = show; + + // add/remove class which has styles for hiding footer + put(this.footerNode, (show ? "!" : ".") + "dgrid-footer-hidden"); + + this.resize(); // to account for (dis)appearance of footer + } + }); +}); diff --git a/widgets/dgrid/OnDemandGrid.js b/widgets/dgrid/OnDemandGrid.js new file mode 100644 index 0000000..3b6f514 --- /dev/null +++ b/widgets/dgrid/OnDemandGrid.js @@ -0,0 +1,3 @@ +define(["dojo/_base/declare", "./Grid", "./OnDemandList"], function(declare, Grid, OnDemandList){ + return declare([Grid, OnDemandList], {}); +}); \ No newline at end of file diff --git a/widgets/dgrid/OnDemandList.js b/widgets/dgrid/OnDemandList.js new file mode 100644 index 0000000..af3067d --- /dev/null +++ b/widgets/dgrid/OnDemandList.js @@ -0,0 +1,415 @@ +define(["./List", "./_StoreMixin", "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/Deferred", "dojo/on", "./util/misc", "put-selector/put"], +function(List, _StoreMixin, declare, lang, Deferred, listen, miscUtil, put){ + +return declare([List, _StoreMixin], { + // minRowsPerPage: Integer + // The minimum number of rows to request at one time. + minRowsPerPage: 25, + // maxRowsPerPage: Integer + // The maximum number of rows to request at one time. + maxRowsPerPage: 250, + // maxEmptySpace: Integer + // Defines the maximum size (in pixels) of unrendered space below the + // currently-rendered rows. Setting this to less than Infinity can be useful if you + // wish to limit the initial vertical scrolling of the grid so that the scrolling is + // not excessively sensitive. With very large grids of data this may make scrolling + // easier to use, albiet it can limit the ability to instantly scroll to the end. + maxEmptySpace: Infinity, + // bufferRows: Integer + // The number of rows to keep ready on each side of the viewport area so that the user can + // perform local scrolling without seeing the grid being built. Increasing this number can + // improve perceived performance when the data is being retrieved over a slow network. + bufferRows: 10, + // farOffRemoval: Integer + // Defines the minimum distance (in pixels) from the visible viewport area + // rows must be in order to be removed. Setting to Infinity causes rows + // to never be removed. + farOffRemoval: 2000, + + rowHeight: 22, + + // queryRowsOverlap: Integer + // Indicates the number of rows to overlap queries. This helps keep + // continuous data when underlying data changes (and thus pages don't + // exactly align) + queryRowsOverlap: 1, + + // pagingDelay: Integer + // Indicates the delay (in milliseconds) to wait before paging in more data + // on scroll. This can be increased for low-bandwidth clients, or to + // reduce the number of requests against a server + pagingDelay: miscUtil.defaultDelay, + + postCreate: function(){ + this.inherited(arguments); + var self = this; + // check visibility on scroll events + listen(this.bodyNode, "scroll", + miscUtil.throttleDelayed(function(event){ self._processScroll(event); }, + null, this.pagingDelay)); + }, + + renderQuery: function(query, preloadNode, options){ + // summary: + // Creates a preload node for rendering a query into, and executes the query + // for the first page of data. Subsequent data will be downloaded as it comes + // into view. + var preload = { + query: query, + count: 0, + node: preloadNode, + options: options + }; + if(!preloadNode){ + var rootQuery = true; + var topPreload = { + node: put(this.contentNode, "div.dgrid-preload", { + rowIndex: 0 + }), + count: 0, + //topPreloadNode.preload = true; + query: query, + next: preload, + options: options + }; + preload.node = preloadNode = put(this.contentNode, "div.dgrid-preload"); + preload.previous = topPreload; + } + // this preload node is used to represent the area of the grid that hasn't been + // downloaded yet + preloadNode.rowIndex = this.minRowsPerPage; + + var priorPreload = this.preload; + if(priorPreload){ + // the preload nodes (if there are multiple) are represented as a linked list, need to insert it + if((preload.next = priorPreload.next) && + // check to make sure that the current scroll position is below this preload + this.bodyNode.scrollTop >= priorPreload.node.offsetTop){ + // the prior preload is above/before in the linked list + preload.previous = priorPreload; + }else{ + // the prior preload is below/after in the linked list + preload.next = priorPreload; + preload.previous = priorPreload.previous; + } + // adjust the previous and next links so the linked list is proper + preload.previous.next = preload; + preload.next.previous = preload; + }else{ + this.preload = preload; + } + + var loadingNode = put(preloadNode, "-div.dgrid-loading"), + innerNode = put(loadingNode, "div.dgrid-below"); + innerNode.innerHTML = this.loadingMessage; + + // Establish query options, mixing in our own. + // (The getter returns a delegated object, so simply using mixin is safe.) + options = lang.mixin(this.get("queryOptions"), options, + {start: 0, count: this.minRowsPerPage, query: query}); + // execute the query + var results = query(options); + var self = this; + // render the result set + Deferred.when(this.renderArray(results, preloadNode, options), function(trs){ + return Deferred.when(results.total || results.length, function(total){ + // remove loading node + put(loadingNode, "!"); + // now we need to adjust the height and total count based on the first result set + var trCount = trs.length; + total = total || trCount; + if(!total){ + self.noDataNode = put(self.contentNode, "div.dgrid-no-data"); + self.noDataNode.innerHTML = self.noDataMessage; + } + var height = 0; + for(var i = 0; i < trCount; i++){ + height += self._calcRowHeight(trs[i]); + } + // only update rowHeight if we actually got results and are visible + if(trCount && height){ self.rowHeight = height / trCount; } + + total -= trCount; + preload.count = total; + preloadNode.rowIndex = trCount; + if(total){ + preloadNode.style.height = Math.min(total * self.rowHeight, self.maxEmptySpace) + "px"; + }else{ + // if total is 0, IE quirks mode can't handle 0px height for some reason, I don't know why, but we are setting display: none for now + preloadNode.style.display = "none"; + } + self._processScroll(); // recheck the scroll position in case the query didn't fill the screen + // can remove the loading node now + return trs; + }); + }); + + // return results so that callers can handle potential of async error + return results; + }, + + refresh: function(){ + this.inherited(arguments); + if(this.store){ + // render the query + var self = this; + this._trackError(function(){ + return self.renderQuery(function(queryOptions){ + return self.store.query(self.query, queryOptions); + }); + }); + } + }, + + _calcRowHeight: function(rowElement){ + // summary: + // Calculate the height of a row. This is a method so it can be overriden for + // plugins that add connected elements to a row, like the tree + + var sibling = rowElement.previousSibling; + return sibling && sibling.offsetTop != rowElement.offsetTop ? + rowElement.offsetHeight : 0; + }, + + lastScrollTop: 0, + _processScroll: function(evt){ + // summary: + // Checks to make sure that everything in the viewable area has been + // downloaded, and triggering a request for the necessary data when needed. + var grid = this, + scrollNode = grid.bodyNode, + // grab current visible top from event if provided, otherwise from node + visibleTop = (evt && evt.scrollTop) || scrollNode.scrollTop, + visibleBottom = scrollNode.offsetHeight + visibleTop, + priorPreload, preloadNode, preload = grid.preload, + lastScrollTop = grid.lastScrollTop, + requestBuffer = grid.bufferRows * grid.rowHeight, + searchBuffer = requestBuffer - grid.rowHeight; // Avoid rounding causing multiple queries + + // XXX: I do not know why this happens. + // munging the actual location of the viewport relative to the preload node by a few pixels in either + // direction is necessary because at least WebKit on Windows seems to have an error that causes it to + // not quite get the entire element being focused in the viewport during keyboard navigation, + // which means it becomes impossible to load more data using keyboard navigation because there is + // no more data to scroll to to trigger the fetch. + // 1 is arbitrary and just gets it to work correctly with our current test cases; don’t wanna go + // crazy and set it to a big number without understanding more about what is going on. + // wondering if it has to do with border-box or something, but changing the border widths does not + // seem to make it break more or less, so I do not know… + var mungeAmount = 1; + + grid.lastScrollTop = visibleTop; + + function removeDistantNodes(preload, distanceOff, traversal, below){ + // we check to see the the nodes are "far off" + var farOffRemoval = grid.farOffRemoval, + preloadNode = preload.node; + // by checking to see if it is the farOffRemoval distance away + if(distanceOff > 2 * farOffRemoval){ + // ok, there is preloadNode that is far off, let's remove rows until we get to in the current viewpoint + var row, nextRow = preloadNode[traversal]; + var reclaimedHeight = 0; + var count = 0; + var toDelete = []; + while((row = nextRow)){ + var rowHeight = grid._calcRowHeight(row); + if(reclaimedHeight + rowHeight + farOffRemoval > distanceOff || (nextRow.className.indexOf("dgrid-row") < 0 && nextRow.className.indexOf("dgrid-loading") < 0)){ + // we have reclaimed enough rows or we have gone beyond grid rows, let's call it good + break; + } + var nextRow = row[traversal]; // have to do this before removing it + var lastObserverIndex, currentObserverIndex = row.observerIndex; + if(currentObserverIndex != lastObserverIndex && lastObserverIndex > -1){ + // we have gathered a whole page of observed rows, we can delete them now + var observers = grid.observers; + var observer = observers[lastObserverIndex]; + observer && observer.cancel(); + observers[lastObserverIndex] = 0; // remove it so we don't call cancel twice + } + reclaimedHeight += rowHeight; + count += row.count || 1; + lastObserverIndex = currentObserverIndex; + // we just do cleanup here, as we will do a more efficient node destruction in the setTimeout below + grid.removeRow(row, true); + toDelete.push(row); + } + // now adjust the preloadNode based on the reclaimed space + preload.count += count; + if(below){ + preloadNode.rowIndex -= count; + adjustHeight(preload); + }else{ + // if it is above, we can calculate the change in exact row changes, which we must do to not mess with the scrolling + preloadNode.style.height = (preloadNode.offsetHeight + reclaimedHeight) + "px"; + } + // we remove the elements after expanding the preload node so that the contraction doesn't alter the scroll position + var trashBin = put("div"); + for(var i = 0; i < toDelete.length; i++){ + put(trashBin, toDelete[i]); // remove it from the DOM + } + setTimeout(function(){ + // we can defer the destruction until later + put(trashBin, "!"); + },1); + } + } + + function adjustHeight(preload, noMax){ + preload.node.style.height = Math.min(preload.count * grid.rowHeight, noMax ? Infinity : grid.maxEmptySpace) + "px"; + } + while(preload && !preload.node.offsetWidth){ + // skip past preloads that are not currently connected + preload = preload.previous; + } + // there can be multiple preloadNodes (if they split, or multiple queries are created), + // so we can traverse them until we find whatever is in the current viewport, making + // sure we don't backtrack + while(preload && preload != priorPreload){ + priorPreload = grid.preload; + grid.preload = preload; + preloadNode = preload.node; + var preloadTop = preloadNode.offsetTop; + var preloadHeight; + + if(visibleBottom + mungeAmount + searchBuffer < preloadTop){ + // the preload is below the line of sight + do{ + preload = preload.previous; + }while(preload && !preload.node.offsetWidth); // skip past preloads that are not currently connected + }else if(visibleTop - mungeAmount - searchBuffer > (preloadTop + (preloadHeight = preloadNode.offsetHeight))){ + // the preload is above the line of sight + do{ + preload = preload.next; + }while(preload && !preload.node.offsetWidth);// skip past preloads that are not currently connected + }else{ + // the preload node is visible, or close to visible, better show it + var offset = ((preloadNode.rowIndex ? visibleTop - requestBuffer : visibleBottom) - preloadTop) / grid.rowHeight; + var count = (visibleBottom - visibleTop + 2 * requestBuffer) / grid.rowHeight; + // utilize momentum for predictions + var momentum = Math.max(Math.min((visibleTop - lastScrollTop) * grid.rowHeight, grid.maxRowsPerPage/2), grid.maxRowsPerPage/-2); + count += Math.min(Math.abs(momentum), 10); + if(preloadNode.rowIndex == 0){ + // at the top, adjust from bottom to top + offset -= count; + } + offset = Math.max(offset, 0); + if(offset < 10 && offset > 0 && count + offset < grid.maxRowsPerPage){ + // connect to the top of the preloadNode if possible to avoid excessive adjustments + count += Math.max(0, offset); + offset = 0; + } + count = Math.min(Math.max(count, grid.minRowsPerPage), + grid.maxRowsPerPage, preload.count); + if(count == 0){ + return; + } + count = Math.ceil(count); + offset = Math.min(Math.floor(offset), preload.count - count); + var options = lang.mixin(grid.get("queryOptions"), preload.options); + preload.count -= count; + var beforeNode = preloadNode, + keepScrollTo, queryRowsOverlap = grid.queryRowsOverlap, + below = preloadNode.rowIndex > 0 && preload; + if(below){ + // add new rows below + var previous = preload.previous; + if(previous){ + removeDistantNodes(previous, visibleTop - (previous.node.offsetTop + previous.node.offsetHeight), 'nextSibling'); + if(offset > 0 && previous.node == preloadNode.previousSibling){ + // all of the nodes above were removed + offset = Math.min(preload.count, offset); + preload.previous.count += offset; + adjustHeight(preload.previous, true); + preload.count -= offset; + preloadNode.rowIndex += offset; + queryRowsOverlap = 0; + }else{ + count += offset; + } + } + options.start = preloadNode.rowIndex - queryRowsOverlap; + preloadNode.rowIndex += count; + }else{ + // add new rows above + if(preload.next){ + // remove out of sight nodes first + removeDistantNodes(preload.next, preload.next.node.offsetTop - visibleBottom, 'previousSibling', true); + var beforeNode = preloadNode.nextSibling; + if(beforeNode == preload.next.node){ + // all of the nodes were removed, can position wherever we want + preload.next.count += preload.count - offset; + preload.next.node.rowIndex = offset + count; + adjustHeight(preload.next); + preload.count = offset; + queryRowsOverlap = 0; + }else{ + keepScrollTo = true; + } + + } + options.start = preload.count; + } + options.count = Math.min(count + queryRowsOverlap, grid.maxRowsPerPage); + if(keepScrollTo){ + keepScrollTo = beforeNode.offsetTop; + } + + adjustHeight(preload); + // create a loading node as a placeholder while the data is loaded + var loadingNode = put(beforeNode, "-div.dgrid-loading[style=height:" + count * grid.rowHeight + "px]"), + innerNode = put(loadingNode, "div.dgrid-" + (below ? "below" : "above")); + innerNode.innerHTML = grid.loadingMessage; + loadingNode.count = count; + // use the query associated with the preload node to get the next "page" + options.query = preload.query; + // Query now to fill in these rows. + // Keep _trackError-wrapped results separate, since if results is a + // promise, it will lose QueryResults functions when chained by `when` + var results = preload.query(options), + trackedResults = grid._trackError(function(){ return results; }); + + if(trackedResults === undefined){ return; } // sync query failed + + // Isolate the variables in case we make multiple requests + // (which can happen if we need to render on both sides of an island of already-rendered rows) + (function(loadingNode, scrollNode, below, keepScrollTo, results){ + Deferred.when(grid.renderArray(results, loadingNode, options), function(){ + // can remove the loading node now + beforeNode = loadingNode.nextSibling; + put(loadingNode, "!"); + if(keepScrollTo && beforeNode){ // beforeNode may have been removed if the query results loading node was a removed as a distant node before rendering + // if the preload area above the nodes is approximated based on average + // row height, we may need to adjust the scroll once they are filled in + // so we don't "jump" in the scrolling position + var pos = grid.getScrollPosition(); + grid.scrollTo({ + // Since we already had to query the scroll position, + // include x to avoid TouchScroll querying it again on its end. + x: pos.x, + y: pos.y + beforeNode.offsetTop - keepScrollTo, + // Don't kill momentum mid-scroll (for TouchScroll only). + preserveMomentum: true + }); + } + if(below){ + // if it is below, we will use the total from the results to update + // the count of the last preload in case the total changes as later pages are retrieved + // (not uncommon when total counts are estimated for db perf reasons) + Deferred.when(results.total || results.length, function(total){ + // recalculate the count + below.count = total - below.node.rowIndex; + // readjust the height + adjustHeight(below); + }); + } + // make sure we have covered the visible area + grid._processScroll(); + }); + }).call(this, loadingNode, scrollNode, below, keepScrollTo, results); + preload = preload.previous; + } + } + } +}); + +}); diff --git a/widgets/dgrid/README.md b/widgets/dgrid/README.md new file mode 100644 index 0000000..80461e7 --- /dev/null +++ b/widgets/dgrid/README.md @@ -0,0 +1,67 @@ +This project provides widgets for lists of data, including simple sets of scrolling rows, +grids of data, on-demand lazy-loaded data, and various plugins for additional functionality. +This project also provides touch scrolling for mobile devices with native style +momentum, bouncing, and scrollbars. + +The dgrid project is available under the same dual BSD/AFLv2 license as the Dojo Toolkit. + +# Installation + +## Automatic Download with CPM + +dgrid can be installed via [CPM](https://github.com/kriszyp/cpm) +using the following command: + + cpm install dgrid + +The above command will automatically find the highest tagged version of dgrid and +install it. Alternatively, the latest development version of dgrid can be +installed by instructing CPM to install from the master branch: + + cpm install dgrid master + +Note that while dgrid lists the dojo package as a dependency, it does not install +dijit, as it is not a hard requirement. Dijit can be additionally installed by +running: + + cpm install dijit + +## Manual Download + +Alternatively, dgrid and its dependencies can be downloaded individually: + +* [xstyle](https://github.com/kriszyp/xstyle) +* [put-selector](https://github.com/kriszyp/put-selector) +* [The Dojo Toolkit](http://dojotoolkit.org) SDK version 1.7 or higher + * Out of the DTK components, Dojo core is the only hard dependency for dgrid; + however, some of the test pages also use components from Dijit, and + Dojox (namely grid for a comparison test, and mobile for a mobile page). + +It is recommended to arrange all dependencies as siblings, resulting in a +directory structure like the following: + +* `dgrid` +* `dijit` (optional, dependency of some dgrid tests) +* `dojo` +* `dojox` (optional, dependency of some dgrid tests) +* `put-selector` +* `xstyle` +* `util` (optional, e.g. if pursuing a custom build) + +dgrid works best with the latest revision of Dojo 1.7 or higher. As of this +writing, [Dojo 1.8.0](http://download.dojotoolkit.org/release-1.8.0/) is +recommended. + +Note that while dgrid supports Dojo 1.8 and may take advantage of features or +fix issues specific to it where possible, it does not have any hard dependency +on APIs new to 1.8, so as to maintain compatibility with 1.7. + +# Documentation + +Documentation for dgrid components is available in the +[dgrid GitHub project wiki](https://github.com/SitePen/dgrid/wiki). +The wiki's content may still be obtained for offline reading by cloning +the wiki repository, as indicated under the "Git Access" tab. + +In addition to the documentation on the wiki, if upgrading from a previous +dgrid release, please be sure to read the changelog, found in CHANGES.md. diff --git a/widgets/dgrid/Selection.js b/widgets/dgrid/Selection.js new file mode 100644 index 0000000..1823951 --- /dev/null +++ b/widgets/dgrid/Selection.js @@ -0,0 +1,331 @@ +define(["dojo/_base/kernel", "dojo/_base/declare", "dojo/_base/Deferred", "dojo/on", "dojo/has", "dojo/aspect", "./List", "dojo/has!touch?./util/touch", "put-selector/put", "dojo/query"], +function(kernel, declare, Deferred, on, has, aspect, List, touchUtil, put){ + +var ctrlEquiv = has("mac") ? "metaKey" : "ctrlKey"; +return declare(null, { + // summary: + // Add selection capabilities to a grid. The grid will have a selection property and + // fire "dgrid-select" and "dgrid-deselect" events. + + // selectionDelegate: String + // Selector to delegate to as target of selection events. + selectionDelegate: ".dgrid-row", + + // selectionEvents: String + // Event (or events, comma-delimited) to listen on to trigger select logic. + // Note: this is ignored in the case of touch devices. + selectionEvents: "mousedown,mouseup,dgrid-cellfocusin", + + // deselectOnRefresh: Boolean + // If true, the selection object will be cleared when refresh is called. + deselectOnRefresh: true, + + //allowSelectAll: Boolean + // If true, allow ctrl/cmd+A to select all rows. + // Also consulted by the selector plugin for showing select-all checkbox. + allowSelectAll: false, + + create: function(){ + this.selection = {}; + return this.inherited(arguments); + }, + postCreate: function(){ + this.inherited(arguments); + this._initSelectionEvents(); // first time; set up event hooks + }, + + // selection: + // An object where the property names correspond to + // object ids and values are true or false depending on whether an item is selected + selection: {}, + // selectionMode: String + // The selection mode to use, can be "none", "multiple", "single", or "extended". + selectionMode: "extended", + + _setSelectionMode: function(mode){ + // summary: + // Updates selectionMode, resetting necessary variables. + if(mode == this.selectionMode){ return; } // prevent unnecessary spinning + + // Start selection fresh when switching mode. + this.clearSelection(); + + this.selectionMode = mode; + }, + setSelectionMode: function(mode){ + kernel.deprecated("setSelectionMode(...)", 'use set("selectionMode", ...) instead', "dgrid 1.0"); + this.set("selectionMode", mode); + }, + + _handleSelect: function(event, currentTarget){ + // don't run if selection mode is none, + // or if coming from a dgrid-cellfocusin from a mousedown + if(this.selectionMode == "none" || + (event.type == "dgrid-cellfocusin" && event.parentType == "mousedown") || + (event.type == "mouseup" && currentTarget != this._waitForMouseUp)){ + return; + } + this._waitForMouseUp = null; + this._selectionTriggerEvent = event; + var ctrlKey = !event.keyCode ? event[ctrlEquiv] : event.ctrlKey; + if(!event.keyCode || !event.ctrlKey || event.keyCode == 32){ + var mode = this.selectionMode, + row = currentTarget, + rowObj = this.row(row), + lastRow = this._lastSelected; + + if(mode == "single"){ + if(lastRow === row){ + // Allow ctrl to toggle selection, even within single select mode. + this.select(row, null, !ctrlKey || !this.isSelected(row)); + }else{ + this.clearSelection(); + this.select(row); + this._lastSelected = row; + } + }else if(this.selection[rowObj.id] && !event.shiftKey && event.type == "mousedown"){ + // we wait for the mouse up if we are clicking a selected item so that drag n' drop + // is possible without losing our selection + this._waitForMouseUp = row; + }else{ + var value; + // clear selection first for non-ctrl-clicks in extended mode, + // as well as for right-clicks on unselected targets + if((event.button != 2 && mode == "extended" && !ctrlKey) || + (event.button == 2 && !(this.selection[rowObj.id]))){ + this.clearSelection(rowObj.id, true); + } + if(!event.shiftKey){ + // null == toggle; undefined == true; + lastRow = value = ctrlKey ? null : undefined; + } + this.select(row, lastRow, value); + + if(!lastRow){ + // update lastRow reference for potential subsequent shift+select + // (current row was already selected by earlier logic) + this._lastSelected = row; + } + } + if(!event.keyCode && (event.shiftKey || ctrlKey)){ + // prevent selection in firefox + event.preventDefault(); + } + } + this._selectionTriggerEvent = null; + }, + + _initSelectionEvents: function(){ + // summary: + // Performs first-time hookup of event handlers containing logic + // required for selection to operate. + + var grid = this, + selector = this.selectionDelegate; + + // This is to stop IE8+'s web accelerator and selection. + // It also stops selection in Chrome/Safari. + on(this.domNode, "selectstart", function(event){ + // In IE, this also bubbles from text selection inside editor fields; + // we don't want to prevent that! + var tag = event.target && event.target.tagName; + if(tag != "INPUT" && tag != "TEXTAREA"){ + event.preventDefault(); + } + }); + + function focus(event){ + grid._handleSelect(event, this); + } + + if(has("touch")){ + // listen for touch taps if available + on(this.contentNode, touchUtil.selector(selector, touchUtil.tap), function(evt){ + grid._handleSelect(evt, this); + }); + }else{ + // listen for actions that should cause selections + on(this.contentNode, on.selector(selector, this.selectionEvents), focus); + } + + // If allowSelectAll is true, allow ctrl/cmd+A to (de)select all rows. + // (Handler further checks against _allowSelectAll, which may be updated + // if selectionMode is changed post-init.) + if(this.allowSelectAll){ + this.on("keydown", function(event) { + if (event[ctrlEquiv] && event.keyCode == 65) { + event.preventDefault(); + grid[grid.allSelected ? "clearSelection" : "selectAll"](); + } + }); + } + + aspect.before(this, "removeRow", function(rowElement, justCleanup){ + var row; + if(!justCleanup){ + row = this.row(rowElement); + // if it is a real row removal for a selected item, deselect it + if(row && (row.id in this.selection)){ this.deselect(rowElement); } + } + }); + }, + + allowSelect: function(row){ + // summary: + // A method that can be overriden to determine whether or not a row (or + // cell) can be selected. By default, all rows (or cells) are selectable. + return true; + }, + + _selectionEventQueue: function(value, type){ + var grid = this, + event = "dgrid-" + (value ? "select" : "deselect"), + rows = this[event], // current event queue (actually cells for CellSelection) + trigger = this._selectionTriggerEvent; + + if (trigger) { + // If selection was triggered by another event, we want to know its type + // to report later. Grab it ahead of the timeout to avoid + // "member not found" errors in IE < 9. + trigger = trigger.type; + } + + if(rows){ return rows; } // return existing queue, allowing to push more + + // Create a timeout to fire an event for the accumulated rows once everything is done. + // We expose the callback in case the event needs to be fired immediately. + setTimeout(this._fireSelectionEvent = function(){ + if(!rows){ return; } // rows will be set only the first time this is called + + var eventObject = { + bubbles: true, + grid: grid + }; + if(trigger){ eventObject.parentType = trigger; } + eventObject[type] = rows; + on.emit(grid.contentNode, event, eventObject); + rows = null; + // clear the queue, so we create a new one as needed + delete grid[event]; + }, 0); + return (rows = this[event] = []); + }, + select: function(row, toRow, value){ + if(value === undefined){ + // default to true + value = true; + } + if(!row.element){ + row = this.row(row); + } + if(this.allowSelect(row)){ + var selection = this.selection; + var previousValue = selection[row.id]; + if(value === null){ + // indicates a toggle + value = !previousValue; + } + var element = row.element; + if(!value && !this.allSelected){ + delete this.selection[row.id]; + }else{ + selection[row.id] = value; + } + if(element){ + // add or remove classes as appropriate + if(value){ + put(element, ".dgrid-selected.ui-state-active"); + }else{ + put(element, "!dgrid-selected!ui-state-active"); + } + } + if(value != previousValue && element){ + // add to the queue of row events + this._selectionEventQueue(value, "rows").push(row); + } + + if(toRow){ + if(!toRow.element){ + toRow = this.row(toRow); + } + var toElement = toRow.element; + var fromElement = row.element; + // find if it is earlier or later in the DOM + var traverser = (toElement && (toElement.compareDocumentPosition ? + toElement.compareDocumentPosition(fromElement) == 2 : + toElement.sourceIndex > fromElement.sourceIndex)) ? "down" : "up"; + while(row.element != toElement && (row = this[traverser](row))){ + this.select(row); + } + } + } + }, + deselect: function(row, toRow){ + this.select(row, toRow, false); + }, + clearSelection: function(exceptId, dontResetLastSelected){ + // summary: + // Deselects any currently-selected items. + // exceptId: Mixed? + // If specified, the given id will not be deselected. + + this.allSelected = false; + for(var id in this.selection){ + if(exceptId !== id){ + this.deselect(id); + } + } + if(!dontResetLastSelected){ + this._lastSelected = null; + } + }, + selectAll: function(){ + this.allSelected = true; + this.selection = {}; // we do this to clear out pages from previous sorts + for(var i in this._rowIdToObject){ + var row = this.row(this._rowIdToObject[i]); + this.select(row.id); + } + }, + isSelected: function(object){ + if(!object){ + return false; + } + if(!object.element){ + object = this.row(object); + } + + return !!this.selection[object.id]; + }, + + refresh: function(){ + if(this.deselectOnRefresh){ + this.clearSelection(); + // Need to fire the selection event now because after the refresh, + // the nodes that we will fire for will be gone. + this._fireSelectionEvent && this._fireSelectionEvent(); + } + this._lastSelected = null; + this.inherited(arguments); + }, + + renderArray: function(){ + var grid = this, + rows = this.inherited(arguments); + + Deferred.when(rows, function(rows){ + var selection = grid.selection, + i, row, selected; + for(i = 0; i < rows.length; i++){ + row = grid.row(rows[i]); + selected = row.id in selection ? selection[row.id] : grid.allSelected; + if(selected){ + grid.select(row, null, selected); + } + } + }); + return rows; + } +}); + +}); diff --git a/widgets/dgrid/TouchScroll.js b/widgets/dgrid/TouchScroll.js new file mode 100644 index 0000000..9955f9a --- /dev/null +++ b/widgets/dgrid/TouchScroll.js @@ -0,0 +1,646 @@ +// FIXME: +// * fully make use of transitions in glide routines + +define(["dojo/_base/declare", "dojo/on", "./util/touch", "./util/has-css3", "put-selector/put", "xstyle/css!./css/TouchScroll.css"], +function(declare, on, touchUtil, has, put){ + var + calcTimerRes = 50, // ms between drag velocity measurements + glideTimerRes = 30, // ms between glide animation ticks + current = {}, // records info for widget(s) currently being scrolled + previous = {}, // records info for widget(s) that were in the middle of being scrolled when someone decided to scroll again + glideThreshold = 1, // speed (in px) below which to stop glide - TODO: remove + scrollbarAdjustment = 8, // number of px to adjust scrollbar dimension calculations + // RegExps for parsing relevant x/y from translate and matrix values: + translateRx = /^translate(?:3d)?\((-?\d+)(?:\.\d*)?(?:px)?, (-?\d+)/, + matrixRx = /^matrix\(1, 0, 0, 1, (-?\d+)(?:\.\d*)?(?:px)?, (-?\d+)/, + // store has-features we need, for computing property/function names: + hasTransitions = has("css-transitions"), + hasTransitionEnd = has("transitionend"), + hasTransforms = has("css-transforms"), + hasTransforms3d = has("css-transforms3d"), + // and declare vars to store info on the properties/functions we'll need + cssPrefix, transitionPrefix, transformProp, translatePrefix, translateSuffix; + + if(hasTransforms3d){ + translatePrefix = "translate3d("; + translateSuffix = ",0)"; + }else if(hasTransforms){ + translatePrefix = "translate("; + translateSuffix = ")"; + } + + if(!hasTransitions || !translatePrefix){ + console.warn("CSS3 features unavailable for touch scroll effects."); + return function(){}; + } + + // figure out strings for use later in events + transformProp = hasTransforms3d || hasTransforms; + transformProp = transformProp === true ? "transform" : transformProp + "Transform"; + transitionPrefix = hasTransitions === true ? "transition" : + hasTransitions + "Transition"; + cssPrefix = hasTransforms === true ? "" : + "-" + hasTransforms.toLowerCase() + "-"; + + function showScrollbars(widget, curr){ + // Handles displaying of X/Y scrollbars as appropriate when a touchstart + // occurs. + + var node = widget.touchNode, + parentNode = node.parentNode, + adjustedParentWidth = parentNode.offsetWidth - scrollbarAdjustment, + adjustedParentHeight = parentNode.offsetHeight - scrollbarAdjustment, + // Also populate scroll/offset properties on curr for reuse, + // to avoid having to repeatedly hit the DOM. + scrollWidth = curr.scrollWidth = node.scrollWidth, + scrollHeight = curr.scrollHeight = node.scrollHeight, + parentWidth = curr.parentWidth = parentNode.offsetWidth, + parentHeight = curr.parentHeight = parentNode.offsetHeight, + scrollbarNode; + + if(scrollWidth > parentWidth){ + if(!widget._scrollbarXNode){ + scrollbarNode = put(parentNode, "div.touchscroll-x"); + } + scrollbarNode = widget._scrollbarXNode = + widget._scrollbarXNode || put(scrollbarNode, "div.touchscroll-bar"); + scrollbarNode.style.width = + adjustedParentWidth * adjustedParentWidth / scrollWidth + "px"; + scrollbarNode.style.left = node.offsetLeft + "px"; + put(parentNode, ".touchscroll-scrollable-x"); + curr.scrollableX = true; + }else{ + put(parentNode, "!touchscroll-scrollable-x"); + } + if(scrollHeight > parentHeight){ + if(!widget._scrollbarYNode){ + scrollbarNode = put(parentNode, "div.touchscroll-y"); + } + scrollbarNode = widget._scrollbarYNode = + widget._scrollbarYNode || put(scrollbarNode, "div.touchscroll-bar"); + scrollbarNode.style.height = + adjustedParentHeight * adjustedParentHeight / scrollHeight + "px"; + scrollbarNode.style.top = node.offsetTop + "px"; + put(parentNode, ".touchscroll-scrollable-y"); + curr.scrollableY = true; + }else{ + put(parentNode, "!touchscroll-scrollable-y"); + } + put(parentNode, "!touchscroll-fadeout"); + } + + function scroll(widget, options){ + // Handles updating of scroll position (from touchmove or glide). + var node = widget.touchNode, + curr = current[widget.id], + pos, hasX, hasY, x, y; + + if(typeof options !== "object"){ + // Allow x, y to be passed directly w/o extra object creation. + // (e.g. from ontouchmove) + x = options; + y = arguments[2]; + options = arguments[3]; + hasX = hasY = true; + }else{ + hasX = "x" in options; + hasY = "y" in options; + + // If either x or y weren't specified, pass through the current value. + if(!hasX || !hasY){ + pos = widget.getScrollPosition(); + } + x = hasX ? options.x : pos.x; + y = hasY ? options.y : pos.y; + } + + // Update transform on touchNode + node.style[transformProp] = + translatePrefix + -x + "px," + -y + "px" + translateSuffix; + + // Update scrollbar positions + if(curr && hasX && widget._scrollbarXNode){ + widget._scrollbarXNode.style[transformProp] = translatePrefix + + (x * curr.parentWidth / curr.scrollWidth) + "px,0" + translateSuffix; + } + if(curr && hasY && widget._scrollbarYNode){ + widget._scrollbarYNode.style[transformProp] = translatePrefix + "0," + + (y * curr.parentHeight / curr.scrollHeight) + "px" + translateSuffix; + } + + // Emit a scroll event that can be captured by handlers, passing along + // scroll information in the event itself (since we already have the info, + // and it'd be difficult to get from the node). + on.emit(widget.touchNode.parentNode, "scroll", { + scrollLeft: x, + scrollTop: y + }); + } + + function getScrollStyle(widget){ + // Returns match object for current scroll position based on transform. + if(current[widget.id]){ + // Mid-transition: determine current X/Y from computed values. + return matrixRx.exec(window.getComputedStyle(widget.touchNode)[transformProp]); + } + // Otherwise, determine current X/Y from applied style. + return translateRx.exec(widget.touchNode.style[transformProp]); + } + + function resetEffects(options){ + // Function to cut glide/bounce short, called in context of an object + // from the current hash; attached only when a glide or bounce occurs. + // Called on touchstart, when touch scrolling is canceled, when + // momentum/bounce finishes, and by scrollTo on instances (in case it's + // called directly during a glide/bounce). + + var widget = this.widget, + nodes = [this.node, widget._scrollbarXNode, widget._scrollbarYNode], + i = nodes.length; + + // Clear glide timer. + if(this.timer){ + clearTimeout(this.timer); + this.timer = null; + } + + // Clear transition handlers, as we're about to cut it short. + if(this.transitionHandler){ + // Unhook any existing transitionend handler, since we'll be + // canceling the transition. + this.transitionHandler.remove(); + } + + // Clear transition duration on main node and scrollbars. + while(i--){ + if(nodes[i]){ nodes[i].style[transitionPrefix + "Duration"] = "0"; } + } + + // Fade out scrollbars unless indicated otherwise (e.g. re-touch). + if(!options || !options.preserveScrollbars){ + put(this.node.parentNode, ".touchscroll-fadeout"); + } + + // Remove this method so it can't be called again. + delete this.resetEffects; + } + + // functions for handling touch events on node to be scrolled + + function ontouchstart(evt){ + var widget = evt.widget, + node = widget.touchNode, + id = widget.id, + posX = 0, + posY = 0, + touch, match, curr; + + // Check touches count (which hasn't counted this event yet); + // ignore touch events on inappropriate number of contact points. + if(touchUtil.countCurrentTouches(evt, node) !== widget.touchesToScroll){ + return; + } + + match = getScrollStyle(widget); + if(match){ + posX = +match[1]; + posY = +match[2]; + } + if((curr = current[id])){ + // stop any active glide or bounce, since it's been re-touched + if(curr.resetEffects){ + curr.resetEffects({ preserveScrollbars: true }); + } + + node.style[transformProp] = + translatePrefix + posX + "px," + posY + "px" + translateSuffix; + + previous[id] = curr; + } + + touch = evt.targetTouches[0]; + curr = current[id] = { + widget: widget, + node: node, + // Subtract touch coords now, then add back later, so that translation + // goes further negative when moving upwards. + startX: posX - touch.pageX, + startY: posY - touch.pageY, + // Initialize lastX/Y, in case of a fast flick (< 1 full calc cycle). + lastX: posX, + lastY: posY, + // Also store original pageX/Y for threshold check. + pageX: touch.pageX, + pageY: touch.pageY, + tickFunc: function(){ calcTick(id); } + }; + curr.timer = setTimeout(curr.tickFunc, calcTimerRes); + } + function ontouchmove(evt){ + var widget = evt.widget, + id = widget.id, + touchesToScroll = widget.touchesToScroll, + curr = current[id], + activeTouches, targetTouches, touch, nx, ny, minX, minY, i; + + // Ignore touchmove events with inappropriate number of contact points. + if(!curr || (activeTouches = touchUtil.countCurrentTouches(evt, widget.touchNode)) !== touchesToScroll){ + // Also cancel touch scrolling if there are too many contact points. + if(activeTouches > touchesToScroll){ + widget.cancelTouchScroll(); + } + return; + } + + targetTouches = evt.targetTouches; + touch = targetTouches[0]; + + // Show touch scrollbars on first sign of drag. + if(!curr.scrollbarsShown){ + if(previous[id] || ( + Math.abs(touch.pageX - curr.pageX) > widget.scrollThreshold || + Math.abs(touch.pageY - curr.pageY) > widget.scrollThreshold)){ + showScrollbars(widget, curr); + curr.scrollbarsShown = true; + + // Add flag to involved touches to provide indication to other handlers. + for(i = targetTouches.length; i--;){ + targetTouches[i].touchScrolled = true; + } + } + } + + // Squelch the event, and scroll the area if beyond the threshold. + evt.preventDefault(); + + if(curr.scrollbarsShown && (curr.scrollableX || curr.scrollableY)){ + nx = curr.scrollableX ? curr.startX + touch.pageX : 0; + ny = curr.scrollableY ? curr.startY + touch.pageY : 0; + + minX = curr.scrollableX ? -(curr.scrollWidth - curr.parentWidth) : 0; + minY = curr.scrollableY ? -(curr.scrollHeight - curr.parentHeight) : 0; + + // If dragged beyond edge, halve the distance between. + if(nx > 0){ + nx = nx / 2; + }else if(nx < minX){ + nx = minX - (minX - nx) / 2; + } + if(ny > 0){ + ny = ny / 2; + }else if(ny < minY){ + ny = minY - (minY - ny) / 2; + } + + scroll(widget, -nx, -ny); // call scroll with positive coordinates + } + } + function ontouchend(evt){ + var widget = evt.widget, + id = widget.id, + curr = current[id]; + + if(!curr || touchUtil.countCurrentTouches(evt, widget.touchNode) != widget.touchesToScroll - 1){ + return; + } + startGlide(id); + } + + // glide-related functions + + function calcTick(id){ + // Calculates current speed of touch drag + var curr = current[id], + node, match, x, y; + + if(!curr){ return; } // no currently-scrolling widget; abort + + node = curr.node; + match = translateRx.exec(node.style[transformProp]); + + if(match){ + x = +match[1]; + y = +match[2]; + + // If previous reference point already exists, calculate velocity + curr.velX = x - curr.lastX; + curr.velY = y - curr.lastY; + + // set previous reference point for future iteration or calculation + curr.lastX = x; + curr.lastY = y; + } else { + curr.lastX = curr.lastY = 0; + } + curr.timer = setTimeout(curr.tickFunc, calcTimerRes); + } + + function bounce(id, lastX, lastY){ + // Function called when a scroll ends, to handle rubber-banding beyond edges. + var curr = current[id], + widget = curr.widget, + node = curr.node, + scrollbarNode, + x = curr.scrollableX ? + Math.max(Math.min(0, lastX), -(curr.scrollWidth - curr.parentWidth)) : + lastX, + y = curr.scrollableY ? + Math.max(Math.min(0, lastY), -(curr.scrollHeight - curr.parentHeight)) : + lastY; + + function end(){ + // Performs reset operations upon end of scroll process. + + // Since transitions have run, delete transitionHandler up-front + // (since it auto-removed itself anyway), then let + // resetEffects do the rest of its usual job. + delete curr.transitionHandler; + curr.resetEffects(); + delete current[id]; + } + + // Timeout will have been cleared before bounce call, so remove timer. + delete curr.timer; + + if (x != lastX || y != lastY){ + curr.transitionHandler = on.once(node, hasTransitionEnd, end); + node.style[transitionPrefix + "Duration"] = widget.bounceDuration + "ms"; + node.style[transformProp] = + translatePrefix + x + "px," + y + "px" + translateSuffix; + + // Also handle transitions for scrollbars. + if(x != lastX && curr.scrollableX){ + scrollbarNode = curr.widget._scrollbarXNode; + scrollbarNode.style[transitionPrefix + "Duration"] = + widget.bounceDuration + "ms"; + if(lastX > x){ + // Further left; bounce back right + scrollbarNode.style[transformProp] = + translatePrefix + "0,0" + translateSuffix; + }else{ + // Further right; bounce back left + scrollbarNode.style[transformProp] = + translatePrefix + + (scrollbarNode.parentNode.offsetWidth - scrollbarNode.offsetWidth) + + "px,0" + translateSuffix; + } + } + if(y != lastY && curr.scrollableY){ + scrollbarNode = curr.widget._scrollbarYNode; + scrollbarNode.style[transitionPrefix + "Duration"] = + widget.bounceDuration + "ms"; + if(lastY > y){ + // Above top; bounce back down + scrollbarNode.style[transformProp] = + translatePrefix + "0,0" + translateSuffix; + }else{ + // Below bottom; bounce back up + scrollbarNode.style[transformProp] = + translatePrefix + "0," + + (scrollbarNode.parentNode.offsetHeight - scrollbarNode.offsetHeight) + + "px" + translateSuffix; + } + } + }else{ + end(); // no rubber-banding necessary; just reset + } + } + + function startGlide(id){ + // starts glide operation when drag ends + var curr = current[id], + prev = previous[id], + match, posX, posY, + INERTIA_ACCELERATION = 1.15; + + delete previous[id]; + + if(curr.timer){ clearTimeout(curr.timer); } + + // Enable usage of resetEffects during glide or bounce. + curr.resetEffects = resetEffects; + + // calculate velocity based on time and displacement since last tick + match = translateRx.exec(curr.node.style[transformProp]); + if(match){ + posX = +match[1]; + posY = +match[2]; + } else { + posX = posY = 0; + } + + // If there is no glide to perform (no exit velocity), or if we are + // beyond boundaries on all applicable edges, immediately bounce back. + if((!curr.velX && !curr.velY) || + ((posX >= 0 || posX <= -(curr.scrollWidth - curr.parentWidth)) && + (posY >= 0 || posY <= -(curr.scrollHeight - curr.parentHeight)))){ + bounce(id, posX, posY); + return; + } + + function sameSign(a, b){ + return ((a.velX <= 0 && b.velX <= 0) || (a.velX >= 0 && b.velX >= 0)) && + ((a.velY <= 0 && b.velY <= 0) || (a.velY >= 0 && b.velY >= 0)); + } + + if(prev && (prev.velX || prev.velY) && sameSign(curr, prev)){ + curr.velX = (curr.velX + prev.velX) * INERTIA_ACCELERATION; + curr.velY = (curr.velY + prev.velY) * INERTIA_ACCELERATION; + } + + // update lastX/Y with current position, for glide calculations + curr.lastX = posX; + curr.lastY = posY; + curr.calcFunc = function(){ calcGlide(id); }; + curr.timer = setTimeout(curr.calcFunc, glideTimerRes); + } + function calcGlide(id){ + // performs glide and decelerates according to widget's glideDecel method + var curr = current[id], + node, parentNode, widget, i, + nx, ny, nvx, nvy, // old/new coords and new velocities + BOUNCE_DECELERATION_AMOUNT = 6; + + if(!curr){ return; } + + node = curr.node; + parentNode = node.parentNode; + widget = curr.widget; + nvx = widget.glideDecel(curr.velX); + nvy = widget.glideDecel(curr.velY); + + if(Math.abs(nvx) >= glideThreshold || Math.abs(nvy) >= glideThreshold){ + // still above stop threshold; update transformation + nx = curr.lastX + nvx; + ny = curr.lastY + nvy; + + // If glide has traveled beyond any edges, institute rubber-band effect + // by further decelerating. + if(nx > 0 || nx < -(curr.scrollWidth - curr.parentWidth)){ + for(i = BOUNCE_DECELERATION_AMOUNT; i--;){ + nvx = widget.glideDecel(nvx); + } + } + if(ny > 0 || ny < -(curr.scrollHeight - curr.parentHeight)){ + for(i = BOUNCE_DECELERATION_AMOUNT; i--;){ + nvy = widget.glideDecel(nvy); + } + } + + // still scrollable; update offsets/velocities and schedule next tick + scroll(widget, -nx, -ny); // call scroll with positive coordinates + // update information + curr.lastX = nx; + curr.lastY = ny; + curr.velX = nvx; + curr.velY = nvy; + curr.timer = setTimeout(curr.calcFunc, glideTimerRes); + }else{ + bounce(id, curr.lastX, curr.lastY); + } + } + + return declare(null, { + // touchesToScroll: Number + // Number of touches to require on the component's touch target node + // in order to trigger scrolling behavior. + touchesToScroll: 1, + + // touchNode: DOMNode? + // Node upon which scroll behavior will be based; transformations will be + // applied to this node, and events and some DOM/styles will be applied + // to its *parent*. If not specified, defaults to containerNode. + touchNode: null, + + // scrollThreshold: Number + // Minimum number of pixels to wait for user to scroll (in any direction) + // before initiating scroll. + scrollThreshold: 10, + + // bounceDuration: Number + // Number of milliseconds which "rubber-banding" transitions + // (i.e. bouncing back from beyond edges) should take. + bounceDuration: 300, + + postCreate: function(){ + this._initTouch(); + this.inherited(arguments); + }, + + _initTouch: function(){ + var node = this.touchNode = this.touchNode || this.containerNode, + widget = this, + parentNode; + + if(!node || !node.parentNode){ + // Bail out if we have no touchNode or containerNode, or if we don't + // seem to have a parent node to work with. + console.warn("TouchScroll requires a nested node upon which to operate."); + return; + } + + parentNode = node.parentNode; + + // Set overflow to hidden in order to prevent any native scroll logic. + parentNode.style.overflow = "hidden"; + + node.style[transitionPrefix + "Property"] = cssPrefix + "transform"; + node.style[transitionPrefix + "TimingFunction"] = + "cubic-bezier(0.33, 0.66, 0.66, 1)"; + + function cancelTouchScroll(){ + widget.cancelTouchScroll(); + } + + function wrapHandler(func){ + return function(evt){ + evt.widget = widget; + evt.cancelTouchScroll = cancelTouchScroll; + func.call(this, evt); + }; + } + + this._touchScrollListeners = [ + on(parentNode, "touchstart", wrapHandler(ontouchstart)), + on(parentNode, "touchmove", wrapHandler(ontouchmove)), + on(parentNode, "touchend,touchcancel", wrapHandler(ontouchend)) + ]; + }, + + destroy: function(){ + var i = this._touchScrollListeners.length; + while(i--){ + this._touchScrollListeners[i].remove(); + } + delete current[this.id]; + + this.inherited(arguments); + }, + + scrollTo: function(options){ + // summary: + // Scrolls the widget to a specific position. + // options: Object + // Object containing target x and/or y position to scroll to + // (if unspecified, scroll in that direction will be preserved). + // Also supports the following other options: + // * preserveMomentum: if true, will not reset any active + // momentum or bounce on the widget + + var curr = current[this.id], + touchNode = this.touchNode, + parentNode = touchNode.parentNode; + + if(!options.preserveMomentum && curr && curr.resetEffects){ + // Stop any glide or bounce occurring before scrolling. + curr.resetEffects(); + } + + // Constrain coordinates within scrollable boundaries. + if(options.x){ + options.x = Math.max(0, Math.min(options.x, + touchNode.scrollWidth - parentNode.offsetWidth)); + } + if(options.y){ + options.y = Math.max(0, Math.min(options.y, + touchNode.scrollHeight - parentNode.offsetHeight)); + } + + scroll(this, options); + }, + + getScrollPosition: function(){ + // summary: + // Determines current translation from computed style + // (if mid-transition), or applied style. + var match = getScrollStyle(this); + return match ? { x: -match[1], y: -match[2] } : { x: 0, y: 0 }; + }, + + cancelTouchScroll: function(){ + // summary: + // Removes any existing scroll information for this component from the + // current map, effectively canceling any TouchScroll behavior for + // that particular touch gesture. + + var curr = current[this.id]; + if(!curr){ return; } + + if(curr.resetEffects){ curr.resetEffects(); } + else{ + if(curr.timer){ clearTimeout(curr.timer); } + put(curr.node.parentNode, ".touchscroll-fadeout"); + } + + delete current[this.id]; + }, + + glideDecel: function(n){ + // summary: + // Deceleration algorithm. Given a number representing velocity, + // returns a new velocity to impose for the next "tick". + // (Don't forget that velocity can be positive or negative!) + return n * 0.9; // Number + } + }); +}); diff --git a/widgets/dgrid/_StoreMixin.js b/widgets/dgrid/_StoreMixin.js new file mode 100644 index 0000000..b5e9790 --- /dev/null +++ b/widgets/dgrid/_StoreMixin.js @@ -0,0 +1,286 @@ +define(["dojo/_base/kernel", "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/Deferred", "dojo/on", "put-selector/put"], +function(kernel, declare, lang, Deferred, listen, put){ + // This module isolates the base logic required by store-aware list/grid + // components, e.g. OnDemandList/Grid and the Pagination extension. + + function emitError(err){ + // called by _trackError in context of list/grid, if an error is encountered + if(typeof err !== "object"){ + // Ensure we actually have an error object, so we can attach a reference. + err = new Error(err); + } + err.grid = this; + + if(listen.emit(this.domNode, "dgrid-error", {error: err, cancelable: true, bubbles: true})){ + console.error(err); + } + } + + return declare(null, { + // store: Object + // The object store (implementing the dojo/store API) from which data is + // to be fetched. + store: null, + + // query: Object + // Specifies query parameter(s) to pass to store.query calls. + query: null, + + // queryOptions: Object + // Specifies additional query options to mix in when calling store.query; + // sort, start, and count are already handled. + queryOptions: null, + + // getBeforePut: boolean + // If true, a get request will be performed to the store before each put + // as a baseline when saving; otherwise, existing row data will be used. + getBeforePut: true, + + // noDataMessage: String + // Message to be displayed when no results exist for a query, whether at + // the time of the initial query or upon subsequent observed changes. + // Defined by _StoreMixin, but to be implemented by subclasses. + noDataMessage: "", + + // loadingMessage: String + // Message displayed when data is loading. + // Defined by _StoreMixin, but to be implemented by subclasses. + loadingMessage: "", + + constructor: function(){ + // Create empty objects on each instance, not the prototype + this.query = {}; + this.queryOptions = {}; + this.dirty = {}; + this._updating = {}; // tracks rows that are mid-update + }, + + _configColumn: function(column){ + // summary: + // Implements extension point provided by Grid to store references to + // any columns with `set` methods, for use during `save`. + if (column.set){ + if(!this._columnsWithSet){ this._columnsWithSet = {}; } + this._columnsWithSet[column.field] = column; + } + }, + + _configColumns: function(){ + // summary: + // Extends Grid to reset _StoreMixin's hash when columns are updated + this._columnsWithSet = null; + return this.inherited(arguments); + }, + + _setStore: function(store, query, queryOptions){ + // summary: + // Assigns a new store (and optionally query/queryOptions) to the list, + // and tells it to refresh. + this.store = store; + this.dirty = {}; // discard dirty map, as it applied to a previous store + this.set("query", query, queryOptions); + }, + _setQuery: function(query, queryOptions){ + // summary: + // Assigns a new query (and optionally queryOptions) to the list, + // and tells it to refresh. + + var sort = queryOptions && queryOptions.sort; + + this.query = query !== undefined ? query : this.query; + this.queryOptions = queryOptions || this.queryOptions; + + // If we have new sort criteria, pass them through sort + // (which will update _sort and call refresh in itself). + // Otherwise, just refresh. + sort ? this.set("sort", sort) : this.refresh(); + }, + setStore: function(store, query, queryOptions){ + kernel.deprecated("setStore(...)", 'use set("store", ...) instead', "dgrid 1.0"); + this.set("store", store, query, queryOptions); + }, + setQuery: function(query, queryOptions){ + kernel.deprecated("setQuery(...)", 'use set("query", ...) instead', "dgrid 1.0"); + this.set("query", query, queryOptions); + }, + + _getQueryOptions: function(){ + // summary: + // Get a fresh queryOptions object, also including the current sort + var options = lang.delegate(this.queryOptions, {}); + if(this._sort.length){ + // Prevents SimpleQueryEngine from doing unnecessary "null" sorts (which can + // change the ordering in browsers that don't use a stable sort algorithm, eg Chrome) + options.sort = this._sort; + } + return options; + }, + _getQuery: function(){ + // summary: + // Implemented consistent with _getQueryOptions so that if query is + // an object, this returns a protected (delegated) object instead of + // the original. + var q = this.query; + return typeof q == "object" && q != null ? lang.delegate(q, {}) : q; + }, + + _setSort: function(property, descending){ + // summary: + // Sort the content + + // prevent default storeless sort logic as long as we have a store + if(this.store){ this._lastCollection = null; } + this.inherited(arguments); + }, + + insertRow: function(object, parent, beforeNode, i, options){ + var store = this.store, + dirty = this.dirty, + id = store && store.getIdentity(object), + dirtyObj; + + if(id in dirty && !(id in this._updating)){ dirtyObj = dirty[id]; } + if(dirtyObj){ + // restore dirty object as delegate on top of original object, + // to provide protection for subsequent changes as well + object = lang.delegate(object, dirtyObj); + } + return this.inherited(arguments); + }, + + updateDirty: function(id, field, value){ + // summary: + // Updates dirty data of a field for the item with the specified ID. + var dirty = this.dirty, + dirtyObj = dirty[id]; + + if(!dirtyObj){ + dirtyObj = dirty[id] = {}; + } + dirtyObj[field] = value; + }, + setDirty: function(id, field, value){ + kernel.deprecated("setDirty(...)", "use updateDirty() instead", "dgrid 1.0"); + this.updateDirty(id, field, value); + }, + + save: function() { + // Keep track of the store and puts + var self = this, + store = this.store, + dirty = this.dirty, + dfd = new Deferred(), promise = dfd.promise, + getFunc = function(id){ + // returns a function to pass as a step in the promise chain, + // with the id variable closured + var data; + return (self.getBeforePut || !(data = self.row(id).data)) ? + function(){ return store.get(id); } : + function(){ return data; }; + }; + + // function called within loop to generate a function for putting an item + function putter(id, dirtyObj) { + // Return a function handler + return function(object) { + var colsWithSet = self._columnsWithSet, + updating = self._updating, + key, data; + // Copy dirty props to the original, applying setters if applicable + for(key in dirtyObj){ + object[key] = dirtyObj[key]; + } + if(colsWithSet){ + // Apply any set methods in column definitions. + // Note that while in the most common cases column.set is intended + // to return transformed data for the key in question, it is also + // possible to directly modify the object to be saved. + for(key in colsWithSet){ + data = colsWithSet[key].set(object); + if(data !== undefined){ object[key] = data; } + } + } + + updating[id] = true; + // Put it in the store, returning the result/promise + return Deferred.when(store.put(object), function() { + // Clear the item now that it's been confirmed updated + delete dirty[id]; + delete updating[id]; + }); + }; + } + + // For every dirty item, grab the ID + for(var id in dirty) { + // Create put function to handle the saving of the the item + var put = putter(id, dirty[id]); + + // Add this item onto the promise chain, + // getting the item from the store first if desired. + promise = promise.then(getFunc(id)).then(put); + } + + // Kick off and return the promise representing all applicable get/put ops. + // If the success callback is fired, all operations succeeded; otherwise, + // save will stop at the first error it encounters. + dfd.resolve(); + return promise; + }, + + revert: function(){ + // summary: + // Reverts any changes since the previous save. + this.dirty = {}; + this.refresh(); + }, + + _trackError: function(func){ + // summary: + // Utility function to handle emitting of error events. + // func: Function|String + // A function which performs some store operation, or a String identifying + // a function to be invoked (sans arguments) hitched against the instance. + // If sync, it can return a value, but may throw an error on failure. + // If async, it should return a promise, which would fire the error + // callback on failure. + // tags: + // protected + + var result; + + if(typeof func == "string"){ func = lang.hitch(this, func); } + + try{ + result = func(); + }catch(err){ + // report sync error + emitError.call(this, err); + } + + // wrap in when call to handle reporting of potential async error + return Deferred.when(result, null, lang.hitch(this, emitError)); + }, + + newRow: function(){ + // Override to remove no data message when a new row appears. + if(this.noDataNode){ + put(this.noDataNode, "!"); + delete this.noDataNode; + } + return this.inherited(arguments); + }, + removeRow: function(rowElement, justCleanup){ + var row = {element: rowElement}; + // Check to see if we are now empty... + if(!justCleanup && this.noDataMessage && + (this.up(row).element === rowElement) && + (this.down(row).element === rowElement)){ + // ...we are empty, so show the no data message. + this.noDataNode = put(this.contentNode, "div.dgrid-no-data"); + this.noDataNode.innerHTML = this.noDataMessage; + } + return this.inherited(arguments); + } + }); +}); diff --git a/widgets/dgrid/css/TouchScroll.css b/widgets/dgrid/css/TouchScroll.css new file mode 100644 index 0000000..37ac1bf --- /dev/null +++ b/widgets/dgrid/css/TouchScroll.css @@ -0,0 +1,55 @@ +/* styles for scrollbars during touch-scroll */ + +.touchscroll-x, .touchscroll-y { + display: none; /* overridden below */ + overflow: hidden; /* cut off ends of scrollbar during rubber-banding */ + position: absolute; + /* establish base style for scrollbar fade-in/out */ + opacity: 0.7; +} +.touchscroll-fadeout .touchscroll-x, .touchscroll-fadeout .touchscroll-y { + opacity: 0; + -webkit-transition: opacity 0.3s ease-out 0.1s; + -moz-transition: opacity 0.3s ease-out 0.1s; + -o-transition: opacity 0.3s ease-out 0.1s; + transition: opacity 0.3s ease-out 0.1s; +} + +.touchscroll-bar { + background-color: rgba(88,88,88,0.97); + border: 1px solid rgba(88,88,88,1); + border-radius: 3px; + + /* the borders aren't anti-aliased on Android, so this smooths it out a bit */ + -webkit-box-shadow: 0 0 1px rgba(88,88,88,0.4); +} + +.touchscroll-x { + left: 1px; + right: 3px; + bottom: 1px; + height: 5px; +} +.touchscroll-y { + top: 1px; + bottom: 3px; + right: 1px; + width: 5px; +} + +.touchscroll-scrollable-x .touchscroll-x, .touchscroll-scrollable-y .touchscroll-y { + display: block; /* display scrollbar when appropriate */ +} + +.touchscroll-bar { + /* Establish transition property and timing function for scrollbars */ + -webkit-transition: transform cubic-bezier(0.33, 0.66, 0.66, 1); + -moz-transition: transform cubic-bezier(0.33, 0.66, 0.66, 1); + -o-transition: transform cubic-bezier(0.33, 0.66, 0.66, 1); + transition: transform cubic-bezier(0.33, 0.66, 0.66, 1); +} + +/* indicator of a successful load */ +#dgrid-css-TouchScroll-loaded { + display: none; +} \ No newline at end of file diff --git a/widgets/dgrid/css/columnset.css b/widgets/dgrid/css/columnset.css new file mode 100644 index 0000000..b564050 --- /dev/null +++ b/widgets/dgrid/css/columnset.css @@ -0,0 +1,40 @@ +.dgrid-column-set { + overflow: hidden; + width: 100%; + position: relative; /* This is needed because we setting position: relative on cells in the grid for focus in IE7*/ +} + +.dgrid-column-set-cell { + vertical-align: top; +} +.dgrid-column-set-scroller { + position: absolute; + bottom: 0px; + overflow-x: auto; + overflow-y: hidden; +} +.dgrid-column-set-scroller-content { + position: relative; + height: 1px; +} + +/* UA-specific hacks*/ +html.has-mozilla .dgrid-column-set *:focus, html.has-safari .dgrid-column-set *:focus { + /* firefox and opera's outline gets cropped by the overflow: hidden, so we add a border*/ + border: 1px dotted black; + outline: 1px dotted black; +} +html.has-ie-7 .dgrid-column-set { + /* in IE7 (strangely not IE6 or IE8+) this is needed instead of 100% to make it not create a horizontal scroll bar*/ + width: auto; +} + +html.has-quirks .dgrid-column-set { + /* at it works in quirks even in IE7*/ + width: 100%; +} + +/* indicator of a successful load */ +#dgrid-css-columnset-loaded { + display: none; +} \ No newline at end of file diff --git a/widgets/dgrid/css/dgrid.css b/widgets/dgrid/css/dgrid.css new file mode 100644 index 0000000..3fb90d7 --- /dev/null +++ b/widgets/dgrid/css/dgrid.css @@ -0,0 +1,202 @@ +/* This stylesheet provides the structural CSS for the dgrid */ +.dgrid { + position: relative; + overflow: hidden; /* This is needed by IE to prevent crazy scrollbar flashing */ + border: 1px solid #ddd; + height: 30em; + display: block; +} + +.dgrid-header { + background-color: #eee; + position: absolute; + right: 17px; /* scrollbar width; revised in List.js if necessary */ + left: 0; +} + +.dgrid-header-scroll { + position: absolute; + top: 0; + right: 0; +} + +.dgrid-footer { + position: absolute; + bottom: 0; + width: 100%; +} + +.dgrid-header-hidden, +html.has-quirks .dgrid-header-hidden .dgrid-cell { + /* + Used to "hide" header, without losing size information for reference. + !important is used to supersede theme styles at higher specificity. + Left/right box styles are untouched, as they may influence width of + .dgrid-content as updated in Grid's resize method. + Note: Still not quite perfect in IE Quirks mode (1px left over). + */ + font-size: 0; /* allow shrinkage in IE Quirks mode for Lists */ + height: 0 !important; + border-top: none !important; + border-bottom: none !important; + margin-top: 0 !important; + margin-bottom: 0 !important; + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +.dgrid-footer-hidden { + /* Hiding footer is much simpler; simply set its display to none. */ + display: none; +} + +.dgrid-sortable { + cursor: pointer; +} +.dgrid-header, .dgrid-header-row, .dgrid-footer { + overflow: hidden; + background-color: #eee; +} + +.dgrid-row-table { + border-collapse: collapse; + border: none; + table-layout: fixed; + empty-cells: show; + width: 100%; /* this becomes 'auto' for IE7 (non-quirks) */ + height: 100%; +} +.dgrid-cell { + padding: 0px; + text-align: left; + overflow: hidden; + vertical-align: top; + border: 1px solid #ddd; + border-top-style: none; + + box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + -webkit-box-sizing: border-box; +} +.dgrid-cell-padding { + padding: 3px; +} + +.dgrid-content { + position: relative; + height: 99%; +} + +.dgrid-scroller { + overflow-x: auto; + overflow-y: scroll; + position: absolute; + top: 0px; + margin-top: 25px; /* this will be adjusted programmatically to fit below the header*/ + bottom: 0px; + width: 100%; +} + +.dgrid-loading { + position: relative; + height: 100%; +} +.dgrid-above { + position: absolute; + bottom: 0; +} + +.ui-icon { + width: 16px; + height: 16px; + background-image: url('images/ui-icons_222222_256x240.png'); +} + +.ui-icon-triangle-1-e { + background-position: -32px -16px; +} +.ui-icon-triangle-1-se { + background-position: -48px -16px; +} + +.dgrid-expando-icon { + width: 16px; + height: 16px; +} +.dgrid-tree-container { + -webkit-transition-duration: 0.3s; + -moz-transition-duration: 0.3s; + -ms-transition-duration: 0.3s; + -o-transition-duration: 0.3s; + transition-duration: 0.3s; + overflow: hidden; +} +.dgrid-tree-container.dgrid-tree-resetting { + -webkit-transition-duration: 0; + -moz-transition-duration: 0; + -ms-transition-duration: 0; + -o-transition-duration: 0; + transition-duration: 0; +} + +/* Single Sort */ +.dgrid-sort-arrow { + background-position: -64px -16px; + display: block; + float: right; + margin: 0 4px 0 5px; + height: 12px; +} +.dgrid-sort-up .dgrid-sort-arrow { + background-position: 0px -16px; +} + +/* selection*/ +.dgrid-selected { + background-color: #bfd6eb; +} + +.dgrid-input { + width: 99%; +} + +/* UA-specific hacks*/ +html.has-mozilla .dgrid *:focus, html.has-opera .dgrid *:focus { + /* firefox's focus doesn't work by default for divs prior to actually tabbing into it. This fixes that + (we don't do any other browsers because we are trying to stay as close to native as possible) */ + outline: 1px dotted; +} +html.has-ie-6-7.has-no-quirks .dgrid-row-table { + /* in IE7 this is needed instead of 100% to make it not create a horizontal scroll bar*/ + width: auto; +} +html.has-quirks .dgrid-row-table, html.has-ie-6 .dgrid-row-table { + /* similar story, height looks too high */ + height: auto; +} + +html.has-quirks .dgrid-header-scroll, +html.has-ie-6 .dgrid-header-scroll { + /* ensure that header scroll node can shrink to 0 height in IE6 & quirks */ + font-size: 0; +} + +html.has-mozilla .dgrid-focus { + /* fix row focus outline (prevents horizontal scroll in FF 3.6) */ + outline-offset: -1px; +} + +/* will be used to calculate the width of the scrollbar */ +.dgrid-scrollbar-measure { + width: 100px; + height: 100px; + overflow: scroll; + position: absolute; + top: -9999px; +} + +/* indicator of a successful load */ +#dgrid-css-dgrid-loaded { + display: none; +} \ No newline at end of file diff --git a/widgets/dgrid/css/dgrid_rtl.css b/widgets/dgrid/css/dgrid_rtl.css new file mode 100644 index 0000000..75ba408 --- /dev/null +++ b/widgets/dgrid/css/dgrid_rtl.css @@ -0,0 +1,35 @@ +/* rtl support + + IMPORTANT: + WebKit places the scrollbars on the RIGHT, even when in RTL mode :( + .dgrid-rtl is added to the domNode if the grid's isRTL is true + .dgrid-rtl-nonwebkit is also added to the domNode if + the grid's isRTL is true AND the client isn't WebKit +*/ +.dgrid-rtl-nonwebkit .dgrid-header { + right: 0; + left: 17px; +} + +.dgrid-rtl-nonwebkit .dgrid-header-scroll { + left: 0px; + right: auto; +} + +.dgrid-rtl .dgrid-cell { + text-align: right; +} + +/* Reverse position of sort arrow */ +.dgrid-rtl .dgrid-sort-arrow { + float: left; + margin: 0 5px 0 4px; +} + +/* Reverse arrows for tree expando states */ +.dgrid-rtl .ui-icon-triangle-1-e { + background-position: -96px -16px; +} +.dgrid-rtl .ui-icon-triangle-1-se { + background-position: -80px -16px; +} \ No newline at end of file diff --git a/widgets/dgrid/css/extensions/ColumnHider.css b/widgets/dgrid/css/extensions/ColumnHider.css new file mode 100644 index 0000000..70fdcf9 --- /dev/null +++ b/widgets/dgrid/css/extensions/ColumnHider.css @@ -0,0 +1,50 @@ +.dgrid-hider-toggle { + font-weight:normal; + cursor: pointer; +} +.dgrid-hider-toggle:hover { + font-weight: bold; +} +html.has-quirks .dgrid-header-scroll .dgrid-hider-toggle, +html.has-ie-6 .dgrid-header-scroll .dgrid-hider-toggle { + font-size: 12px; /* override zeroed font-size for IE6/Quirks in dgrid.css */ +} + +.dgrid-hider-menu { + position: absolute; + top: 0; + right: 17px; + width: 184px; + background-color: #fff; + border: 1px solid black; + z-index: 99999; + padding: 4px; + overflow-x: hidden; + overflow-y: auto; +} + +.dgrid-hider-menu-row { + position: relative; + padding: 2px; +} +.dgrid-hider-menu-check { + position: absolute; + top: 2px; + left: 2px; + padding: 0; +} +.dgrid-hider-menu-label { + display: block; + padding-left: 20px; +} + +html.has-quirks .dgrid-hider-menu-check, +html.has-ie-6-7 .dgrid-hider-menu-check { + /* IE < 8 seems to ignore padding: 0, so counteract */ + top: 0; + left: 0; +} + +#dgrid-css-extensions-ColumnHider-loaded { + display: none; +} diff --git a/widgets/dgrid/css/extensions/ColumnReorder.css b/widgets/dgrid/css/extensions/ColumnReorder.css new file mode 100644 index 0000000..aff1cd8 --- /dev/null +++ b/widgets/dgrid/css/extensions/ColumnReorder.css @@ -0,0 +1,14 @@ +.dgrid-header .dgrid-cell { + display: table-cell; /* override any spurious dojoDndItem settings elsewhere */ +} + +.dgrid-header .dojoDndItemBefore { + border-left: 2px dotted #000 !important; +} +.dgrid-header .dojoDndItemAfter { + border-right: 2px dotted #000 !important; +} + +#dgrid-css-extensions-ColumnReorder-loaded { /* load indicator for xstyle */ + display: none; +} diff --git a/widgets/dgrid/css/extensions/ColumnResizer.css b/widgets/dgrid/css/extensions/ColumnResizer.css new file mode 100644 index 0000000..9ae5cf9 --- /dev/null +++ b/widgets/dgrid/css/extensions/ColumnResizer.css @@ -0,0 +1,61 @@ +.dgrid-column-resizer { + position: absolute; + height: 100%; + width: 2px; + background-color: #666; + z-index: 1000; + top: 0; +} + +.dgrid-resize-handle { + height: 100px; + width: 0; + position: absolute; + right: -1px; + top:-4px; + cursor: col-resize; + z-index: 999; + border-left: 5px solid transparent; + outline: none; +} +html.has-ie-6 .dgrid-resize-handle { + border-color: pink; + filter: chroma(color=pink); +} +.dgrid-resize-header-container { + height:100%; +} + +/* make the resize handles larger on touch-capable devices */ +html.has-touch .dgrid-resize-handle { + border-left: 20px solid transparent; +} +html.has-touch .dgrid-column-resizer { + width: 2px; +} + +html.has-no-quirks .dgrid-resize-header-container { + /* set to relative so we can do the resizing against this node... + except when in quirks mode, where we have to use the th */ + position: relative; +} +html.has-ie-6 .dgrid-resize-header-container { + position: static; /* and in IE6 we have to do the th hack */ +} +.dgrid-header .dgrid-cell-padding { + overflow: hidden; + padding-right:0px; +} +html.has-ie-6 .dgrid-header .dgrid-cell-padding { + margin-right: 4px; /* for IE6, keep the resizer visible */ +} +html.has-ie-6 .dgrid-header .dgrid-sort-arrow { + margin-right: 0; /* for IE6, zero out the right margin due to the right margin for the container */ +} +html.has-quirks .dgrid-header .dgrid-cell-padding, html.has-ie-6 .dgrid-header .dgrid-cell { + position:relative; +} + +#dgrid-css-extensions-ColumnResizer-loaded { + display: none; +} diff --git a/widgets/dgrid/css/extensions/CompoundColumns.css b/widgets/dgrid/css/extensions/CompoundColumns.css new file mode 100644 index 0000000..402f0e7 --- /dev/null +++ b/widgets/dgrid/css/extensions/CompoundColumns.css @@ -0,0 +1,8 @@ +.dgrid-spacer-row th { /* Need to make these cells zero height/invisible, but still force the table layout */ + padding-top: 0; + padding-bottom: 0; + border-top: none; + border-bottom: none; +} + +#dgrid-css-extensions-CompoundColumns-loaded { display: none; } \ No newline at end of file diff --git a/widgets/dgrid/css/extensions/Pagination.css b/widgets/dgrid/css/extensions/Pagination.css new file mode 100644 index 0000000..776b274 --- /dev/null +++ b/widgets/dgrid/css/extensions/Pagination.css @@ -0,0 +1,44 @@ +.dgrid-status { + padding: 2px; +} + +.dgrid-pagination .dgrid-status { + float: left; +} + +.dgrid-pagination .dgrid-navigation, .dgrid-pagination .dgrid-page-size { + float: right; +} +.dgrid-navigation a { + font-weight: bold; + text-decoration: none; + color: inherit; + padding: 0 4px; +} +.has-ie-6-7 .dgrid-navigation a, +.has-ie.has-quirks .dgrid-navigation a { + /* IE < 8 (and IE quirksmode) doesn't support inherit in most cases. + Unfortunately this makes more work for some skins as well... + */ + color: #000; +} +.dgrid-first, .dgrid-last, .dgrid-next, .dgrid-previous { + font-size: 130%; +} +.dgrid-pagination .dgrid-page-disabled, +.has-ie-6-7 .dgrid-navigation .dgrid-page-disabled, +.has-ie.has-quirks .dgrid-navigation .dgrid-page-disabled { + color: #aaa; + cursor: default; +} +.dgrid-page-input { + margin-top: 1px; + width: 2em; + text-align: center; +} +.dgrid-page-size { + margin: 1px 4px 0 4px; +} + +/* indicator of a successful load */ +#dgrid-css-extensions-Pagination-loaded { display: none; } \ No newline at end of file diff --git a/widgets/dgrid/css/has-transforms3d.css b/widgets/dgrid/css/has-transforms3d.css new file mode 100644 index 0000000..5fc7194 --- /dev/null +++ b/widgets/dgrid/css/has-transforms3d.css @@ -0,0 +1,16 @@ +/* these media queries assist in feature detection of 3D transforms */ + +.has-csstransforms3d { + position: absolute; + visibility: hidden; +} +@media (transform-3d) { .csstransforms3d { left: 9px; } } +@media (-ms-transform-3d) { .csstransforms3d { left: 10px; } } +@media (-o-transform-3d) { .csstransforms3d { left: 11px; } } +@media (-moz-transform-3d) { .csstransforms3d { left: 12px; } } +@media (-webkit-transform-3d) { .csstransforms3d { left: 13px; } } + +/* indicator of a successful load via xstyle */ +#dgrid-css-has-transforms3d-loaded { + display: none; +} \ No newline at end of file diff --git a/widgets/dgrid/css/images/ui-icons_222222_256x240.png b/widgets/dgrid/css/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000..b273ff1 Binary files /dev/null and b/widgets/dgrid/css/images/ui-icons_222222_256x240.png differ diff --git a/widgets/dgrid/css/images/ui-icons_222222_256x240_white.png b/widgets/dgrid/css/images/ui-icons_222222_256x240_white.png new file mode 100644 index 0000000..5c386cf Binary files /dev/null and b/widgets/dgrid/css/images/ui-icons_222222_256x240_white.png differ diff --git a/widgets/dgrid/css/skins/README.md b/widgets/dgrid/css/skins/README.md new file mode 100644 index 0000000..c8ef2a4 --- /dev/null +++ b/widgets/dgrid/css/skins/README.md @@ -0,0 +1,8 @@ +This folder contains skins based on the Dijit themes (claro, tundra, soria, and nihilo), +as well as a number of other skins (e.g. cactus, sage, slate, squid). + +The skins are partially based on generic skin elements from the jquery-ui themeroller CSS +convention. The dgrid component follows this convention. + +Any other stylesheet based on the themeroller convention can ostensibly +also be used to skin the dgrid. \ No newline at end of file diff --git a/widgets/dgrid/css/skins/cactus.css b/widgets/dgrid/css/skins/cactus.css new file mode 100644 index 0000000..bd377d5 --- /dev/null +++ b/widgets/dgrid/css/skins/cactus.css @@ -0,0 +1,108 @@ +.cactus .ui-widget-content { + border: none; + background: #faffef; + color: #000; +} + +.cactus .dgrid-header-row { + border-bottom: none; +} + +.cactus .ui-widget-header, +.cactus .dgrid-footer { + color: #fff; + background: #333; /* Old browsers */ + background: -moz-linear-gradient(top, #4e4e4e 0%, #555555 12%, #636363 25%, #505050 39%, #303030 49%, #000000 50%, #1c1c1c 60%, #292929 76%, #1e1e1e 91%, #141414 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4e4e4e), color-stop(12%,#555555), color-stop(25%,#636363), color-stop(39%,#505050), color-stop(49%,#303030), color-stop(50%,#000000), color-stop(60%,#1c1c1c), color-stop(76%,#292929), color-stop(91%,#1e1e1e), color-stop(100%,#141414)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* IE10+ */ + background: linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4e4e4e', endColorstr='#141414',GradientType=0 ); /* IE6-9 */ +} + +.cactus .ui-widget-header th { + padding: 7px 3px; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.9); + border-color: #111; + text-transform: uppercase; +} + +.cactus .dgrid-cell { + border-color: #bbc581; + border-top: none; + border-right: none; + border-left: none; +} + +.cactus .dgrid-row-even { + border-top: 1px solid #FFF; +} + +.cactus .dgrid-row-odd { + background: #9a6; + background-image: -moz-linear-gradient(top, rgba(185,203,127,1) 0%, rgba(143,160,91,1) 100%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(185,203,127,1)), color-stop(100%,rgba(143,160,91,1))); + background-image: -webkit-linear-gradient(top, rgba(185,203,127,1) 0%,rgba(143,160,91,1) 100%); + background-image: -o-linear-gradient(top, rgba(185,203,127,1) 0%,rgba(143,160,91,1) 100%); + background-image: -ms-linear-gradient(top, rgba(185,203,127,1) 0%,rgba(143,160,91,1) 100%); + background-image: linear-gradient(top, rgba(185,203,127,1) 0%,rgba(143,160,91,1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b9cb7f', endColorstr='#8fa05b',GradientType=0 ); + text-shadow: 0 1px 0 rgba(255,255,255,.9); + border-top: 1px solid #ccd595; + color: #FFF; + text-shadow: 0 -1px 0 rgba(0,0,0,.3); +} + +.cactus .dgrid-row-odd .dgrid-cell { + border-top: 1px solid #e9efbd; +} + +.cactus .dgrid-row:hover, +.cactus .dgrid-row:hover .dgrid-cell { + background: #555; + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.9); + border-color: #555; +} + +.cactus .dgrid-selected, +.cactus .dgrid-selected .dgrid-cell { + background: #333; + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.9); + border-color: #333; +} + +.cactus .ui-state-active:hover { + background: #333; + border-color: #333; +} + +.cactus .ui-state-highlight { + background: #d6e5a5; /* background-color doesn't override CSS3 gradient */ + filter: none; /* override gradient in IE */ + color: #000; + text-shadow: none; +} + +/* need to use white-based image for sort arrow */ +.cactus .dgrid-sort-arrow { + background-image: url("../images/ui-icons_222222_256x240_white.png"); +} + +/* for ColumnReorder */ +.cactus .dgrid-header .dojoDndItemBefore { + border-left: 2px dotted #fff !important; +} +.cactus .dgrid-header .dojoDndItemAfter { + border-right: 2px dotted #fff !important; +} + +/* for pagination in IE < 8 + quirks, need additional rule to override link color */ +.has-ie-6-7 .cactus .dgrid-navigation a, +.has-ie.has-quirks .cactus .dgrid-navigation a { + color: #fff; +} \ No newline at end of file diff --git a/widgets/dgrid/css/skins/claro.css b/widgets/dgrid/css/skins/claro.css new file mode 100644 index 0000000..5948fa8 --- /dev/null +++ b/widgets/dgrid/css/skins/claro.css @@ -0,0 +1,67 @@ +.claro .dgrid { + border: 1px solid #aaa; + background: #fff; + color: #000; +} + +.claro .ui-widget-header { + font-weight: bold; +} +.claro .ui-widget-header, +.claro .dgrid-footer { + background: #ebf0f5; /* Old browsers */ + background: -moz-linear-gradient(top, #ebf0f5 0%, #d5e0ea 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ebf0f5), color-stop(100%,#d5e0ea)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ebf0f5 0%,#d5e0ea 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ebf0f5 0%,#d5e0ea 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #ebf0f5 0%,#d5e0ea 100%); /* IE10+ */ + background: linear-gradient(top, #ebf0f5 0%,#d5e0ea 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ebf0f5', endColorstr='#d5e0ea',GradientType=0 ); /* IE6-9 */ +} +.claro .ui-widget-header .dgrid-cell:hover { + background: #ebf1f6; /* Old browsers */ + background: -moz-linear-gradient(top, #ffffff 0%, #d2e0eb 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#d2e0eb)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ffffff 0%,#d2e0eb 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ffffff 0%,#d2e0eb 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #ffffff 0%,#d2e0eb 100%); /* IE10+ */ + background: linear-gradient(top, #ffffff 0%,#d2e0eb 100%); /* W3C */ +} + +.claro .ui-state-default { + -webkit-transition-duration: 0.2s; + -moz-transition-duration: 0.2s; + -o-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-property: background-color, border-color; + -moz-transition-property: background-color, border-color; + -o-transition-property: background-color, border-color; + transition-property: background-color, border-color; + background: url("images/row_back.png") #fff repeat-x; +} +.has-ie-6 .claro .ui-state-default { + background-image: none; +} +.claro .ui-state-default:hover { + background-color: #e9f2fe; +} + +.claro .ui-state-active { + background-color: #cee6fa; +} + +.claro .ui-state-active:hover { + background-color: #9bc6f2; +} + +.claro .ui-state-highlight { + background-color: #ff6; +} + +.claro .dgrid-cell { + border-color: #edc; +} + +.claro .ui-widget-header .dgrid-cell { + border-color: #bbb; +} diff --git a/widgets/dgrid/css/skins/images/row_back.png b/widgets/dgrid/css/skins/images/row_back.png new file mode 100644 index 0000000..b283068 Binary files /dev/null and b/widgets/dgrid/css/skins/images/row_back.png differ diff --git a/widgets/dgrid/css/skins/nihilo.css b/widgets/dgrid/css/skins/nihilo.css new file mode 100644 index 0000000..5cee4fe --- /dev/null +++ b/widgets/dgrid/css/skins/nihilo.css @@ -0,0 +1,38 @@ +.nihilo .dgrid { + border-color: #bba; +} + +.nihilo .ui-widget-content { + background: #fff; + color: #000; +} +.nihilo .ui-widget-header { + background: #fff; + border-bottom-color: #919191; +} +.nihilo .dgrid-footer { + background: #fff; + border-top: 1px solid #919191; +} +.nihilo .dgrid-header .dgrid-cell { + border-right-color: #acab99; +} + +.nihilo .ui-state-active { + background-color: #aec7e3; +} +.nihilo .ui-state-default:hover { + background-color: #ffe284; +} + +.nihilo .ui-state-highlight { + background-color: #ff6; +} + +.nihilo .dgrid-cell { + border-color: #ddc; +} + +.nihilo .ui-widget-header .dgrid-cell { + border-color: #bba; +} \ No newline at end of file diff --git a/widgets/dgrid/css/skins/sage.css b/widgets/dgrid/css/skins/sage.css new file mode 100644 index 0000000..1397282 --- /dev/null +++ b/widgets/dgrid/css/skins/sage.css @@ -0,0 +1,86 @@ +.sage .ui-widget-content { + border: none; + background: #fff; + color: #000; + text-shadow: 0 1px 0 rgba(255,255,255,.9); +} + +.sage .dgrid-header-row { + border-bottom: none; +} + +.sage .ui-widget-header, .sage .dgrid-footer { + color: #fff; + background: #333; /* Old browsers */ + background: -moz-linear-gradient(top, #4e4e4e 0%, #555555 12%, #636363 25%, #505050 39%, #303030 49%, #000000 50%, #1c1c1c 60%, #292929 76%, #1e1e1e 91%, #141414 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4e4e4e), color-stop(12%,#555555), color-stop(25%,#636363), color-stop(39%,#505050), color-stop(49%,#303030), color-stop(50%,#000000), color-stop(60%,#1c1c1c), color-stop(76%,#292929), color-stop(91%,#1e1e1e), color-stop(100%,#141414)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* IE10+ */ + background: linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4e4e4e', endColorstr='#141414',GradientType=0 ); /* IE6-9 */ +} + +.sage .ui-widget-header th { + padding: 7px 3px; + font-weight: bold; + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.9); + border-color: #111; + text-transform: uppercase; +} + +.sage .dgrid-cell { + border-color: #bbc581; + border-top: none; + border-right: none; + border-left: none; +} + +.sage .dgrid-row-odd { + background: #f2f7e8; +} + +.sage .dgrid-row:hover { + background: #c5dca6; + color: #333; + text-shadow: 0 1px 0 rgba(255,255,255,.5); +} + +.sage .ui-state-default { + -webkit-transition-duration: 0.1s; + -moz-transition-duration: 0.1s; + transition-duration: 0.1s; + -webkit-transition-property: background-color, border-color; + -moz-transition-property: background-color, border-color; + transition-property: background-color, border-color; +} + +.sage .dgrid-selected, +.sage .dgrid-selected:hover { + background: #b3d18b; + text-shadow: 0 1px 0 rgba(255,255,255,.6); +} + +.sage .ui-state-highlight { + background-color: #d5e8bd; +} + +/* need to use white-based image for sort arrow */ +.sage .dgrid-sort-arrow { + background-image: url("../images/ui-icons_222222_256x240_white.png"); +} + +/* for ColumnReorder */ +.sage .dgrid-header .dojoDndItemBefore { + border-left: 2px dotted #fff !important; +} +.sage .dgrid-header .dojoDndItemAfter { + border-right: 2px dotted #fff !important; +} + +/* for pagination in IE < 8 + quirks, need additional rule to override link color */ +.has-ie-6-7 .sage .dgrid-navigation a, +.has-ie.has-quirks .sage .dgrid-navigation a { + color: #fff; +} \ No newline at end of file diff --git a/widgets/dgrid/css/skins/slate.css b/widgets/dgrid/css/skins/slate.css new file mode 100644 index 0000000..176cc8f --- /dev/null +++ b/widgets/dgrid/css/skins/slate.css @@ -0,0 +1,68 @@ +.slate .ui-widget-content { + background: #fff; + color: #000; + text-shadow: 0 1px 0 rgba(255,255,255,.9); +} + +.slate .dgrid-header-row { + border-bottom: none; +} + +.slate .ui-widget-header, .slate .dgrid-footer { + color: #fff; + background: #333; /* Old browsers */ + background: -moz-linear-gradient(top, #4e4e4e 0%, #555555 12%, #636363 25%, #505050 39%, #303030 49%, #000000 50%, #1c1c1c 60%, #292929 76%, #1e1e1e 91%, #141414 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4e4e4e), color-stop(12%,#555555), color-stop(25%,#636363), color-stop(39%,#505050), color-stop(49%,#303030), color-stop(50%,#000000), color-stop(60%,#1c1c1c), color-stop(76%,#292929), color-stop(91%,#1e1e1e), color-stop(100%,#141414)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* IE10+ */ + background: linear-gradient(top, #4e4e4e 0%,#555555 12%,#636363 25%,#505050 39%,#303030 49%,#000000 50%,#1c1c1c 60%,#292929 76%,#1e1e1e 91%,#141414 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4e4e4e', endColorstr='#141414',GradientType=0 ); /* IE6-9 */ +} + +.slate .ui-widget-header th { + padding: 7px 3px; + font-weight: bold; + color: #FFF; + text-shadow: 0 -1px 0 rgba(0,0,0,.9); + border-color: #111; + text-transform: uppercase; +} + +.slate .dgrid-row-odd { + background-color: #f7f7f7; +} + +.slate .dgrid-row:hover { + background-color: #ddd; +} + +.slate .ui-state-active, +.slate .ui-state-active:hover { + background-color: #555; + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.5); +} + +.slate .ui-state-highlight { + background-color: #999; +} + +/* need to use white-based image for sort arrow */ +.slate .dgrid-sort-arrow { + background-image: url("../images/ui-icons_222222_256x240_white.png"); +} + +/* for ColumnReorder */ +.slate .dgrid-header .dojoDndItemBefore { + border-left: 2px dotted #fff !important; +} +.slate .dgrid-header .dojoDndItemAfter { + border-right: 2px dotted #fff !important; +} + +/* for pagination in IE < 8 + quirks, need additional rule to override link color */ +.has-ie-6-7 .slate .dgrid-navigation a, +.has-ie.has-quirks .slate .dgrid-navigation a { + color: #fff; +} \ No newline at end of file diff --git a/widgets/dgrid/css/skins/soria.css b/widgets/dgrid/css/skins/soria.css new file mode 100644 index 0000000..c520a94 --- /dev/null +++ b/widgets/dgrid/css/skins/soria.css @@ -0,0 +1,46 @@ +.soria .dgrid { + border-color: #bba; +} + +.soria .ui-widget-content { + background: #fff; + color: #000; +} +.soria .ui-widget-header, .soria .dgrid-footer { + background: #f2f4fe; /* Old browsers */ + background: -moz-linear-gradient(top, #f2f4fe 0%, #d0dff5 50%, #c6d8f0 51%, #c2d5ef 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f2f4fe), color-stop(50%,#d0dff5), color-stop(51%,#c6d8f0), color-stop(100%,#c2d5ef)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f2f4fe 0%,#d0dff5 50%,#c6d8f0 51%,#c2d5ef 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f2f4fe 0%,#d0dff5 50%,#c6d8f0 51%,#c2d5ef 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #f2f4fe 0%,#d0dff5 50%,#c6d8f0 51%,#c2d5ef 100%); /* IE10+ */ + background: linear-gradient(top, #f2f4fe 0%,#d0dff5 50%,#c6d8f0 51%,#c2d5ef 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f2f4fe', endColorstr='#c2d5ef',GradientType=0 ); /* IE6-9 */ +} +.soria .ui-widget-header th:hover { + background: #d4deec; /* Old browsers */ + background: -moz-linear-gradient(top, #dae2ed 0%, #b2c7e8 49%, #a8c1eb 50%, #9ebaec 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#dae2ed), color-stop(49%,#b2c7e8), color-stop(50%,#a8c1eb), color-stop(100%,#9ebaec)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #dae2ed 0%,#b2c7e8 49%,#a8c1eb 50%,#9ebaec 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #dae2ed 0%,#b2c7e8 49%,#a8c1eb 50%,#9ebaec 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #dae2ed 0%,#b2c7e8 49%,#a8c1eb 50%,#9ebaec 100%); /* IE10+ */ + background: linear-gradient(top, #dae2ed 0%,#b2c7e8 49%,#a8c1eb 50%,#9ebaec 100%); /* W3C */ +} + +.soria .ui-state-active { + background-color: #aec7e3; +} +.soria .ui-state-default:hover { + background-color: #60a1ea; +} + +.soria .ui-state-highlight { + background-color: #ff6; +} + +.soria .dgrid-cell { + border-color: #ddc; +} + +.soria .ui-widget-header .dgrid-cell { + border-color: #bba; +} diff --git a/widgets/dgrid/css/skins/squid.css b/widgets/dgrid/css/skins/squid.css new file mode 100644 index 0000000..a1bbd71 --- /dev/null +++ b/widgets/dgrid/css/skins/squid.css @@ -0,0 +1,84 @@ +.squid .ui-widget-content { + border: 1px solid #555; + background: #000; + color: #fff; + text-shadow: 0 -1px 0 rgba(0,0,0,.7); +} +.squid .ui-icon { + background-image: url("../images/ui-icons_222222_256x240_white.png"); +} + +.squid .dgrid-header { + padding: 0 1px; /* add side padding to align with borders on content area */ +} + +.squid .ui-widget-header, .squid .dgrid-footer { + color: #fff; + background: #2d1f14; /* Old browsers */ + background: -moz-linear-gradient(top, #140e09 0%, #2d1f14 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#140e09), color-stop(100%,#2d1f14)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #140e09 0%,#2d1f14 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #140e09 0%,#2d1f14 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #140e09 0%,#2d1f14 100%); /* IE10+ */ + background: linear-gradient(top, #140e09 0%,#2d1f14 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#140e09', endColorstr='#2d1f14',GradientType=0 ); /* IE6-9 */ + color: #fff; + font-weight: bold; +} +.squid .ui-widget-header:hover { + background: #000; /* Old browsers */ + background: -moz-linear-gradient(top, #000000 0%, #2d1f14 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#000000), color-stop(100%,#2d1f14)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #000000 0%,#2d1f14 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #000000 0%,#2d1f14 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #000000 0%,#2d1f14 100%); /* IE10+ */ + background: linear-gradient(top, #000000 0%,#2d1f14 100%); /* W3C */ +} + +.squid .ui-state-default { + -webkit-transition-duration: 0.2s; + -moz-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-property: background-color, border-color; + -moz-transition-property: background-color, border-color; + transition-property: background-color, border-color; + background: url("images/row_back.png") #000 repeat-x; +} +.squid .ui-state-default:hover { + background-color: #444; +} + +.has-ie-6 .squid .ui-state-default { + background-image: none; +} + +.squid .ui-state-active { + background-color: #64390d; + text-shadow: 0 -1px 0 rgba(0,0,0,.3); +} + +.squid .ui-state-active:hover { + background-color: #8b6b4a; +} + +.squid .ui-state-highlight { + background-color: #666; +} + +.squid .dgrid-cell { + border-color: #ccc; +} + +/* for ColumnReorder */ +.squid .dgrid-header .dojoDndItemBefore { + border-left: 2px dotted #fff !important; +} +.squid .dgrid-header .dojoDndItemAfter { + border-right: 2px dotted #fff !important; +} + +/* for pagination in IE < 8 + quirks, need additional rule to override link color */ +.has-ie-6-7 .squid .dgrid-navigation a, +.has-ie.has-quirks .squid .dgrid-navigation a { + color: #fff; +} \ No newline at end of file diff --git a/widgets/dgrid/css/skins/tundra.css b/widgets/dgrid/css/skins/tundra.css new file mode 100644 index 0000000..f67d59e --- /dev/null +++ b/widgets/dgrid/css/skins/tundra.css @@ -0,0 +1,48 @@ +.tundra .dgrid { + border-color: #bba; +} + +.tundra .ui-widget-content { + background: #fff; + color: #000; +} + +.tundra .ui-widget-header, .tundra .dgrid-footer { + background: #e8e8e8; /* Old browsers */ + background: -moz-linear-gradient(top, #ffffff 0%, #e8e8e8 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#e8e8e8)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ffffff 0%,#e8e8e8 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ffffff 0%,#e8e8e8 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #ffffff 0%,#e8e8e8 100%); /* IE10+ */ + background: linear-gradient(top, #ffffff 0%,#e8e8e8 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e8e8e8',GradientType=0 ); /* IE6-9 */ + font-weight: bold; +} +.tundra .ui-widget-header th:hover { + background: #f6f6f6; /* Old browsers */ + background: -moz-linear-gradient(top, #ffffff 0%, #eeeeee 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#eeeeee)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ffffff 0%,#eeeeee 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ffffff 0%,#eeeeee 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 100%); /* IE10+ */ + background: linear-gradient(top, #ffffff 0%,#eeeeee 100%); /* W3C */ +} + +.tundra .ui-state-active { + background-color: #aec7e3; +} +.tundra .ui-state-default:hover { + background-color: #60a1ea; +} + +.tundra .ui-state-highlight { + background-color: #ff6; +} + +.tundra .dgrid-cell { + border-color: #ddc; +} + +.tundra .ui-widget-header .dgrid-cell { + border-color: #bba; +} diff --git a/widgets/dgrid/demos/dTuned/data.js b/widgets/dgrid/demos/dTuned/data.js new file mode 100644 index 0000000..9d9ade3 --- /dev/null +++ b/widgets/dgrid/demos/dTuned/data.js @@ -0,0 +1,143 @@ +define(["dojo/store/Memory", "dojo/store/Observable"], function(Memory, Observable){ + // create some song data, and return a Memory Store from it. + var data = { + identifier: "Key", + label: "Name", + items: [ + {"Key":"1","Name":"Grind","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"284","TrackNumber":"1","Year":"1995"}, + {"Key":"2","Name":"Brush Away","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"202","TrackNumber":"2","Year":"1995"}, + {"Key":"3","Name":"Sludge Factory","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"432","TrackNumber":"3","Year":"1995"}, + {"Key":"4","Name":"Heaven Beside You","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"327","TrackNumber":"4","Year":"1995"}, + {"Key":"5","Name":"Head Creeps","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"388","TrackNumber":"5","Year":"1995"}, + {"Key":"6","Name":"Again","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"245","TrackNumber":"6","Year":"1995"}, + {"Key":"7","Name":"Shame In You","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"335","TrackNumber":"7","Year":"1995"}, + {"Key":"8","Name":"God Am","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"248","TrackNumber":"8","Year":"1995"}, + {"Key":"9","Name":"So Close","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"165","TrackNumber":"9","Year":"1995"}, + {"Key":"10","Name":"Nothin' Song","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"340","TrackNumber":"10","Year":"1995"}, + {"Key":"11","Name":"Frogs","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"498","TrackNumber":"11","Year":"1995"}, + {"Key":"12","Name":"Over Now","Artist":"Alice In Chains","Album":"Alice In Chains","Genre":"Alternative","Time":"423","TrackNumber":"12","Year":"1995"}, + {"Key":"13","Name":"Man In the Box","Artist":"Alice In Chains","Album":"Facelift","Genre":"Alternative","Time":"284","TrackNumber":"2","Year":"1990"}, + {"Key":"14","Name":"Get Born Again","Artist":"Alice In Chains","Album":"Music Bank","Genre":"Alternative","Time":"324","TrackNumber":"1","Year":"1999"}, + {"Key":"15","Name":"Tempting Time","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"323","TrackNumber":"1","Year":"2009"}, + {"Key":"16","Name":"Soraya","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"266","TrackNumber":"2","Year":"2009"}, + {"Key":"17","Name":"Thoroughly At Home","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"241","TrackNumber":"3","Year":"2009"}, + {"Key":"18","Name":"On Impulse","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"369","TrackNumber":"4","Year":"2009"}, + {"Key":"19","Name":"Tessitura","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"66","TrackNumber":"5","Year":"2009"}, + {"Key":"20","Name":"Behaving Badly","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"266","TrackNumber":"6","Year":"2009"}, + {"Key":"21","Name":"The Price of Everything and the Value of Nothing","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"332","TrackNumber":"7","Year":"2009"}, + {"Key":"22","Name":"CAFO","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"401","TrackNumber":"8","Year":"2009"}, + {"Key":"23","Name":"Inamorata","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"368","TrackNumber":"9","Year":"2009"}, + {"Key":"24","Name":"Point to Point","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"104","TrackNumber":"10","Year":"2009"}, + {"Key":"25","Name":"Modern Meat","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"126","TrackNumber":"11","Year":"2009"}, + {"Key":"26","Name":"Song of Solomon","Artist":"Animals As Leaders","Album":"Animals As Leaders","Genre":"Rock","Time":"256","TrackNumber":"12","Year":"2009"}, + {"Key":"27","Name":"Hunter","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"255","TrackNumber":"1","Year":"1997"}, + {"Key":"28","Name":"J\u00c3\u00b2ga","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"305","TrackNumber":"2","Year":"1997"}, + {"Key":"29","Name":"Unravel","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"201","TrackNumber":"3","Year":"1997"}, + {"Key":"30","Name":"Bachelorette","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"312","TrackNumber":"4","Year":"1997"}, + {"Key":"31","Name":"All Neon Like","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"352","TrackNumber":"5","Year":"1997"}, + {"Key":"32","Name":"5 Years","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"268","TrackNumber":"6","Year":"1997"}, + {"Key":"33","Name":"Immature","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"186","TrackNumber":"7","Year":"1997"}, + {"Key":"34","Name":"Alarm Call","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"259","TrackNumber":"8","Year":"1997"}, + {"Key":"35","Name":"Pluto","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"199","TrackNumber":"9","Year":"1997"}, + {"Key":"36","Name":"All Is Full of Love","Artist":"Bjork","Album":"Homogenic","Genre":"Electronica","Time":"272","TrackNumber":"10","Year":"1997"}, + {"Key":"37","Name":"Sunday","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"285","TrackNumber":"1","Year":"2002"}, + {"Key":"38","Name":"Cactus","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"174","TrackNumber":"2","Year":"2002"}, + {"Key":"39","Name":"Slip Away","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"364","TrackNumber":"3","Year":"2002"}, + {"Key":"40","Name":"Slow Burn","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"281","TrackNumber":"4","Year":"2002"}, + {"Key":"41","Name":"Afraid","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"208","TrackNumber":"5","Year":"2002"}, + {"Key":"42","Name":"I've Been Waiting For You","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"180","TrackNumber":"6","Year":"2002"}, + {"Key":"43","Name":"I Would Be Your Slave","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"313","TrackNumber":"7","Year":"2002"}, + {"Key":"44","Name":"I Took A Trip On A Gemini Spaceship","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"246","TrackNumber":"8","Year":"2002"}, + {"Key":"45","Name":"5:15 The Angels Have Gone","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"301","TrackNumber":"9","Year":"2002"}, + {"Key":"46","Name":"Everyone Says 'Hi'","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"237","TrackNumber":"10","Year":"2002"}, + {"Key":"47","Name":"A Better Future","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"251","TrackNumber":"11","Year":"2002"}, + {"Key":"48","Name":"Heathen (The Rays)","Artist":"David Bowie","Album":"Heathen","Genre":"Progressive Rock","Time":"256","TrackNumber":"12","Year":"2002"}, + {"Key":"49","Name":"The New Bison","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"290","TrackNumber":"1","Year":"2007"}, + {"Key":"50","Name":"Very Small Rock","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"300","TrackNumber":"2","Year":"2007"}, + {"Key":"51","Name":"1996 A.D.","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"274","TrackNumber":"3","Year":"2007"}, + {"Key":"52","Name":"Rise! Marc Anthony","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"432","TrackNumber":"4","Year":"2007"}, + {"Key":"53","Name":"Calgon for Hetfield","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"325","TrackNumber":"5","Year":"2007"}, + {"Key":"54","Name":"Lefse los Cubanos","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"337","TrackNumber":"6","Year":"2007"}, + {"Key":"55","Name":"He's OK","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"379","TrackNumber":"7","Year":"2007"}, + {"Key":"56","Name":"Brown Lights","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"431","TrackNumber":"8","Year":"2007"}, + {"Key":"57","Name":"Hence the Turtleneck","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"331","TrackNumber":"9","Year":"2007"}, + {"Key":"58","Name":"Most Popular to Succeed","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"545","TrackNumber":"10","Year":"2007"}, + {"Key":"59","Name":"Density In Dan's Fan City","Artist":"Happy Apple","Album":"Happy Apple Back On Top","Genre":"Jazz","Time":"424","TrackNumber":"11","Year":"2007"}, + {"Key":"60","Name":"Take Wes Chandler For Instance","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"329","TrackNumber":"1","Year":"2001"}, + {"Key":"61","Name":"You & Mattel Vs Me & Coleco","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"332","TrackNumber":"2","Year":"2001"}, + {"Key":"62","Name":"Who Is Your Midwest Representation?","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"179","TrackNumber":"3","Year":"2001"}, + {"Key":"63","Name":"November","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"337","TrackNumber":"4","Year":"2001"}, + {"Key":"64","Name":"The Invasion Has Become","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"358","TrackNumber":"5","Year":"2001"}, + {"Key":"65","Name":"A Waltz For The Few Remaining","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"478","TrackNumber":"6","Year":"2001"}, + {"Key":"66","Name":"Homage Ritchie Valens","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"315","TrackNumber":"7","Year":"2001"}, + {"Key":"67","Name":"Acknowledge The Ascot","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"540","TrackNumber":"8","Year":"2001"}, + {"Key":"68","Name":"Koala Bear Wearing A T-Shirt With Your Corporate Logo","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"245","TrackNumber":"9","Year":"2001"}, + {"Key":"69","Name":"Buffalo '98","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"793","TrackNumber":"10","Year":"2001"}, + {"Key":"70","Name":"Long Live Rock & Roll","Artist":"Happy Apple","Album":"Please Refrain From Fronting","Genre":"Jazz","Time":"170","TrackNumber":"11","Year":"2001"}, + {"Key":"71","Name":"Take a Bow","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"275","TrackNumber":"1","Year":"2006"}, + {"Key":"72","Name":"Starlight","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"239","TrackNumber":"2","Year":"2006"}, + {"Key":"73","Name":"Supermassive Black Hole","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"210","TrackNumber":"3","Year":"2006"}, + {"Key":"74","Name":"Map of the Problematique","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"258","TrackNumber":"4","Year":"2006"}, + {"Key":"75","Name":"Soldier's Poem","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"124","TrackNumber":"5","Year":"2006"}, + {"Key":"76","Name":"Invincible","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"300","TrackNumber":"6","Year":"2006"}, + {"Key":"77","Name":"Assassin","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"211","TrackNumber":"7","Year":"2006"}, + {"Key":"78","Name":"Exo-Politics","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"233","TrackNumber":"8","Year":"2006"}, + {"Key":"79","Name":"City of Delusion","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"288","TrackNumber":"9","Year":"2006"}, + {"Key":"80","Name":"Hoodoo","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"223","TrackNumber":"10","Year":"2006"}, + {"Key":"81","Name":"Knights of Cydonia","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"367","TrackNumber":"11","Year":"2006"}, + {"Key":"82","Name":"Glorious","Artist":"Muse","Album":"Black Holes and Revelations","Genre":"Alternative","Time":"281","TrackNumber":"12","Year":"2006"}, + {"Key":"83","Name":"Rolling","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"216","TrackNumber":"1","Year":""}, + {"Key":"84","Name":"Misinformed","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"204","TrackNumber":"2","Year":""}, + {"Key":"85","Name":"Circles","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"186","TrackNumber":"3","Year":""}, + {"Key":"86","Name":"Blame","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"300","TrackNumber":"4","Year":""}, + {"Key":"87","Name":"St. Louise Is Listening","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"268","TrackNumber":"5","Year":""}, + {"Key":"88","Name":"Maybe I'll Come Down","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"272","TrackNumber":"6","Year":""}, + {"Key":"89","Name":"Houston","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"243","TrackNumber":"7","Year":""}, + {"Key":"90","Name":"$300","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"187","TrackNumber":"8","Year":""}, + {"Key":"91","Name":"Fully Retractable","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"205","TrackNumber":"9","Year":""}, + {"Key":"92","Name":"Monster Man","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"256","TrackNumber":"10","Year":""}, + {"Key":"93","Name":"Pensacola","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"255","TrackNumber":"11","Year":""}, + {"Key":"94","Name":"I Miss the Girl","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"243","TrackNumber":"12","Year":""}, + {"Key":"95","Name":"So Far I Have Not Found the Sc","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"173","TrackNumber":"13","Year":""}, + {"Key":"96","Name":"The Incumbent","Artist":"Soul Coughing","Album":"El Oso","Genre":"Indie","Time":"406","TrackNumber":"14","Year":""}, + {"Key":"97","Name":"Futures","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"229","TrackNumber":"1","Year":"2006"}, + {"Key":"98","Name":"Throw it all away","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"320","TrackNumber":"2","Year":"2006"}, + {"Key":"99","Name":"Seeing things","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"309","TrackNumber":"3","Year":"2006"}, + {"Key":"100","Name":"The pageant of the bizarre","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"261","TrackNumber":"4","Year":"2006"}, + {"Key":"101","Name":"You're my flame","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"195","TrackNumber":"5","Year":"2006"}, + {"Key":"102","Name":"Left behind","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"75","TrackNumber":"6","Year":"2006"}, + {"Key":"103","Name":"Today","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"243","TrackNumber":"7","Year":"2006"}, + {"Key":"104","Name":"This fine social scene","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"267","TrackNumber":"8","Year":"2006"}, + {"Key":"105","Name":"Your place","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"361","TrackNumber":"9","Year":"2006"}, + {"Key":"106","Name":"If I can't have you","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"202","TrackNumber":"10","Year":"2006"}, + {"Key":"107","Name":"Crosses","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"399","TrackNumber":"11","Year":"2006"}, + {"Key":"108","Name":"Waiting to die","Artist":"Zero 7","Album":"The Garden","Genre":"Trip-Hop","Time":"219","TrackNumber":"12","Year":"2006"}, + {"Key":"109","Name":"I Have Seen","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"307","TrackNumber":"1","Year":"2001"}, + {"Key":"110","Name":"Polaris","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"288","TrackNumber":"2","Year":"2001"}, + {"Key":"111","Name":"Destiny","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"337","TrackNumber":"3","Year":"2001"}, + {"Key":"112","Name":"Give It Away","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"317","TrackNumber":"4","Year":"2001"}, + {"Key":"113","Name":"Simple Things","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"264","TrackNumber":"5","Year":"2001"}, + {"Key":"114","Name":"Red Dust","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"340","TrackNumber":"6","Year":"2001"}, + {"Key":"115","Name":"Distractions","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"316","TrackNumber":"7","Year":"2001"}, + {"Key":"116","Name":"In The Waiting Line","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"272","TrackNumber":"8","Year":"2001"}, + {"Key":"117","Name":"Out Of Town","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"287","TrackNumber":"9","Year":"2001"}, + {"Key":"118","Name":"This World","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"335","TrackNumber":"10","Year":"2001"}, + {"Key":"119","Name":"Likufanele","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"371","TrackNumber":"11","Year":"2001"}, + {"Key":"120","Name":"End Theme","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"219","TrackNumber":"12","Year":"2001"}, + {"Key":"121","Name":"Salt Water Sound","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"330","TrackNumber":"13","Year":"2001"}, + {"Key":"122","Name":"Spinning","Artist":"Zero 7","Album":"Simple Things","Genre":"Trip-Hop","Time":"363","TrackNumber":"14","Year":"2001"} + ] + }; + + // ugly shortcut, since there's 122 items here and I don't feel like manually editing it. + for(var i=0, l=data.items.length; i + + + dTuned + + + + + + + + + + +
                      +
                      +
                      +
                      +
                      +
                      + + diff --git a/widgets/dgrid/demos/dTuned/resources/dTuned.css b/widgets/dgrid/demos/dTuned/resources/dTuned.css new file mode 100644 index 0000000..ad66a75 --- /dev/null +++ b/widgets/dgrid/demos/dTuned/resources/dTuned.css @@ -0,0 +1,46 @@ +.dTuned .ui-widget { + background: #fff; + color: #000; +} +.dTuned .ui-widget-header { + background-image: url(images/grid-header-bg.png); + background-repeat: repeat-x; + font-weight: bold; +} +.dTuned .ui-widget-header .dgrid-cell:hover, +.dTuned .ui-widget-header .dgrid-sort-up, +.dTuned .ui-widget-header .dgrid-sort-down{ + background-image: url(images/grid-header-sorted.png); + background-repeat: repeat-x; +} + +.dTuned .ui-state-default { + -webkit-transition-duration: 0.2s; + -moz-transition-duration: 0.2s; + -o-transition-duration: 0.2s; + transition-duration: 0.2s; + -webkit-transition-property: background-color, border-color; + -moz-transition-property: background-color, border-color; + -o-transition-property: background-color, border-color; + transition-property: background-color, border-color; + background: url("../../../css/skins/images/row_back.png") #fff repeat-x; +} +.has-ie-6 .dTuned .ui-state-default { + background-image: none; +} + +.dTuned .ui-state-default:hover { + background-color: #e9f2fe; +} + +.dTuned .ui-state-active { + background-color: #cee6fa; +} + +.dTuned .ui-state-active:hover { + background-color: #9bc6f2; +} + +.dTuned .ui-state-highlight { + background-color: #ff6; +} diff --git a/widgets/dgrid/demos/dTuned/resources/images/grid-header-bg.png b/widgets/dgrid/demos/dTuned/resources/images/grid-header-bg.png new file mode 100644 index 0000000..48b4151 Binary files /dev/null and b/widgets/dgrid/demos/dTuned/resources/images/grid-header-bg.png differ diff --git a/widgets/dgrid/demos/dTuned/resources/images/grid-header-separator.png b/widgets/dgrid/demos/dTuned/resources/images/grid-header-separator.png new file mode 100644 index 0000000..096223c Binary files /dev/null and b/widgets/dgrid/demos/dTuned/resources/images/grid-header-separator.png differ diff --git a/widgets/dgrid/demos/dTuned/resources/images/grid-header-sorted-separator.png b/widgets/dgrid/demos/dTuned/resources/images/grid-header-sorted-separator.png new file mode 100644 index 0000000..9a856e6 Binary files /dev/null and b/widgets/dgrid/demos/dTuned/resources/images/grid-header-sorted-separator.png differ diff --git a/widgets/dgrid/demos/dTuned/resources/images/grid-header-sorted.png b/widgets/dgrid/demos/dTuned/resources/images/grid-header-sorted.png new file mode 100644 index 0000000..d18a28c Binary files /dev/null and b/widgets/dgrid/demos/dTuned/resources/images/grid-header-sorted.png differ diff --git a/widgets/dgrid/demos/dTuned/resources/images/header-background.png b/widgets/dgrid/demos/dTuned/resources/images/header-background.png new file mode 100644 index 0000000..3cf44b6 Binary files /dev/null and b/widgets/dgrid/demos/dTuned/resources/images/header-background.png differ diff --git a/widgets/dgrid/demos/dTuned/resources/images/header.png b/widgets/dgrid/demos/dTuned/resources/images/header.png new file mode 100644 index 0000000..cccf125 Binary files /dev/null and b/widgets/dgrid/demos/dTuned/resources/images/header.png differ diff --git a/widgets/dgrid/demos/multiview/index.html b/widgets/dgrid/demos/multiview/index.html new file mode 100644 index 0000000..4013c86 --- /dev/null +++ b/widgets/dgrid/demos/multiview/index.html @@ -0,0 +1,219 @@ + + + + + Multi-view dgrid + + + + + +

                      + This page demonstrates presenting several different views within a single + Grid instance, by swapping out the renderRow function. + The details and gallery views also demonstrate switching the grid header + off by calling setShowHeader. + (In fact, if only the Details and Gallery views were desired, + the List module would be sufficient.) +

                      +

                      + In addition, the "table" view demonstrates hooking up a click callback, + in order to display more information when a row is clicked. +

                      +

                      Switch View: + + + +

                      +
                      + + + + \ No newline at end of file diff --git a/widgets/dgrid/demos/multiview/resources/df-128.png b/widgets/dgrid/demos/multiview/resources/df-128.png new file mode 100644 index 0000000..b870084 Binary files /dev/null and b/widgets/dgrid/demos/multiview/resources/df-128.png differ diff --git a/widgets/dgrid/demos/multiview/resources/df-32.png b/widgets/dgrid/demos/multiview/resources/df-32.png new file mode 100644 index 0000000..ff3cad3 Binary files /dev/null and b/widgets/dgrid/demos/multiview/resources/df-32.png differ diff --git a/widgets/dgrid/demos/multiview/resources/df-64.png b/widgets/dgrid/demos/multiview/resources/df-64.png new file mode 100644 index 0000000..f8382dd Binary files /dev/null and b/widgets/dgrid/demos/multiview/resources/df-64.png differ diff --git a/widgets/dgrid/demos/multiview/resources/dojo-128.png b/widgets/dgrid/demos/multiview/resources/dojo-128.png new file mode 100644 index 0000000..aa5affe Binary files /dev/null and b/widgets/dgrid/demos/multiview/resources/dojo-128.png differ diff --git a/widgets/dgrid/demos/multiview/resources/dojo-32.png b/widgets/dgrid/demos/multiview/resources/dojo-32.png new file mode 100644 index 0000000..e96eecb Binary files /dev/null and b/widgets/dgrid/demos/multiview/resources/dojo-32.png differ diff --git a/widgets/dgrid/demos/multiview/resources/dojo-64.png b/widgets/dgrid/demos/multiview/resources/dojo-64.png new file mode 100644 index 0000000..bb32290 Binary files /dev/null and b/widgets/dgrid/demos/multiview/resources/dojo-64.png differ diff --git a/widgets/dgrid/demos/todo/index.html b/widgets/dgrid/demos/todo/index.html new file mode 100644 index 0000000..367d84e --- /dev/null +++ b/widgets/dgrid/demos/todo/index.html @@ -0,0 +1,213 @@ + + + + + Todo List + + + + + + + +
                      +
                      + + + +
                      +
                      + + +
                      +
                      + + + + \ No newline at end of file diff --git a/widgets/dgrid/editor.js b/widgets/dgrid/editor.js new file mode 100644 index 0000000..542baf6 --- /dev/null +++ b/widgets/dgrid/editor.js @@ -0,0 +1,404 @@ +define([ + "dojo/_base/kernel", + "dojo/_base/lang", + "dojo/_base/array", + "dojo/_base/Deferred", + "dojo/on", + "dojo/aspect", + "dojo/has", + "./Grid", + "put-selector/put", + "dojo/_base/sniff" +], function(kernel, lang, arrayUtil, Deferred, on, aspect, has, Grid, put){ + +// Variables to track info for cell currently being edited (editOn only). +var activeCell, activeValue, activeOptions; + +function updateInputValue(input, value){ + // common code for updating value of a standard input + input.value = value; + if(input.type == "radio" || input.type == "checkbox"){ + input.checked = !!value; + } +} + +function dataFromValue(value, oldValue){ + // Default logic for translating values from editors; + // tries to preserve type if possible. + if(typeof oldValue == "number"){ + value = isNaN(value) ? value : parseFloat(value); + }else if(typeof oldValue == "boolean"){ + value = value == "true" ? true : value == "false" ? false : value; + }else if(oldValue instanceof Date){ + var asDate = new Date(value); + value = isNaN(asDate.getTime()) ? value : asDate; + } + return value; +} + +// intermediary frontend to dataFromValue for HTML and widget editors +function dataFromEditor(column, cmp){ + if(typeof cmp.get == "function"){ // widget + return dataFromValue(cmp.get("value")); + }else{ // HTML input + return dataFromValue( + cmp[cmp.type == "checkbox" || cmp.type == "radio" ? "checked" : "value"]); + } +} + +function setProperty(grid, cellElement, oldValue, value, triggerEvent){ + // Updates dirty hash and fires dgrid-datachange event for a changed value. + var cell, row, column, eventObject; + // test whether old and new values are inequal, with coercion (e.g. for Dates) + if(!(oldValue >= value && oldValue <= value)){ + cell = grid.cell(cellElement); + row = cell.row; + column = cell.column; + if(column.field && row){ + // TODO: remove rowId in lieu of cell (or grid.row/grid.cell) + // (keeping for the moment for back-compat, but will note in changes) + eventObject = { + grid: grid, + cell: cell, + rowId: row.id, + oldValue: oldValue, + value: value, + bubbles: true, + cancelable: true + }; + if(triggerEvent && triggerEvent.type){ + eventObject.parentType = triggerEvent.type; + } + + if(on.emit(cellElement, "dgrid-datachange", eventObject)){ + if(grid.updateDirty){ + // for OnDemandGrid: update dirty data, and save if autoSave is true + grid.updateDirty(row.id, column.field, value); + // perform auto-save (if applicable) in next tick to avoid + // unintentional mishaps due to order of handler execution + column.autoSave && setTimeout(function(){ grid._trackError("save"); }, 0); + } + }else{ + // else keep the value the same + return oldValue; + } + } + } + return value; +} + +// intermediary frontend to setProperty for HTML and widget editors +function setPropertyFromEditor(grid, column, cmp, triggerEvent) { + var value; + if(!cmp.isValid || cmp.isValid()){ + value = setProperty(grid, (cmp.domNode || cmp).parentNode, + activeCell ? activeValue : cmp._dgridLastValue, + dataFromEditor(column, cmp), triggerEvent); + + if(activeCell){ // for editors with editOn defined + activeValue = value; + }else{ // for always-on editors, update _dgridLastValue immediately + cmp._dgridLastValue = value; + } + } +} + +// editor creation/hookup/placement logic + +function createEditor(column){ + // Creates an editor instance based on column definition properties, + // and hooks up events. + var editor = column.editor, + editOn = column.editOn, + grid = column.grid, + isWidget = typeof editor != "string", // string == standard HTML input + args, cmp, node, putstr, handleChange; + + args = column.editorArgs || {}; + if(typeof args == "function"){ args = args.call(grid, column); } + + if(isWidget){ + cmp = new editor(args); + node = cmp.focusNode || cmp.domNode; + + // Add dgrid-input to className to make consistent with HTML inputs. + node.className += " dgrid-input"; + + // For editOn editors, connect to onBlur rather than onChange, since + // the latter is delayed by setTimeouts in Dijit and will fire too late. + cmp.connect(cmp, editOn ? "onBlur" : "onChange", function(){ + if(!cmp._dgridIgnoreChange){ + setPropertyFromEditor(grid, column, this, {type: "widget"}); + } + }); + }else{ + handleChange = function(evt){ + var target = evt.target; + if("_dgridLastValue" in target && target.className.indexOf("dgrid-input") > -1){ + setPropertyFromEditor(grid, column, target, evt); + } + }; + + // considerations for standard HTML form elements + if(!column.grid._hasInputListener){ + // register one listener at the top level that receives events delegated + grid._hasInputListener = true; + grid.on("change", function(evt){ handleChange(evt); }); + // also register a focus listener + } + + putstr = editor == "textarea" ? "textarea" : + "input[type=" + editor + "]"; + cmp = node = put(putstr + ".dgrid-input", lang.mixin({ + name: column.field, + tabIndex: isNaN(column.tabIndex) ? -1 : column.tabIndex + }, args)); + + if(has("ie") < 9 || (has("ie") && has("quirks"))){ + // IE<9 / quirks doesn't fire change events for all the right things, + // and it doesn't bubble. + if(editor == "radio" || editor == "checkbox"){ + // listen for clicks since IE doesn't fire change events properly for checks/radios + on(cmp, "click", function(evt){ handleChange(evt); }); + }else{ + on(cmp, "change", function(evt){ handleChange(evt); }); + } + } + } + + // XXX: stop mousedown propagation to prevent confusing Keyboard mixin logic + // with certain widgets; perhaps revising KB's `handledEvent` would be better. + on(node, "mousedown", function(evt){ evt.stopPropagation(); }); + + return cmp; +} + +function createSharedEditor(column, originalRenderCell){ + // Creates an editor instance with additional considerations for + // shared usage across an entire column (for columns with editOn specified). + + var cmp = createEditor(column), + isWidget = cmp.domNode, + node = cmp.domNode || cmp, + focusNode = cmp.focusNode || node, + reset = isWidget ? + function(){ cmp.set("value", cmp._dgridLastValue); } : + function(){ + updateInputValue(cmp, cmp._dgridLastValue); + // call setProperty again in case we need to revert a previous change + setPropertyFromEditor(column.grid, column, cmp); + }, + keyHandle; + + function onblur(){ + var parentNode = node.parentNode, + cell = column.grid.cell(node), + i = parentNode.children.length - 1, + options = { alreadyHooked: true }, + renderedNode; + + // Remove the editor from the cell, to be reused later. + parentNode.removeChild(node); + + // Clear out the rest of the cell's contents, then re-render with new value. + while(i--){ put(parentNode.firstChild, "!"); } + Grid.appendIfNode(parentNode, column.renderCell( + column.grid.row(parentNode).data, activeValue, parentNode, + activeOptions ? lang.delegate(options, activeOptions) : options)); + + // reset state now that editor is deactivated + activeCell = activeValue = activeOptions = null; + column._editorBlurHandle.pause(); + } + + function dismissOnKey(evt){ + // Contains logic for reacting to enter/escape keypresses to save/cancel edits. + // Returns boolean specifying whether this key event should dismiss the field. + var key = evt.keyCode || evt.which; + + if(key == 27){ // escape: revert + dismiss + reset(); + activeValue = cmp._dgridLastValue; + focusNode.blur(); + }else if(key == 13 && column.dismissOnEnter !== false){ // enter: dismiss + // FIXME: Opera is "reverting" even in this case + focusNode.blur(); + } + } + + // hook up enter/esc key handling + keyHandle = on(focusNode, "keydown", dismissOnKey); + + // hook up blur handler, but don't activate until widget is activated + (column._editorBlurHandle = on.pausable(cmp, "blur", onblur)).pause(); + + return cmp; +} + +function showEditor(cmp, column, cell, value){ + // Places a shared editor into the newly-active cell in the column. + // Also called when rendering an editor in an "always-on" editor column. + + var grid = column.grid, + editor = column.editor, + isWidget = cmp.domNode; + + // for regular inputs, we can update the value before even showing it + if(!isWidget){ updateInputValue(cmp, value); } + + cell.innerHTML = ""; + put(cell, cmp.domNode || cmp); + + if(isWidget){ + // For widgets, ensure startup is called before setting value, + // to maximize compatibility with flaky widgets like dijit/form/Select. + if(!cmp._started){ cmp.startup(); } + + // Set value, but ensure it isn't processed as a user-generated change. + // (Clear flag on a timeout to wait for delayed onChange to fire first) + cmp._dgridIgnoreChange = true; + cmp.set("value", value); + setTimeout(function(){ cmp._dgridIgnoreChange = false; }, 0); + } + // track previous value for short-circuiting or in case we need to revert + cmp._dgridLastValue = value; + // if this is an editor with editOn, also update activeValue + // (activeOptions will have been updated previously) + if(activeCell){ activeValue = value; } +} + +function edit(cell) { + // summary: + // Method to be mixed into grid instances, which will show/focus the + // editor for a given grid cell. Also used by renderCell. + // cell: Object + // Cell (or something resolvable by grid.cell) to activate editor on. + // returns: + // If the cell is editable, returns a promise resolving to the editor + // input/widget when the cell editor is focused. + // If the cell is not editable, returns null. + + var row, column, cellElement, dirty, field, value, cmp, dfd; + + if(!cell.column){ cell = this.cell(cell); } + column = cell.column; + field = column.field; + cellElement = cell.element.contents || cell.element; + + if((cmp = column.editorInstance)){ // shared editor (editOn used) + if(activeCell != cellElement && + (!column.canEdit || column.canEdit(cell.row.data, value))){ + activeCell = cellElement; + row = cell.row; + dirty = this.dirty && this.dirty[row.id]; + value = (dirty && field in dirty) ? dirty[field] : + column.get ? column.get(row.data) : row.data[field]; + + showEditor(column.editorInstance, column, cellElement, value); + + // focus / blur-handler-resume logic is surrounded in a setTimeout + // to play nice with Keyboard's dgrid-cellfocusin as an editOn event + dfd = new Deferred(); + setTimeout(function(){ + // focus the newly-placed control (supported by form widgets and HTML inputs) + if(cmp.focus){ cmp.focus(); } + // resume blur handler once editor is focused + if(column._editorBlurHandle){ column._editorBlurHandle.resume(); } + dfd.resolve(cmp); + }, 0); + + return dfd.promise; + } + }else if(column.editor){ // editor but not shared; always-on + cmp = cellElement.widget || cellElement.input; + if(cmp){ + dfd = new Deferred(); + if(cmp.focus){ cmp.focus(); } + dfd.resolve(cmp); + return dfd.promise; + } + } + return null; +} + +// editor column plugin function + +return function(column, editor, editOn){ + // summary: + // Adds editing capability to a column's cells. + + var originalRenderCell = column.renderCell || Grid.defaultRenderCell, + listeners = [], + isWidget; + + // accept arguments as parameters to editor function, or from column def, + // but normalize to column def. + column.editor = editor = editor || column.editor || "text"; + column.editOn = editOn = editOn || column.editOn; + + isWidget = typeof editor != "string"; + + // warn for widgetArgs -> editorArgs; TODO: remove @ 1.0 + if(column.widgetArgs){ + kernel.deprecated("column.widgetArgs", "use column.editorArgs instead", + "dgrid 1.0"); + column.editorArgs = column.widgetArgs; + } + + aspect.after(column, "init", editOn ? function(){ + var grid = column.grid; + if(!grid.edit){ grid.edit = edit; } + + // Create one shared widget/input to be swapped into the active cell. + column.editorInstance = createSharedEditor(column, originalRenderCell); + } : function(){ + var grid = column.grid; + if(!grid.edit){ grid.edit = edit; } + + if(isWidget){ + // add advice for cleaning up widgets in this column + listeners.push(aspect.before(grid, "removeRow", function(rowElement){ + // destroy our widget during the row removal operation + var cellElement = grid.cell(rowElement, column.id).element, + widget = (cellElement.contents || cellElement).widget; + if(widget){ widget.destroyRecursive(); } + })); + } + }); + + aspect.after(column, "destroy", function(){ + arrayUtil.forEach(listeners, function(l){ l.remove(); }); + if(column._editorBlurHandle){ column._editorBlurHandle.remove(); } + + if(editOn && isWidget){ column.editorInstance.destroyRecursive(); } + }); + + column.renderCell = editOn ? function(object, value, cell, options){ + // TODO: Consider using event delegation + // (Would require using dgrid's focus events for activating on focus, + // which we already advocate in README for optimal use) + + if(!options || !options.alreadyHooked){ + // in IE<8, cell is the child of the td due to the extra padding node + on(cell.tagName == "TD" ? cell : cell.parentNode, editOn, function(){ + activeOptions = options; + column.grid.edit(this); + }); + } + + // initially render content in non-edit mode + return originalRenderCell.call(column, object, value, cell, options); + + } : function(object, value, cell, options){ + // always-on: create editor immediately upon rendering each cell + var cmp = createEditor(column); + + showEditor(cmp, column, cell, value); + + // Maintain reference for later use. + cell[isWidget ? "widget" : "input"] = cmp; + }; + + return column; +}; +}); diff --git a/widgets/dgrid/extensions/ColumnHider.js b/widgets/dgrid/extensions/ColumnHider.js new file mode 100644 index 0000000..38d55c1 --- /dev/null +++ b/widgets/dgrid/extensions/ColumnHider.js @@ -0,0 +1,242 @@ +define(["dojo/_base/declare", "dojo/has", "dojo/on", "put-selector/put", "xstyle/css!../css/extensions/ColumnHider.css"], +function(declare, has, listen, put){ +/* + * Column Hider plugin for dgrid + * Originally contributed by TRT 2011-09-28 + * + * A dGrid plugin that attaches a menu to a dgrid, along with a way of opening it, + * that will allow you to show and hide columns. A few caveats: + * + * 1. Menu placement is entirely based on CSS definitions. + * 2. If you want columns initially hidden, you must add "hidden: true" to your + * column definition. + * 3. This implementation does NOT support ColumnSet, and has not been tested + * with multi-subrow records. + * 4. Column show/hide is controlled via straight up HTML checkboxes. If you + * are looking for something more fancy, you'll probably need to use this + * definition as a template to write your own plugin. + * + */ + + var activeGrid, // references grid for which the menu is currently open + bodyListener, // references pausable event handler for body mousedown + // Need to handle old IE specially for checkbox listener and for attribute. + hasIE = has("ie"), + hasIEQuirks = hasIE && has("quirks"), + forAttr = hasIE < 8 || hasIEQuirks ? "htmlFor" : "for"; + + function getColumnIdFromCheckbox(cb, grid){ + // Given one of the checkboxes from the hider menu, + // return the id of the corresponding column. + // (e.g. gridIDhere-hider-menu-check-colIDhere -> colIDhere) + return cb.id.substr(grid.id.length + 18); + } + + return declare(null, { + // hiderMenuNode: DOMNode + // The node for the menu to show/hide columns. + hiderMenuNode: null, + + // hiderToggleNode: DOMNode + // The node for the toggler to open the menu. + hiderToggleNode: null, + + // _hiderMenuOpened: Boolean + // Records the current open/closed state of the menu. + _hiderMenuOpened: false, + + // _columnHiderRules: Object + // Hash containing handles returned from styleColumn. + _columnHiderRules: null, + + // _columnHiderCheckboxes: Object + // Hash containing checkboxes generated for menu items. + _columnHiderCheckboxes: null, + + _renderHiderMenuEntries: function(){ + // summary: + // Iterates over subRows for the sake of adding items to the + // column hider menu. + + var subRows = this.subRows, + srLength, cLength, sr, c; + + for(sr = 0, srLength = subRows.length; sr < srLength; sr++){ + for(c = 0, cLength = subRows[sr].length; c < cLength; c++){ + this._renderHiderMenuEntry(subRows[sr][c]); + } + } + }, + + _renderHiderMenuEntry: function(col){ + var id = col.id, + div, checkId, checkbox; + + if(col.hidden){ + // Hidden state is true; hide the column. + this._columnHiderRules[id] = this.styleColumn(id, "display: none"); + } + + // Allow cols to opt out of the hider (e.g. for selector column). + if(col.unhidable){ return; } + + // Create the checkbox and label for each column selector. + div = put("div.dgrid-hider-menu-row"); + checkId = this.domNode.id + "-hider-menu-check-" + id; + + this._columnHiderCheckboxes[id] = checkbox = put(div, "input#" + checkId + + ".dgrid-hider-menu-check.hider-menu-check-" + id + "[type=checkbox]"); + put(div, "label.dgrid-hider-menu-label.hider-menu-label" + id + + "[" + forAttr + "=" + checkId + "]", col.label || col.field || ""); + + put(this.hiderMenuNode, div); + + if(!col.hidden){ + // Hidden state is false; checkbox should be initially checked. + // (Need to do this after adding to DOM to avoid IE6 clobbering it.) + checkbox.checked = true; + } + }, + + renderHeader: function(){ + var grid = this, + hiderMenuNode = this.hiderMenuNode, + hiderToggleNode = this.hiderToggleNode, + id; + + this.inherited(arguments); + + if(!hiderMenuNode){ // first run + // Assume that if this plugin is used, then columns are hidable. + // Create the toggle node. + hiderToggleNode = this.hiderToggleNode = + put(this.headerScrollNode, "div.dgrid-hider-toggle.dgrid-cell-padding", "+"); + + this._listeners.push(listen(hiderToggleNode, "click", function(e){ + grid._toggleColumnHiderMenu(e); + })); + + // Create the column list, with checkboxes. + hiderMenuNode = this.hiderMenuNode = + put("div#dgrid-hider-menu-" + this.id + ".dgrid-hider-menu"); + + // Make sure our menu is initially hidden, then attach to the document. + hiderMenuNode.style.display = "none"; + put(this.domNode, hiderMenuNode); + + // Hook up delegated listener for modifications to checkboxes. + this._listeners.push(listen(hiderMenuNode, + ".dgrid-hider-menu-check:" + (hasIE < 9 || hasIEQuirks ? "click" : "change"), + function(e){ + grid._updateColumnHiddenState( + getColumnIdFromCheckbox(e.target, grid), !e.target.checked); + } + )); + this._listeners.push(listen(hiderMenuNode, "mousedown", function(e){ + // Stop click events from propagating here, so that we can simply + // track body clicks for hide without having to drill-up to check. + e.stopPropagation(); + })); + + // Hook up top-level mousedown listener if it hasn't been yet. + if(!bodyListener){ + bodyListener = listen.pausable(document, "mousedown", function(e){ + // If an event reaches this listener, the menu is open, + // but a click occurred outside, so close the dropdown. + activeGrid && activeGrid._toggleColumnHiderMenu(e); + }); + bodyListener.pause(); // pause initially; will resume when menu opens + } + }else{ // subsequent run + // Remove active rules, and clear out the menu (to be repopulated). + for (id in this._columnHiderRules){ + this._columnHiderRules[id].remove(); + } + hiderMenuNode.innerHTML = ""; + } + + this._columnHiderCheckboxes = {}; + this._columnHiderRules = {}; + + // Populate menu with checkboxes/labels based on current columns. + this._renderHiderMenuEntries(); + }, + + isColumnHidden: function(id){ + // summary: + // Convenience method to determine current hidden state of a column + return !!this._columnHiderRules[id]; + }, + + _toggleColumnHiderMenu: function(){ + var hidden = this._hiderMenuOpened, // reflects hidden state after toggle + hiderMenuNode = this.hiderMenuNode, + domNode = this.domNode; + + // Show or hide the hider menu + hiderMenuNode.style.display = (hidden ? "none" : ""); + + // Adjust height of menu + if (hidden) { + // Clear the set size + hiderMenuNode.style.height = ""; + } else { + // Adjust height of the menu if necessary + // Why 12? Based on menu default paddings and border, we need + // to adjust to be 12 pixels shorter. Given the infrequency of + // this style changing, we're assuming it will remain this + // static value of 12 for now, to avoid pulling in any sort of + // computed styles. + if (hiderMenuNode.offsetHeight > domNode.offsetHeight - 12) { + hiderMenuNode.style.height = (domNode.offsetHeight - 12) + "px"; + } + } + + // Pause or resume the listener for clicks outside the menu + bodyListener[hidden ? "pause" : "resume"](); + + // Update activeGrid appropriately + activeGrid = hidden ? null : this; + + // Toggle the instance property + this._hiderMenuOpened = !hidden; + }, + + _updateColumnHiddenState: function(id, hidden){ + // summary: + // Performs internal work for toggleColumnHiddenState; see the public + // method for more information. + + if(!hidden){ + this._columnHiderRules[id].remove(); + delete this._columnHiderRules[id]; + }else{ + this._columnHiderRules[id] = this.styleColumn(id, "display: none;"); + } + // emit event to notify of column state change + listen.emit(this.domNode, "dgrid-columnstatechange", { + column: this.columns[id], + hidden: hidden + }); + + // adjust the size of the header + this.resize(); + }, + + toggleColumnHiddenState: function(id, hidden){ + // summary: + // Shows or hides the column with the given id. + // id: String + // ID of column to show/hide. + // hide: Boolean? + // If specified, explicitly sets the hidden state of the specified + // column. If unspecified, toggles the column from the current state. + + if(typeof hidden === "undefined"){ hidden = !this._columnHiderRules[id]; } + this._updateColumnHiddenState(id, hidden); + + // Since this can be called directly, re-sync the appropriate checkbox. + this._columnHiderCheckboxes[id].checked = !hidden; + } + }); +}); diff --git a/widgets/dgrid/extensions/ColumnReorder.js b/widgets/dgrid/extensions/ColumnReorder.js new file mode 100644 index 0000000..60f3eda --- /dev/null +++ b/widgets/dgrid/extensions/ColumnReorder.js @@ -0,0 +1,177 @@ +define([ + "dojo/_base/lang", + "dojo/_base/declare", + "dojo/_base/array", + "dojo/on", + "dojo/query", + "dojo/dnd/Source", + "put-selector/put", + "xstyle/css!../css/extensions/ColumnReorder.css" +], function(lang, declare, arrayUtil, on, query, DndSource, put){ + var dndTypeRx = /-(\d+)(?:-(\d+))?$/; // used to determine subrow from dndType + + // The following 2 functions are used by onDropInternal logic for + // retrieving/modifying a given subRow. The `match` variable in each is + // expected to be the result of executing dndTypeRx on a subRow ID. + + function getMatchingSubRow(grid, match) { + var hasColumnSets = match[2], + rowOrSet = grid[hasColumnSets ? "columnSets" : "subRows"][match[1]]; + + return hasColumnSets ? rowOrSet[match[2]] : rowOrSet; + } + + function setMatchingSubRow(grid, match, subRow) { + if(match[2]){ + grid.columnSets[match[1]][match[2]] = subRow; + }else{ + grid.subRows[match[1]] = subRow; + } + } + + var ColumnDndSource = declare(DndSource, { + // summary: + // Custom dojo/dnd source extension configured specifically for + // dgrid column reordering. + + copyState: function(){ return false; }, // never copy + + checkAcceptance: function(source, nodes){ + return source == this; // self-accept only + }, + + _legalMouseDown: function(evt){ + // Overridden to prevent blocking ColumnResizer resize handles. + return evt.target.className.indexOf("dgrid-resize-handle") > -1 ? false : + this.inherited(arguments); + }, + + onDropInternal: function(nodes){ + var grid = this.grid, + match = dndTypeRx.exec(nodes[0].getAttribute("dndType")), + structureProperty = match[2] ? "columnSets" : "subRows", + oldSubRow = getMatchingSubRow(grid, match), + columns = grid.columns; + + // First, allow original DnD logic to place node in new location. + this.inherited(arguments); + + if(!match){ return; } + + // Then, iterate through the header cells in their new order, + // to populate a new row array to assign as a new sub-row to the grid. + // (Wait until the next turn to avoid errors in Opera.) + setTimeout(function(){ + var newSubRow = arrayUtil.map(nodes[0].parentNode.childNodes, function(col) { + return columns[col.columnId]; + }), + eventObject; + + setMatchingSubRow(grid, match, newSubRow); + + eventObject = { + grid: grid, + subRow: newSubRow, + column: columns[nodes[0].columnId], + bubbles: true, + cancelable: true, + // Set parentType to indicate this is the result of user interaction. + parentType: "dnd" + }; + // Set columnSets or subRows depending on which the grid is using. + eventObject[structureProperty] = grid[structureProperty]; + + // Emit a custom event which passes the new structure. + // Allow calling preventDefault() to cancel the reorder operation. + if(on.emit(grid.domNode, "dgrid-columnreorder", eventObject)){ + // Event was not canceled - force processing of modified structure. + grid.set(structureProperty, grid[structureProperty]); + }else{ + // Event was canceled - revert the structure and re-render the header + // (since the inherited logic invoked above will have shifted cells). + setMatchingSubRow(grid, match, oldSubRow); + grid.renderHeader(); + } + }, 0); + } + }); + + var ColumnReorder = declare(null, { + // summary: + // Extension allowing reordering of columns in a grid via drag'n'drop. + // Reordering of columns within the same subrow or columnset is also + // supported; between different ones is not. + + // columnDndConstructor: Function + // Constructor to call for instantiating DnD sources within the grid's + // header. + columnDndConstructor: ColumnDndSource, + + _initSubRowDnd: function(subRow, dndType){ + // summary: + // Initializes a dojo/dnd source for one subrow of a grid; + // this could be its only subrow, one of several, or a subrow within a + // columnset. + + var dndParent, c, len, col, th; + + for(c = 0, len = subRow.length; c < len; c++){ + col = subRow[c]; + if(col.reorderable === false){ continue; } + + th = col.headerNode; + if(th.tagName != "TH"){ th = th.parentNode; } // from IE < 8 padding + // Add dojoDndItem class, and a dndType unique to this subrow. + put(th, ".dojoDndItem[dndType=" + dndType + "]"); + + if(!dndParent){ dndParent = th.parentNode; } + } + + if(dndParent){ // (if dndParent wasn't set, no columns are draggable!) + this._columnDndSources.push(new this.columnDndConstructor(dndParent, { + horizontal: true, + grid: this + })); + } + }, + + renderHeader: function(){ + var dndTypePrefix = "dgrid-" + this.id + "-", + csLength, cs; + + this.inherited(arguments); + + // After header is rendered, set up a dnd source on each of its subrows. + + this._columnDndSources = []; + + if(this.columnSets){ + // Iterate columnsets->subrows->columns. + for(cs = 0, csLength = this.columnSets.length; cs < csLength; cs++){ + arrayUtil.forEach(this.columnSets[cs], function(subRow, sr){ + this._initSubRowDnd(subRow, dndTypePrefix + cs + "-" + sr); + }, this); + } + }else{ + // Iterate subrows->columns. + arrayUtil.forEach(this.subRows, function(subRow, sr){ + this._initSubRowDnd(subRow, dndTypePrefix + sr); + }, this); + } + }, + + _destroyColumns: function(){ + if(this._columnDndSources){ + // Destroy old dnd sources. + arrayUtil.forEach(this._columnDndSources, function(source){ + source.destroy(); + }); + } + + this.inherited(arguments); + } + }); + + ColumnReorder.ColumnDndSource = ColumnDndSource; + return ColumnReorder; +}); diff --git a/widgets/dgrid/extensions/ColumnResizer.js b/widgets/dgrid/extensions/ColumnResizer.js new file mode 100644 index 0000000..6e96e55 --- /dev/null +++ b/widgets/dgrid/extensions/ColumnResizer.js @@ -0,0 +1,335 @@ +define(["dojo/_base/declare", "dojo/on", "dojo/query", "dojo/_base/lang", "dojo/dom", "dojo/dom-geometry", "dojo/has", "../util/misc", "put-selector/put", "dojo/_base/html", "xstyle/css!../css/extensions/ColumnResizer.css"], +function(declare, listen, query, lang, dom, geom, has, miscUtil, put){ + +var hasPointFromNode = has("touch") && webkitConvertPointFromNodeToPage; + +function addRowSpan(table, span, startRow, column, id){ + // loop through the rows of the table and add this column's id to + // the rows' column + for(var i=1; ivalue pairs + // The keys will be the column id's; the values will be the first-row column + // that column's resizer should be associated with. + + var i = subRows.length, + l = i, + numCols = subRows[0].length, + table = new Array(i); + + // create table-like structure in an array so it can be populated + // with row-spans and col-spans + while(i--){ + table[i] = new Array(numCols); + } + + var associations = {}; + + for(i=0; i 1){ + addRowSpan(table, cell.rowSpan, i, j, cell.id); + } + + // colSpans are only applicable in the second or greater rows + // and only if the colSpan is greater than 1 + if(i>0 && cell.colSpan && cell.colSpan > 1){ + for(k=1; k 1){ + addRowSpan(table, cell.rowSpan, i, j, cell.id); + } + } + } + associations[cell.id] = subRows[0][j].id; + js++; + } + } + + return associations; +} + +return declare(null, { + resizeNode: null, + minWidth: 40, //minimum column width in px + gridWidth: null, //place holder for the grid width property + _resizedColumns: false, //flag that indicates if resizer has converted column widths to px + + resizeColumnWidth: function(colId, width){ + // Summary: + // calls grid's styleColumn function to add a style for the column + // colId: String + // column id + // width: Integer + // new width of the column + + // Keep track of old styles so we don't get a long list in the stylesheet + + // don't react to widths <= 0, e.g. for hidden columns + if(width <= 0){ return; } + + var old = this._columnStyles[colId], + x = this.styleColumn(colId, "width: " + width + "px;"); + + old && old.remove(); + + // keep a reference for future removal + this._columnStyles[colId] = x; + }, + + configStructure: function(){ + // Reset and remove column styles when a new structure is set + this._resizedColumns = false; + for(var name in this._columnStyles){ + this._columnStyles[name].remove(); + } + this._columnStyles = {}; + + this.inherited(arguments); + }, + renderHeader: function(){ + this.inherited(arguments); + + var grid = this; + grid.gridWidth = grid.headerNode.clientWidth - 1; //for some reason, total column width needs to be 1 less than this + + var assoc; + if(this.columnSets && this.columnSets.length){ + var csi = this.columnSets.length; + while(csi--){ + assoc = lang.mixin(assoc||{}, subRowAssoc(this.columnSets[csi])); + } + }else if(this.subRows && this.subRows.length > 1){ + assoc = subRowAssoc(this.subRows); + } + + var colNodes = query(".dgrid-cell", grid.headerNode), + i = colNodes.length; + while(i--){ + var colNode = colNodes[i], + id = colNode.columnId, + col = grid.columns[id], + childNodes = colNode.childNodes; + + if(!col){ continue; } + + var headerTextNode = put("div.dgrid-resize-header-container"); + colNode.contents = headerTextNode; + + // move all the children to the header text node + while(childNodes.length > 0){ + put(headerTextNode, childNodes[0]); + } + + put(colNode, headerTextNode, "div.dgrid-resize-handle.resizeNode-"+id).columnId = + assoc ? assoc[id] : id; + } + + if(!grid.mouseMoveListen){ + // establish listeners for initiating, dragging, and finishing resize + listen(grid.headerNode, + ".dgrid-resize-handle:mousedown" + + (has("touch") ? ",.dgrid-resize-handle:touchstart" : ""), + function(e){ + grid._resizeMouseDown(e, this); + grid.mouseMoveListen.resume(); + grid.mouseUpListen.resume(); + } + ); + grid.mouseMoveListen = listen.pausable(document.body, + "mousemove" + (has("touch") ? ",touchmove" : ""), + miscUtil.throttleDelayed(function(e){ grid._updateResizerPosition(e); }) + ); + grid.mouseUpListen = listen.pausable(document.body, + "mouseup" + (has("touch") ? ",touchend" : ""), + function(e){ + grid._resizeMouseUp(e); + grid.mouseMoveListen.pause(); + grid.mouseUpListen.pause(); + } + ); + // initially pause the move/up listeners until a drag happens + grid.mouseMoveListen.pause(); + grid.mouseUpListen.pause(); + } + }, // end renderHeader + + _resizeMouseDown: function(e, target){ + // Summary: + // called when mouse button is pressed on the header + // e: Object + // mousedown event object + + // preventDefault actually seems to be enough to prevent browser selection + // in all but IE < 9. setSelectable works for those. + e.preventDefault(); + dom.setSelectable(this.domNode, false); + var grid = this; + grid._startX = grid._getResizeMouseLocation(e); //position of the target + + // Grab the position of the grid within the body; will be used to place the resizer in the correct place + // Since geom.position returns an incorrect "x" value (due to mobile zoom and getBoundingClientRect()), + // webkitConvertPointFromNodeToPage and WebKitPoint will provide a more accurate point + grid._gridX = hasPointFromNode ? + webkitConvertPointFromNodeToPage(grid.bodyNode, new WebKitPoint(0, 0)).x : + geom.position(grid.bodyNode).x; + + grid._targetCell = query(".dgrid-column-" + target.columnId, grid.headerNode)[0]; + + // show resizer + if(!grid._resizer){ + grid._resizer = put(grid.domNode, "div.dgrid-column-resizer"); + } + + grid._resizer.style.display = "block"; + grid._updateResizerPosition(e); + }, + _resizeMouseUp: function(e){ + // Summary: + // called when mouse button is released + // e: Object + // mouseup event object + + this._readyToResize = false; + + //This is used to set all the column widths to a static size + if(!this._resizedColumns){ + var colNodes = query(".dgrid-cell", this.headerNode); + + if(this.columnSets && this.columnSets.length){ + colNodes = colNodes.filter(function(node){ + var idx = node.columnId.split("-"); + return idx[0] == "0"; + }); + }else if(this.subRows && this.subRows.length > 1){ + colNodes = colNodes.filter(function(node){ + return node.columnId.charAt(0) == "0"; + }); + } + + // Get a set of sizes before we start mutating, to avoid + // weird disproportionate measures if the grid has set + // column widths, but no full grid width set + var colSizes = colNodes.map(function(colNode){ + return colNode.offsetWidth; + }); + + // Set a baseline size for each column based on + // its original measure + colNodes.forEach(function(colNode, i){ + this.resizeColumnWidth(colNode.columnId, colSizes[i]); + }, this); + + this._resizedColumns = true; + } + dom.setSelectable(this.domNode, true); + + var cell = this._targetCell, + delta = this._getResizeMouseLocation(e) - this._startX, //final change in position of resizer + newWidth = cell.offsetWidth + delta, //the new width after resize + obj = this._getResizedColumnWidths(),//get current total column widths before resize + totalWidth = obj.totalWidth, + lastCol = obj.lastColId, + lastColWidth = query(".dgrid-column-"+lastCol, this.headerNode)[0].offsetWidth; + + if(cell.columnId != lastCol){ + if(totalWidth + delta < this.gridWidth) { + //need to set last column's width to auto + this.styleColumn(lastCol, "width: auto;"); + }else if(lastColWidth-delta <= this.minWidth) { + //change last col width back to px, unless it is the last column itself being resized... + this.resizeColumnWidth(lastCol, this.minWidth); + } + } + if(newWidth < this.minWidth){ + //enforce minimum widths + newWidth = this.minWidth; + } + + this.resizeColumnWidth(cell.columnId, newWidth); + this.resize(); + this._hideResizer(); + }, + _updateResizerPosition: function(e){ + // Summary: + // updates position of resizer bar as mouse moves + // e: Object + // mousemove event object + + var mousePos = this._getResizeMouseLocation(e), + delta = mousePos - this._startX, //change from where user clicked to where they drag + cell = this._targetCell, + left = mousePos - this._gridX; + if(cell.offsetWidth + delta < this.minWidth){ + left = this._startX - this._gridX - (cell.offsetWidth - this.minWidth); + } + this._resizer.style.left = left + "px"; + }, + + _hideResizer: function(){ + // Summary: + // sets resizer bar display to none + this._resizer.style.display = "none"; + }, + _getResizeMouseLocation: function(e){ + //Summary: + // returns position of mouse relative to the left edge + // e: event object + // mouse move event object + var posX = 0; + if(e.pageX){ + posX = e.pageX; + }else if(e.clientX){ + posX = e.clientX + document.body.scrollLeft + + document.documentElement.scrollLeft; + } + return posX; + }, + _getResizedColumnWidths: function (){ + //Summary: + // returns object containing new column width and column id + var totalWidth = 0, + colNodes = query(".dgrid-cell", this.headerNode); + + // For ColumnSets and subRows, only the top row of columns matters + if(this.columnSets && this.columnSets.length){ + colNodes = colNodes.filter(function(node){ + var idx = node.columnId.split("-"); + return idx[1] == "0"; + }); + }else if(this.subRows && this.subRows.length > 1){ + colNodes = colNodes.filter(function(node){ + return node.columnId.charAt(0) == "0"; + }); + } + + var i = colNodes.length; + if(!i){ return {}; } + + var lastColId = colNodes[i-1].columnId; + + while(i--){ + totalWidth += colNodes[i].offsetWidth; + } + return {totalWidth: totalWidth, lastColId: lastColId}; + } +}); +}); diff --git a/widgets/dgrid/extensions/CompoundColumns.js b/widgets/dgrid/extensions/CompoundColumns.js new file mode 100644 index 0000000..8c597b1 --- /dev/null +++ b/widgets/dgrid/extensions/CompoundColumns.js @@ -0,0 +1,85 @@ +define(["dojo/_base/lang", "dojo/_base/declare", "dgrid/Grid", "put-selector/put", "xstyle/css!../css/extensions/CompoundColumns.css"], + function(lang, declare, Grid, put){ + return declare(null, { + // summary: + // Extension allowing for specification of columns with additional + // header rows spanning multiple columns for strictly display purposes. + // Only works on `columns` arrays, not `columns` objects or `subRows` + // (nor ColumnSets). + // description: + // CompoundColumns allows nested header cell configurations, wherein the + // higher-level headers may span multiple columns and are for + // display purposes only. + // These nested header cells are configured using a special recursive + // `children` property in the column definition, where only the deepest + // children are ultimately rendered in the grid as actual columns. + // In addition, the deepest child columns may be rendered without + // individual headers by specifying `showChildHeaders: false` on the parent. + + configStructure: function(){ + // create a set of sub rows for the header row so we can do compound columns + // the first row is a special spacer row + var columns = (this.subRows && this.subRows[0]) || this.columns, + headerRows = [[]], + contentColumns = []; + // This first row is spacer row that will be made invisible (zero height) + // with CSS, but it must be rendered as the first row since that is what + // the table layout is driven by. + headerRows[0].className = "dgrid-spacer-row"; + + function processColumns(columns, level, hasLabel){ + var numColumns = 0, + noop = function(){}, + i, column, children, hasChildLabels; + + for(i in columns){ + column = columns[i]; + children = column.children; + hasChildLabels = column.children && (column.showChildHeaders !== false); + if(children){ + // it has children, recursively process the children + numColumns += (column.colSpan = processColumns(children, level + 1, hasChildLabels)); + }else{ + // it has no children, it is a normal header, add it to the content columns + contentColumns.push(column); + // add each one to the first spacer header row for proper layout of the header cells + headerRows[0].push(lang.delegate(column, {renderHeaderCell: noop})); + numColumns++; + } + if(!hasChildLabels){ + // create a header version of the column where we can define a specific rowSpan + // we define the rowSpan as a negative, the number of levels less than the total number of rows, which we don't know yet + column = lang.delegate(column, {rowSpan: -level}); + } + // add the column to the header rows at the appropriate level + if(hasLabel){ + (headerRows[level] || (headerRows[level] = [])).push(column); + } + } + return numColumns; + } + processColumns(columns, 1, true); + + var numHeaderRows = headerRows.length, + i, j, headerRow, headerColumn; + // Now go back through and increase the rowSpans of the headers to be + // total rows minus the number of levels they are at. + for(i = 0; i < numHeaderRows; i++){ + headerRow = headerRows[i]; + for(j = 0; j < headerRow.length; j++){ + headerColumn = headerRow[j]; + if(headerColumn.rowSpan < 1){ + headerColumn.rowSpan += numHeaderRows; + } + } + } + // we need to set this to be used for subRows, so we make it a single row + contentColumns = [contentColumns]; + // set our header rows so that the grid will use the alternate header row + // configuration for rendering the headers + contentColumns.headerRows = headerRows; + this.subRows = contentColumns; + this.inherited(arguments); + } + }); +}); diff --git a/widgets/dgrid/extensions/DijitRegistry.js b/widgets/dgrid/extensions/DijitRegistry.js new file mode 100644 index 0000000..ae021c0 --- /dev/null +++ b/widgets/dgrid/extensions/DijitRegistry.js @@ -0,0 +1,43 @@ +define(["dojo/_base/declare", "dijit/registry"], +function(declare, registry){ + return declare(null, { + // summary: + // A dgrid extension which will add the grid to the dijit registry, + // so that startup() will be successfully called by dijit layout widgets + // with dgrid children. + + buildRendering: function(){ + registry.add(this); + this.inherited(arguments); + // Note: for dojo 2.0 may rename widgetId to dojo._scopeName + "_widgetId" + this.domNode.setAttribute("widgetId", this.id); + }, + + startup: function(){ + if(this._started){ return; } + this.inherited(arguments); + + var widget = registry.getEnclosingWidget(this.domNode.parentNode); + // If we have a parent layout container widget, it will handle resize, + // so remove the window resize listener added by List. + if(widget && widget.isLayoutContainer){ + this._resizeHandle.remove(); + } + }, + + destroyRecursive: function() { + this.destroy(); + }, + + destroy: function(){ + this.inherited(arguments); + registry.remove(this.id); + }, + + getChildren: function(){ + // provide hollow implementation for logic which assumes its existence + // (e.g. dijit/form/_FormMixin) + return []; + } + }); +}); diff --git a/widgets/dgrid/extensions/DnD.js b/widgets/dgrid/extensions/DnD.js new file mode 100644 index 0000000..b7afade --- /dev/null +++ b/widgets/dgrid/extensions/DnD.js @@ -0,0 +1,279 @@ +define([ + "dojo/_base/declare", + "dojo/_base/lang", + "dojo/_base/array", + "dojo/_base/Deferred", + "dojo/aspect", + "dojo/on", + "dojo/topic", + "dojo/has", + "dojo/dnd/Source", + "dojo/dnd/Manager", + "dojo/_base/NodeList", + "put-selector/put", + "dojo/has!touch?../util/touch", + "dojo/has!touch?./_DnD-touch-autoscroll", + "xstyle/css!dojo/resources/dnd.css" +], function(declare, lang, arrayUtil, Deferred, aspect, on, topic, has, DnDSource, DnDManager, NodeList, put, touchUtil){ + // Requirements: + // * requires a store (sounds obvious, but not all Lists/Grids have stores...) + // * must support options.before in put calls + // (if undefined, put at end) + // * should support copy + // (copy should also support options.before as above) + + // TODOs: + // * consider sending items rather than nodes to onDropExternal/Internal + // * consider emitting store errors via OnDemandList._trackError + + var GridDnDSource = declare(DnDSource, { + grid: null, + + getObject: function(node){ + // summary: + // getObject is a method which should be defined on any source intending + // on interfacing with dgrid DnD. + + var grid = this.grid; + // Extract item id from row node id (gridID-row-*). + return grid.store.get(node.id.slice(grid.id.length + 5)); + }, + _legalMouseDown: function(evt){ + // Fix _legalMouseDown to only allow starting drag from an item + // (not from bodyNode outside contentNode). + var legal = this.inherited(arguments); + return legal && evt.target != this.grid.bodyNode; + }, + + // DnD method overrides + onDrop: function(sourceSource, nodes, copy){ + var targetSource = this, + targetRow = this._targetAnchor = this.targetAnchor, // save for Internal + grid = this.grid, + store = grid.store; + + if(!this.before && targetRow){ + // target before next node if dropped within bottom half of this node + // (unless there's no node to target at all) + targetRow = targetRow.nextSibling; + } + targetRow = targetRow && grid.row(targetRow); + + Deferred.when(targetRow && store.get(targetRow.id), function(target){ + // Note: if dropping after the last row, or into an empty grid, + // target will be undefined. Thus, it is important for store to place + // item last in order if options.before is undefined. + + // Delegate to onDropInternal or onDropExternal for rest of logic. + // These are passed the target item as an additional argument. + if(targetSource != sourceSource){ + targetSource.onDropExternal(sourceSource, nodes, copy, target); + }else{ + targetSource.onDropInternal(nodes, copy, target); + } + }); + }, + onDropInternal: function(nodes, copy, targetItem){ + var store = this.grid.store, + targetSource = this, + grid = this.grid, + anchor = targetSource._targetAnchor, + targetRow; + + if(anchor){ // (falsy if drop occurred in empty space after rows) + targetRow = this.before ? anchor.previousSibling : anchor.nextSibling; + } + + // Don't bother continuing if the drop is really not moving anything. + // (Don't need to worry about edge first/last cases since dropping + // directly on self doesn't fire onDrop, but we do have to worry about + // dropping last node into empty space beyond rendered rows.) + if(!copy && (targetRow === nodes[0] || + (!targetItem && grid.down(grid.row(nodes[0])).element == nodes[0]))){ + return; + } + + nodes.forEach(function(node){ + Deferred.when(targetSource.getObject(node), function(object){ + // For copy DnD operations, copy object, if supported by store; + // otherwise settle for put anyway. + // (put will relocate an existing item with the same id, i.e. move). + store[copy && store.copy ? "copy" : "put"](object, { + before: targetItem + }); + }); + }); + }, + onDropExternal: function(sourceSource, nodes, copy, targetItem){ + // Note: this default implementation expects that two grids do not + // share the same store. There may be more ideal implementations in the + // case of two grids using the same store (perhaps differentiated by + // query), dragging to each other. + var store = this.grid.store, + sourceGrid = sourceSource.grid; + + // TODO: bail out if sourceSource.getObject isn't defined? + nodes.forEach(function(node, i){ + Deferred.when(sourceSource.getObject(node), function(object){ + if(!copy){ + if(sourceGrid){ + // Remove original in the case of inter-grid move. + // (Also ensure dnd source is cleaned up properly) + Deferred.when(sourceGrid.store.getIdentity(object), function(id){ + !i && sourceSource.selectNone(); // deselect all, one time + sourceSource.delItem(node.id); + sourceGrid.store.remove(id); + }); + }else{ + sourceSource.deleteSelectedNodes(); + } + } + // Copy object, if supported by store; otherwise settle for put + // (put will relocate an existing item with the same id). + // Note that we use store.copy if available even for non-copy dnd: + // since this coming from another dnd source, always behave as if + // it is a new store item if possible, rather than replacing existing. + store[store.copy ? "copy" : "put"](object, { + before: targetItem + }); + }); + }); + }, + + onDndStart: function(source, nodes, copy){ + // Listen for start events to apply style change to avatar. + + this.inherited(arguments); // DnDSource.prototype.onDndStart.apply(this, arguments); + if(source == this){ + // If TouchScroll is in use, cancel any pending scroll operation. + if(this.grid.cancelTouchScroll){ this.grid.cancelTouchScroll(); } + + // Set avatar width to half the grid's width. + // Kind of a naive default, but prevents ridiculously wide avatars. + DnDManager.manager().avatar.node.style.width = + this.grid.domNode.offsetWidth / 2 + "px"; + } + }, + + onMouseDown: function(evt){ + // Cancel the drag operation on presence of more than one contact point. + // (This check will evaluate to false under non-touch circumstances.) + if(has("touch") && this.isDragging && + touchUtil.countCurrentTouches(evt, this.grid.touchNode) > 1){ + topic.publish("/dnd/cancel"); + DnDManager.manager().stopDrag(); + }else{ + this.inherited(arguments); + } + }, + + onMouseMove: function(evt){ + // If we're handling touchmove, only respond to single-contact events. + if(!has("touch") || touchUtil.countCurrentTouches(evt, this.grid.touchNode) === 1){ + this.inherited(arguments); + } + }, + + checkAcceptance: function(source, nodes){ + // Augment checkAcceptance to block drops from sources without getObject. + return source.getObject && + DnDSource.prototype.checkAcceptance.apply(this, arguments); + }, + getSelectedNodes: function(){ + // If dgrid's Selection mixin is in use, synchronize with it, using a + // map of node references (updated on dgrid-[de]select events). + + if(!this.grid.selection){ + return this.inherited(arguments); + } + var t = new NodeList(), + id; + for(id in this.grid.selection){ + t.push(this._selectedNodes[id]); + } + return t; // NodeList + } + // TODO: could potentially also implement copyState to jive with default + // onDrop* implementations (checking whether store.copy is available); + // not doing that just yet until we're sure about default impl. + }); + + var DnD = declare(null, { + // dndSourceType: String + // Specifies the type which will be set for DnD items in the grid, + // as well as what will be accepted by it by default. + dndSourceType: "dgrid-row", + + // dndParams: Object + // Object containing params to be passed to the DnD Source constructor. + dndParams: null, + + // dndConstructor: Function + // Constructor from which to instantiate the DnD Source. + // Defaults to the GridSource constructor defined/exposed by this module. + dndConstructor: GridDnDSource, + + postMixInProperties: function(){ + this.inherited(arguments); + // ensure dndParams is initialized + this.dndParams = lang.mixin({ accept: [this.dndSourceType] }, this.dndParams); + }, + + postCreate: function(){ + this.inherited(arguments); + + // Make the grid's content a DnD source/target. + this.dndSource = new (this.dndConstructor || GridDnDSource)( + this.bodyNode, + lang.mixin(this.dndParams, { + // add cross-reference to grid for potential use in inter-grid drop logic + grid: this, + dropParent: this.contentNode + }) + ); + + // If dgrid's Selection mixin is in use, set up handlers to maintain references. + var selectedNodes, selectRow, deselectRow; + + if(this.selection){ + selectedNodes = this.dndSource._selectedNodes = {}; + selectRow = function(row){ + selectedNodes[row.id] = row.element; + }; + deselectRow = function(row){ + delete selectedNodes[row.id]; + }; + + this.on("dgrid-select", function(event){ + arrayUtil.forEach(event.rows, selectRow); + }); + this.on("dgrid-deselect", function(event){ + arrayUtil.forEach(event.rows, deselectRow); + }); + } + + aspect.after(this, "destroy", function(){ + delete this.dndSource._selectedNodes; + selectedNodes = null; + this.dndSource.destroy(); + }, true); + }, + + insertRow: function(object){ + // override to add dojoDndItem class to make the rows draggable + var row = this.inherited(arguments), + type = typeof this.getObjectDndType == "function" ? + this.getObjectDndType(object) : [this.dndSourceType]; + + put(row, ".dojoDndItem"); + this.dndSource.setItem(row.id, { + data: object, + type: type instanceof Array ? type : [type] + }); + return row; + } + }); + DnD.GridSource = GridDnDSource; + + return DnD; +}); diff --git a/widgets/dgrid/extensions/Pagination.js b/widgets/dgrid/extensions/Pagination.js new file mode 100644 index 0000000..a4c7f3d --- /dev/null +++ b/widgets/dgrid/extensions/Pagination.js @@ -0,0 +1,279 @@ +define(["../_StoreMixin", "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/Deferred", + "dojo/on", "dojo/query", "dojo/string", "dojo/has", "put-selector/put", "dojo/i18n!./nls/pagination", + "dojo/_base/sniff", "xstyle/css!../css/extensions/Pagination.css"], +function(_StoreMixin, declare, lang, Deferred, on, query, string, has, put, i18n){ + return declare(_StoreMixin, { + // summary: + // An extension for adding discrete pagination to a List or Grid. + + // rowsPerPage: Number + // Number of rows (items) to show on a given page. + rowsPerPage: 10, + + // pagingTextBox: Boolean + // Indicates whether or not to show a textbox for paging. + pagingTextBox: false, + // previousNextArrows: Boolean + // Indicates whether or not to show the previous and next arrow links. + previousNextArrows: true, + // firstLastArrows: Boolean + // Indicates whether or not to show the first and last arrow links. + firstLastArrows: false, + + // pagingLinks: Number + // The number of page links to show on each side of the current page + // Set to 0 (or false) to disable page links. + pagingLinks: 2, + // pageSizeOptions: Array[Number] + // This provides options for different page sizes in a drop-down. + // If it is empty (default), no page size drop-down will be displayed. + pageSizeOptions: [], + + // i18nPagination: Object + // This object contains all of the internationalized strings as + // key/value pairs. + i18nPagination: i18n, + + showFooter: true, + _currentPage: 1, + _total: 0, + + buildRendering: function(){ + this.inherited(arguments); + + // add pagination to footer + var grid = this, + paginationNode = this.paginationNode = + put(this.footerNode, "div.dgrid-pagination"), + statusNode = this.paginationStatusNode = + put(paginationNode, "div.dgrid-status"), + pageSizeOptions = this.pageSizeOptions, + i18n = this.i18nPagination, + navigationNode, node; + + statusNode.tabIndex = 0; + + if(pageSizeOptions.length){ + var sizeSelect = put(paginationNode, 'select.dgrid-page-size'), + i; + for(i = 0; i < pageSizeOptions.length; i++){ + put(sizeSelect, 'option', pageSizeOptions[i], {value: pageSizeOptions[i]}); + } + on(sizeSelect, "change", function(){ + grid.rowsPerPage = +sizeSelect.value; + grid.gotoPage(1); + }); + } + + // initialize some content into paginationStatusNode, to ensure + // accurate results on initial resize call + statusNode.innerHTML = string.substitute(i18n.status, + { start: 1, end: 1, total: 0 }); + + navigationNode = this.paginationNavigationNode = + put(paginationNode, "div.dgrid-navigation"); + + if(this.firstLastArrows){ + // create a first-page link + node = put(navigationNode, "a[href=javascript:].dgrid-first", "«"); + node.setAttribute("aria-label", i18n.gotoFirst); + } + if(this.previousNextArrows){ + // create a previous link + node = put(navigationNode, "a[href=javascript:].dgrid-previous", "‹"); + node.setAttribute("aria-label", i18n.gotoPrev); + } + + this.paginationLinksNode = put(navigationNode, "span.dgrid-pagination-links"); + if(this.previousNextArrows){ + // create a next link + node = put(navigationNode, "a[href=javascript:].dgrid-next", "›"); + node.setAttribute("aria-label", i18n.gotoNext); + } + if(this.firstLastArrows){ + // create a last-page link + node = put(navigationNode, "a[href=javascript:].dgrid-last", "»"); + node.setAttribute("aria-label", i18n.gotoLast); + } + + on(navigationNode, "a:click", function(){ + var cls = this.className, + curr, max; + + if(grid._isLoading || cls.indexOf("dgrid-page-disabled") > -1){ + return; + } + + curr = grid._currentPage; + max = Math.ceil(grid._total / grid.rowsPerPage); + + // determine navigation target based on clicked link's class + if(cls == "dgrid-page-link"){ + grid.gotoPage(+this.innerHTML, true); // the innerHTML has the page number + }else if(cls == "dgrid-first"){ + grid.gotoPage(1); + }else if(cls == "dgrid-last"){ + grid.gotoPage(max); + }else if(cls == "dgrid-previous"){ + grid.gotoPage(curr - 1); + }else if(cls == "dgrid-next"){ + grid.gotoPage(curr + 1); + } + }); + + }, + _updateNavigation: function(focusLink){ + // summary: + // Update status and navigation controls based on total count from query + + var grid = this, + i18n = this.i18nPagination, + linksNode = this.paginationLinksNode, + currentPage = this._currentPage, + pagingLinks = this.pagingLinks, + paginationNavigationNode = this.paginationNavigationNode, + end = Math.ceil(this._total / this.rowsPerPage), + pagingTextBoxHandle = this._pagingTextBoxHandle; + + function pageLink(page){ + var link; + if(grid.pagingTextBox && page == currentPage){ + // use a paging text box if enabled instead of just a number + link = put(linksNode, 'input.dgrid-page-input[type=text][value=$]', currentPage); + link.setAttribute("aria-label", i18n.jumpPage); + grid._pagingTextBoxHandle = on(link, "change", function(){ + var value = +this.value; + if(!isNaN(value) && value > 0 && value <= end){ + grid.gotoPage(+this.value, true); + } + }); + }else{ + // normal link + link = put(linksNode, + 'a[href=javascript:]' + (page == currentPage ? '.dgrid-page-disabled' : '') + '.dgrid-page-link', + page); + link.setAttribute("aria-label", i18n.gotoPage); + } + if(page == currentPage && focusLink){ + // focus on it if we are supposed to retain the focus + link.focus(); + } + } + + if(pagingTextBoxHandle){ pagingTextBoxHandle.remove(); } + linksNode.innerHTML = ""; + query(".dgrid-first, .dgrid-previous", paginationNavigationNode).forEach(function(link){ + put(link, (currentPage == 1 ? "." : "!") + "dgrid-page-disabled"); + }); + query(".dgrid-last, .dgrid-next", paginationNavigationNode).forEach(function(link){ + put(link, (currentPage >= end ? "." : "!") + "dgrid-page-disabled"); + }); + + if(pagingLinks && end > 0){ + // always include the first page (back to the beginning) + pageLink(1); + var start = currentPage - pagingLinks; + if(start > 2) { + // visual indication of skipped page links + put(linksNode, "span.dgrid-page-skip", "..."); + }else{ + start = 2; + } + // now iterate through all the page links we should show + for(var i = start; i < Math.min(currentPage + pagingLinks + 1, end); i++){ + pageLink(i); + } + if(currentPage + pagingLinks + 1 < end){ + put(linksNode, "span.dgrid-page-skip", "..."); + } + // last link + pageLink(end); + }else if(grid.pagingTextBox){ + // The pageLink function is also used to create the paging textbox. + pageLink(currentPage); + } + }, + + refresh: function(){ + if(!this.store){ + throw new Error("Pagination requires a store to operate."); + } + this.inherited(arguments); + // reset to first page + this.gotoPage(1); + }, + + gotoPage: function(page, focusLink){ + // summary: + // Loads the given page. Note that page numbers start at 1. + var grid = this; + this._trackError(function(){ + var count = grid.rowsPerPage, + start = (page - 1) * count, + options = lang.mixin(grid.get("queryOptions"), { + start: start, + count: count + // current sort is also included by get("queryOptions") + }), + results, + contentNode = grid.contentNode, + rows = grid._rowIdToObject, + substrLen = 5 + grid.id.length, // trimmed from front of row IDs + r, loadingNode; + + // remove any currently-rendered rows + for(r in rows){ + grid.row(r.substr(substrLen)).remove(); + } + grid._rowIdToObject = {}; + contentNode.innerHTML = ""; + + loadingNode = put(contentNode, "div.dgrid-loading"); + loadingNode.innerHTML = grid.loadingMessage; + + // set flag to deactivate pagination event handlers until loaded + grid._isLoading = true; + + // Run new query and pass it into renderArray + results = grid.store.query(grid.query, options); + + return Deferred.when(grid.renderArray(results, loadingNode, options), function(){ + put(loadingNode, "!"); + delete grid._isLoading; + // Reset scroll Y-position now that new page is loaded. + grid.scrollTo({ y: 0 }); + + Deferred.when(results.total, function(total){ + if(!total){ + // If there are no results, display the no data message. + grid.noDataNode = put(grid.contentNode, "div.dgrid-no-data"); + grid.noDataNode.innerHTML = grid.noDataMessage; + } + + // Update status text based on now-current page and total. + grid.paginationStatusNode.innerHTML = string.substitute(grid.i18nPagination.status, { + start: Math.min(start + 1, total), + end: Math.min(total, start + count), + total: total + }); + grid._total = total; + grid._currentPage = page; + + // It's especially important that _updateNavigation is called only + // after renderArray is resolved as well (to prevent jumping). + grid._updateNavigation(focusLink); + }); + + if (has("ie") < 7 || (has("ie") && has("quirks"))) { + // call resize in old IE in case grid is set to height: auto + grid.resize(); + } + }, function(error){ + // enable loading again before throwing the error + delete grid._isLoading; + throw error; + }); + }); + } + }); +}); diff --git a/widgets/dgrid/extensions/_DnD-touch-autoscroll.js b/widgets/dgrid/extensions/_DnD-touch-autoscroll.js new file mode 100644 index 0000000..46ec1a4 --- /dev/null +++ b/widgets/dgrid/extensions/_DnD-touch-autoscroll.js @@ -0,0 +1,93 @@ +define([ + "dojo/aspect", + "dojo/dom-geometry", + "dojo/dnd/autoscroll", + "../List" +], function(aspect, domGeometry, autoscroll, List){ + // summary: + // This module patches the autoScrollNodes function from the + // dojo/dnd/autoscroll module, in order to behave properly for + // dgrid TouchScroll components. + + var original = autoscroll.autoScrollNodes, + instances, findEnclosing; + + // In order to properly detect autoscroll cases for dgrid+TouchScroll + // instances, we need to register instances so that we can look them up based + // on child nodes later. + + instances = {}; + aspect.after(List.prototype, "postCreate", function(r){ + var id = this.id; + // Since this code is only hooked in some cases, don't throw an error here, + // but do warn since duplicate IDs or improper destruction are likely going + // to lead to unintended consequences. + if(instances[id]){ + console.warn("dgrid instance registered with duplicate id '" + id + "'"); + } + instances[id] = this; + return r; + }); + aspect.after(List.prototype, "destroy", function(r){ + delete instances[this.id]; + return r; + }); + findEnclosing = function(node){ + var id, instance; + while(node){ + if((id = node.id) && (instance = instances[id])){ return instance; } + node = node.parentNode; + } + }; + + autoscroll.autoScrollNodes = function(evt){ + var node = evt.target, + list = findEnclosing(node), + pos, nodeX, nodeY, thresholdX, thresholdY, dx, dy, oldScroll, newScroll; + + if(list){ + // We're inside a dgrid component with TouchScroll; handle using the + // getScrollPosition and scrollTo APIs instead of scrollTop/Left. + // All logic here is designed to be functionally equivalent to the + // existing logic in the original dojo/dnd/autoscroll function. + + node = list.touchNode.parentNode; + pos = domGeometry.position(node, true); + nodeX = evt.pageX - pos.x; + nodeY = evt.pageY - pos.y; + // Use standard threshold, unless element is too small to warrant it. + thresholdX = Math.min(autoscroll.H_TRIGGER_AUTOSCROLL, pos.w / 2); + thresholdY = Math.min(autoscroll.V_TRIGGER_AUTOSCROLL, pos.h / 2); + + // Check whether event occurred beyond threshold in any given direction. + // If so, we will scroll by an amount equal to the calculated threshold. + if(nodeX < thresholdX){ + dx = -thresholdX; + }else if(nodeX > pos.w - thresholdX){ + dx = thresholdX; + } + + if(nodeY < thresholdY){ + dy = -thresholdY; + }else if(nodeY > pos.h - thresholdY){ + dy = thresholdY; + } + + // Perform any warranted scrolling. + if(dx || dy){ + oldScroll = list.getScrollPosition(); + newScroll = {}; + if(dx){ newScroll.x = oldScroll.x + dx; } + if(dy){ newScroll.y = oldScroll.y + dy; } + + list.scrollTo(newScroll); + return; + } + } + // If we're not inside a dgrid component with TouchScroll, fall back to + // the original logic to handle scroll on other elements and the document. + original.call(this, evt); + }; + + return autoscroll; +}); \ No newline at end of file diff --git a/widgets/dgrid/extensions/nls/es/pagination.js b/widgets/dgrid/extensions/nls/es/pagination.js new file mode 100644 index 0000000..445396c --- /dev/null +++ b/widgets/dgrid/extensions/nls/es/pagination.js @@ -0,0 +1,3 @@ +define({ + status: "${start} - ${end} de ${total} resultados" +}); diff --git a/widgets/dgrid/extensions/nls/ja/pagination.js b/widgets/dgrid/extensions/nls/ja/pagination.js new file mode 100644 index 0000000..9048a85 --- /dev/null +++ b/widgets/dgrid/extensions/nls/ja/pagination.js @@ -0,0 +1,3 @@ +define({ + status: "検索結果${total}件中${start}件から${end}件までを表示。" +}); diff --git a/widgets/dgrid/extensions/nls/pagination.js b/widgets/dgrid/extensions/nls/pagination.js new file mode 100644 index 0000000..0921395 --- /dev/null +++ b/widgets/dgrid/extensions/nls/pagination.js @@ -0,0 +1,13 @@ +define({ + root: { + status: "${start} - ${end} of ${total} results", + gotoFirst: "Go to first page", + gotoNext: "Go to next page", + gotoPrev: "Go to previous page", + gotoLast: "Go to last page", + gotoPage: "Go to page", + jumpPage: "Jump to page" + }, + es: true, + ja: true +}); diff --git a/widgets/dgrid/package.js b/widgets/dgrid/package.js new file mode 100644 index 0000000..aa86206 --- /dev/null +++ b/widgets/dgrid/package.js @@ -0,0 +1,23 @@ +var miniExcludes = { + "dgrid/CHANGES.md": 1, + "dgrid/LICENSE": 1, + "dgrid/README.md": 1, + "dgrid/package": 1 + }, + isTestRe = /\/test\//; + +var profile = { + resourceTags: { + test: function(filename, mid){ + return isTestRe.test(filename); + }, + + miniExclude: function(filename, mid){ + return /\/(?:test|demos)\//.test(filename) || mid in miniExcludes; + }, + + amd: function(filename, mid){ + return /\.js$/.test(filename); + } + } +}; \ No newline at end of file diff --git a/widgets/dgrid/package.json b/widgets/dgrid/package.json new file mode 100644 index 0000000..9b533d9 --- /dev/null +++ b/widgets/dgrid/package.json @@ -0,0 +1,30 @@ +{ + "name": "dgrid", + "author": "Kris Zyp", + "description": "A lightweight, mobile-ready, data-driven, modular widget designed for lists and grids", + "licenses": [ + { + "type": "AFLv2.1", + "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43" + }, + { + "type": "BSD", + "url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13" + } + ], + "repository": { + "type":"git", + "url":"http://github.com/SitePen/dgrid" + }, + "dependencies": { + "dojo": "1.7.2", + "put-selector": "0.3.1", + "xstyle": "0.0.5" + }, + "directories": { + "lib": "." + }, + "main": "./OnDemandGrid", + "icon": "http://packages.dojofoundation.org/images/dgrid.png", + "dojoBuild": "package.js" +} diff --git a/widgets/dgrid/selector.js b/widgets/dgrid/selector.js new file mode 100644 index 0000000..250c11d --- /dev/null +++ b/widgets/dgrid/selector.js @@ -0,0 +1,166 @@ +define(["dojo/_base/kernel", "dojo/_base/array", "dojo/on", "dojo/aspect", "dojo/_base/sniff", "put-selector/put"], +function(kernel, arrayUtil, on, aspect, has, put){ + return function(column, type){ + + var listeners = [], + grid, headerCheckbox; + + if(column.type){ + column.selectorType = column.type; + kernel.deprecated("columndef.type", "use columndef.selectorType instead", "dgrid 1.0"); + } + // accept type as argument to Selector function, or from column def + column.selectorType = type = type || column.selectorType || "checkbox"; + column.sortable = false; + + function changeInput(value){ + // creates a function that modifies the input on an event + return function(event){ + var rows = event.rows, + len = rows.length, + state = "false", + selection, mixed, i; + + for(i = 0; i < len; i++){ + var element = grid.cell(rows[i], column.id).element; + if(!element){ continue; } // skip if row has been entirely removed + element = (element.contents || element).input; + if(!element.disabled){ + // only change the value if it is not disabled + element.checked = value; + element.setAttribute("aria-checked", value); + } + } + if(headerCheckbox.type == "checkbox"){ + selection = grid.selection; + mixed = false; + // see if the header checkbox needs to be indeterminate + for(i in selection){ + // if there is anything in the selection, than it is indeterminate + if(selection[i] != grid.allSelected){ + mixed = true; + break; + } + } + headerCheckbox.indeterminate = mixed; + headerCheckbox.checked = grid.allSelected; + if (mixed) { + state = "mixed"; + } else if (grid.allSelected) { + state = "true"; + } + headerCheckbox.setAttribute("aria-checked", state); + } + }; + } + + function onSelect(event){ + // we would really only care about click, since other input sources, like spacebar + // trigger a click, but the click event doesn't provide access to the shift key in firefox, so + // listen for keydown's as well to get an event in firefox that we can properly retrieve + // the shiftKey property from + if(event.type == "click" || event.keyCode == 32 || (!has("opera") && event.keyCode == 13) || event.keyCode === 0){ + var row = grid.row(event), lastRow = grid._lastSelected && grid.row(grid._lastSelected); + grid._selectionTriggerEvent = event; + if(type == "radio"){ + if(!lastRow || lastRow.id != row.id){ + grid.clearSelection(); + grid.select(row, null, true); + grid._lastSelected = row.element; + } + }else{ + if(row){ + if(event.shiftKey){ + // make sure the last input always ends up checked for shift key + changeInput(true)({rows: [row]}); + }else{ + // no shift key, so no range selection + lastRow = null; + } + lastRow = event.shiftKey ? lastRow : null; + grid.select(lastRow || row, row, lastRow ? undefined : null); + grid._lastSelected = row.element; + }else{ + put(this, (grid.allSelected ? "!" : ".") + "dgrid-select-all"); + grid[grid.allSelected ? "clearSelection" : "selectAll"](); + } + } + grid._selectionTriggerEvent = null; + } + } + + function setupSelectionEvents(){ + // register one listener at the top level that receives events delegated + grid._hasSelectorInputListener = true; + listeners.push(aspect.before(grid, "_initSelectionEvents", function(){ + // listen for clicks and keydown as the triggers + this.on(".dgrid-selector:click,.dgrid-selector:keydown", onSelect); + })); + var handleSelect = grid._handleSelect; + grid._handleSelect = function(event){ + // ignore the default select handler for events that originate from the selector column + if(this.cell(event).column != column){ + handleSelect.apply(this, arguments); + } + }; + if(typeof column.disabled == "function"){ + // we override this method to have selections follow the disabled method for selectability + var originalAllowSelect = grid.allowSelect; + grid.allowSelect = function(row){ + return originalAllowSelect.call(this, row) && !column.disabled(row.data); + }; + } + // register listeners to the select and deselect events to change the input checked value + listeners.push(grid.on("dgrid-select", changeInput(true))); + listeners.push(grid.on("dgrid-deselect", changeInput(false))); + } + + var disabled = column.disabled; + var renderInput = typeof type == "function" ? type : function(value, cell, object){ + var parent = cell.parentNode; + // must set the class name on the outer cell in IE for keystrokes to be intercepted + put(parent && parent.contents ? parent : cell, ".dgrid-selector"); + var input = cell.input || (cell.input = put(cell, "input[type="+type + "]", { + tabIndex: isNaN(column.tabIndex) ? -1 : column.tabIndex, + disabled: disabled && (typeof disabled == "function" ? disabled(object) : disabled), + checked: value + })); + input.setAttribute("aria-checked", !!value); + + if(!grid._hasSelectorInputListener){ + setupSelectionEvents(); + } + + return input; + }; + + aspect.after(column, "init", function(){ + grid = column.grid; + }); + + aspect.after(column, "destroy", function(){ + arrayUtil.forEach(listeners, function(l){ l.remove(); }); + grid._hasSelectorInputListener = false; + }); + + column.renderCell = function(object, value, cell, options, header){ + var row = object && grid.row(object); + value = row && grid.selection[row.id]; + + if(header && (type == "radio" || typeof object == "string" || !grid.allowSelectAll)){ + cell.appendChild(document.createTextNode(object||"")); + if(!grid._hasSelectorInputListener){ + setupSelectionEvents(); + } + }else{ + renderInput(value, cell, object); + } + }; + column.renderHeaderCell = function(th){ + column.renderCell(column.label || {}, null, th, null, true); + headerCheckbox = th.lastChild; + }; + + return column; + }; +}); diff --git a/widgets/dgrid/test/GridFromHtml.html b/widgets/dgrid/test/GridFromHtml.html new file mode 100644 index 0000000..3b14753 --- /dev/null +++ b/widgets/dgrid/test/GridFromHtml.html @@ -0,0 +1,187 @@ + + + + GridFromHtml Tests + + + + + + + + +

                      Compare the outcome of the first group of tests with the first group in + test_legacy.html.

                      + +

                      1a: GridFromHtml with single column with formatter for _item field

                      +
                      + + + + + +
                      Step
                      +

                      1b: GridFromHtml with single column with get for order field

                      + + + + + + + + +
                      Stepnamewhat to do
                      +

                      1c: GridFromHtml with single column with renderCell function

                      +

                      (should look same as previous but with underlined first header cell + and italicized first column values)

                      + + + + + + +
                      Stepnamewhat to do
                      + +
                      + +

                      2a: same as 1a, parsed from declarative markup (using a store)

                      + + + + + + +
                      Step
                      +

                      2b: same as 1b, parsed from declarative markup (using a store)

                      + + + + + + + + +
                      Stepnamewhat to do
                      +

                      2c: same as 1c, parsed from declarative markup (using a store)

                      + + + + + + +
                      Stepnamewhat to do
                      + +
                      + +

                      3a: Testing boolean property (un)specification in GridFromHtml

                      + + + + + + + + + + +
                      No sortable attr (defaults to true)sortable="false"
                      sortable="true"
                      + + + diff --git a/widgets/dgrid/test/GridFromHtml_Editors.html b/widgets/dgrid/test/GridFromHtml_Editors.html new file mode 100644 index 0000000..c9b7676 --- /dev/null +++ b/widgets/dgrid/test/GridFromHtml_Editors.html @@ -0,0 +1,88 @@ + + + + Test declarative cell editors + + + + + + + + + + + +

                      1: GridFromHtml instantiated programmatically, + with columns using editor plugin

                      + + + + + + + + + +
                      DateText editable on dblclickStateUneditable
                      +

                      2: Fully declarative GridFromHtml, + with columns using editor plugin

                      + + + + + + + +
                      DateText editable on dblclickStateUneditable
                      + + diff --git a/widgets/dgrid/test/Grid_headerless.html b/widgets/dgrid/test/Grid_headerless.html new file mode 100644 index 0000000..77cdb46 --- /dev/null +++ b/widgets/dgrid/test/Grid_headerless.html @@ -0,0 +1,144 @@ + + + + Test Grid Common Cases + + + + + + + +

                      Headerless Grid Tests

                      +

                      A basic grid

                      +
                      +

                      A grid with a tree column

                      +
                      +
                      +

                      Same as basic grid, but with column widths exceeding grid width

                      +

                      (for testing that nodes occupy correct widths - pay attention to hover/selection style)

                      +
                      +

                      Editor Grid

                      +

                      + + diff --git a/widgets/dgrid/test/Grid_mouseevents.html b/widgets/dgrid/test/Grid_mouseevents.html new file mode 100644 index 0000000..d76e490 --- /dev/null +++ b/widgets/dgrid/test/Grid_mouseevents.html @@ -0,0 +1,104 @@ + + + + Test delegated mouse events + + + + + + + +

                      A basic grid rendered from an array, with simulated mouseover events

                      +
                      +

                      A grid with a tree column

                      +
                      + + diff --git a/widgets/dgrid/test/Grid_rendering.html b/widgets/dgrid/test/Grid_rendering.html new file mode 100644 index 0000000..26fdcdc --- /dev/null +++ b/widgets/dgrid/test/Grid_rendering.html @@ -0,0 +1,180 @@ + + + + Test renderRow and legacy functionality + + + + + + + +

                      This page tests various responsibilities of renderRow in Grid.js, + particularly those sensitive to dojox-grid-friendly properties.

                      +

                      1a: Grid with single column with formatter for _item field (legacy grid behavior)

                      +
                      +

                      1b: Grid with single column with get for order field (legacy grid behavior)

                      +
                      +

                      1c: Grid with single column with renderCell function

                      +

                      (dgrid behavior; should look same as previous but with underlined first header cell + and italicized first column values)

                      +
                      + +
                      + +

                      2a: Grid on domnode with no initial id, id specified as param (will become DOM node id)

                      +
                      +

                      2b: Grid on domnode with no initial id, dgrid / DOM node id autogenerated

                      +
                      +

                      2c: Grid with no srcNodeRef (placed inside another domNode), id specified as param (will become DOM node id)

                      +
                      +

                      2d: Grid with no srcNodeRef (placed inside another domNode), dgrid / DOM node id autogenerated

                      +
                      + + diff --git a/widgets/dgrid/test/JsonRest.html b/widgets/dgrid/test/JsonRest.html new file mode 100644 index 0000000..2982fe1 --- /dev/null +++ b/widgets/dgrid/test/JsonRest.html @@ -0,0 +1,69 @@ + + + + Test JsonRest store + + + + + + + +

                      A basic grid with JsonRest store

                      +
                      +
                      + + + + +
                      Focus first row/cell: + + + +
                      +
                      Focus header (no argument): + +
                      + + + diff --git a/widgets/dgrid/test/List_no_params.html b/widgets/dgrid/test/List_no_params.html new file mode 100644 index 0000000..7a919e2 --- /dev/null +++ b/widgets/dgrid/test/List_no_params.html @@ -0,0 +1,31 @@ + + + + Test Simple List + + + + + + + +

                      A basic list, instantiated with no constructor arguments

                      +
                      + + diff --git a/widgets/dgrid/test/OnDemand.html b/widgets/dgrid/test/OnDemand.html new file mode 100644 index 0000000..53d55e3 --- /dev/null +++ b/widgets/dgrid/test/OnDemand.html @@ -0,0 +1,336 @@ + + + + Test Grid Store Observation + + + + + + + + +

                      Simple test to show setting a new store and query to dgrid

                      +
                      + + + + +
                      + + + + + + +

                      +

                      Simple test to show editor effect on store data

                      +
                      +
                      +

                      Dirty Items

                      +
                      +
                      +
                      +

                      The following buttons switch between stores in the left grid above, + to test handling of errors on put operations.

                      +

                      Note that clicking Save will NOT emit an error event on the grid, + as direct invocations of save are expected to handle the returned promise + as they see fit. However, modifying an editor field with autoSave enabled + will cause an error event to be emitted, since in this case it is + the grid which initiated the operation.

                      + + + +
                      +

                      Simple test to show adding/deleting items

                      +

                      (With an OnDemandList alongside, also observing the changes)

                      +
                      +
                      +
                      + + +
                      + + diff --git a/widgets/dgrid/test/OnDemand_promises.html b/widgets/dgrid/test/OnDemand_promises.html new file mode 100644 index 0000000..f4f2549 --- /dev/null +++ b/widgets/dgrid/test/OnDemand_promises.html @@ -0,0 +1,66 @@ + + + + + + + + + +

                      This test page tests two types of stores that return promises from their + query methods: the dojo/store/DataStore wrapper for dojo/data stores, + and an instance of dojo/store/Memory with a promise returned from query.

                      +

                      Grid w/ ItemFileWriteStore via DataStore

                      +
                      +

                      Grid w/ dojo/store w/ async query results

                      +
                      + + + + diff --git a/widgets/dgrid/test/OnDemand_tile.html b/widgets/dgrid/test/OnDemand_tile.html new file mode 100644 index 0000000..4c49ee6 --- /dev/null +++ b/widgets/dgrid/test/OnDemand_tile.html @@ -0,0 +1,76 @@ + + + + + Test Tile Layout in OnDemandList + + + + + +

                      OnDemandLists with a tiled layout, for testing virtual paging

                      +
                      +
                      +
                      + +
                      + + diff --git a/widgets/dgrid/test/Selection.html b/widgets/dgrid/test/Selection.html new file mode 100644 index 0000000..7876abd --- /dev/null +++ b/widgets/dgrid/test/Selection.html @@ -0,0 +1,125 @@ + + + + Test Selection + + + + + + + +

                      A grid with row-level selection (and row-level navigation)

                      +

                      Use Ctrl/Cmd+A to test keyboard select-all with different selectionModes

                      +
                      Set selectionMode: + + + + +
                      +
                      +

                      A grid with cell-level selection (default selection mode of "extended")

                      +
                      +

                      A grid with cell-level selection and selection mode of "single"

                      +
                      +

                      A grid with cell-level selection and selection mode of "multiple"

                      +
                      +

                      A grid with cell-level selection and selection mode of "none" initially

                      +

                      Use Ctrl/Cmd+A to test keyboard select-all with different selectionModes

                      +
                      Set selectionMode: + + + + +
                      +
                      +

                      A grid with row-level selection and cell-level navigation

                      +
                      + + diff --git a/widgets/dgrid/test/addCssRule.html b/widgets/dgrid/test/addCssRule.html new file mode 100644 index 0000000..2ffeebf --- /dev/null +++ b/widgets/dgrid/test/addCssRule.html @@ -0,0 +1,74 @@ + + + + list.addCssRule tests + + + +

                      Click a box to remove its unique style.

                      +
                       
                      +
                      +

                      Simple stress test (open console / use Firebug Lite for IE): +

                      + + + + \ No newline at end of file diff --git a/widgets/dgrid/test/autoheight.html b/widgets/dgrid/test/autoheight.html new file mode 100644 index 0000000..c023955 --- /dev/null +++ b/widgets/dgrid/test/autoheight.html @@ -0,0 +1,61 @@ + + + + Test height:auto + + + + + + + +

                      A basic grid with height:auto

                      +
                      + dgrid + + diff --git a/widgets/dgrid/test/common_cases.html b/widgets/dgrid/test/common_cases.html new file mode 100644 index 0000000..48e8aa4 --- /dev/null +++ b/widgets/dgrid/test/common_cases.html @@ -0,0 +1,128 @@ + + + + Test Grid Common Cases + + + + + + + + +

                      A basic grid (70% of page width)

                      +
                      + +

                      A grid with tree column and then a simple list (floated left)

                      +
                      +
                      +

                      Same as basic grid, but with column widths exceeding grid width

                      +

                      (for testing that nodes occupy correct widths - pay attention to hover/selection style)

                      +
                      + + diff --git a/widgets/dgrid/test/complex_column.html b/widgets/dgrid/test/complex_column.html new file mode 100644 index 0000000..1e79855 --- /dev/null +++ b/widgets/dgrid/test/complex_column.html @@ -0,0 +1,264 @@ + + + + Test Grid Complex Columns + + + + + + + + +

                      A Grid with subrows (no ColumnSets)

                      +
                      +

                      Buttons to test resetting subRows: + + +

                      +

                      A Grid with locking columns but only one subrow

                      +
                      +

                      A Grid with complex columns including locking columns and subrows

                      +
                      +

                      Buttons to test resetting columnSets: + + + +

                      +

                      (set 3 tests 3 columnsets, to test proper cleanup of scrollers when + going back to 2)

                      +

                      Same grid, instantiated programmatically from HTML table

                      + + + + + + + + + + + + + + + +
                      Column 1Column 2Column 1Column 4
                      Column 3Column 5
                      +

                      Another complex grid instantiated from an HTML table

                      + + + + + + + + + + + + + + + + + + + + + + + +
                      rowspan=3colspan=2rowspan=2colspan=2rowspan=2
                      rowspan=2Column 3rowspan=2Column 5
                      Column 2Column 3colspan=2
                      +
                      +

                      Same as previous two tables, instantiated declaratively

                      + + + + + + + + + + + + + + + +
                      Column 1Column 2Column 1Column 4
                      Column 3Column 5
                      + + + + + + + + + + + + + + + + + + + + + + + +
                      rowspan=3colspan=2rowspan=2colSpan:2rowSpan:2
                      rowspan=2Column 3rowspan=2Column 5
                      Column 2Column 3colspan=2
                      + + diff --git a/widgets/dgrid/test/data/DeferredWrapper.js b/widgets/dgrid/test/data/DeferredWrapper.js new file mode 100644 index 0000000..c05af7a --- /dev/null +++ b/widgets/dgrid/test/data/DeferredWrapper.js @@ -0,0 +1,35 @@ +define(["dojo/_base/lang", "dojo/_base/Deferred", "dojo/store/util/QueryResults"],function(lang, Deferred, QueryResults){ + // summary: + // Creates a store that wraps the delegate store's query results and total in Deferred + // instances. If delay is set, the Deferreds will be resolved asynchronously after delay +/-50% + // milliseconds to simulate network requests that may come back out of order. + return function(store, delay){ + return lang.delegate(store, { + query: function(query, options){ + var queryResult = store.query(query, options); + + var totalDeferred = new Deferred(); + var resultsDeferred = new Deferred(); + resultsDeferred.total = totalDeferred; + + var resolveTotal = function(){ + totalDeferred.resolve(queryResult.total); + }; + var resolveResults = function(){ + resultsDeferred.resolve(queryResult); + }; + + if(delay){ + setTimeout(resolveTotal, delay * (Math.random() + 0.5)); + setTimeout(resolveResults, delay * (Math.random() + 0.5)); + } + else{ + resolveTotal(); + resolveResults(); + } + + return QueryResults(resultsDeferred); + } + }); + } +}); \ No newline at end of file diff --git a/widgets/dgrid/test/data/base.js b/widgets/dgrid/test/data/base.js new file mode 100644 index 0000000..f3c8193 --- /dev/null +++ b/widgets/dgrid/test/data/base.js @@ -0,0 +1,307 @@ +// example sample data and code +define(["dojo/_base/lang", "dojo/_base/Deferred", "dojo/store/Memory", "dojo/store/Observable", "dojo/store/util/QueryResults"], +function(lang, Deferred, Memory, Observable, QueryResults){ + // some sample data + // global var "data" + data = { + identifier: 'id', + label: 'id', + items: [] + }; + data_list = [ + { col1: "normal", col2: false, col3: "new", col4: 'But are not followed by two hexadecimal', col5: 29.91, col6: 10, col7: false }, + { col1: "important", col2: false, col3: "new", col4: 'Because a % sign always indicates', col5: 9.33, col6: -5, col7: false }, + { col1: "important", col2: false, col3: "read", col4: 'Signs can be selectively', col5: 19.34, col6: 0, col7: true }, + { col1: "note", col2: false, col3: "read", col4: 'However the reserved characters', col5: 15.63, col6: 0, col7: true }, + { col1: "normal", col2: false, col3: "replied", col4: 'It is therefore necessary', col5: 24.22, col6: 5.50, col7: true }, + { col1: "important", col2: false, col3: "replied", col4: 'To problems of corruption by', col5: 9.12, col6: -3, col7: true }, + { col1: "note", col2: false, col3: "replied", col4: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris', col5: 12.15, col6: -4, col7: false } + ]; + + var rows = 100; + for(var i=0, l=data_list.length; i 0.5, + bool2: Math.random() > 0.5, + state: testStateStore.data[Math.floor(Math.random() * 50)].abbreviation, + state2: testStateStore.data[Math.floor(Math.random() * 50)].abbreviation + }); + } + // global var testTypesStore + testTypesStore = Observable(new Memory({data: typesData})); + + + var testCountryData = [ + { id: 'AF', name:'Africa', type:'continent', population:'900 million', area: '30,221,532 sq km', + timezone: '-1 UTC to +4 UTC'}, + { id: 'EG', name:'Egypt', type:'country', parent: 'AF' }, + { id: 'Cairo', name:'Cairo', type:'city', parent: 'EG' }, + { id: 'KE', name:'Kenya', type:'country', parent: 'AF'}, + { id: 'Nairobi', name:'Nairobi', type:'city', parent: 'KE' }, + { id: 'Mombasa', name:'Mombasa', type:'city', parent: 'KE' }, + { id: 'SD', name:'Sudan', type:'country', parent: 'AF'}, + { id: 'Khartoum', name:'Khartoum', type:'city', parent: 'SD' }, + { id: 'AS', name:'Asia', type:'continent', population: '3.2 billion'}, + { id: 'CN', name:'China', type:'country', parent: 'AS' }, + { id: 'Shanghai', name:'Shanghai', type:'city', parent: 'CN' }, + { id: 'IN', name:'India', type:'country', parent: 'AS' }, + { id: 'Calcutta', name:'Calcutta', type:'city', parent: 'IN' }, + { id: 'RU', name:'Russia', type:'country', parent: 'AS' }, + { id: 'Moscow', name:'Moscow', type:'city', parent: 'RU' }, + { id: 'MN', name:'Mongolia', type:'country', parent: 'AS' }, + { id: 'UlanBator', name:'Ulan Bator', type:'city', parent: 'MN' }, + { id: 'OC', name:'Oceania', type:'continent', population:'21 million'}, + { id: 'AU', name:'Australia', type:'country', population:'21 million', parent: 'OC' }, + { id: 'Sydney', name:'Sydney', type:'city', parent: 'AU' }, + { id: 'EU', name:'Europe', type:'continent', population: '774 million' }, + { id: 'DE', name:'Germany', type:'country', parent: 'EU' }, + { id: 'Berlin', name:'Berlin', type:'city', parent: 'DE' }, + { id: 'FR', name:'France', type:'country', parent: 'EU' }, + { id: 'Paris', name:'Paris', type:'city', parent: 'FR' }, + { id: 'ES', name:'Spain', type:'country', parent: 'EU' }, + { id: 'Madrid', name:'Madrid', type:'city', parent: 'ES' }, + { id: 'IT', name:'Italy', type:'country', parent: 'EU' }, + { id: 'Rome', name:'Rome', type:'city', parent: 'IT' }, + { id: 'NA', name:'North America', type:'continent', population: '575 million'}, + { id: 'MX', name:'Mexico', type:'country', population:'108 million', area:'1,972,550 sq km', parent: 'NA' }, + { id: 'Mexico City', name:'Mexico City', type:'city', population:'19 million', timezone:'-6 UTC', parent: 'MX'}, + { id: 'Guadalajara', name:'Guadalajara', type:'city', population:'4 million', timezone:'-6 UTC', parent: 'MX' }, + { id: 'CA', name:'Canada', type:'country', population:'33 million', area:'9,984,670 sq km', parent: 'NA' }, + { id: 'Ottawa', name:'Ottawa', type:'city', population:'0.9 million', timezone:'-5 UTC', parent: 'CA'}, + { id: 'Toronto', name:'Toronto', type:'city', population:'2.5 million', timezone:'-5 UTC', parent: 'CA' }, + { id: 'US', name:'United States of America', type:'country', parent: 'NA' }, + { id: 'New York', name:'New York', type:'city', parent: 'US' }, + { id: 'SA', name:'South America', type:'continent', population: '445 million' }, + { id: 'BR', name:'Brazil', type:'country', population:'186 million', parent: 'SA' }, + { id: 'Brasilia', name:'Brasilia', type:'city', parent: 'BR' }, + { id: 'AR', name:'Argentina', type:'country', population:'40 million', parent: 'SA' }, + { id: 'BuenosAires', name:'Buenos Aires', type:'city', parent: 'AR' } + ]; + + // global var testCountryStore + testCountryStore = Observable(new Memory({ + data: testCountryData, + getChildren: function(parent, options){ + return this.query({parent: parent.id}, options); + }, + mayHaveChildren: function(parent){ + return parent.type != "city"; + }, + query: function(query, options){ + var def = new Deferred(); + var immediateResults = this.queryEngine(query, options)(this.data); + setTimeout(function(){ + def.resolve(immediateResults); + }, 200); + var results = QueryResults(def.promise); + return results; + } + })); + + // global var testSyncCountryStore + testSyncCountryStore = Observable(new Memory({ + data: testCountryData, + getChildren: function(parent, options){ + return this.query({parent: parent.id}, options); + }, + mayHaveChildren: function(parent){ + return parent.type != "city"; + } + })); + + function calculateOrder(store, object, before, orderField){ + // Calculates proper value of order for an item to be placed before another + var afterOrder, beforeOrder = 0; + if (!orderField) { orderField = "order"; } + + if(before){ + // calculate midpoint between two items' orders to fit this one + afterOrder = before[orderField]; + store.query({}, {}).forEach(function(object){ + var ord = object[orderField]; + if(ord > beforeOrder && ord < afterOrder){ + beforeOrder = ord; + } + }); + return (afterOrder + beforeOrder) / 2; + }else{ + // find maximum order and place this one after it + afterOrder = 0; + store.query({}, {}).forEach(function(object){ + var ord = object[orderField]; + if(ord > afterOrder){ afterOrder = ord; } + }); + return afterOrder + 1; + } + } + // global function createOrderedStore + createOrderedStore = function(data, options){ + // Instantiate a Memory store modified to support ordering. + return Observable(new Memory(lang.mixin({data: data, + idProperty: "name", + put: function(object, options){ + if(options && options.before){ + object.order = calculateOrder(this, object, options.before); + } + return Memory.prototype.put.call(this, object, options); + }, + // Memory's add does not need to be augmented since it calls put + copy: function(object, options){ + // summary: + // Given an item already in the store, creates a copy of it. + // (i.e., shallow-clones the item sans id, then calls add) + var k, obj = {}, id, idprop = this.idProperty, i = 0; + for (k in object){ + obj[k] = object[k]; + } + // Ensure unique ID. + // NOTE: this works for this example (where id's are strings); + // Memory should autogenerate random numeric IDs, but + // something seems to be falling through the cracks currently... + id = object[idprop]; + if(id in this.index){ + // rev id + while(this.index[id + "(" + (++i) + ")"]){} + obj[idprop] = id + "(" + i + ")"; + } + this.add(obj, options); + }, + query: function(query, options){ + options = options || {}; + options.sort = [{attribute:"order"}]; + return Memory.prototype.query.call(this, query, options); + } + }, options))); + }; + // global var testOrderedData + testOrderedData = [ + {order: 1, name:"preheat", description:"Preheat your oven to 350°F"}, + {order: 2, name:"mix dry", description:"In a medium bowl, combine flour, salt, and baking soda"}, + {order: 3, name:"mix butter", description:"In a large bowl, beat butter, then add the brown sugar and white sugar then mix"}, + {order: 4, name:"mix together", description:"Slowly add the dry ingredients from the medium bowl to the wet ingredients in the large bowl, mixing until the dry ingredients are totally combined"}, + {order: 5, name:"chocolate chips", description:"Add chocolate chips"}, + {order: 6, name:"make balls", description:"Scoop up a golf ball size amount of dough with a spoon and drop in onto a cookie sheet"}, + {order: 7, name:"bake", description:"Put the cookies in the oven and bake for about 10-14 minutes"}, + {order: 8, name:"remove", description:"Using a spatula, lift cookies off onto wax paper or a cooling rack"}, + {order: 9, name:"eat", description:"Eat and enjoy!"} + ]; + // global var testOrderedStore + testOrderedStore = createOrderedStore(testOrderedData); + return testStore; +}); diff --git a/widgets/dgrid/test/data/perf.js b/widgets/dgrid/test/data/perf.js new file mode 100644 index 0000000..59828c8 --- /dev/null +++ b/widgets/dgrid/test/data/perf.js @@ -0,0 +1,18 @@ +define(["dojo/store/Memory", "dojo/store/Observable"],function(Memory, Observable){ + var perfData = [], today = new Date().getTime(); + for(var i = 0; i < 20000; i++){ + perfData.push({ + id: i, + integer: Math.floor(Math.random() * 100), + floatNum: Math.random() * 100, + date: new Date(today * Math.random() * 2), + date2: new Date(today - Math.random() * 1000000000), + text: "A number in text " + Math.random(), + bool: Math.random() > 0.5, + bool2: Math.random() > 0.5, + price: Math.random() * 100, + today: new Date(today) + }); + } + return Observable(new Memory({data: perfData})); +}); \ No newline at end of file diff --git a/widgets/dgrid/test/data/rest.js b/widgets/dgrid/test/data/rest.js new file mode 100644 index 0000000..5a707d2 --- /dev/null +++ b/widgets/dgrid/test/data/rest.js @@ -0,0 +1,11 @@ +[ + {"order": 1, "name":"preheat", "description":"Preheat your oven to 350°F"}, + {"order": 2, "name":"mix dry", "description":"In a medium bowl, combine flour, salt, and baking soda"}, + {"order": 3, "name":"mix butter", "description":"In a large bowl, beat butter, then add the brown sugar and white sugar then mix"}, + {"order": 4, "name":"mix together", "description":"Slowly add the dry ingredients from the medium bowl to the wet ingredients in the large bowl, mixing until the dry ingredients are totally combined"}, + {"order": 5, "name":"chocolate chips", "description":"Add chocolate chips"}, + {"order": 6, "name":"make balls", "description":"Scoop up a golf ball size amount of dough with a spoon and drop in onto a cookie sheet"}, + {"order": 7, "name":"bake", "description":"Put the cookies in the oven and bake for about 10-14 minutes"}, + {"order": 8, "name":"remove", "description":"Using a spatula, lift cookies off onto wax paper or a cooling rack"}, + {"order": 9, "name":"eat", "description":"Eat and enjoy!"} +] diff --git a/widgets/dgrid/test/data/rest.php b/widgets/dgrid/test/data/rest.php new file mode 100644 index 0000000..ddf8db5 --- /dev/null +++ b/widgets/dgrid/test/data/rest.php @@ -0,0 +1,34 @@ + 120){ + $end = 120; + } +}else{ + $start = 0; + $end = 40; +} +header("Content-Range: " . "items ".$start."-".$end."/120"); +echo '['; +for ($i = $start; $i <= $end; $i++) { + if($i != $start){ + echo ','; + } + echo '{"id":"'.$id_prefix.$i.'","name":"Item '.$i.'","comment":"hello"}'; +} +echo ']'; +?> diff --git a/widgets/dgrid/test/destroy.html b/widgets/dgrid/test/destroy.html new file mode 100644 index 0000000..1db3f4a --- /dev/null +++ b/widgets/dgrid/test/destroy.html @@ -0,0 +1,95 @@ + + + + Test List/Grid Destruction + + + + + + + +

                      A basic grid and list rendered from an array

                      +
                      +
                      +
                      + + + +
                      +

                      Try resizing the browser window after destroying the grid/list, to ensure + no event handlers are left dangling that cause errors. Also check to make sure there are no widgets (count of 0) after they are destroyed.

                      + + diff --git a/widgets/dgrid/test/dijit_layout.html b/widgets/dgrid/test/dijit_layout.html new file mode 100644 index 0000000..4b54c8b --- /dev/null +++ b/widgets/dgrid/test/dijit_layout.html @@ -0,0 +1,107 @@ + + + + Test renderRow and legacy functionality + + + + + + + +
                      +
                      + + Show declarative dialog + +
                      +
                      + +
                      +
                      + +
                      + +
                      +
                      +
                      + +
                      +
                      +
                      +
                      +
                      + +
                      +
                      +
                      + + diff --git a/widgets/dgrid/test/dijit_layout_mixed.html b/widgets/dgrid/test/dijit_layout_mixed.html new file mode 100644 index 0000000..152b9ff --- /dev/null +++ b/widgets/dgrid/test/dijit_layout_mixed.html @@ -0,0 +1,82 @@ + + + + + Test dijit mixed layout + + + + + + +
                      +
                      +
                      +
                      +
                      +
                      +
                      +
                      Test dijit mixed layout - programmatic dgrid in declarative layout
                      +
                      + + diff --git a/widgets/dgrid/test/dijit_layout_programmatic.html b/widgets/dgrid/test/dijit_layout_programmatic.html new file mode 100644 index 0000000..484148b --- /dev/null +++ b/widgets/dgrid/test/dijit_layout_programmatic.html @@ -0,0 +1,141 @@ + + + + Test dijit programmatic layout + + + + + + + +
                      + + diff --git a/widgets/dgrid/test/editor.html b/widgets/dgrid/test/editor.html new file mode 100644 index 0000000..72e5a85 --- /dev/null +++ b/widgets/dgrid/test/editor.html @@ -0,0 +1,298 @@ + + + + Test Cell Editors + + + + + + + +

                      Testing Editors

                      + (Testing editors using Dijit widgets requires the dijit package to be installed) +
                      +

                      +

                      +

                      +

                      +
                      +
                      +
                      + + + + + diff --git a/widgets/dgrid/test/editor_more_widgets.html b/widgets/dgrid/test/editor_more_widgets.html new file mode 100644 index 0000000..b32b9f4 --- /dev/null +++ b/widgets/dgrid/test/editor_more_widgets.html @@ -0,0 +1,99 @@ + + + + Test Dijit Cell Editors + + + + + + + +

                      Another basic grid with more editors

                      + (Click fields to edit) +
                      + + + diff --git a/widgets/dgrid/test/editor_no_store.html b/widgets/dgrid/test/editor_no_store.html new file mode 100644 index 0000000..eca3e2f --- /dev/null +++ b/widgets/dgrid/test/editor_no_store.html @@ -0,0 +1,71 @@ + + + + Test Editors in Store-less Grid + + + + + + + +

                      A basic grid with editors

                      +

                      (This test requires dijit to be installed)

                      +

                      This test uses a grid without a store, including editors with + autoSave:true, to test editor's usability in regular + Grid (which lacks a save method / dirty attribute). +

                      + + diff --git a/widgets/dgrid/test/editor_widgets.html b/widgets/dgrid/test/editor_widgets.html new file mode 100644 index 0000000..5770df9 --- /dev/null +++ b/widgets/dgrid/test/editor_widgets.html @@ -0,0 +1,80 @@ + + + + Test Dijit Cell Editors + + + + + + + +

                      A basic grid with editors

                      + (This test requires dijit to be installed) +
                      + + + diff --git a/widgets/dgrid/test/extensions/ColumnHider.html b/widgets/dgrid/test/extensions/ColumnHider.html new file mode 100644 index 0000000..3ed6d4b --- /dev/null +++ b/widgets/dgrid/test/extensions/ColumnHider.html @@ -0,0 +1,80 @@ + + + + + Test Column Hider Extension + + + + + + +

                      A basic grid with the column hider plugin

                      +
                      +
                      Buttons to test changing column structure: + + +
                      +

                      Another grid w/ ColumnHider and ColumnResizer

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/ColumnHider_MenuHeight.html b/widgets/dgrid/test/extensions/ColumnHider_MenuHeight.html new file mode 100644 index 0000000..2e80c81 --- /dev/null +++ b/widgets/dgrid/test/extensions/ColumnHider_MenuHeight.html @@ -0,0 +1,54 @@ + + + + + Test Column Hider Extension: Large Menu + + + + + + +

                      A grid with many, many columns, to demonstrate the resizing ColumnHider menu

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/ColumnReorder.html b/widgets/dgrid/test/extensions/ColumnReorder.html new file mode 100644 index 0000000..5bfa65d --- /dev/null +++ b/widgets/dgrid/test/extensions/ColumnReorder.html @@ -0,0 +1,96 @@ + + + + + Test Simple Grid ColumnReorder + + + + + + + + +

                      A basic grid with column re-ordering

                      +
                      +
                      Buttons to test resetting columns: + + +
                      +
                      Button to test canceling dgrid-columnreorder events: + +
                      +

                      A grid with column re-ordering and resizing, and a non-reorderable Step field

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/ColumnReorder_complex.html b/widgets/dgrid/test/extensions/ColumnReorder_complex.html new file mode 100644 index 0000000..56982a9 --- /dev/null +++ b/widgets/dgrid/test/extensions/ColumnReorder_complex.html @@ -0,0 +1,79 @@ + + + + + Test Complex Grid ColumnReorder + + + + + + + + +

                      A grid with column re-ordering within simple columns or subrows

                      +
                      +
                      Buttons to test different column arrangements in the grid above: + + +
                      +

                      A grid with column re-ordering within columnsets

                      +
                      + + + + + diff --git a/widgets/dgrid/test/extensions/ColumnResizer.html b/widgets/dgrid/test/extensions/ColumnResizer.html new file mode 100644 index 0000000..a672ad1 --- /dev/null +++ b/widgets/dgrid/test/extensions/ColumnResizer.html @@ -0,0 +1,90 @@ + + + + Test Grid Column Resize + + + + + + + + +

                      A basic grid with column resizing

                      +
                      +
                      Buttons to test changing column structure: + + +
                      +

                      Another grid w/ columns whose width initially exceed the table's width

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/ColumnResizer_complex.html b/widgets/dgrid/test/extensions/ColumnResizer_complex.html new file mode 100644 index 0000000..2628e55 --- /dev/null +++ b/widgets/dgrid/test/extensions/ColumnResizer_complex.html @@ -0,0 +1,77 @@ + + + + Test Grid Complex Columns + + + + + + + + +

                      A resize Grid with complex columns including locking columns and subrows

                      +
                      +

                      A resize Grid with complex columns including subrows, no ColumnSets

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/CompoundColumns.html b/widgets/dgrid/test/extensions/CompoundColumns.html new file mode 100644 index 0000000..54a04a4 --- /dev/null +++ b/widgets/dgrid/test/extensions/CompoundColumns.html @@ -0,0 +1,66 @@ + + + + Test Grid Compound Columns + + + + + + + + +

                      A basic grid with compound columns

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/DnD.html b/widgets/dgrid/test/extensions/DnD.html new file mode 100644 index 0000000..f634d13 --- /dev/null +++ b/widgets/dgrid/test/extensions/DnD.html @@ -0,0 +1,231 @@ + + + + + Test Grid DnD + + + + + + +

                      Drag 'n' Drop

                      +
                      +
                      +
                      Another Target
                      +
                      Another Source
                      +
                      A source only accepting grues
                      +
                      +

                      Example of 2 grids w/ inter-grid DnD, sharing the same store

                      +
                      +
                      +
                      + + diff --git a/widgets/dgrid/test/extensions/DnD_touch.html b/widgets/dgrid/test/extensions/DnD_touch.html new file mode 100644 index 0000000..e7e3799 --- /dev/null +++ b/widgets/dgrid/test/extensions/DnD_touch.html @@ -0,0 +1,113 @@ + + + + + Test Grid DnD on Touch Devices + + + + + + +

                      This page contains grids with DnD set up for touch devices.

                      +
                      +

                      no handles, 2-finger scroll

                      +
                      +
                      +
                      +

                      handles, 1-finger scroll elsewhere

                      +
                      +
                      + + diff --git a/widgets/dgrid/test/extensions/DnD_tree.html b/widgets/dgrid/test/extensions/DnD_tree.html new file mode 100644 index 0000000..d06a74e --- /dev/null +++ b/widgets/dgrid/test/extensions/DnD_tree.html @@ -0,0 +1,80 @@ + + + + + DnD test dragging tree children + + + + + +

                      This page includes a simple example demonstrating ability to drag both + tree parents and children. Note that this requires Dojo 1.8 or higher.

                      +
                      +
                      + + + diff --git a/widgets/dgrid/test/extensions/Pagination.html b/widgets/dgrid/test/extensions/Pagination.html new file mode 100644 index 0000000..df491e5 --- /dev/null +++ b/widgets/dgrid/test/extensions/Pagination.html @@ -0,0 +1,158 @@ + + + + + Test Pagination Extension + + + + + + +

                      A basic grid with the Pagination extension

                      +

                      Configuration

                      +
                      +
                      +
                      +
                      +
                      + +
                      +
                      +
                      +
                      +
                      Buttons to test changing column structure: + + + + + + +
                      + +

                      A grid with declarative structure, with the Pagination extension

                      + + + + + + + + +
                      Column 1Column 2Column 3Column 4Column 5
                      + +

                      An autoheight grid with the Pagination extension, + with a rows-per-page drop-down

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/Pagination_i18n.html b/widgets/dgrid/test/extensions/Pagination_i18n.html new file mode 100644 index 0000000..e873de3 --- /dev/null +++ b/widgets/dgrid/test/extensions/Pagination_i18n.html @@ -0,0 +1,91 @@ + + + + + Test Pagination Extension + + + + + + +

                      A paginated grid with custom i18n

                      +

                      Assuming success, the status in the lower left should read "Total: 100 - Showing from 1 to 10"

                      +
                      + + diff --git a/widgets/dgrid/test/extensions/Pagination_tree.html b/widgets/dgrid/test/extensions/Pagination_tree.html new file mode 100644 index 0000000..63d89a2 --- /dev/null +++ b/widgets/dgrid/test/extensions/Pagination_tree.html @@ -0,0 +1,138 @@ + + + + + Test Pagination Extension with tree plugin + + + + + + +

                      A basic grid with the Pagination extension with tree plugin

                      +

                      Configuration

                      +
                      +
                      +
                      +
                      +
                      + +
                      +
                      +
                      +
                      +

                      An autoheight grid with the Pagination extension, + with a rows-per-page drop-down

                      +
                      + + diff --git a/widgets/dgrid/test/has-css3.html b/widgets/dgrid/test/has-css3.html new file mode 100644 index 0000000..f52a0f3 --- /dev/null +++ b/widgets/dgrid/test/has-css3.html @@ -0,0 +1,23 @@ + + + + + has-css3 tests + + + + + +

                      has-css3 tests

                      +
                      + + diff --git a/widgets/dgrid/test/mobile_grid.html b/widgets/dgrid/test/mobile_grid.html new file mode 100644 index 0000000..77f7a8c --- /dev/null +++ b/widgets/dgrid/test/mobile_grid.html @@ -0,0 +1,70 @@ + + + + + + + Mobile dgrid test + + + + + + + + + + +
                      +
                      +

                      View dgrid

                      +

                      dgrid Mobile Test

                      + +
                        +
                      • + View dgrid +
                      • +
                      • + View 2 +
                      • +
                      +
                      + +
                      +

                      View 1-2

                      +
                        +
                      • + Home +
                      • +
                      • + View 2 +
                      • +
                      +
                      +
                      +
                      + +
                      +

                      View 2

                      +
                        +
                      • + Back +
                      • +
                      +
                      + + diff --git a/widgets/dgrid/test/performance.html b/widgets/dgrid/test/performance.html new file mode 100644 index 0000000..d4c6e2a --- /dev/null +++ b/widgets/dgrid/test/performance.html @@ -0,0 +1,70 @@ + + + + Test Performance + + + + + + + +

                      A performance test designed to go head to head with DataGrid's test_datagrid_performance.html

                      +
                      + + diff --git a/widgets/dgrid/test/performance_dojox_datagrid.html b/widgets/dgrid/test/performance_dojox_datagrid.html new file mode 100644 index 0000000..4700c2f --- /dev/null +++ b/widgets/dgrid/test/performance_dojox_datagrid.html @@ -0,0 +1,75 @@ + + + + Test dojox.grid.DataGrid Performance + + + + + + + +

                      A performance test designed to go head to head with dgrid's test_performance.html

                      +
                      + + diff --git a/widgets/dgrid/test/performance_slow_network.html b/widgets/dgrid/test/performance_slow_network.html new file mode 100644 index 0000000..7a1acd2 --- /dev/null +++ b/widgets/dgrid/test/performance_slow_network.html @@ -0,0 +1,71 @@ + + + + Test performance on a (simulated) slow network + + + + + + + +

                      Tests handling of large data sets on a (simulated) slow network, with results being returned + after a delay and out of order, and the total number of rows being returned at a different + time to the result set.

                      +
                      + + diff --git a/widgets/dgrid/test/performance_widgets.html b/widgets/dgrid/test/performance_widgets.html new file mode 100644 index 0000000..f0b475e --- /dev/null +++ b/widgets/dgrid/test/performance_widgets.html @@ -0,0 +1,100 @@ + + + + Test Performance with Editors + + + + + + + +

                      Note: this test requires dijit.

                      + +

                      A performance test designed to go head to head with DataGrid's test_datagrid_performance.html

                      +
                      + + + + + diff --git a/widgets/dgrid/test/rtl.html b/widgets/dgrid/test/rtl.html new file mode 100644 index 0000000..547d9c0 --- /dev/null +++ b/widgets/dgrid/test/rtl.html @@ -0,0 +1,169 @@ + + + + Test Right-To-Left + + + + + + + +

                      A basic grid

                      +
                      + +

                      Same as basic grid, but with column widths exceeding grid width

                      +

                      (for testing horizontal scrolling)

                      +
                      + +

                      Lazy-loading tree grid, with store functionality

                      +
                      + +

                      Headerless grid

                      +
                      + +

                      Grid with editors

                      +
                      + + + diff --git a/widgets/dgrid/test/selector.html b/widgets/dgrid/test/selector.html new file mode 100644 index 0000000..f96d152 --- /dev/null +++ b/widgets/dgrid/test/selector.html @@ -0,0 +1,108 @@ + + + + Test Grid Selectors + + + + + + + +

                      A grid with checkbox selectors, with select-all enabled + (top checkbox, and Ctrl/Cmd+A)

                      +
                      + + + + +
                      +
                      +

                      A grid with radio selectors

                      +
                      + + +
                      +
                      +

                      A grid with checkbox selectors, with select-all disabled

                      +
                      + + diff --git a/widgets/dgrid/test/set_class.html b/widgets/dgrid/test/set_class.html new file mode 100644 index 0000000..f72ae82 --- /dev/null +++ b/widgets/dgrid/test/set_class.html @@ -0,0 +1,127 @@ + + + + + Test Setting Class + + + + + + +

                      Lists with initially-defined classes

                      +
                      +
                      +
                      +

                      Grids with initially-defined classes

                      +
                      +
                      +
                      +

                      Declarative grid with initially-defined class

                      + + + + + + +
                      stepnamewhat to do
                      + + diff --git a/widgets/dgrid/test/simple_grid.html b/widgets/dgrid/test/simple_grid.html new file mode 100644 index 0000000..b076e4b --- /dev/null +++ b/widgets/dgrid/test/simple_grid.html @@ -0,0 +1,76 @@ + + + + Test Simple Grid Creation + + + + + + + +

                      A basic grid rendered from an array

                      +
                      +
                      Buttons to test resetting columns: + + +
                      +
                      Buttons to test programmatic sort (on order field): + + +
                      + + diff --git a/widgets/dgrid/test/skin.html b/widgets/dgrid/test/skin.html new file mode 100644 index 0000000..d7d91f4 --- /dev/null +++ b/widgets/dgrid/test/skin.html @@ -0,0 +1,105 @@ + + + + Test Skin + + + + + + +

                      A basic grid using the skin

                      +
                      +
                      + (for testing highlight color)
                      +

                      Test more skins!

                      +

                      Loading dgrid.css before skin:

                      +

                      Loading dgrid.css after skin:

                      + + + + diff --git a/widgets/dgrid/test/sparse_sort.html b/widgets/dgrid/test/sparse_sort.html new file mode 100644 index 0000000..584a78e --- /dev/null +++ b/widgets/dgrid/test/sparse_sort.html @@ -0,0 +1,62 @@ + + + + Test sorting of sparsely-populated fields + + + + + + + +

                      This page allows testing sorting of columns that are sparsely populated or + completely unpopulated.

                      +

                      Grid from array

                      +
                      +

                      Grid from store

                      +
                      + + diff --git a/widgets/dgrid/test/tree.html b/widgets/dgrid/test/tree.html new file mode 100644 index 0000000..2b978f6 --- /dev/null +++ b/widgets/dgrid/test/tree.html @@ -0,0 +1,170 @@ + + + + Test Tree Grid + + + + + + + +

                      Lazy-loading tree grid, with store functionality

                      +
                      + + + + + + + + + +

                      Tree grid with checkbox selector to test select-all on children

                      +
                      + (to test column.init/destroy) +

                      Tree grid with subRows

                      +
                      +

                      Tree grid with columnSets

                      +
                      + + diff --git a/widgets/dgrid/test/tree_editor.html b/widgets/dgrid/test/tree_editor.html new file mode 100644 index 0000000..cac47e2 --- /dev/null +++ b/widgets/dgrid/test/tree_editor.html @@ -0,0 +1,56 @@ + + + + Test Tree Grid + + + + + + + +

                      Lazy-loading tree grid, with editable tree cells (tree→editor)

                      +
                      + + + + diff --git a/widgets/dgrid/test/tree_expand.html b/widgets/dgrid/test/tree_expand.html new file mode 100644 index 0000000..366cc25 --- /dev/null +++ b/widgets/dgrid/test/tree_expand.html @@ -0,0 +1,99 @@ + + + + Test Tree Grid + + + + + + + + +

                      This page tests auto-expansion of tree grids.

                      +

                      NOTE: it is strongly recommended that you only + automatically expand for synchronous stores (or at least, stores which aren't + going back to the server for each child request); otherwise you are likely + to put a significant strain on your server, and the performance of the client + will suffer as a result. +

                      + +
                      +

                      Auto-expanding tree grid, asynchronous store

                      +
                      + + +
                      + +
                      +

                      Auto-expanding tree grid, synchronous store

                      +
                      + + +
                      + + diff --git a/widgets/dgrid/test/tree_expand_hidden.html b/widgets/dgrid/test/tree_expand_hidden.html new file mode 100644 index 0000000..236fd6a --- /dev/null +++ b/widgets/dgrid/test/tree_expand_hidden.html @@ -0,0 +1,60 @@ + + + + Test TreeGrid + + + + + + + +

                      Tree grid rendered in a hideable node (to test expansion while hidden)

                      + + +
                      +
                      +
                      + + diff --git a/widgets/dgrid/test/tundra_custom.html b/widgets/dgrid/test/tundra_custom.html new file mode 100644 index 0000000..18bd552 --- /dev/null +++ b/widgets/dgrid/test/tundra_custom.html @@ -0,0 +1,66 @@ + + + + Test Tundra Theme + + + + + + + +

                      A basic grid customized based on the tundra theme

                      +
                      + + diff --git a/widgets/dgrid/test/widths.html b/widgets/dgrid/test/widths.html new file mode 100644 index 0000000..5cf3340 --- /dev/null +++ b/widgets/dgrid/test/widths.html @@ -0,0 +1,129 @@ + + + + Test List/Grid Width Scenarios + + + + + + + +

                      Simple lists

                      +

                      Testing float + no width, width > row contents, width < row contents

                      +
                      +
                      +
                      +

                      Grid with no column widths specified

                      +
                      +

                      Grid with column widths exceeding grid width

                      +

                      (for testing that nodes occupy correct widths - + pay attention to hover/selection style and scroll right)

                      +

                      +
                      +

                      Grid like previous, but with headers initially hidden

                      +

                      +
                      + + diff --git a/widgets/dgrid/tree.js b/widgets/dgrid/tree.js new file mode 100644 index 0000000..ab58081 --- /dev/null +++ b/widgets/dgrid/tree.js @@ -0,0 +1,289 @@ +define(["dojo/_base/declare", "dojo/_base/array", "dojo/_base/Deferred", "dojo/has", "dojo/query", "dojo/on", "dojo/aspect", "./Grid", "dojo/has!touch?./util/touch", "put-selector/put"], +function(declare, arrayUtil, Deferred, has, querySelector, on, aspect, Grid, touchUtil, put){ + +return function(column){ + // summary: + // Adds tree navigation capability to a column. + + var originalRenderCell = column.renderCell || Grid.defaultRenderCell; + + var currentLevel, // tracks last rendered item level (for aspected insertRow) + clicked; // tracks row that was clicked (for expand dblclick event handling) + + column.shouldExpand = column.shouldExpand || function(row, level, previouslyExpanded){ + // summary: + // Function called after each row is inserted to determine whether + // expand(rowElement, true) should be automatically called. + // The default implementation re-expands any rows that were expanded + // the last time they were rendered (if applicable). + + return previouslyExpanded; + }; + + aspect.after(column, "init", function(){ + var grid = column.grid, + colSelector = ".dgrid-content .dgrid-column-" + column.id, + transitionEventSupported, + listeners = [], // to be removed when this column is "destroyed" + tr, query; + + if(!grid.store){ + throw new Error("dgrid tree column plugin requires a store to operate."); + } + + if (!column.renderExpando){ + // If no renderExpando function exists, create one with default logic. + column.renderExpando = function(level, hasChildren, expanded){ + var dir = this.grid.isRTL ? "right" : "left", + cls = ".dgrid-expando-icon", + node; + if(hasChildren){ + cls += ".ui-icon.ui-icon-triangle-1-" + (expanded ? "se" : "e"); + } + node = put("div" + cls + "[style=margin-" + dir + ": " + + (level * (this.indentWidth || 9)) + "px; float: " + dir + "]"); + node.innerHTML = " "; // for opera to space things properly + return node; + }; + } + + // Set up the event listener once and use event delegation for better memory use. + listeners.push(grid.on( + column.expandOn || ".dgrid-expando-icon:click," + colSelector + ":dblclick," + colSelector + ":keydown", + function(event){ + var row = grid.row(event); + if((!grid.store.mayHaveChildren || grid.store.mayHaveChildren(row.data)) && + (event.type != "keydown" || event.keyCode == 32) && + !(event.type == "dblclick" && clicked && clicked.count > 1 && + row.id == clicked.id && event.target.className.indexOf("dgrid-expando-icon") > -1)){ + grid.expand(row); + } + + // If the expando icon was clicked, update clicked object to prevent + // potential over-triggering on dblclick (all tested browsers but IE < 9). + if(event.target.className.indexOf("dgrid-expando-icon") > -1){ + if(clicked && clicked.id == grid.row(event).id){ + clicked.count++; + }else{ + clicked = { + id: grid.row(event).id, + count: 1 + }; + } + } + }) + ); + + if(has("touch")){ + // Also listen on double-taps of the cell. + listeners.push(grid.on(touchUtil.selector(colSelector, touchUtil.dbltap), + function(){ grid.expand(this); })); + } + + // Set up hash to store IDs of expanded rows + if(!grid._expanded){ grid._expanded = {}; } + + listeners.push(aspect.after(grid, "insertRow", function(rowElement){ + // Auto-expand (shouldExpand) considerations + var row = this.row(rowElement), + expanded = column.shouldExpand(row, currentLevel, this._expanded[row.id]); + + if(expanded){ this.expand(rowElement, true, true); } + return rowElement; // pass return value through + })); + + listeners.push(aspect.before(grid, "removeRow", function(rowElement, justCleanup){ + var connected = rowElement.connected; + if(connected){ + // if it has a connected expando node, we process the children + querySelector(">.dgrid-row", connected).forEach(function(element){ + grid.removeRow(element, true); + }); + // now remove the connected container node + if(!justCleanup){ + put(connected, "!"); + } + } + })); + + if(column.collapseOnRefresh){ + // Clear out the _expanded hash on each call to cleanup + // (which generally coincides with refreshes, as well as destroy). + listeners.push(aspect.after(grid, "cleanup", function(){ + this._expanded = {}; + })); + } + + grid._calcRowHeight = function(rowElement){ + // we override this method so we can provide row height measurements that + // include the children of a row + var connected = rowElement.connected; + // if connected, need to consider this in the total row height + return rowElement.offsetHeight + (connected ? connected.offsetHeight : 0); + }; + + grid.expand = function(target, expand, noTransition){ + // summary: + // Expands the row corresponding to the given target. + // target: Object + // Row object (or something resolvable to one) to expand/collapse. + // expand: Boolean? + // If specified, designates whether to expand or collapse the row; + // if unspecified, toggles the current state. + + var row = target.element ? target : grid.row(target); + + target = row.element; + target = target.className.indexOf("dgrid-expando-icon") > -1 ? target : + querySelector(".dgrid-expando-icon", target)[0]; + + if(target && target.mayHaveChildren){ + // toggle or set expand/collapsed state based on optional 2nd argument + var expanded = expand === undefined ? !this._expanded[row.id] : expand; + + // update the expando display + target.className = "dgrid-expando-icon ui-icon ui-icon-triangle-1-" + (expanded ? "se" : "e"); + var preloadNode = target.preloadNode, + rowElement = row.element, + container, options; + + if(!preloadNode){ + // if the children have not been created, create a container, a preload node and do the + // query for the children + container = rowElement.connected = put('div.dgrid-tree-container');//put(rowElement, '+... + preloadNode = target.preloadNode = put(rowElement, '+', container, 'div.dgrid-preload'); + var query = function(options){ + return grid.store.getChildren(row.data, options); + }; + query.level = target.level; + if(column.allowDuplicates){ + // If allowDuplicates is specified, include parentId in options + // in order to facilitate unique IDs for each occurrence of the + // same item under multiple different parents. + options = { parentId: row.id }; + } + Deferred.when( + grid.renderQuery ? + grid._trackError(function(){ + return grid.renderQuery(query, preloadNode, options); + }) : + grid.renderArray(query(options), preloadNode, {query: query}), + function(){ + // Expand once results are retrieved, if the row is still expanded. + if(grid._expanded[row.id]){ + var scrollHeight = container.scrollHeight; + container.style.height = scrollHeight ? scrollHeight + "px" : "auto"; + } + } + ); + var transitionend = function(event){ + // NOTE: this == container + var height = this.style.height; + if(height){ + // after expansion, ensure display is correct, and we set it to none for hidden containers to improve performance + this.style.display = height == "0px" ? "none" : "block"; + } + if(event){ + // now we need to reset the height to be auto, so future height changes + // (from children expansions, for example), will expand to the right height + // However setting the height to auto or "" will cause an animation to zero height for some + // reason, so we set the transition to be zero duration for the time being + put(this, ".dgrid-tree-resetting"); + setTimeout(function(){ + // now we can turn off the zero duration transition after we have let it render + put(container, "!dgrid-tree-resetting"); + }); + // this was triggered as a real event, we remember that so we don't fire the setTimeout's in the future + transitionEventSupported = true; + }else if(!transitionEventSupported){ + // if this was not triggered as a real event, we remember that so we shortcut animations + transitionEventSupported = false; + } + // now set the height to auto + this.style.height = ""; + }; + on(container, "transitionend,webkitTransitionEnd,oTransitionEnd,MSTransitionEnd", transitionend); + if(!transitionEventSupported){ + setTimeout(function(){ + transitionend.call(container); + }, 600); + } + } + + // Show or hide all the children. + + container = rowElement.connected; + container.hidden = !expanded; + var containerStyle = container.style, + scrollHeight; + + // make sure it is visible so we can measure it + if(transitionEventSupported === false || noTransition){ + containerStyle.display = expanded ? "block" : "none"; + containerStyle.height = ""; + }else{ + if(expanded){ + containerStyle.display = "block"; + scrollHeight = container.scrollHeight; + containerStyle.height = "0px"; + } + else{ + // if it will be hidden we need to be able to give a full height + // without animating it, so it has the right starting point to animate to zero + put(container, ".dgrid-tree-resetting"); + containerStyle.height = container.scrollHeight + "px"; + } + // Perform a transition for the expand or collapse. + setTimeout(function(){ + put(container, "!dgrid-tree-resetting"); + containerStyle.height = + expanded ? (scrollHeight ? scrollHeight + "px" : "auto") : "0px"; + }); + } + + // Update _expanded map. + if(expanded){ + this._expanded[row.id] = true; + }else{ + delete this._expanded[row.id]; + } + } + }; // end function grid.expand + + // Set up a destroy function on column to tear down the listeners/aspects + // established above if the grid's columns are redefined later. + aspect.after(column, "destroy", function(){ + arrayUtil.forEach(listeners, function(l){ l.remove(); }); + // Delete methods we added/overrode on the instance. + delete grid.expand; + delete grid._calcRowHeight; + }); + }); + + column.renderCell = function(object, value, td, options){ + // summary: + // Renders a cell that can be expanded, creating more rows + + var grid = column.grid, + level = Number(options && options.query && options.query.level) + 1, + mayHaveChildren = !grid.store.mayHaveChildren || grid.store.mayHaveChildren(object), + parentId = options.parentId, + expando, node; + + level = currentLevel = isNaN(level) ? 0 : level; + expando = column.renderExpando(level, mayHaveChildren, + grid._expanded[(parentId ? parentId + "-" : "") + grid.store.getIdentity(object)]); + expando.level = level; + expando.mayHaveChildren = mayHaveChildren; + + node = originalRenderCell.call(column, object, value, td, options); + if(node && node.nodeType){ + put(td, expando); + put(td, node); + }else{ + td.insertBefore(expando, td.firstChild); + } + }; + return column; +}; +}); \ No newline at end of file diff --git a/widgets/dgrid/util/has-css3.js b/widgets/dgrid/util/has-css3.js new file mode 100644 index 0000000..a1b99d9 --- /dev/null +++ b/widgets/dgrid/util/has-css3.js @@ -0,0 +1,78 @@ +define(["dojo/has", "xstyle/css!../css/has-transforms3d.css"], +function(has){ + // This module defines feature tests for CSS3 features such as transitions. + // The css-transitions, css-transforms, and css-transforms3d has-features + // can report either boolean or string: + // * false indicates no support + // * true indicates prefix-less support + // * string indicates the vendor prefix under which the feature is supported + + var cssPrefixes = ["ms", "O", "Moz", "Webkit"]; + + has.add("css-transitions", function(global, doc, element){ + var style = element.style, + i; + + if(style.transitionProperty !== undefined){ // standard, no vendor prefix + return true; + } + for (i = cssPrefixes.length; i--;) { + if (style[cssPrefixes[i] + "TransitionProperty"] !== undefined) { + return cssPrefixes[i]; // vendor-specific css property prefix + } + } + + return false; // otherwise, not supported + }); + + has.add("transitionend", function(){ + // Infer transitionend event name based on CSS transitions has-feature. + var tpfx = has("css-transitions"); + if(!tpfx){ return false; } + if(tpfx === true){ return "transitionend"; } + return { + ms: "MSTransitionEnd", + O: "oTransitionEnd", + Moz: "transitionend", + Webkit: "webkitTransitionEnd" + }[tpfx]; + }); + + has.add("css-transforms", function(global, doc, element){ + var style = element.style, i; + if (style.transformProperty !== undefined) { + return true; // standard, no vendor prefix + } + for (i = cssPrefixes.length; i--;) { + if (style[cssPrefixes[i] + "Transform"] !== undefined) { + return cssPrefixes[i]; + } + } + + return false; // otherwise, not supported + }); + + has.add("css-transforms3d", function(global, doc, element){ + var left, prefix; + + // Apply csstransforms3d class to test transform-3d media queries. + element.className = "has-csstransforms3d"; + // Add to body to allow measurement. + document.body.appendChild(element); + left = element.offsetLeft; + + if (left === 9) { + return true; // standard, no prefix + } else if (left > 9){ + // Matched one of the vendor prefixes; offset indicates which. + prefix = cssPrefixes[left - 10]; + return prefix || false; + } + document.body.removeChild(element); + element.className = ""; + + return false; // otherwise, not supported + }); + + return has; +}); diff --git a/widgets/dgrid/util/misc.js b/widgets/dgrid/util/misc.js new file mode 100644 index 0000000..354506e --- /dev/null +++ b/widgets/dgrid/util/misc.js @@ -0,0 +1,54 @@ +define([], function(){ + // This module defines miscellaneous utility methods. + + var util = { + defaultDelay: 15, + throttle: function(cb, context, delay){ + // summary: + // Returns a function which calls the given callback at most once per + // delay milliseconds. (Inspired by plugd) + var ran = false; + delay = delay || util.defaultDelay; + return function(){ + if(ran){ return; } + ran = true; + cb.apply(context, arguments); + setTimeout(function(){ ran = false; }, delay); + } + }, + throttleDelayed: function(cb, context, delay){ + // summary: + // Like throttle, except that the callback runs after the delay, + // rather than before it. + var ran = false; + delay = delay || util.defaultDelay; + return function(){ + if(ran){ return; } + ran = true; + var a = arguments; + setTimeout(function(){ + ran = false; + cb.apply(context, a); + }, delay); + } + }, + debounce: function(cb, context, delay){ + // summary: + // Returns a function which calls the given callback only after a + // certain time has passed without successive calls. (Inspired by plugd) + var timer; + delay = delay || util.defaultDelay; + return function(){ + if(timer){ + clearTimeout(timer); + timer = null; + } + var a = arguments; + timer = setTimeout(function(){ + cb.apply(context, a); + }, delay); + } + } + }; + return util; +}); \ No newline at end of file diff --git a/widgets/dgrid/util/mouse.js b/widgets/dgrid/util/mouse.js new file mode 100644 index 0000000..adfd76b --- /dev/null +++ b/widgets/dgrid/util/mouse.js @@ -0,0 +1,35 @@ +define(["dojo/on", "dojo/dom", "dojo/query"], function(on, dom){ + function handler(selector, type){ + // summary: + // Creates a handler function usable as a simulated event to dojo/on, + // which fires only if the mouse is moving into or out of the node of + // interest indicated by the selector. + // This is similar, but not identical, to what dojo/mouse does. + // selector: String + // Query selector for event delegation. + // type: String + // Event to delegate on (mouseover or mouseout). + + return function(node, listener){ + return on(node, selector + ":" + type, function(evt){ + if(!dom.isDescendant(evt.relatedTarget, this)){ + return listener.call(this, evt); + } + }); + }; + } + + return { + // Provide enter/leave events for rows, cells, and header cells. + // (Header row is trivial since there's only one.) + enterRow: handler(".dgrid-content .dgrid-row", "mouseover"), + enterCell: handler(".dgrid-content .dgrid-cell", "mouseover"), + enterHeaderCell: handler(".dgrid-header .dgrid-cell", "mouseover"), + leaveRow: handler(".dgrid-content .dgrid-row", "mouseout"), + leaveCell: handler(".dgrid-content .dgrid-cell", "mouseout"), + leaveHeaderCell: handler(".dgrid-header .dgrid-cell", "mouseout"), + + // Also expose the handler function, so people can do what they want. + createDelegatingHandler: handler + }; +}); \ No newline at end of file diff --git a/widgets/dgrid/util/touch.js b/widgets/dgrid/util/touch.js new file mode 100644 index 0000000..04aed78 --- /dev/null +++ b/widgets/dgrid/util/touch.js @@ -0,0 +1,123 @@ +define(["dojo/on", "dojo/query"], +function(on, query){ + // This module exposes useful functions for working with touch devices. + + var util = { + // Overridable defaults related to extension events defined below. + tapRadius: 10, + dbltapTime: 250, + + selector: function(selector, eventType, children){ + // summary: + // Reimplementation of on.selector, taking an iOS quirk into account + return function(target, listener){ + var bubble = eventType.bubble; + if(bubble){ + // the event type doesn't naturally bubble, but has a bubbling form, use that + eventType = bubble; + }else if(children !== false){ + // for normal bubbling events we default to allowing children of the selector + children = true; + } + return on(target, eventType, function(event){ + var eventTarget = event.target; + + // iOS tends to report the text node an event was fired on, rather than + // the top-level element; this may end up causing errors in selector engines + if(eventTarget.nodeType == 3){ eventTarget = eventTarget.parentNode; } + + // there is a selector, so make sure it matches + while(!query.matches(eventTarget, selector, target)){ + if(eventTarget == target || !children || !(eventTarget = eventTarget.parentNode)){ // intentional assignment + return; + } + } + return listener.call(eventTarget, event); + }); + }; + }, + + countCurrentTouches: function(evt, node){ + // summary: + // Given a touch event and a DOM node, counts how many current touches + // presently lie within that node. Useful in cases where an accurate + // count is needed but tracking changedTouches won't suffice because + // other handlers stop events from bubbling high enough. + + var i, numTouches, touch; + for(i = 0, numTouches = 0; (touch = evt.touches[i]); ++i){ + if(node.contains(touch.target)){ + ++numTouches; + } + } + return numTouches; + } + }; + + function handleTapStart(target, listener, evt, prevent){ + // Common function for handling tap detection. + // The passed listener will only be fired when and if a touchend is fired + // which confirms the overall gesture resembled a tap. + + if(evt.targetTouches.length > 1){ return; } // ignore multitouch + + var start = evt.changedTouches[0], + startX = start.screenX, + startY = start.screenY; + + prevent && evt.preventDefault(); + + var endListener = on(target, "touchend", function(evt){ + var end = evt.changedTouches[0]; + if(!evt.targetTouches.length){ + // only call listener if this really seems like a tap + if(Math.abs(end.screenX - startX) < util.tapRadius && + Math.abs(end.screenY - startY) < util.tapRadius){ + prevent && evt.preventDefault(); + listener.call(this, evt); + } + endListener.remove(); + } + }); + } + + function tap(target, listener){ + // Function usable by dojo/on as a synthetic tap event. + return on(target, "touchstart", function(evt){ + handleTapStart(target, listener, evt); + }); + } + + function dbltap(target, listener){ + // Function usable by dojo/on as a synthetic double-tap event. + var first, timeout; + + return on(target, "touchstart", function(evt){ + if(!first){ + // first potential tap: detect as usual, but with specific logic + handleTapStart(target, function(evt){ + first = evt.changedTouches[0]; + timeout = setTimeout(function(){ first = timeout = null; }, util.dbltapTime); + }, evt); + }else{ + handleTapStart(target, function(evt){ + // bail out if first was cleared between 2nd touchstart and touchend + if(!first){ return; } + var second = evt.changedTouches[0]; + // only call listener if both taps occurred near the same place + if(Math.abs(second.screenX - first.screenX) < util.tapRadius && + Math.abs(second.screenY - first.screenY) < util.tapRadius){ + timeout && clearTimeout(timeout); + first = timeout = null; + listener.call(this, evt); + } + }, evt, true); + } + }); + } + + util.tap = tap; + util.dbltap = dbltap; + + return util; +}); \ No newline at end of file diff --git a/widgets/dijit.js b/widgets/dijit.js new file mode 100644 index 0000000..6820045 --- /dev/null +++ b/widgets/dijit.js @@ -0,0 +1 @@ +define(["dijit/main"], function(main){return main;}); \ No newline at end of file diff --git a/widgets/dojo.js b/widgets/dojo.js new file mode 100644 index 0000000..92cfd19 --- /dev/null +++ b/widgets/dojo.js @@ -0,0 +1 @@ +define(["dojo/main"], function(main){return main;}); \ No newline at end of file diff --git a/widgets/package.json b/widgets/package.json new file mode 100644 index 0000000..febd0f7 --- /dev/null +++ b/widgets/package.json @@ -0,0 +1,16 @@ +/* +Product: OpenXT +Project: Synchronizer Administration Web UI +Copyright© Citrix 2012 +*/ + +{ + "directories": {}, + "dependencies": { + "dgrid": "0.3.3", + "put-selector": "0.3.2", + "xstyle": "0.0.5", + "dojo": "1.8.1", + "dijit": "1.8.1" + } +} \ No newline at end of file diff --git a/widgets/put-selector.js b/widgets/put-selector.js new file mode 100644 index 0000000..0f4b480 --- /dev/null +++ b/widgets/put-selector.js @@ -0,0 +1 @@ +define(["put-selector/./put"], function(main){return main;}); \ No newline at end of file diff --git a/widgets/put-selector/README.md b/widgets/put-selector/README.md new file mode 100644 index 0000000..d2d5bba --- /dev/null +++ b/widgets/put-selector/README.md @@ -0,0 +1,301 @@ +This put-selector/put module/package provides a high-performance, lightweight +(~1.5KB minified, ~0.7KB gzipped with other code) function for creating +and manipulating DOM elements with succinct, elegant, familiar CSS selector-based +syntax across all browsers and platforms (including HTML generation on NodeJS). +The single function from the module creates or updates DOM elements by providing +a series of arguments that can include reference elements, selector strings, properties, +and text content. The put() function utilizes the proven techniques for optimal performance +on modern browsers to ensure maximum speed. + +Installation/Usage +---------------- + +The put.js module can be simply downloaded and used a plain script (creates a global +put() function), as an AMD module (exports the put() function), or as a NodeJS (or any +server side JS environment) module. +It can also be installed with CPM: + + cpm install put-selector + +and then reference the "put-selector" module as a dependency. +or installed for Node with NPM: + + npm install put-selector + +and then: + + put = require("put-selector"); + +Creating Elements +---------------- + +Type selector syntax (no prefix) can be used to indicate the type of element to be created. For example: + + newDiv = put("div"); + +will create a new <div> element. We can put a reference element in front of the selector +string and the <div> will be appended as a child to the provided element: + + put(parent, "div"); + +The selector .class-name can be used to assign the class name. For example: + + put("div.my-class") + +would create an element <div class="my-class"> (an element with a class of "my-class"). + +The selector #id can be used to assign an id and [name=value] can be used to +assign additional attributes to the element. For example: + + newInput = put(parent, "input.my-input#address[type=checkbox]"); + +Would create an input element with a class name of "my-input", an id of "address", +and the type attribute set to "checkbox". The attribute assignment will always use +setAttribute to assign the attribute to the element. Multiple attributes and classes +can be assigned to a single element. + +The put function returns the last top level element created or referenced. In the +examples above, the newly create element would be returned. + +Modifying Elements +---------------- + +One can also modify elements with selectors. If the tag name is omitted (and no +combinators have been used), the reference element will be modified by the selector. +For example, to add the class "foo" to element, we could write: + + put(element, ".foo"); + +Likewise, we could set attributes, here we set the "role" attribute to "presentation": + + put(element, "[role=presentation]"); + +And these can be combined also. For example, we could set the id and an attribute in +one statement: + + put(element, "#id[tabIndex=2]"); + +One can also remove classes from elements by using the "!" operator in place of a ".". +To remove the "foo" class from an element, we could write: + + put(element, "!foo"); + +We can also use the "!" operator to remove attributes as well. Prepending an attribute name +with "!" within brackets will remove it. To remove the "role" attribute, we could write: + + put(element, "[!role]"); + +Deleting Elements +-------------- + +To delete an element, we can simply use the "!" operator by itself as the entire selector: + + put(elementToDelete, "!"); + +This will destroy the element from the DOM (using parent innerHTML destruction that reduces memory leaks in IE). + +Text Content +----------- + +The put() arguments may also include a subsequent string (or any primitive value including +boolean and numbers) argument immediately +following a selector, in which case it is used as the text inside of the new/referenced element. +For example, here we could create a new <div> with the text "Hello, World" inside. + + newDiv = put(parent, "div", "Hello, World"); + +The text is escaped, so any string will show up as is, and will not be parsed as HTML. + +Children and Combinators +----------------------- + +CSS combinators can be used to create child elements and sibling elements. For example, +we can use the child operator (or the descendant operator, it acts the same here) to +create nested elements: + + spanInsideOfDiv = put(reference, "div.outer span.inner"); + +This would create a new span element (with a class name of "inner") as a child of a +new div element (with a class name of "outer") as a child of the reference element. The +span element would be returned. We can also use the sibling operator to reference +the last created element or the reference element. In the example we indicate that +we want to create sibling of the reference element: + + newSpan = put(reference, "+span"); + +Would create a new span element directly after the reference element (reference and +newSpan would be siblings.) We can also use the "-" operator to indicate that the new element +should go before: + + newSpan = put(reference, "-span"); + +This new span element will be inserted before the reference element in the DOM order. +Note that "-" is valid character in tags and classes, so it will only be interpreted as a +combinator if it is the first character or if it is preceded by a space. + +The sibling operator can reference the last created element as well. For example +to add two td element to a table row: + + put(tableRow, "td+td"); + +The last created td will be returned. + +The parent operator, "<" can be used to reference the parent of the last created +element or reference element. In this example, we go crazy, and create a full table, +using the parent operator (applied twice) to traverse back up the DOM to create another table row +after creating a td element: + + newTable = put(referenceElement, "table.class-name#id tr td[colSpan=2]<