diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index ba2d2fb92..000000000 --- a/LICENSE.md +++ /dev/null @@ -1,202 +0,0 @@ -License -======= - -JSDoc 3 is free software, licensed under the Apache License, -Version 2.0 (the "License"); you MAY NOT use this software except in -compliance with the License. You have permission to use it for commercial, -non-commercial, or any other purpose you like, according to the -License below. - -Copyright (c) 2011 Michael Mathews -All rights reserved. - -You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -implied. See the License for the specific language governing -permissions and limitations under the License. - -You may obtain the source code for JSDoc 3 at -https://github.com/micmath/JSDoc - - -In Addition -=========== - -Third party software is included, used-by or distributed along -with JSDoc 3. Each is provided under its own license and has source -available from other locations. - - -Rhino (build/java/classes/js.jar) ----- - -Rhino is open source and licensed by Mozilla under the MPL 1.1 or -later/GPL 2.0 or later licenses. - -https://developer.mozilla.org/en/Rhino_License - -You may obtain the source code for Rhino from the Mozilla web site at -http://www.mozilla.org/rhino/download.html - -json2xml (modules/goessner/json2xml) ----- - -Copyright (c) Stefan Goessner 2006 - -json2xml is licensed under Creative Commons GNU LGPL License, -http://creativecommons.org/licenses/LGPL/2.1/ - -You may obtain the source code for json2xml at -http://goessner.net/download/prj/jsonxml/ - -Node (modules/common/assert, modules/common/util) ----- - -Copyright 2009, 2010 Ryan Lienhart Dahl. 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. - -You may obtain the source code for Node at -https://github.com/ry/node - -JSONSchema Validator (modules/sitepen/jsonschema) ----- - -Copyright (c) 2007 Kris Zyp SitePen (www.sitepen.com) - -Licensed under the MIT license. You may obtain a copy of the -License at http://www.opensource.org/licenses/mit-license.php - -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. - -You may obtain the source code for JSONSchema Validator at -http://github.com/kriszyp/commonjs-utils/blob/master/lib/json-schema.js -http://www.sitepen.com/blog/2010/03/02/commonjs-utilities/ - -markdown-js (modules/evilstreak/markdown) ----- - -markdown-js is released under the MIT license. You may obtain a copy of the -License at http://www.opensource.org/licenses/mit-license.php - -Copyright (c) 2009-2010 Dominic Baggott -Copyright (c) 2009-2010 Ash Berlin - -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. - -You may obtain the source code for markdown-js at -https://github.com/evilstreak/markdown-js - -sqlitejdbc (build/java/classes/sqlitejdbc-v054.jar) ----- - -Copyright (c) 2007 David Crawshaw - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -You may obtain the source code for sqlitejdbc at -https://github.com/crawshaw/sqlitejdbc - -Underscore Template (templates/lib/underscore/template.js) ----- - -Underscore.js 1.1.4 - -Copyright (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. - -Underscore is freely distributable under the MIT license. -Portions of Underscore are inspired or borrowed from Prototype, -Oliver Steele's Functional, and John Resig's Micro-Templating. - -For all details and documentation: -http://documentcloud.github.com/underscore/#template - -TaffyDB (modules/typicaljoe/taffy.js) ----- - -Copyright (c) 2008 All rights reserved. - -Software License Agreement (BSD License) - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following condition is met: - -Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -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. - -You may obtain the source code for TaffyDB at -https://github.com/typicaljoe/taffydb \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 8cb8c36c8..000000000 --- a/README.md +++ /dev/null @@ -1,95 +0,0 @@ -JSDoc 3 -======= - -An inline API documentation processor for JavaScript. JSDoc 3 is intended to be -an upgrade to JsDoc Toolkit (JSDoc 2). - -Notice ------- - -This is *pre-release software*! It is under active development, not complete -and is not suitable for reqular use yet. - -Installation ------------- - -Download a copy of JSDoc 3 from the official Git Hub repository here: - - -To build the jar file that runs JSDoc 3, use the Apache ant build tool: - - cd jsdoc - ant - -This will create a file named `jsdoc.jar` in the project base directory. - -To test that the newly installed app is working, execute the following: - - java -jar jsdoc.jar --test - -Usage ------ - -This example assumes that your working directory is the jsdoc application base -directory: - - java -jar jsdoc.jar yourSourceCodeFile.js - -For help regarding the supported commandline options use the --help option. - - java -jar jsdoc.jar --help - -Included with JSDoc 3 is a bash shell script that can simplify the command line -usage slightly. For example: - - ./jsdoc --help - ./jsdoc yourSourceCodeFile.js - -Dependencies ------------- - -JSDoc 3 utilises the Mozilla Rhino engine, which requires Java. JSDoc 3 is known -to work with version 1.6.0_24 of Java. - -JSDoc 3 uses advanced features in the Rhino application which are only -available in or after Rhino 1.7 release 3. A copy of this version of Rhino is -included in JSDoc so this is not normally an issue that the user needs to be -concerned with. However, in rare cases, users may have their Java CLASSPATH -configured to override that included Rhino and point to some older version of -Rhino instead. If this is the case, simply correct the CLASSPATH to remove the -older Rhino. - -The build script for JSDoc 3 requires Apache ant. It is know to work with -version 1.8.2 of ant. - -Debugging ---------- - -Rhino is not always very friendly when it comes to reporting errors in -JavaScript. Luckily it comes with a full-on debugger included that can be much -more useful than a simple stack trace. To invoke JSDoc with the debugger try the -following command: - - $ java -classpath build-files/java/classes/js.jar \ - org.mozilla.javascript.tools.debugger.Main main.js `pwd` \ - your/script.js - -This will open a debugging window. Choose "Break on Exceptions" from the "Debug" -menu, then press the "Run" button. If there is an error, you should see exactly -where it is in the source code. - -See Also --------- - -Project Documentation: (under development) -JSDoc User's Group: -Subversion Mirror: -Project Annoncements: - -License -------- - -JSDoc 3 is copyright (c) 2011 Michael Mathews - -See file "LICENSE.md" in this distribution for more details about -terms of use. \ No newline at end of file diff --git a/build-files/build.properties b/build-files/build.properties deleted file mode 100644 index ecaaae159..000000000 --- a/build-files/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -app.name=jsdoc -app.version=3.0.0beta1 diff --git a/build-files/java/classes/js.jar b/build-files/java/classes/js.jar deleted file mode 100644 index 64d9e3aff..000000000 Binary files a/build-files/java/classes/js.jar and /dev/null differ diff --git a/build-files/java/classes/sqlitejdbc-v054.jar b/build-files/java/classes/sqlitejdbc-v054.jar deleted file mode 100644 index 180de267e..000000000 Binary files a/build-files/java/classes/sqlitejdbc-v054.jar and /dev/null differ diff --git a/build-files/java/src/Run.java b/build-files/java/src/Run.java deleted file mode 100644 index 37222cfb7..000000000 --- a/build-files/java/src/Run.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - OVERVIEW: - A bootstrap tool for running main.js. Assumes main.js is in - the same directory as the run.jar file. - - Its duty is simply to add the absolute path for main.js as - the first argument to the main.js script itself. This enables - the script to know it's own directory, useful for accessing - resources via relative filepaths. - - AUTHOR: Michael Mathews - LICENSE: Apache License 2.0 - See file 'LICENSE.md' in this project. - USAGE: java -jar run.jar - */ - -import java.io.File; -import java.net.URL; -import java.util.*; - -public class Run { - // requires java.io.File, java.net.URL - public static void main(String[] args) throws java.io.IOException { - - // get the absolute file path to the jar file containing this class - ClassLoader loader = Run.class.getClassLoader(); - - // url is like "file:/Users/michael/WorkArea/jsdoc/run.jar!/Run.class" - String jarUrl = loader.getResource("Run.class").getPath(); - - // parse the filepath out of the URL - String delims = "[:!]"; - String[] tokens = jarUrl.split(delims); - String jarPath = tokens[1]; - - // the base directory, assumed to contain main.js - String jarDir = new File(jarPath).getParent(); - String mainPath = jarDir + "/main.js"; - - // Rhino eats the first arg (the path to the script file it is running) - // so we add it twice: one for Rhino the next for us - String[] mainArgs = {mainPath, mainPath}; - String[] allArgs = concat(mainArgs, args); - - // main.js will now get arguments like: - // ["/abs/path/to/main.js", "-a", "aval", "-b", "bval"] - org.mozilla.javascript.tools.shell.Main.main(allArgs); - } - - // requires java.util - public static String[] concat(String[] a, String[] b) { - List ab = new ArrayList(a.length + b.length); - Collections.addAll(ab, a); - Collections.addAll(ab, b); - return ab.toArray(new String[] {}); - } -} diff --git a/build-files/templates/package.json b/build-files/templates/package.json deleted file mode 100644 index 3a39d8d9d..000000000 --- a/build-files/templates/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@app.name@", - "version": "@app.version@", - "revision": "@timestamp@", - "description": "An automatic documentation generator for javascript.", - "keywords": [ "documentation", "javascript" ], - "licenses": [ - { - "type": "Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0" - } - ], - "repositories": [ - { - "type": "git", - "url": "git://github.com/micmath/jsdoc.git" - }, - { - "type": "svn", - "url": "https://jsdoc.googlecode.com/svn/trunk" - } - ], - "bugs": "http://code.google.com/p/jsdoc/issues/list", - "contributors" : [ - { - "name": "Michael Mathews", - "email": "micmath@gmail.com" - } - ], - "maintainers": [ - { - "name": "Michael Mathews", - "email": "micmath@gmail.com" - } - ] -} diff --git a/build.xml b/build.xml deleted file mode 100644 index 7e2edd039..000000000 --- a/build.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/conf.json b/conf.json index 4978ae7fa..aab9c0a16 100644 --- a/conf.json +++ b/conf.json @@ -1,14 +1,5 @@ { - "tags": { - "allowUnknownTags": true - }, - - "source": { - "includePattern": ".+\\.js(doc)?$", - "excludePattern": "(^|\\/)_" - }, - "plugins": [ + "./plugin/md" ] - } \ No newline at end of file diff --git a/jsdoc b/jsdoc deleted file mode 100755 index 0ccd48d0d..000000000 --- a/jsdoc +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -BASEDIR=`dirname $0` -java -classpath ${BASEDIR}/build-files/java/classes/js.jar -jar ${BASEDIR}/jsdoc.jar $@ - -# or possibly? -# java -classpath build-files/java/classes/js.jar org.mozilla.javascript.tools.shell.Main main.js `pwd` \ No newline at end of file diff --git a/jsdoc.js b/jsdoc.js new file mode 100644 index 000000000..2261bce9f --- /dev/null +++ b/jsdoc.js @@ -0,0 +1,66 @@ +// like: java -classpath ~/Scripts/js.jar org.mozilla.javascript.tools.shell.Main jsdoc.js examples/test1.js +// or: node jsdoc examples/test1.js + +(function() { + + // normalise rhino + if (typeof load !== 'undefined') { + load('lib/rhino-shim.js'); + } + + // global modules + global._ = require('underscore'); + _.mixin(require('underscore.string')); + global.signals = require('signals'); + + // needed modules + var fs = require('fs'), + opts = require('jsdoc/options').parse( process.argv.slice(2) ), + dumper = require('jsdoc/util/dumper'); + + // user configuration + try { + var conf = JSON.parse( + fs.readFileSync('./conf.json', 'utf-8') + ); + } + catch (e) { + throw('Configuration file cannot be evaluated. '+e); + } + + if (typeof conf.plugins !== 'undefined') { + for (var i = 0, len = conf.plugins.length; i < len; i++) { + require(conf.plugins[i]); + } + } + + if (opts.help) { + console.log('USAGE: node main.js yourfile.js'); + process.exit(0); + } + + var srcFile = opts._[0]; + + var src = fs.readFileSync(srcFile, 'utf-8'); + + var parser = require('jsdoc/parser'); + + var docs = parser.parse(src); + +// docs = _.map(docs, function(doc) { +// if (!doc.jsdoc) { doc.jsdoc = {}; } +// doc.jsdoc.longname = doc.longname; +// return doc.jsdoc; +// }); + + if (opts.expel) { + console.log( dumper.dump(docs) ); + } + else { + var taffy = require('./templates/lib/taffy'); + var publisher = require('./templates/default'); + + console.log( publisher.publish( new taffy(docs) ) ); + } + +})(); \ No newline at end of file diff --git a/lib/Rhino-Require/README.md b/lib/Rhino-Require/README.md new file mode 100644 index 000000000..c1d6023ee --- /dev/null +++ b/lib/Rhino-Require/README.md @@ -0,0 +1,29 @@ +Rhino-Require +==== + +A nodejs-like implementation of the commonjs `require` function, implemented +to be compatible with the Mozilla Rhino JavaScript engine. + +Usage +---- + +Assuming you have created a JavaScript commonjs module and saved it at +`./node_modules/twiddler/index.js` + + module.exports = { + twiddle: function(str) { + } + }; + +You can then load that module into your `./main.js` script using Rhino-Require. + + load('lib/rhino-require.js'); + + var twiddler = require('twiddler'); + twiddler.twiddle('foo'); + +License +---- + +Written by Michael Mathews. Licensed as public domain. + diff --git a/lib/Rhino-Require/src/require.js b/lib/Rhino-Require/src/require.js new file mode 100644 index 000000000..a9623e601 --- /dev/null +++ b/lib/Rhino-Require/src/require.js @@ -0,0 +1,262 @@ +/* + Rhino-Require is Public Domain + + + The author or authors of this code dedicate any and all copyright interest + in this code to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and successors. We + intend this dedication to be an overt act of relinquishment in perpetuity of + all present and future rights to this code under copyright law. + */ + +(function(global) { + + var require = global.require = function(id) { /*debug*///console.log('require('+id+')'); + if (typeof arguments[0] !== 'string') throw 'USAGE: require(moduleId)'; + + var moduleContent = '', + moduleUri; + + moduleUri = require.resolve(id); + moduleContent = ''; + + var file = new java.io.File(moduleUri); + try { + var scanner = new java.util.Scanner(file).useDelimiter("\\Z"); + moduleContent = String( scanner.next() ); + } + catch(e) { + throw 'Unable to read file at: '+moduleUri+', '+e; + } + + if (moduleContent) { + try { + var f = new Function('require', 'exports', 'module', moduleContent), + exports = require.cache[moduleUri] || {}, + module = { id: id, uri: moduleUri, exports: exports }; + + + require._root.unshift(moduleUri); + f.call({}, require, exports, module); + require._root.shift(); + } + catch(e) { + throw 'Unable to require source code from "' + moduleUri + '": ' + e.toSource(); + } + + exports = module.exports || exports; + require.cache[id] = exports; + } + else { + throw 'The requested module cannot be returned: no content for id: "' + id + '" in paths: ' + require.paths.join(', '); + } + + return exports; + } + require._root = ['']; + require.paths = []; + require.cache = {}; // cache module exports. Like: {id: exported} + + var SLASH = Packages.java.io.File.separator; + + /** Given a module id, try to find the path to the associated module. + */ + require.resolve = function(id) { + // TODO: 1. load node core modules + + // 2. dot-relative module id, like './foo/bar' + var parts = id.match(/^(\.?\.(?:\\|\/)|(?:\\|\/))(.+)$/), + isRelative = false, + isAbsolute = false, + basename = id; + + if (parts) { + isRelative = parts[1] === './' || parts[1] === '.\\' || parts[1] === '../' || parts[1] === '.\\'; + isAbsolute = parts[1] === '/' || parts[1] === '\\'; + basename = parts[2]; + } + + if (typeof basename !== 'undefined') { + + if (isAbsolute) { + rootedId = id; + } + else { + var root = (isRelative? toDir(require._root[0] || '.') : '.'), + rootedId = deDotPath(root + SLASH + id), + uri = ''; + } + + if ( uri = loadAsFile(rootedId) ) { } + else if ( uri = loadAsDir(rootedId) ) { } + else if ( uri = loadNodeModules(rootedId) ) { } + else if ( uri = nodeModulesPaths(rootedId, 'rhino_modules') ) { } + else if ( uri = nodeModulesPaths(rootedId, 'node_modules') ) { } + + if (uri !== '') return toAbsolute(uri); + + throw 'Require Error: Not found.'; + } + } + + /** Given a path, return the base directory of that path. + @example toDir('/foo/bar/somefile.js'); => '/foo/bar' + */ + function toDir(path) { + var file = new java.io.File(path); + + if (file.isDirectory()) { + return path; + } + + var parts = path.split(/[\\\/]/); + parts.pop(); + return parts.join(SLASH); + } + + /** Returns true if the given path exists and is a file. + */ + function isFile(path) { + var file = new java.io.File(path); + + if (file.isFile()) { + return true; + } + + return false; + } + + /** Returns true if the given path exists and is a directory. + */ + function isDir(path) { + var file = new java.io.File(path); + + if (file.isDirectory()) { + return true; + } + + return false; + } + + /** Get the path of the current working directory + */ + function getCwd() { + return deDotPath( toDir( ''+new java.io.File('.').getAbsolutePath() ) ); + } + + function toAbsolute(relPath) { + absPath = ''+new java.io.File(relPath).getAbsolutePath(); + absPath = deDotPath(absPath); + return absPath; + } + + function deDotPath(path) { + return String(path) + .replace(/(\/|\\)[^\/\\]+\/\.\.(\/|\\)/g, SLASH) + .replace(/(\/|\\)\.(\/|\\|$)/g, SLASH); + } + + /** Assume the id is a file, try to find it. + */ + function loadAsFile(id) { + if ( isFile(id) ) { return id; } + + if ( isFile(id+'.js') ) { return id+'.js'; } + + if ( isFile(id+'.node') ) { throw 'Require Error: .node files not supported'; } + } + + /** Assume the id is a directory, try to find a module file within it. + */ + function loadAsDir(id) { + if (!isDir(id)) { + return; + } + // look for the "main" property of the package.json file + if ( isFile(id+SLASH+'package.json') ) { + var packageJson = readFileSync(id+SLASH+'package.json', 'utf-8'); + eval( 'packageJson = '+ packageJson); + if (packageJson.hasOwnProperty('main')) { + var main = deDotPath(id + SLASH + packageJson.main); + return require.resolve(main); + } + } + + if ( isFile(id+SLASH+'index.js') ) { + return id+SLASH+'index.js'; + } + } + + function loadNodeModules(id) { + var path, + uri; + for (var i = 0, len = require.paths.length; i < len; i++) { + path = require.paths[i]; + if (isDir(path)) { + path = deDotPath(path + SLASH + id); + + uri = loadAsFile(path); + if (typeof uri !== 'undefined') { + return uri; + } + + uri = loadAsDir(path); + if (typeof uri !== 'undefined') { + return uri; + } + } + } + } + + function nodeModulesPaths(id, moduleFolder) { + var cwd = getCwd(), + dirs = cwd.split(SLASH), + dir, + path, + filename, + uri; + + while (dirs.length) { + dir = dirs.join(SLASH); + path = dir+SLASH+moduleFolder; + + if ( isDir(path) ) { + filename = deDotPath(path+SLASH+id); + + if ( uri = loadAsFile(filename) ) { + uri = uri.replace(cwd, '.'); + return uri; + } + + if ( uri = loadAsDir(filename) ) { + uri = uri.replace(cwd, '.'); + return uri; + } + } + + dirs.pop(); + } + } + + function readFileSync(filename, encoding, callback) { + if (typeof arguments[1] === 'function') { + encoding = null; + callback = arguments[1]; + } + + encoding = encoding || java.lang.System.getProperty('file.encoding'); + + try { + var content = new java.util.Scanner( + new java.io.File(filename), + encoding + ).useDelimiter("\\Z"); + + return String( content.next() ); + } + catch (e) { + return ''; + } + } + +})(this); \ No newline at end of file diff --git a/lib/Rhino-Require/test/README.md b/lib/Rhino-Require/test/README.md new file mode 100644 index 000000000..9d60454f5 --- /dev/null +++ b/lib/Rhino-Require/test/README.md @@ -0,0 +1,5 @@ +To run the tests, change your current working directory to this test folder. + +Then: + + java -classpath js.jar org.mozilla.javascript.tools.shell.Main run.js diff --git a/lib/Rhino-Require/test/lib/jsmock.js b/lib/Rhino-Require/test/lib/jsmock.js new file mode 100644 index 000000000..3e78f5552 --- /dev/null +++ b/lib/Rhino-Require/test/lib/jsmock.js @@ -0,0 +1,377 @@ +/* +* JSMock 1.2.2, a mock object library for JavaScript +* Copyright (C) 2006 Justin DeWind +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library 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 +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +JSMock = { + extend: function(object) { + var mockControl = new MockControl(); + object.createMock = function(objectToMock) {return mockControl.createMock(objectToMock)}; + object.resetMocks = function() {mockControl.reset()}; + object.verifyMocks = function() {mockControl.verify()}; + + if(!object.tearDown) { + object.tearDown = function() { + object.verifyMocks(); + } + } + else if(object.tearDown.constructor == Function) { + object.__oldTearDown__ = object.tearDown; + object.tearDown = function() { + object.__oldTearDown__(); + object.verifyMocks(); + } + } + } +} + +function MockControl() { + this.__expectationMatcher = new ExpectationMatcher(); + this.__lastMock = null; + this.__lastCallName = null; +} + +MockControl.prototype = { + + createMock: function(objectToMock) { + var mock = { calls: [], expects: function() {this.__recording = true; return this}, __recording: false}; + mock.expect = mock.expects; + + if(objectToMock != null) { + + if( typeof(objectToMock) == 'function' ) { + this.__createMethods(objectToMock, mock); + this.__createMethods(new objectToMock(), mock); + } + else if( typeof(objectToMock) == 'object') { + this.__createMethods(objectToMock, mock); + } + else { + throw new Error("Cannot mock out a " + typeof(objectToMock)); + } + + } + + var self = this; + mock.addMockMethod = function(method) { self.__createMethod(self, mock, method); } + + return mock; + }, + + andReturn: function(returnValue) { + this.__verifyLastMockNotNull("Cannot set return value without an expectation"); + this.__initializeReturnExpectationForMock(); + this.__lastMock.calls[this.__lastCallName].push( function() { return returnValue; }); + }, + + andThrow: function(throwMsg) { + this.__verifyLastMockNotNull("Cannot throw error without an expectation"); + this.__initializeReturnExpectationForMock(); + this.__lastMock.calls[this.__lastCallName].push( function() { throw new Error(throwMsg); }); + }, + + andStub: function(block) { + this.__verifyLastMockNotNull("Cannot stub without an expectation"); + if( typeof(block) != 'function') { + throw new Error("Stub must be a function"); + } + this.__initializeReturnExpectationForMock(); + this.__lastMock.calls[this.__lastCallName].push( function() { return block.apply(this, arguments); }); + }, + + reset: function() { + this.__expectationMatcher.reset(); + }, + + verify: function() { + if(!this.__expectationMatcher.matches()) + { + discrepancy = this.__expectationMatcher.discrepancy(); + message = discrepancy.message; + method = discrepancy.behavior.method + formattedArgs = ArgumentFormatter.format(discrepancy.behavior.methodArguments); + this.__expectationMatcher.reset(); + throw new Error(message + ": " + method + "(" + formattedArgs + ")"); + } + else { + this.__expectationMatcher.reset(); + } + + }, + + __createMethods: function(object, mock) { + for( property in object ) { + if( this.__isPublicMethod(object, property) ) { + this.__createMethod( this, mock, property ); + } + } + }, + + __createMethod: function(control, mock, method) { + mock[method] = + function() { + if( mock.__recording ) { + control.__lastMock = mock; + control.__lastCallName = method; + control.__expectationMatcher.addExpectedMethodCall( mock, method, arguments ); + mock.__recording = false; + return control; + } + else { + control.__expectationMatcher.addActualMethodCall( mock, method, arguments ); + if( mock.calls[method] != null) { + returnValue = mock.calls[method].shift(); + if( typeof(returnValue) == 'function') { + return returnValue.apply(this, arguments); + } + } + } + } + }, + + __isPublicMethod: function(object, property) { + try { + return typeof(object[property]) == 'function' && property.charAt(0) != "_"; + } catch(e) { + return false; + } + }, + + __verifyLastMockNotNull: function(throwMsg) { + if(this.__lastMock == null) { + throw new Error(throwMsg); + } + }, + + __initializeReturnExpectationForMock: function() { + if(typeof(this.__lastMock.calls[this.__lastCallName]) == 'undefined') { + this.__lastMock.calls[this.__lastCallName] = []; + } + } +} + +function ExpectationMatcher() { + this.__expectationBehaviorList = []; + this.__actualBehaviorList = []; + this.__discrepancy = null; + +} + +ExpectationMatcher.prototype = { + addExpectedMethodCall: function(caller, method, methodArguments ) { + this.__expectationBehaviorList.push(new InvocationBehavior(caller, method, methodArguments)); + }, + + addActualMethodCall: function(caller, method, methodArguments ) { + this.__actualBehaviorList.push(new InvocationBehavior(caller, method, methodArguments)); + }, + + matches: function() { + var self = this; + var matches = true; + + this.__expectationBehaviorList.eachIndexForJsMock(function(index, expectedBehavior) { + var actualBehavior = (self.__actualBehaviorList.length > index) ? self.__actualBehaviorList[index] : null; + + if(matches) { + if( actualBehavior === null ) { + self.__discrepancy = new Discrepancy("Expected function not called", expectedBehavior); + matches = false; + } + else if( expectedBehavior.method != actualBehavior.method ) { + self.__discrepancy = new Discrepancy("Surprise call", actualBehavior); + matches = false; + } + else if( expectedBehavior.caller != actualBehavior.caller ) { + self.__discrepancy = new Discrepancy("Surprise call from unexpected caller", actualBehavior); + matches = false; + } + else if( !self.__matchArguments(expectedBehavior.methodArguments, actualBehavior.methodArguments) ) { + self.__discrepancy = new Discrepancy("Unexpected Arguments", actualBehavior); + matches = false; + } + } + }); + + if( this.__actualBehaviorList.length > this.__expectationBehaviorList.length && matches ) { + this.__discrepancy = new Discrepancy("Surprise call", this.__actualBehaviorList[this.__expectationBehaviorList.length]); + matches = false + } + + return matches; + }, + + reset: function() { + this.__expectationBehaviorList = []; + this.__actualBehaviorList = []; + this.__discrepancy = null; + }, + + discrepancy: function() { + return this.__discrepancy; + }, + + __matchArguments: function(expectedArgs, actualArgs) { + var expectedArray = this.__convertArgumentsToArray(expectedArgs); + var actualArray = this.__convertArgumentsToArray(actualArgs); + return ArgumentMatcher.matches(expectedArray, actualArray); + }, + + __convertArgumentsToArray: function(args) { + var convertedArguments = []; + + for(var i = 0; i < args.length; i++) { + convertedArguments[i] = args[i]; + } + + return convertedArguments; + } +} + +function InvocationBehavior(caller, method, methodArguments) { + this.caller = caller; + this.method = method; + this.methodArguments = methodArguments; +} + +function TypeOf(type) { + if(typeof(type) != 'function') + throw new Error("Can only take constructors"); + + this.type = type; +} + +TypeOf.isA = function(type) { return new TypeOf(type); }; + +ArgumentMatcher = { + + matches: function(expected, actual) { + return this.__delegateMatching(expected, actual); + }, + + __delegateMatching: function(expected, actual) { + if( expected == null ) { + return this.__match( expected, actual ); + } + else if( expected.constructor == TypeOf ) { + return this.__match(expected.type, actual.constructor); + } + else if( expected.constructor == Array ) { + return this.__matchArrays(expected, actual); + } + else { + return this.__match(expected, actual); + } + }, + + __match: function(expected, actual) { + return ( expected == actual ); + }, + + __matchArrays: function(expected, actual) { + if ( actual == null) + return false; + + if( actual.constructor != Array) + return false; + + if( expected.length != actual.length ) + return false; + + for(var i = 0; i < expected.length; i++ ) { + if( !this.__delegateMatching(expected[i], actual[i]) ) + return false; + } + + return true; + } +} + +function Discrepancy(message, behavior) { + if(behavior.constructor != InvocationBehavior) + throw new Error("The behavior can only be an InvocationBehavior object"); + + this.message = message; + this.behavior = behavior; +} + +ArgumentFormatter = { + + format: function(args) { + var formattedArgs = ""; + for(var i = 0; i < args.length; i++) { + if( args[i] == null ) { + formattedArgs += ( formattedArgs == "" ) ? "null" : ", " + "null"; + } + else if( args[i].constructor == TypeOf || args[i].constructor == Function) { + var func = ( args[i].constructor == TypeOf ) ? args[i].type : args[i]; + formattedArgs += ( formattedArgs == "" ) ? this.__formatFunction(func) : ", " + this.__formatFunction(func); + } + else if( typeof(args[i]) == "string" ) { + formattedArgs += ( formattedArgs == "" ) ? "\"" + args[i].toString() + "\"" : ", \"" + args[i].toString() + "\"" + } + else if( args[i].constructor == Array ) { + formattedArgs += ( formattedArgs == "" ) ? "[" + this.format(args[i]) + "]" : ", [" + this.format(args[i]) + "]"; + } + else { + formattedArgs += ( formattedArgs == "" ) ? args[i].toString() : ", " + args[i].toString(); + } + } + return formattedArgs; + }, + + __formatFunction: function(func) { + // Manual checking is done for internal/native functions + // since Safari will not display them correctly + // for the intended regex parsing. + + if(func == Array) { + return "Array"; + } else if(func == Date) { + return "Date"; + } else if(func == Object) { + return "Object"; + } else if(func == String) { + return "String"; + } else if(func == Function) { + return "Function"; + } else if(func == RegExp) { + return "RegExp"; + } else if(func == Error) { + return "Error"; + } else if(func == Number) { + return "Number"; + } else if(func == Boolean) { + return "Boolean"; + } + var formattedFunc = func.toString().match(/function (\w+)/); + + return ( formattedFunc == null ) ? "{{Closure}}" : formattedFunc[1]; + } + +} + +/* Helpers */ + +// Implemented each method with a unique name to avoid conflicting +// with other libraries that implement it. +Array.prototype.eachIndexForJsMock = function(block) { + for(var index = 0; index < this.length; index++) + { + block(index, this[index]); + } +} diff --git a/lib/Rhino-Require/test/lib/nodeunit.js b/lib/Rhino-Require/test/lib/nodeunit.js new file mode 100644 index 000000000..47ffd8233 --- /dev/null +++ b/lib/Rhino-Require/test/lib/nodeunit.js @@ -0,0 +1,1762 @@ +/*! + * Nodeunit + * https://github.com/caolan/nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * json2.js + * http://www.JSON.org/json2.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ +nodeunit = (function(){ +/* + http://www.JSON.org/json2.js + 2010-11-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + 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 && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + 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.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.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'); + }; + } +}()); +var assert = {}; +var types = {}; +var core = {}; +var nodeunit = {}; +var reporter = {}; +(function(){ + + var async = {}; + + // global on the server, window in the browser + var root = this; + var 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 b ? 1 : 0; + }), function(x){return x.value;})); + }) + }; + + async.auto = function(tasks, callback){ + callback = callback || function(){}; + var keys = _keys(tasks); + if(!keys.length) return callback(null); + + var completed = []; + + var listeners = []; + var addListener = function(fn){ + listeners.unshift(fn); + }; + var removeListener = function(fn){ + for(var i=0; i +// +// 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 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. + + +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({message: message, actual: actual, expected: expected}) + +assert.AssertionError = function AssertionError (options) { + this.name = "AssertionError"; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +// code from util.inherits in node +assert.AssertionError.super_ = Error; + + +// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call +// TODO: test what effect this may have +var ctor = function () { this.constructor = assert.AssertionError; }; +ctor.prototype = Error.prototype; +assert.AssertionError.prototype = new ctor(); + + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name+":", this.message].join(' '); + } else { + return [ this.name+":" + , JSON.stringify(this.expected ) + , this.operator + , JSON.stringify(this.actual) + ].join(" "); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, "==", assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, "==", assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (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 _deepEqual(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 (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as determined by !==. +// assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } +}; + +function _throws (shouldThrow, block, err, message) { + var exception = null, + threw = false, + typematters = true; + + message = message || ""; + + //handle optional arguments + if (arguments.length == 3) { + if (typeof(err) == "string") { + message = err; + typematters = false; + } + } else if (arguments.length == 2) { + typematters = false; + } + + try { + block(); + } catch (e) { + threw = true; + exception = e; + } + + if (shouldThrow && !threw) { + fail( "Missing expected exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if (!shouldThrow && threw && typematters && exception instanceof err) { + fail( "Got unwanted exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if ((shouldThrow && threw && typematters && !(exception instanceof err)) || + (!shouldThrow && threw)) { + throw exception; + } +}; + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert['throws'] = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function (err) { if (err) {throw err;}}; +})(assert); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + + + +/** + * Creates assertion objects representing the result of an assert call. + * Accepts an object or AssertionError as its argument. + * + * @param {object} obj + * @api public + */ + +exports.assertion = function (obj) { + return { + method: obj.method || '', + message: obj.message || (obj.error && obj.error.message) || '', + error: obj.error, + passed: function () { + return !this.error; + }, + failed: function () { + return Boolean(this.error); + } + }; +}; + +/** + * Creates an assertion list object representing a group of assertions. + * Accepts an array of assertion objects. + * + * @param {Array} arr + * @param {Number} duration + * @api public + */ + +exports.assertionList = function (arr, duration) { + var that = arr || []; + that.failures = function () { + var failures = 0; + for (var i=0; i'; +}; + + +/** + * Run all tests within each module, reporting the results + * + * @param {Array} files + * @api public + */ + +exports.run = function (modules, options) { + var start = new Date().getTime(); + exports.addStyles(); + + var html = ''; + nodeunit.runModules(modules, { + moduleStart: function (name) { + html += '

' + name + '

'; + html += '
    '; + }, + testDone: function (name, assertions) { + if (!assertions.failures()) { + html += '
  1. ' + name + '
  2. '; + } + else { + html += '
  3. ' + name; + for (var i=0; i'; + } + html += '
    ';
    +                        html += a.error.stack || a.error;
    +                        html += '
    '; + } + }; + html += '
  4. '; + } + }, + moduleDone: function () { + html += '
'; + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + if (assertions.failures()) { + html += '

FAILURES: ' + assertions.failures() + + '/' + assertions.length + ' assertions failed (' + + assertions.duration + 'ms)

'; + } + else { + html += '

OK: ' + assertions.length + + ' assertions (' + assertions.duration + 'ms)

'; + } + if (typeof document === 'undefined' && typeof print !== 'undefined') { + return print( html.replace(/
  • /g, '(/) ').replace(/
  • /g, '(X) ').replace(//g, "\n").replace(/<\/h\d>/g, "\n").replace(/
    /g, "\n ").replace(/<\/(li|div)>/g, "\n").replace(/<[^>]+?>/g, '') ); + } + document.body.innerHTML += html; + } + }); +}; +})(reporter); +nodeunit = core; +nodeunit.assert = assert; +nodeunit.reporter = reporter; +nodeunit.run = reporter.run; +return nodeunit; })(); \ No newline at end of file diff --git a/lib/Rhino-Require/test/mock/bar/myModuleLib/bar.js b/lib/Rhino-Require/test/mock/bar/myModuleLib/bar.js new file mode 100644 index 000000000..7875591b5 --- /dev/null +++ b/lib/Rhino-Require/test/mock/bar/myModuleLib/bar.js @@ -0,0 +1,2 @@ +exports.name = 'bar'; +exports.extras = require('./extras'); \ No newline at end of file diff --git a/lib/Rhino-Require/test/mock/bar/myModuleLib/extras.js b/lib/Rhino-Require/test/mock/bar/myModuleLib/extras.js new file mode 100644 index 000000000..9d62c3e9a --- /dev/null +++ b/lib/Rhino-Require/test/mock/bar/myModuleLib/extras.js @@ -0,0 +1 @@ +exports.name = 'extras'; \ No newline at end of file diff --git a/lib/Rhino-Require/test/mock/bar/package.json b/lib/Rhino-Require/test/mock/bar/package.json new file mode 100644 index 000000000..6ab8a81f4 --- /dev/null +++ b/lib/Rhino-Require/test/mock/bar/package.json @@ -0,0 +1,10 @@ +{ + "name": "my-test-package", + "version": "1.2.3", + "description": "Blah blah blah", + "keywords": [ + "package", + "example" + ], + "main": "./myModuleLib/bar.js" +} \ No newline at end of file diff --git a/changes.md b/lib/Rhino-Require/test/mock/foo.js similarity index 100% rename from changes.md rename to lib/Rhino-Require/test/mock/foo.js diff --git a/test/cases/src/_dir2/four.js b/lib/Rhino-Require/test/mock/zop/index.js similarity index 100% rename from test/cases/src/_dir2/four.js rename to lib/Rhino-Require/test/mock/zop/index.js diff --git a/test/cases/src/_ignored.js b/lib/Rhino-Require/test/node_modules/baz/index.js similarity index 100% rename from test/cases/src/_ignored.js rename to lib/Rhino-Require/test/node_modules/baz/index.js diff --git a/test/cases/src/dir1/three.js b/lib/Rhino-Require/test/node_modules/foobar.js similarity index 100% rename from test/cases/src/dir1/three.js rename to lib/Rhino-Require/test/node_modules/foobar.js diff --git a/lib/Rhino-Require/test/run.js b/lib/Rhino-Require/test/run.js new file mode 100644 index 000000000..f77d3b546 --- /dev/null +++ b/lib/Rhino-Require/test/run.js @@ -0,0 +1,113 @@ +// USAGE: java -classpath ~/Scripts/js.jar org.mozilla.javascript.tools.shell.Main test.js + +load('./lib/nodeunit.js'); +//load('lib/jsmock.js'); + +load('../src/require.js'); + +module = typeof module === 'undefined'? {} : module; +var test = module.exports = { + 'Basic tests.': { + 'The require function should be defined.': function(t) { + t.expect(1); + t.equal( typeof require, 'function' ); + t.done(); + }, + 'The require function should take one required string argument.': function(t) { + t.expect(3); + t['throws']( function() { require(); }); + t['throws']( function() { require(1); } ); + t['throws']( function() { require(''); } ); + t.done(); + } + }, + 'The require.resolve function.': { + 'The require.resolve function should be defined.': function(t) { + t.expect(1); + t.equal( typeof require.resolve, 'function' ); + t.done(); + }, + 'When an id starts with "./" it should resolve relative to the current working directory.': function(t) { + t.expect(1); + t.equal( require.resolve( './mock/foo'), toAbsolute('./mock/foo.js') ); + t.done(); + }, + 'When an id starts with "./" it should resolve relative to the current running module.': function(t) { + t.expect(1); + require._root.unshift('./mock/bar.js'); + t.equal( require.resolve('./foo'), toAbsolute('./mock/foo.js') ); + require._root.shift(); + t.done(); + }, + 'When an id does not start with "./" it should resolve relative to the cwd.': function(t) { + t.expect(1); + require._root.unshift('blah/one/two.js'); + t.equal( require.resolve('mock/foo'), toAbsolute('./mock/foo.js') ); + require._root.shift(); + t.done(); + } + }, + 'Resolve from package.json.': { + 'The require.resolve function should use the "main" property from package.json.': function(t) { + t.expect(1); + t.equal( require.resolve('./mock/bar'), toAbsolute('./mock/bar/myModuleLib/bar.js') ); + t.done(); + } + }, + 'Resolve from index file.': { + 'The require.resolve function should use the "index.js" file.': function(t) { + t.expect(1); + t.equal( require.resolve('./mock/zop'), toAbsolute('./mock/zop/index.js') ); + t.done(); + } + }, + 'Resolve from require.paths.': { + 'The require.resolve function should use the require.paths values.': function(t) { + t.expect(1); + require.paths.push('./mock'); + t.equal( require.resolve('foo'), toAbsolute('./mock/foo.js') ); + require.paths.pop(); + + t.done(); + } + }, + 'Resolve from node_modules.': { + 'The require.resolve function should use the node_modules dir.': function(t) { + t.expect(1); + t.equal( require.resolve('foobar'), toAbsolute('./node_modules/foobar.js') ); + t.done(); + }, + 'The require.resolve function should look for index in node_modules dir.': function(t) { + t.expect(1); + t.equal( require.resolve('baz'), toAbsolute('./node_modules/baz/index.js') ); + t.done(); + } + }, + 'Require from package.json.': { + 'The required module should be returned when it is listed in package.json.': function(t) { + t.expect(3); + var bar = require('mock/bar'); + t.equal( typeof bar, 'object' ); + t.equal( bar.name, 'bar' ); + t.equal( bar.extras.name, 'extras' ); + t.done(); + }, + 'The required dot-relative module should be returned when it is listed in package.json.': function(t) { + t.expect(2); + var bar = require('./mock/bar'); + t.equal( typeof bar, 'object' ); + t.equal( bar.name, 'bar' ); + t.done(); + } + } +}; + +var SLASH = Packages.java.io.File.separator; +function toAbsolute(relPath) { + var absPath = String( new java.io.File(relPath).getAbsolutePath() ) + .replace(/(\/|\\)[^\/\\]+\/\.\.(\/|\\)/g, SLASH) + .replace(/(\/|\\)\.(\/|\\|$)/g, SLASH); + return absPath; +} + +nodeunit.run(test); \ No newline at end of file diff --git a/lib/json.js b/lib/json.js new file mode 100644 index 000000000..ae21742e0 --- /dev/null +++ b/lib/json.js @@ -0,0 +1,238 @@ +// This source code is free for use in the public domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// http://code.google.com/p/json-sans-eval/ + +/** + * Parses a string of well-formed JSON text. + * + * If the input is not well-formed, then behavior is undefined, but it is + * deterministic and is guaranteed not to modify any object other than its + * return value. + * + * This does not use `eval` so is less likely to have obscure security bugs than + * json2.js. + * It is optimized for speed, so is much faster than json_parse.js. + * + * This library should be used whenever security is a concern (when JSON may + * come from an untrusted source), speed is a concern, and erroring on malformed + * JSON is *not* a concern. + * + * Pros Cons + * +-----------------------+-----------------------+ + * json_sans_eval.js | Fast, secure | Not validating | + * +-----------------------+-----------------------+ + * json_parse.js | Validating, secure | Slow | + * +-----------------------+-----------------------+ + * json2.js | Fast, some validation | Potentially insecure | + * +-----------------------+-----------------------+ + * + * json2.js is very fast, but potentially insecure since it calls `eval` to + * parse JSON data, so an attacker might be able to supply strange JS that + * looks like JSON, but that executes arbitrary javascript. + * If you do have to use json2.js with untrusted data, make sure you keep + * your version of json2.js up to date so that you get patches as they're + * released. + * + * @param {string} json per RFC 4627 + * @param {function (this:Object, string, *):*} opt_reviver optional function + * that reworks JSON objects post-parse per Chapter 15.12 of EcmaScript3.1. + * If supplied, the function is called with a string key, and a value. + * The value is the property of 'this'. The reviver should return + * the value to use in its place. So if dates were serialized as + * {@code { "type": "Date", "time": 1234 }}, then a reviver might look like + * {@code + * function (key, value) { + * if (value && typeof value === 'object' && 'Date' === value.type) { + * return new Date(value.time); + * } else { + * return value; + * } + * }}. + * If the reviver returns {@code undefined} then the property named by key + * will be deleted from its container. + * {@code this} is bound to the object containing the specified property. + * @return {Object|Array} + * @author Mike Samuel + */ +var jsonParse = (function () { + var number + = '(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)'; + var oneChar = '(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]' + + '|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))'; + var string = '(?:\"' + oneChar + '*\")'; + + // Will match a value in a well-formed JSON file. + // If the input is not well-formed, may match strangely, but not in an unsafe + // way. + // Since this only matches value tokens, it does not match whitespace, colons, + // or commas. + var jsonToken = new RegExp( + '(?:false|true|null|[\\{\\}\\[\\]]' + + '|' + number + + '|' + string + + ')', 'g'); + + // Matches escape sequences in a string literal + var escapeSequence = new RegExp('\\\\(?:([^u])|u(.{4}))', 'g'); + + // Decodes escape sequences in object literals + var escapes = { + '"': '"', + '/': '/', + '\\': '\\', + 'b': '\b', + 'f': '\f', + 'n': '\n', + 'r': '\r', + 't': '\t' + }; + function unescapeOne(_, ch, hex) { + return ch ? escapes[ch] : String.fromCharCode(parseInt(hex, 16)); + } + + // A non-falsy value that coerces to the empty string when used as a key. + var EMPTY_STRING = new String(''); + var SLASH = '\\'; + + // Constructor to use based on an open token. + var firstTokenCtors = { '{': Object, '[': Array }; + + var hop = Object.hasOwnProperty; + + return function (json, opt_reviver) { + // Split into tokens + var toks = json.match(jsonToken); + // Construct the object to return + var result; + var tok = toks[0]; + var topLevelPrimitive = false; + if ('{' === tok) { + result = {}; + } else if ('[' === tok) { + result = []; + } else { + // The RFC only allows arrays or objects at the top level, but the JSON.parse + // defined by the EcmaScript 5 draft does allow strings, booleans, numbers, and null + // at the top level. + result = []; + topLevelPrimitive = true; + } + + // If undefined, the key in an object key/value record to use for the next + // value parsed. + var key; + // Loop over remaining tokens maintaining a stack of uncompleted objects and + // arrays. + var stack = [result]; + for (var i = 1 - topLevelPrimitive, n = toks.length; i < n; ++i) { + tok = toks[i]; + + var cont; + switch (tok.charCodeAt(0)) { + default: // sign or digit + cont = stack[0]; + cont[key || cont.length] = +(tok); + key = void 0; + break; + case 0x22: // '"' + tok = tok.substring(1, tok.length - 1); + if (tok.indexOf(SLASH) !== -1) { + tok = tok.replace(escapeSequence, unescapeOne); + } + cont = stack[0]; + if (!key) { + if (cont instanceof Array) { + key = cont.length; + } else { + key = tok || EMPTY_STRING; // Use as key for next value seen. + break; + } + } + cont[key] = tok; + key = void 0; + break; + case 0x5b: // '[' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = []); + key = void 0; + break; + case 0x5d: // ']' + stack.shift(); + break; + case 0x66: // 'f' + cont = stack[0]; + cont[key || cont.length] = false; + key = void 0; + break; + case 0x6e: // 'n' + cont = stack[0]; + cont[key || cont.length] = null; + key = void 0; + break; + case 0x74: // 't' + cont = stack[0]; + cont[key || cont.length] = true; + key = void 0; + break; + case 0x7b: // '{' + cont = stack[0]; + stack.unshift(cont[key || cont.length] = {}); + key = void 0; + break; + case 0x7d: // '}' + stack.shift(); + break; + } + } + // Fail if we've got an uncompleted object. + if (topLevelPrimitive) { + if (stack.length !== 1) { throw new Error(); } + result = result[0]; + } else { + if (stack.length) { throw new Error(); } + } + + if (opt_reviver) { + // Based on walk as implemented in http://www.json.org/json2.js + var walk = function (holder, key) { + var value = holder[key]; + if (value && typeof value === 'object') { + var toDelete = null; + for (var k in value) { + if (hop.call(value, k) && value !== holder) { + // Recurse to properties first. This has the effect of causing + // the reviver to be called on the object graph depth-first. + + // Since 'this' is bound to the holder of the property, the + // reviver can access sibling properties of k including ones + // that have not yet been revived. + + // The value returned by the reviver is used in place of the + // current value of property k. + // If it returns undefined then the property is deleted. + var v = walk(value, k); + if (v !== void 0) { + value[k] = v; + } else { + // Deleting properties inside the loop has vaguely defined + // semantics in ES3 and ES3.1. + if (!toDelete) { toDelete = []; } + toDelete.push(k); + } + } + } + if (toDelete) { + for (var i = toDelete.length; --i >= 0;) { + delete value[toDelete[i]]; + } + } + } + return opt_reviver.call(holder, key, value); + }; + result = walk({ '': result }, ''); + } + + return result; + }; +})(); \ No newline at end of file diff --git a/lib/nodeunit.js b/lib/nodeunit.js new file mode 100644 index 000000000..47ffd8233 --- /dev/null +++ b/lib/nodeunit.js @@ -0,0 +1,1762 @@ +/*! + * Nodeunit + * https://github.com/caolan/nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * json2.js + * http://www.JSON.org/json2.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ +nodeunit = (function(){ +/* + http://www.JSON.org/json2.js + 2010-11-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + 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 && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + 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.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.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'); + }; + } +}()); +var assert = {}; +var types = {}; +var core = {}; +var nodeunit = {}; +var reporter = {}; +(function(){ + + var async = {}; + + // global on the server, window in the browser + var root = this; + var 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 b ? 1 : 0; + }), function(x){return x.value;})); + }) + }; + + async.auto = function(tasks, callback){ + callback = callback || function(){}; + var keys = _keys(tasks); + if(!keys.length) return callback(null); + + var completed = []; + + var listeners = []; + var addListener = function(fn){ + listeners.unshift(fn); + }; + var removeListener = function(fn){ + for(var i=0; i +// +// 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 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. + + +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({message: message, actual: actual, expected: expected}) + +assert.AssertionError = function AssertionError (options) { + this.name = "AssertionError"; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +// code from util.inherits in node +assert.AssertionError.super_ = Error; + + +// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call +// TODO: test what effect this may have +var ctor = function () { this.constructor = assert.AssertionError; }; +ctor.prototype = Error.prototype; +assert.AssertionError.prototype = new ctor(); + + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name+":", this.message].join(' '); + } else { + return [ this.name+":" + , JSON.stringify(this.expected ) + , this.operator + , JSON.stringify(this.actual) + ].join(" "); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, "==", assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, "==", assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (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 _deepEqual(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 (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as determined by !==. +// assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } +}; + +function _throws (shouldThrow, block, err, message) { + var exception = null, + threw = false, + typematters = true; + + message = message || ""; + + //handle optional arguments + if (arguments.length == 3) { + if (typeof(err) == "string") { + message = err; + typematters = false; + } + } else if (arguments.length == 2) { + typematters = false; + } + + try { + block(); + } catch (e) { + threw = true; + exception = e; + } + + if (shouldThrow && !threw) { + fail( "Missing expected exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if (!shouldThrow && threw && typematters && exception instanceof err) { + fail( "Got unwanted exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if ((shouldThrow && threw && typematters && !(exception instanceof err)) || + (!shouldThrow && threw)) { + throw exception; + } +}; + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert['throws'] = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function (err) { if (err) {throw err;}}; +})(assert); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + + + +/** + * Creates assertion objects representing the result of an assert call. + * Accepts an object or AssertionError as its argument. + * + * @param {object} obj + * @api public + */ + +exports.assertion = function (obj) { + return { + method: obj.method || '', + message: obj.message || (obj.error && obj.error.message) || '', + error: obj.error, + passed: function () { + return !this.error; + }, + failed: function () { + return Boolean(this.error); + } + }; +}; + +/** + * Creates an assertion list object representing a group of assertions. + * Accepts an array of assertion objects. + * + * @param {Array} arr + * @param {Number} duration + * @api public + */ + +exports.assertionList = function (arr, duration) { + var that = arr || []; + that.failures = function () { + var failures = 0; + for (var i=0; i'; +}; + + +/** + * Run all tests within each module, reporting the results + * + * @param {Array} files + * @api public + */ + +exports.run = function (modules, options) { + var start = new Date().getTime(); + exports.addStyles(); + + var html = ''; + nodeunit.runModules(modules, { + moduleStart: function (name) { + html += '

    ' + name + '

    '; + html += '
      '; + }, + testDone: function (name, assertions) { + if (!assertions.failures()) { + html += '
    1. ' + name + '
    2. '; + } + else { + html += '
    3. ' + name; + for (var i=0; i'; + } + html += '
      ';
      +                        html += a.error.stack || a.error;
      +                        html += '
      '; + } + }; + html += '
    4. '; + } + }, + moduleDone: function () { + html += '
    '; + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + if (assertions.failures()) { + html += '

    FAILURES: ' + assertions.failures() + + '/' + assertions.length + ' assertions failed (' + + assertions.duration + 'ms)

    '; + } + else { + html += '

    OK: ' + assertions.length + + ' assertions (' + assertions.duration + 'ms)

    '; + } + if (typeof document === 'undefined' && typeof print !== 'undefined') { + return print( html.replace(/
  • /g, '(/) ').replace(/
  • /g, '(X) ').replace(//g, "\n").replace(/<\/h\d>/g, "\n").replace(/
    /g, "\n ").replace(/<\/(li|div)>/g, "\n").replace(/<[^>]+?>/g, '') ); + } + document.body.innerHTML += html; + } + }); +}; +})(reporter); +nodeunit = core; +nodeunit.assert = assert; +nodeunit.reporter = reporter; +nodeunit.run = reporter.run; +return nodeunit; })(); \ No newline at end of file diff --git a/lib/rhino-shim.js b/lib/rhino-shim.js new file mode 100644 index 000000000..263c0727f --- /dev/null +++ b/lib/rhino-shim.js @@ -0,0 +1,69 @@ +global = this; + +load('lib/Rhino-Require/src/require.js'); + +(function(rhinoArgs) { + var dumper; + global.console = { + log: function(/*...*/) { + var args = Array.prototype.slice.call(arguments, 0), + dumper = dumper || require('jsdoc/util/dumper'); + + for (var i = 0, len = args.length; i < len; i++) { + if (typeof args[i] !== 'string') { + args[i] = dumper.dump(args[i]); + } + } + + print( args.join(' ') ); + } + }; + + global.process = { + exit: function(n) { + n = n || 0; + java.lang.System.exit(n); + }, + argv: [null, null].concat( Array.prototype.slice.call(rhinoArgs, 0) ) + }; + + if (typeof JSON === 'undefined') { // JSON is defined in Rhino 1.7+ + load('lib/json.js'); + global.JSON = { + parse: function(raw) { + return jsonParse(raw); + }, + stringify: function(o) { + return ''+ o; + } + } + } + + (function () { + var counter = 1; + var timeoutIds = {}; + + global.setTimeout = function(fn, delay) { + var id = counter++, + timer = new java.util.Timer(); + + timeoutIds[id] = [ + new JavaAdapter(java.util.TimerTask,{run: function(){fn(); timer.purge(); timer.cancel();}}), + timer + ]; + + timer.schedule(timeoutIds[id][0], delay); + return id; + } + + global.clearTimeout = function(id) { + if (typeof timeoutIds[id] !== 'undefined') { + timeoutIds[id][0].cancel(); + timeoutIds[id][1].purge(); + timeoutIds[id][1].cancel(); + delete timeoutIds[id]; + } + } + })(); + +})(arguments); \ No newline at end of file diff --git a/main.js b/main.js deleted file mode 100644 index a837c8f34..000000000 --- a/main.js +++ /dev/null @@ -1,273 +0,0 @@ -/** - * @project jsdoc - * @author Michael Mathews - * @license See LICENSE.md file included in this distribution. - */ - -// try: $ java -classpath build-files/java/classes/js.jar org.mozilla.javascript.tools.shell.Main main.js `pwd` script/to/parse.js - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - -/** The absolute path to the base directory of the jsdoc application. - @type string - @global - */ -const BASEDIR = (arguments[0].replace(/([\/\\])main\.js$/, '$1') + '/').replace('//', '/'); // expects argument[0] to the abspath to main.js - -/** Include a JavaScript module, defined in the CommonJS way. - @param {string} id The identifier of the module you require. - @returns {mixed} The module's "exports" value. - @see - */ -function require(id) { // like commonjs - var moduleContent = '', - moduleUri; - - for (var i = 0, len = require.paths.length; i < len; i++) { - moduleUri = require.paths[i] + '/' + id + '.js'; - moduleContent = ''; - - var file = new java.io.File(moduleUri); - if ( file.exists() && file.canRead() && !file.isDirectory() ) { - try { - var scanner = new java.util.Scanner(file).useDelimiter("\\Z"); - moduleContent = String( scanner.next() ); - } - catch(ignored) { } - - if (moduleContent) { break; } - } - } - - if (moduleContent) { - try { - var f = new Function('require', 'exports', 'module', moduleContent), - exports = require.cache[moduleUri] || {}, - module = { id: id, uri: moduleUri }; - - - f.call({}, require, exports, module); - } - catch(e) { - throw 'Unable to require source code from "' + moduleUri + '": ' + e.toSource(); - } - - exports = module.exports || exports; - require.cache[id] = exports; - } - else { - throw 'The requested module cannot be returned: no content for id: "' + id + '" in paths: ' + require.paths.join(', '); - } - - return exports; -} -require.root = BASEDIR; -require.paths = [ require.root + 'modules', require.root + 'modules/common' ]; -require.cache = {}; // cache module exports. Like: {id: exported} - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - - -/** Data representing the environment in which this app is running. - @namespace -*/ -env = { - /** Running start and finish times. */ - run: { - start: new Date(), - finish: null - }, - - /** - The command line arguments passed into jsdoc. - @type Array - */ - args: Array.prototype.slice.call(arguments, 1), // jsdoc.jar adds argument[0], the abspath to main.js, user args follow - - - /** - The parsed JSON data from the configuration file. - @type Object - */ - conf: {}, - - /** - The command line arguments, parsed into a key/value hash. - @type Object - @example if (env.opts.help) { print 'Helpful message.'; } - */ - opts: {} -}; - -/** - Data that must be shared across the entire application. - @namespace -*/ -app = { - jsdoc: { - scanner: new (require('jsdoc/src/scanner').Scanner)(), - parser: new (require('jsdoc/src/parser').Parser)(), - name: require('jsdoc/name') - } -} - -try { main(); } -catch(e) { - if (e.rhinoException != null) { - e.rhinoException.printStackTrace(); - } - else throw e; -} -finally { env.run.finish = new Date(); } - -/** Print string/s out to the console. - @param {string} ... String/s to print out to console. - */ -function print() { - for (var i = 0, leni = arguments.length; i < leni; i++) { - java.lang.System.out.println('' + arguments[i]); - } -} - -/** - Try to recursively print out all key/values in an object. - @global - @param {Object} ... Object/s to dump out to console. - */ -function dump() { - for (var i = 0, leni = arguments.length; i < leni; i++) { - print( require('common/dumper').dump(arguments[i]) ); - } -} - -/** @global - @param {string} filepath The path to the script file to include (read and execute). -*/ -function include(filepath) { - try { - load(BASEDIR + filepath); - } - catch (e) { - print('Cannot include "' + BASEDIR + filepath + '": '+e); - } -} - -/** - Cause the VM running jsdoc to exit running. - @param {number} [n = 0] The exit status. - */ -function exit(n) { - n = n || 0; - java.lang.System.exit(n); -} - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - - -/** - Run the jsoc application. - */ -function main() { - var sourceFiles, - packageJson, - docs, - jsdoc = { - opts: { - parser: require('jsdoc/opts/parser'), - } - }; - - env.opts = jsdoc.opts.parser.parse(env.args); - - try { - env.conf = JSON.parse( - require('fs').read( env.opts.configure || BASEDIR+'conf.json' ) - ); - } - catch (e) { - throw('Configuration file cannot be evaluated. '+e); - } - - if (env.opts.query) { - env.opts.query = require('query').toObject(env.opts.query); - } - - if (env.opts.help) { - print( jsdoc.opts.parser.help() ); - exit(0); - } - else if (env.opts.test) { - include('test/runner.js'); - exit(0); - } - - // allow user-defined plugins to register listeners - if (env.conf.plugins) { - for (var i = 0, leni = env.conf.plugins.length; i < leni; i++) { - include(env.conf.plugins[i]); - } - } - - // any source file named package.json is treated special - for (var i = 0, l = env.opts._.length; i < l; i++ ) { - if (/\bpackage\.json$/i.test(env.opts._[i])) { - packageJson = require('fs').read( env.opts._[i] ); - env.opts._.splice(i--, 1); - } - } - - if (env.opts._.length > 0) { // are there any files to scan and parse? - - var includeMatch = (env.conf.source && env.conf.source.includePattern)? new RegExp(env.conf.source.includePattern) : null, - excludeMatch = (env.conf.source && env.conf.source.excludePattern)? new RegExp(env.conf.source.excludePattern) : null; - - sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), includeMatch, excludeMatch); - - require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); - - docs = app.jsdoc.parser.parse(sourceFiles, env.opts.encoding); - - if (packageJson) { - var packageDocs = new (require('jsdoc/package').Package)(packageJson); - packageDocs.files = sourceFiles || []; - docs.push(packageDocs); - } - - function indexAll(docs) { - var lookupTable = {}; - - docs.forEach(function(doc) { - if ( !lookupTable.hasOwnProperty(doc.longname) ) { - lookupTable[doc.longname] = []; - } - lookupTable[doc.longname].push(doc); - }); - docs.index = lookupTable; - } - - indexAll(docs); - - require('jsdoc/borrow').resolveBorrows(docs); - - if (env.opts.expel) { - dump(docs); - exit(0); - } - - env.opts.template = env.opts.template || 'default'; - - // should define a global "publish" function - include('templates/' + env.opts.template + '/publish.js'); - - if (typeof publish === 'function') { - publish( - new (require('typicaljoe/taffy'))(docs), - env.opts - ); - } - else { // TODO throw no publish warning? - } - } -} \ No newline at end of file diff --git a/modules/common/args.js b/modules/common/args.js deleted file mode 100644 index 6bb7a3996..000000000 --- a/modules/common/args.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - Parse the command line arguments. - @module common/args - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ -(function() { - - /** - Create an instance of the parser. - @classdesc A parser to interpret the key value pairs entered on the command - line. - @constructor - */ - exports.ArgParser = function() { - this._options = []; - } - - exports.ArgParser.prototype._getOptionByShortName = function(name) { - for (var i = this._options.length; i--;) { - if (this._options[i].shortName === name) { return this._options[i]; } - } - return null; - } - - exports.ArgParser.prototype._getOptionByLongName = function(name) { - for (var i = this._options.length; i--;) { - if (this._options[i].longName === name) { return this._options[i]; } - } - return null; - } - - /** - * Provide information about a legal option. - * @param {character} shortName The short name of the option, entered like: -T. - * @param {string} longName The equivalent long name of the option, entered like: --test. - * @param {boolean} hasValue Does this option require a value? Like: -t templatename - * @param {string} helpText - * @example - * myParser.addOption('t', 'template', true, 'The path to the template.'); - * myParser.addOption('h', 'help', false, 'Show the help message.'); - */ - exports.ArgParser.prototype.addOption = function(shortName, longName, hasValue, helpText) { - this._options.push({shortName: shortName, longName: longName, hasValue: hasValue, helpText: helpText}); - }; - - /** - Generate a summary of all the options with corresponding help text. - @returns {string} - */ - exports.ArgParser.prototype.help = function() { - var help = 'OPTIONS:\n', - option; - - for (var i = 0, leni = this._options.length; i < leni; i++) { - option = this._options[i]; - - if (option.shortName) { - help += '-' + option.shortName + (option.longName? ' or ' : ''); - } - - if (option.longName) { - help += '--' + option.longName; - } - - if (option.hasValue) { - help += ' '; - } - - help += ' ' + option.helpText + '\n'; - } - - return help; - }; - - /** - Get the options. - @param {Array.} args An array, like ['-x', 'hello'] - @param {Object} [defaults={}] An optional collection of default values. - @returns {Object} The keys will be the longNames, or the shortName if - no longName is defined for that option. The values will be the values - provided, or `true` if the option accepts no value. - */ - exports.ArgParser.prototype.parse = function(args, defaults) { - var result = defaults || {}; - - result._ = []; - - for (var i = 0, leni = args.length; i < leni; i++) { - var arg = '' + args[i], - next = (i < leni-1)? '' + args[i+1] : null, - option, - shortName, - longName, - name, - value = null; - - // like -t - if (arg.charAt(0) === '-') { - - // like: --template - if (arg.charAt(1) === '-') { - name = longName = arg.slice(2); - option = this._getOptionByLongName(longName); - } - else { - name = shortName = arg.slice(1); - option = this._getOptionByShortName(shortName); - } - - if (option === null) { - throw new Error( 'Unknown command line option found: ' + name ); - } - - if (option.hasValue) { - value = next; - i++; - - if (value === null || value.charAt(0) === '-') { - throw new Error( 'Command line option requires a value: ' + name ); - } - } - else { - value = true; - } - - if (option.longName && shortName) { - name = option.longName; - } - - result[name] = value; - } - else { - result._.push(arg); - } - } - - return result; - } -})(); \ No newline at end of file diff --git a/modules/common/assert.js b/modules/common/assert.js deleted file mode 100644 index a693dfbcc..000000000 --- a/modules/common/assert.js +++ /dev/null @@ -1,302 +0,0 @@ -// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 -// -// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! -// -// Originally from narwhal.js (http://narwhaljs.org) -// Copyright (c) 2009 Thomas Robinson <280north.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 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. - -// UTILITY -var common = { util: require('common/util') };; -var pSlice = Array.prototype.slice; - -// 1. The assert module provides functions that throw -// AssertionError's when particular conditions are not met. The -// assert module must conform to the following interface. - -var assert = exports; - -// 2. The AssertionError is defined in assert. -// new assert.AssertionError({ message: message, -// actual: actual, -// expected: expected }) - -assert.AssertionError = function AssertionError(options) { - this.name = 'AssertionError'; - this.message = options.message; - this.actual = options.actual; - this.expected = options.expected; - this.operator = options.operator; - var stackStartFunction = options.stackStartFunction || fail; - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, stackStartFunction); - } -}; -common.util.inherits(assert.AssertionError, Error); - -assert.AssertionError.prototype.toString = function() { - if (this.message) { - return [this.name + ':', this.message].join(' '); - } else { - return [this.name + ':', - JSON.stringify(this.expected), - this.operator, - JSON.stringify(this.actual)].join(' '); - } -}; - -// assert.AssertionError instanceof Error - -assert.AssertionError.__proto__ = Error.prototype; - -// At present only the three keys mentioned above are used and -// understood by the spec. Implementations or sub modules can pass -// other keys to the AssertionError's constructor - they will be -// ignored. - -// 3. All of the following functions must throw an AssertionError -// when a corresponding condition is not met, with a message that -// may be undefined if not provided. All assertion methods provide -// both the actual and expected values to the assertion error for -// display purposes. - -function fail(actual, expected, message, operator, stackStartFunction) { - throw new assert.AssertionError({ - message: message, - actual: actual, - expected: expected, - operator: operator, - stackStartFunction: stackStartFunction - }); -} - -// EXTENSION! allows for well behaved errors defined elsewhere. -assert.fail = fail; - -// 4. Pure assertion tests whether a value is truthy, as determined -// by !!guard. -// assert.ok(guard, message_opt); -// This statement is equivalent to assert.equal(true, guard, -// message_opt);. To test strictly for the value true, use -// assert.strictEqual(true, guard, message_opt);. - -assert.ok = function ok(value, message) { - if (!!!value) fail(value, true, message, '==', assert.ok); -}; - -// 5. The equality assertion tests shallow, coercive equality with -// ==. -// assert.equal(actual, expected, message_opt); - -assert.equal = function equal(actual, expected, message) { - if (actual != expected) fail(actual, expected, message, '==', assert.equal); -}; - -// 6. The non-equality assertion tests for whether two objects are not equal -// with != assert.notEqual(actual, expected, message_opt); - -assert.notEqual = function notEqual(actual, expected, message) { - if (actual == expected) { - fail(actual, expected, message, '!=', assert.notEqual); - } -}; - -// 7. The equivalence assertion tests a deep equality relation. -// assert.deepEqual(actual, expected, message_opt); - -assert.deepEqual = function deepEqual(actual, expected, message) { - if (!_deepEqual(actual, expected)) { - fail(actual, expected, message, 'deepEqual', assert.deepEqual); - } -}; - -function _deepEqual(actual, expected) { - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - - } else if (this.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 _deepEqual(a, b); - } - try { - var ka = Object.keys(a), - kb = Object.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 (!_deepEqual(a[key], b[key])) return false; - } - return true; -} - -// 8. The non-equivalence assertion tests for any deep inequality. -// assert.notDeepEqual(actual, expected, message_opt); - -assert.notDeepEqual = function notDeepEqual(actual, expected, message) { - if (_deepEqual(actual, expected)) { - fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); - } -}; - -// 9. The strict equality assertion tests strict equality, as determined by ===. -// assert.strictEqual(actual, expected, message_opt); - -assert.strictEqual = function strictEqual(actual, expected, message) { - if (actual !== expected) { - fail(actual, expected, message, '===', assert.strictEqual); - } -}; - -// 10. The strict non-equality assertion tests for strict inequality, as -// determined by !==. assert.notStrictEqual(actual, expected, message_opt); - -assert.notStrictEqual = function notStrictEqual(actual, expected, message) { - if (actual === expected) { - fail(actual, expected, message, '!==', assert.notStrictEqual); - } -}; - -function expectedException(actual, expected) { - if (!actual || !expected) { - return false; - } - - if (expected instanceof RegExp) { - return expected.test(actual); - } else if (actual instanceof expected) { - return true; - } else if ( expected.call({}, actual) === true ) { - return true; - } - - return false; -} - -function _throws(shouldThrow, block, expected, message) { - var actual; - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - try { - block(); - } catch (e) { - actual = e; - } - - message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + - (message ? ' ' + message : '.'); - - if (shouldThrow && !actual) { - fail('Missing expected exception' + message); - } - - if (!shouldThrow && expectedException(actual, expected)) { - fail('Got unwanted exception' + message); - } - - if ((shouldThrow && actual && expected && - !expectedException(actual, expected)) || (!shouldThrow && actual)) { - throw actual; - } -} - -// 11. Expected to throw an error: -// assert.throws(block, Error_opt, message_opt); - -assert['throws'] = function(block, /*optional*/error, /*optional*/message) { - _throws.apply(this, [true].concat(pSlice.call(arguments))); -}; - -// EXTENSION! This is annoying to write outside this module. -assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { - _throws.apply(this, [false].concat(pSlice.call(arguments))); -}; - -assert.ifError = function(err) { if (err) {throw err;}}; \ No newline at end of file diff --git a/modules/common/events.js b/modules/common/events.js deleted file mode 100644 index d2e9c500e..000000000 --- a/modules/common/events.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - Functions related to events. Designed to be mixed into other classes. - @exports common/events - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. -*/ -module.exports = { - /** - @param {string} type - @param {function} handler - @returns {this} - */ - on: function(type, handler, context) { - if ( !type || !handler ) { - return; - } - if ( !context ) { context = this; } - - if ( !this.__bindings ) { this.__bindings = {}; } - // TODO check here for hasOwnProperty? - if ( !this.__bindings[type] ) { this.__bindings[type] = []; } - - var call = function(e) { - return handler.call(context, e); - }; - this.__bindings[type].push( {handler: handler, call: call} ); - - return this; // chainable - }, - - /** - @param {string} type - @param {object} [eventData] - @returns {this} - */ - fire: function(eventType, eventData) { - if ( !eventType || !this.__bindings || !this.__bindings[eventType] ) { - return; - } - - // run handlers in first-in-first-run order - for (var i = 0, leni = this.__bindings[eventType].length; i < leni; i++) { - if ( false === this.__bindings[eventType][i].call(eventData) ) { - if (eventData) { eventData.defaultPrevented = true; } - } - } - - return this; // chainable - }, - - /** - @param {string} type - @param {function} handler - */ - removeListener: function(type, handler) { - if ( !type || !handler || !this.__bindings || !this.__bindings[type] ) { - return; - } - - var i = this.__bindings[type].length; - while ( i-- ) { - if ( this.__bindings[type][i].handler === handler ) { - this.__bindings[type].splice(i, 1); - } - } - - if (this.__bindings[type].length === 0) { - delete this.__bindings[type]; - } - } -}; \ No newline at end of file diff --git a/modules/common/fs.js b/modules/common/fs.js deleted file mode 100644 index 04f67182f..000000000 --- a/modules/common/fs.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - Functions related to interaction with the filesystem. - @module common/fs - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ - -(function() { - var slash = java.lang.System.getProperty('file.separator') || '/', - File = java.io.File, - defaultEncoding = java.lang.System.getProperty('file.encoding'); - - /** - Read the contents of a file. - @param {string} path - @param {string} encoding - @returns {string} The contents of the file. - */ - exports.read = function(path, encoding) { - var options = options || {}, - encoding = encoding || defaultEncoding, - input; - - input = new java.util.Scanner( - new File(path), - encoding - ).useDelimiter("\\Z"); - - return String( input.next() ); - } - - /** - Write the content to a file. - @param {string} path - @param {string} content - @param {string} encoding - */ - exports.write = function(path, content, encoding) { - var options = options || {}, - encoding = encoding || defaultEncoding, - output; - - output = new java.io.PrintWriter( - new java.io.OutputStreamWriter( - new java.io.FileOutputStream(path), - encoding - ) - ); - - output.write(content); - output.flush(); - output.close(); - } - - /** - * Check if a file exists. - * @param {string} path The file to check. - * @returns {boolean} - */ - exports.exists = function(path) { - var file = new File(path); - - if (file.isDirectory()){ - return true; - } - if (!file.exists()){ - return false; - } - if (!file.canRead()){ - return false; - } - return true; - } - - /** - * Get a list of all files in a given directory. Will not include files that - * start with a dot. - * @type string[] - * @param {string} dir The starting directory to look in. - * @param {number} [recurse=1] How many levels deep to scan. - * @returns {string[]} An array of {string} paths to the files in the given directory. - */ - exports.ls = function(dir, recurse, _allFiles, _path) { - var files, - file; - - if (typeof _path === 'undefined') { // initially - _allFiles = []; - _path = [dir]; - } - - if (_path.length === 0) { return _allFiles; } - if (typeof recurse === 'undefined') { recurse = 1; } - - dir = new File(dir); - if (!dir.directory) { return [String(dir)]; } - files = dir.list(); - - for (var f = 0, lenf = files.length; f < lenf; f++) { - file = String(files[f]); - - if (file.match(/^\.[^\.\/\\]/)) { continue; } // skip dot files - - if ((new File(_path.join(slash) + slash + file)).list()) { // it's a directory - _path.push(file); - - if (_path.length - 1 < recurse) { - exports.ls(_path.join(slash), recurse, _allFiles, _path); - } - _path.pop(); - } - else { // it's a file - _allFiles.push( - fixSlash( (_path.join(slash) + slash + file) ) - ); - } - } - - return _allFiles; - } - - exports.copyFile = function(inFile, outDir, fileName) { - if (fileName == null) fileName = exports.toFile(inFile); - - outDir = exports.toDir(outDir); - - var inFile = new File(inFile); - var outFile = new File(outDir+slash+fileName); - - var bis = new Packages.java.io.BufferedInputStream(new Packages.java.io.FileInputStream(inFile), 4096); - var bos = new Packages.java.io.BufferedOutputStream(new Packages.java.io.FileOutputStream(outFile), 4096); - var theChar; - while ((theChar = bis.read()) != -1) { - bos.write(theChar); - } - bos.close(); - bis.close(); - } - - exports.toDir = function(path) { - var file = new File(path); - - if (file.isDirectory()){ - return path; - } - - var parts = path.split(/[\\\/]/); - parts.pop(); - - return parts.join(slash); - } - - exports.toFile = function(path) { - var parts = path.split(/[\\\/]/); - return parts.pop(); - } - - exports.mkPath = function(/**Array*/ path) { - if (path.constructor != Array) path = path.split(/[\\\/]/); - var make = ""; - for (var i = 0, l = path.length; i < l; i++) { - make += path[i] + slash; - if (! exports.exists(make)) { - exports.makeDir(make); - } - } - } - - exports.makeDir = function(/**string*/ path) { - var dirPath = (exports.toDir(path)); - (new File(dirPath)).mkdir(); - } - - // fix multiple slashes, like one//two - function fixSlash(path) { - return path.replace(/[\/\\]+/g, slash); - } - -})(); - diff --git a/modules/common/query.js b/modules/common/query.js deleted file mode 100644 index 075f89857..000000000 --- a/modules/common/query.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - @description Support parsing of command line querystrings into JS objects. - @module common/query - @example - - -q 'format=xml&root+node=documentation&publish=' - - > becomes - - { - "format": "xml", - "root node": "documentation", - "publish": true - } -*/ -(function() { - var query = module.exports = { - /** - @name module:common/query.toObject - @param {string} querystring - @returns {object} - */ - toObject: function(querystring) { - var object = {}; - - querystring.replace( - new RegExp('([^?=&]+)(?:=([^&]*))?', 'g'), - function($0, key, val) { - object[query._decode(key)] = - (typeof val !== 'undefined')? query._decode(val) : true; - } - ); - - return object; - }, - - /** @private */ - _decode: function(string) { - return decodeURIComponent( string.replace(/\+/g, ' ') ); - } - }; -})(); \ No newline at end of file diff --git a/modules/common/sqlite.js b/modules/common/sqlite.js deleted file mode 100644 index 7bc9f97ad..000000000 --- a/modules/common/sqlite.js +++ /dev/null @@ -1,55 +0,0 @@ -importPackage(java.sql); -java.lang.Class.forName('org.sqlite.JDBC'); - -var DB = exports.DB = function(dbname) { - this.dbname = dbname; -} - -DB.prototype.connect = function() { - this.conn = DriverManager.getConnection('jdbc:sqlite:' + this.dbname); - return this; -} - -DB.prototype.prepare = function(statement) { // like "insert into symbols values (?, ?, ?);" - this.prep = this.conn.prepareStatement(statement); - return this; -} - -DB.prototype.values = function() { // replaces "?" placeholders in prepared statement - for (var i = 0, leni = arguments.length; i < leni; i++) { - this.prep.setString(i+1, arguments[i]); - } - this.prep.addBatch(); - - return this; -} - -DB.prototype.commit = function() { - this.conn.setAutoCommit(false); - this.prep.executeBatch(); - this.conn.setAutoCommit(true); - - return this; -} - -DB.prototype.query = function(statement) { // like "select * from symbols;" - this.stat = this.conn.createStatement(); - this.resultSet = this.stat.executeQuery(statement); - - return this.resultSet; -} - -DB.prototype.update = function(statement) { // like "create table symbols (id, longname, kind);" - this.stat = this.conn.createStatement(); - this.resultSet = this.stat.executeUpdate(statement); - - return this; -} - -DB.prototype.close = function() { - if (this.resultSet) this.resultSet.close(); - if (this.stat) this.stat.close(); - if (this.conn) this.conn.close(); - - return this; -} diff --git a/modules/common/util.js b/modules/common/util.js deleted file mode 100644 index 1b57ee349..000000000 --- a/modules/common/util.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - @module common/util - */ - -exports.print = function() { - for (var i = 0, len = arguments.length; i < len; ++i) { - java.lang.System.out.print(String(arguments[i])); - } -}; - -exports.puts = function() { - for (var i = 0, len = arguments.length; i < len; ++i) { - java.lang.System.out.println(arguments[i] + '\n'); - } -}; - -exports.debug = function(x) { - exports.puts('DEBUG: ' + x + '\n'); -}; - -var error = exports.error = function(x) { - for (var i = 0, len = arguments.length; i < len; ++i) { - exports.puts(arguments[i] + '\n'); - } -}; - -exports.format = { - stylize: function(str, styleType) { - // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics - var styles = - { '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] }; - - var style = - { 'special': 'cyan', - 'number': 'blue', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' }[styleType]; - - if (style) { - return '\033[' + styles[style][0] + 'm' + str + - '\033[' + styles[style][1] + 'm'; - } else { - return str; - } - }, - - pad: function (n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); - } -} - -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - -/** - * Create a timestamp string. - * @returns {string} Like 26 Feb 2011 16:19:34 - */ -exports.timestamp = function() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], d.getFullYear(), time].join(' '); -} - -exports.log = function(msg) { - exports.puts(exports.timestamp() + ' - ' + msg.toString()); -}; - -/** - * Inherit the prototype methods from one constructor into another. - * @param {function} ctor Constructor function which needs to inherit the prototype. - * @param {function} superCtor Constructor function to inherit prototype from. - */ -exports.inherits = function(ctor, superCtor) { - ctor.super_ = superCtor; - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { value: ctor, enumerable: false } - }); -}; - -/** - * Mix in the members of a source object over a target object. - * @param {object} target - * @param {object} source - */ -exports.mixin = function(target, source /*...*/){ - var sourceProperty; - - for (var i = 1, len = arguments.length; i < len; i++) { - source = arguments[i]; - - for (sourceProperty in source) { - if( source.hasOwnProperty(sourceProperty) ) { - target[sourceProperty] = source[sourceProperty]; // overwrites target property - } - } - } - - return target; -}; \ No newline at end of file diff --git a/modules/evilstreak/markdown.js b/modules/evilstreak/markdown.js deleted file mode 100644 index cdfbeb654..000000000 --- a/modules/evilstreak/markdown.js +++ /dev/null @@ -1,1446 +0,0 @@ -// Released under MIT license -// Copyright (c) 2009-2010 Dominic Baggott -// Copyright (c) 2009-2010 Ash Berlin - -(function( expose ) { - -/** - * class Markdown - * - * Markdown processing in Javascript done right. We have very particular views - * on what constitutes 'right' which include: - * - * - produces well-formed HTML (this means that em and strong nesting is - * important) - * - * - has an intermediate representation to allow processing of parsed data (We - * in fact have two, both as [JsonML]: a markdown tree and an HTML tree). - * - * - is easily extensible to add new dialects without having to rewrite the - * entire parsing mechanics - * - * - has a good test suite - * - * This implementation fulfills all of these (except that the test suite could - * do with expanding to automatically run all the fixtures from other Markdown - * implementations.) - * - * ##### Intermediate Representation - * - * *TODO* Talk about this :) Its JsonML, but document the node names we use. - * - * [JsonML]: http://jsonml.org/ "JSON Markup Language" - **/ -var Markdown = expose.Markdown = function Markdown(dialect) { - switch (typeof dialect) { - case "undefined": - this.dialect = Markdown.dialects.Gruber; - break; - case "object": - this.dialect = dialect; - break; - default: - if (dialect in Markdown.dialects) { - this.dialect = Markdown.dialects[dialect]; - } - else { - throw new Error("Unknown Markdown dialect '" + String(dialect) + "'"); - } - break; - } - this.em_state = []; - this.strong_state = []; - this.debug_indent = ""; -} - -/** - * parse( markdown, [dialect] ) -> JsonML - * - markdown (String): markdown string to parse - * - dialect (String | Dialect): the dialect to use, defaults to gruber - * - * Parse `markdown` and return a markdown document as a Markdown.JsonML tree. - **/ -expose.parse = function( source, dialect ) { - // dialect will default if undefined - var md = new Markdown( dialect ); - return md.toTree( source ); -} - -/** - * toHTML( markdown ) -> String - * toHTML( md_tree ) -> String - * - markdown (String): markdown string to parse - * - md_tree (Markdown.JsonML): parsed markdown tree - * - * Take markdown (either as a string or as a JsonML tree) and run it through - * [[toHTMLTree]] then turn it into a well-formated HTML fragment. - **/ -expose.toHTML = function toHTML( source ) { - var input = expose.toHTMLTree( source ); - - return expose.renderJsonML( input ); -} - -/** - * toHTMLTree( markdown, [dialect] ) -> JsonML - * toHTMLTree( md_tree ) -> JsonML - * - markdown (String): markdown string to parse - * - dialect (String | Dialect): the dialect to use, defaults to gruber - * - md_tree (Markdown.JsonML): parsed markdown tree - * - * Turn markdown into HTML, represented as a JsonML tree. If a string is given - * to this function, it is first parsed into a markdown tree by calling - * [[parse]]. - **/ -expose.toHTMLTree = function toHTMLTree( input, dialect ) { - // convert string input to an MD tree - if ( typeof input ==="string" ) input = this.parse( input, dialect ); - - // Now convert the MD tree to an HTML tree - - // remove references from the tree - var attrs = extract_attr( input ), - refs = {}; - - if ( attrs && attrs.references ) { - refs = attrs.references; - } - - var html = convert_tree_to_html( input, refs ); - merge_text_nodes( html ); - return html; -} - -var mk_block = Markdown.mk_block = function(block, trail, line) { - // Be helpful for default case in tests. - if ( arguments.length == 1 ) trail = "\n\n"; - - var s = new String(block); - s.trailing = trail; - // To make it clear its not just a string - s.toSource = function() { - return "Markdown.mk_block( " + - uneval(block) + - ", " + - uneval(trail) + - ", " + - uneval(line) + - " )" - } - - if (line != undefined) - s.lineNumber = line; - - return s; -} - -function count_lines( str ) { - var n = 0, i = -1;; - while ( ( i = str.indexOf('\n', i+1) ) != -1) n++; - return n; -} - -// Internal - split source into rough blocks -Markdown.prototype.split_blocks = function splitBlocks( input, startLine ) { - // [\s\S] matches _anything_ (newline or space) - var re = /([\s\S]+?)($|\n(?:\s*\n|$)+)/g, - blocks = [], - m; - - var line_no = 1; - - if ( ( m = (/^(\s*\n)/)(input) ) != null ) { - // skip (but count) leading blank lines - line_no += count_lines( m[0] ); - re.lastIndex = m[0].length; - } - - while ( ( m = re(input) ) != null ) { - blocks.push( mk_block( m[1], m[2], line_no ) ); - line_no += count_lines( m[0] ); - } - - return blocks; -} - -/** - * Markdown#processBlock( block, next ) -> undefined | [ JsonML, ... ] - * - block (String): the block to process - * - next (Array): the following blocks - * - * Process `block` and return an array of JsonML nodes representing `block`. - * - * It does this by asking each block level function in the dialect to process - * the block until one can. Succesful handling is indicated by returning an - * array (with zero or more JsonML nodes), failure by a false value. - * - * Blocks handlers are responsible for calling [[Markdown#processInline]] - * themselves as appropriate. - * - * If the blocks were split incorrectly or adjacent blocks need collapsing you - * can adjust `next` in place using shift/splice etc. - * - * If any of this default behaviour is not right for the dialect, you can - * define a `__call__` method on the dialect that will get invoked to handle - * the block processing. - */ -Markdown.prototype.processBlock = function processBlock( block, next ) { - var cbs = this.dialect.block, - ord = cbs.__order__; - - if ( "__call__" in cbs ) { - return cvs.__call__.call(this, block, next); - } - - for ( var i = 0; i < ord.length; i++ ) { - //D:this.debug( "Testing", ord[i] ); - var res = cbs[ ord[i] ].call( this, block, next ); - if ( res ) { - //D:this.debug(" matched"); - if ( !res instanceof Array || ( res.length > 0 && !( res[0] instanceof Array ) ) ) - this.debug(ord[i], "didn't return a proper array"); - //D:this.debug( "" ); - return res; - } - } - - // Uhoh! no match! Should we throw an error? - return []; -} - -Markdown.prototype.processInline = function processInline( block ) { - return this.dialect.inline.__call__.call( this, String( block ) ); -} - -/** - * Markdown#toTree( source ) -> JsonML - * - source (String): markdown source to parse - * - * Parse `source` into a JsonML tree representing the markdown document. - **/ -// custom_tree means set this.tree to `custom_tree` and restore old value on return -Markdown.prototype.toTree = function toTree( source, custom_root ) { - var blocks = source instanceof Array - ? source - : this.split_blocks( source ); - - // Make tree a member variable so its easier to mess with in extensions - var old_tree = this.tree; - try { - this.tree = custom_root || this.tree || [ "markdown" ]; - - blocks: - while ( blocks.length ) { - var b = this.processBlock( blocks.shift(), blocks ); - - // Reference blocks and the like won't return any content - if ( !b.length ) continue blocks; - - this.tree.push.apply( this.tree, b ); - } - return this.tree; - } - finally { - if ( custom_root ) - this.tree = old_tree; - } - -} - -// Noop by default -Markdown.prototype.debug = function () { - var args = Array.prototype.slice.call( arguments); - args.unshift(this.debug_indent); - print.apply( print, args ); -} - -Markdown.prototype.loop_re_over_block = function( re, block, cb ) { - // Dont use /g regexps with this - var m, - b = block.valueOf(); - - while ( b.length && (m = re(b) ) != null) { - b = b.substr( m[0].length ); - cb.call(this, m); - } - return b; -} - -/** - * Markdown.dialects - * - * Namespace of built-in dialects. - **/ -Markdown.dialects = {}; - -/** - * Markdown.dialects.Gruber - * - * The default dialect that follows the rules set out by John Gruber's - * markdown.pl as closely as possible. Well actually we follow the behaviour of - * that script which in some places is not exactly what the syntax web page - * says. - **/ -Markdown.dialects.Gruber = { - block: { - atxHeader: function atxHeader( block, next ) { - var m = block.match( /^(#{1,6})\s*(.*?)\s*#*\s*(?:\n|$)/ ); - - if ( !m ) return undefined; - - var header = [ "header", { level: m[ 1 ].length }, m[ 2 ] ]; - - if ( m[0].length < block.length ) - next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); - - return [ header ]; - }, - - setextHeader: function setextHeader( block, next ) { - var m = block.match( /^(.*)\n([-=])\2\2+(?:\n|$)/ ); - - if ( !m ) return undefined; - - var level = ( m[ 2 ] === "=" ) ? 1 : 2; - var header = [ "header", { level : level }, m[ 1 ] ]; - - if ( m[0].length < block.length ) - next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); - - return [ header ]; - }, - - code: function code( block, next ) { - // | Foo - // |bar - // should be a code block followed by a paragraph. Fun - // - // There might also be adjacent code block to merge. - - var ret = [], - re = /^(?: {0,3}\t| {4})(.*)\n?/, - lines; - - // 4 spaces + content - var m = block.match( re ); - - if ( !m ) return undefined; - - block_search: - do { - // Now pull out the rest of the lines - var b = this.loop_re_over_block( - re, block.valueOf(), function( m ) { ret.push( m[1] ) } ); - - if (b.length) { - // Case alluded to in first comment. push it back on as a new block - next.unshift( mk_block(b, block.trailing) ); - break block_search; - } - else if (next.length) { - // Check the next block - it might be code too - var m = next[0].match( re ); - - if ( !m ) break block_search; - - // Pull how how many blanks lines follow - minus two to account for .join - ret.push ( block.trailing.replace(/[^\n]/g, '').substring(2) ); - - block = next.shift(); - } - else - break block_search; - } while (true); - - return [ [ "code_block", ret.join("\n") ] ]; - }, - - horizRule: function horizRule( block, next ) { - // this needs to find any hr in the block to handle abutting blocks - var m = block.match( /^(?:([\s\S]*?)\n)?[ \t]*([-_*])(?:[ \t]*\2){2,}[ \t]*(?:\n([\s\S]*))?$/ ); - - if ( !m ) { - return undefined; - } - - var jsonml = [ [ "hr" ] ]; - - // if there's a leading abutting block, process it - if ( m[ 1 ] ) { - jsonml.unshift.apply( jsonml, this.processBlock( m[ 1 ], [] ) ); - } - - // if there's a trailing abutting block, stick it into next - if ( m[ 3 ] ) { - next.unshift( mk_block( m[ 3 ] ) ); - } - - return jsonml; - }, - - // There are two types of lists. Tight and loose. Tight lists have no whitespace - // between the items (and result in text just in the
  • ) and loose lists, - // which have an empty line between list items, resulting in (one or more) - // paragraphs inside the
  • . - // - // There are all sorts weird edge cases about the original markdown.pl's - // handling of lists: - // - // * Nested lists are supposed to be indented by four chars per level. But - // if they aren't, you can get a nested list by indenting by less than - // four so long as the indent doesn't match an indent of an existing list - // item in the 'nest stack'. - // - // * The type of the list (bullet or number) is controlled just by the - // first item at the indent. Subsequent changes are ignored unless they - // are for nested lists - // - lists: (function( ) { - // Use a closure to hide a few variables. - var any_list = "[*+-]|\\d\\.", - bullet_list = /[*+-]/, - number_list = /\d+\./, - // Capture leading indent as it matters for determining nested lists. - is_list_re = new RegExp( "^( {0,3})(" + any_list + ")[ \t]+" ), - indent_re = "(?: {0,3}\\t| {4})"; - - // TODO: Cache this regexp for certain depths. - // Create a regexp suitable for matching an li for a given stack depth - function regex_for_depth( depth ) { - - return new RegExp( - // m[1] = indent, m[2] = list_type - "(?:^(" + indent_re + "{0," + depth + "} {0,3})(" + any_list + ")\\s+)|" + - // m[3] = cont - "(^" + indent_re + "{0," + (depth-1) + "}[ ]{0,4})" - ); - } - function expand_tab( input ) { - return input.replace( / {0,3}\t/g, " " ); - } - - // Add inline content `inline` to `li`. inline comes from processInline - // so is an array of content - function add(li, loose, inline, nl) { - if (loose) { - li.push( [ "para" ].concat(inline) ); - return; - } - // Hmmm, should this be any block level element or just paras? - var add_to = li[li.length -1] instanceof Array && li[li.length - 1][0] == "para" - ? li[li.length -1] - : li; - - // If there is already some content in this list, add the new line in - if (nl && li.length > 1) inline.unshift(nl); - - for (var i=0; i < inline.length; i++) { - var what = inline[i], - is_str = typeof what == "string"; - if (is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" ) - { - add_to[ add_to.length-1 ] += what; - } - else { - add_to.push( what ); - } - } - } - - // contained means have an indent greater than the current one. On - // *every* line in the block - function get_contained_blocks( depth, blocks ) { - - var re = new RegExp( "^(" + indent_re + "{" + depth + "}.*?\\n?)*$" ), - replace = new RegExp("^" + indent_re + "{" + depth + "}", "gm"), - ret = []; - - while ( blocks.length > 0 ) { - if ( re( blocks[0] ) ) { - var b = blocks.shift(), - // Now remove that indent - x = b.replace( replace, ""); - - ret.push( mk_block( x, b.trailing, b.lineNumber ) ); - } - break; - } - return ret; - } - - // passed to stack.forEach to turn list items up the stack into paras - function paragraphify(s, i, stack) { - var list = s.list; - var last_li = list[list.length-1]; - - if (last_li[1] instanceof Array && last_li[1][0] == "para") { - return; - } - if (i+1 == stack.length) { - // Last stack frame - // Keep the same array, but replace the contents - last_li.push( ["para"].concat( last_li.splice(1) ) ); - } - else { - var sublist = last_li.pop(); - last_li.push( ["para"].concat( last_li.splice(1) ), sublist ); - } - } - - // The matcher function - return function( block, next ) { - var m = block.match( is_list_re ); - if ( !m ) return undefined; - - function make_list( m ) { - var list = bullet_list( m[2] ) - ? ["bulletlist"] - : ["numberlist"]; - - stack.push( { list: list, indent: m[1] } ); - return list; - } - - - var stack = [], // Stack of lists for nesting. - list = make_list( m ), - last_li, - loose = false, - ret = [ stack[0].list ]; - - // Loop to search over block looking for inner block elements and loose lists - loose_search: - while( true ) { - // Split into lines preserving new lines at end of line - var lines = block.split( /(?=\n)/ ); - - // We have to grab all lines for a li and call processInline on them - // once as there are some inline things that can span lines. - var li_accumulate = ""; - - // Loop over the lines in this block looking for tight lists. - tight_search: - for (var line_no=0; line_no < lines.length; line_no++) { - var nl = "", - l = lines[line_no].replace(/^\n/, function(n) { nl = n; return "" }); - - // TODO: really should cache this - var line_re = regex_for_depth( stack.length ); - - m = l.match( line_re ); - //print( "line:", uneval(l), "\nline match:", uneval(m) ); - - // We have a list item - if ( m[1] !== undefined ) { - // Process the previous list item, if any - if ( li_accumulate.length ) { - add( last_li, loose, this.processInline( li_accumulate ), nl ); - // Loose mode will have been dealt with. Reset it - loose = false; - li_accumulate = ""; - } - - m[1] = expand_tab( m[1] ); - var wanted_depth = Math.floor(m[1].length/4)+1; - //print( "want:", wanted_depth, "stack:", stack.length); - if ( wanted_depth > stack.length ) { - // Deep enough for a nested list outright - //print ( "new nested list" ); - list = make_list( m ); - last_li.push( list ); - last_li = list[1] = [ "listitem" ]; - } - else { - // We aren't deep enough to be strictly a new level. This is - // where Md.pl goes nuts. If the indent matches a level in the - // stack, put it there, else put it one deeper then the - // wanted_depth deserves. - var found = stack.some(function(s, i) { - if ( s.indent != m[1] ) return false; - list = s.list; // Found the level we want - stack.splice(i+1); // Remove the others - //print("found"); - return true; // And stop looping - }); - - if (!found) { - //print("not found. l:", uneval(l)); - wanted_depth++; - if (wanted_depth <= stack.length) { - stack.splice(wanted_depth); - //print("Desired depth now", wanted_depth, "stack:", stack.length); - list = stack[wanted_depth-1].list; - //print("list:", uneval(list) ); - } - else { - //print ("made new stack for messy indent"); - list = make_list(m); - last_li.push(list); - } - } - - //print( uneval(list), "last", list === stack[stack.length-1].list ); - last_li = [ "listitem" ]; - list.push(last_li); - } // end depth of shenegains - nl = ""; - } - - // Add content - if (l.length > m[0].length) { - li_accumulate += nl + l.substr( m[0].length ); - } - } // tight_search - - if ( li_accumulate.length ) { - add( last_li, loose, this.processInline( li_accumulate ), nl ); - // Loose mode will have been dealt with. Reset it - loose = false; - li_accumulate = ""; - } - - // Look at the next block - we might have a loose list. Or an extra - // paragraph for the current li - var contained = get_contained_blocks( stack.length, next ); - - // Deal with code blocks or properly nested lists - if (contained.length > 0) { - // Make sure all listitems up the stack are paragraphs - stack.forEach( paragraphify, this ); - - last_li.push.apply( last_li, this.toTree( contained, [] ) ); - } - - var next_block = next[0] && next[0].valueOf() || ""; - - if ( next_block.match(is_list_re) || next_block.match( /^ / ) ) { - block = next.shift(); - - // Check for an HR following a list: features/lists/hr_abutting - var hr = this.dialect.block.horizRule( block, next ); - - if (hr) { - ret.push.apply(ret, hr); - break; - } - - // Make sure all listitems up the stack are paragraphs - stack.forEach( paragraphify , this ); - - loose = true; - continue loose_search; - } - break; - } // loose_search - - return ret; - } - })(), - - blockquote: function blockquote( block, next ) { - if ( !block.match( /^>/m ) ) - return undefined; - - var jsonml = []; - - // separate out the leading abutting block, if any - if ( block[ 0 ] != ">" ) { - var lines = block.split( /\n/ ), - prev = []; - - // keep shifting lines until you find a crotchet - while ( lines.length && lines[ 0 ][ 0 ] != ">" ) { - prev.push( lines.shift() ); - } - - // reassemble! - block = lines.join( "\n" ); - jsonml.push.apply( jsonml, this.processBlock( prev.join( "\n" ), [] ) ); - } - - // if the next block is also a blockquote merge it in - while ( next.length && next[ 0 ][ 0 ] == ">" ) { - var b = next.shift(); - block += block.trailing + b; - block.trailing = b.trailing; - } - - // Strip off the leading "> " and re-process as a block. - var input = block.replace( /^> ?/gm, '' ), - old_tree = this.tree; - jsonml.push( this.toTree( input, [ "blockquote" ] ) ); - - return jsonml; - }, - - referenceDefn: function referenceDefn( block, next) { - var re = /^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/; - // interesting matches are [ , ref_id, url, , title, title ] - - if ( !block.match(re) ) - return undefined; - - // make an attribute node if it doesn't exist - if ( !extract_attr( this.tree ) ) { - this.tree.splice( 1, 0, {} ); - } - - var attrs = extract_attr( this.tree ); - - // make a references hash if it doesn't exist - if ( attrs.references === undefined ) { - attrs.references = {}; - } - - var b = this.loop_re_over_block(re, block, function( m ) { - - if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' ) - m[2] = m[2].substring( 1, m[2].length - 1 ); - - var ref = attrs.references[ m[1].toLowerCase() ] = { - href: m[2] - }; - - if (m[4] !== undefined) - ref.title = m[4]; - else if (m[5] !== undefined) - ref.title = m[5]; - - } ); - - if (b.length) - next.unshift( mk_block( b, block.trailing ) ); - - return []; - }, - - para: function para( block, next ) { - // everything's a para! - return [ ["para"].concat( this.processInline( block ) ) ]; - } - } -} - -Markdown.dialects.Gruber.inline = { - __call__: function inline( text, patterns ) { - // Hmmm - should this function be directly in Md#processInline, or - // conversely, should Md#processBlock be moved into block.__call__ too - var out = [ ], - m, - // Look for the next occurange of a special character/pattern - re = new RegExp( "([\\s\\S]*?)(" + (patterns.source || patterns) + ")", "g" ), - lastIndex = 0; - - //D:var self = this; - //D:self.debug("processInline:", uneval(text) ); - function add(x) { - //D:self.debug(" adding output", uneval(x)); - if (typeof x == "string" && typeof out[out.length-1] == "string") - out[ out.length-1 ] += x; - else - out.push(x); - } - - while ( ( m = re.exec(text) ) != null) { - if ( m[1] ) add( m[1] ); // Some un-interesting text matched - else m[1] = { length: 0 }; // Or there was none, but make m[1].length == 0 - - var res; - if ( m[2] in this.dialect.inline ) { - res = this.dialect.inline[ m[2] ].call( - this, - text.substr( m.index + m[1].length ), m, out ); - } - // Default for now to make dev easier. just slurp special and output it. - res = res || [ m[2].length, m[2] ]; - - var len = res.shift(); - // Update how much input was consumed - re.lastIndex += ( len - m[2].length ); - - // Add children - res.forEach(add); - - lastIndex = re.lastIndex; - } - - // Add last 'boring' chunk - if ( text.length > lastIndex ) - add( text.substr( lastIndex ) ); - - return out; - }, - - "\\": function escaped( text ) { - // [ length of input processed, node/children to add... ] - // Only esacape: \ ` * _ { } [ ] ( ) # * + - . ! - if ( text.match( /^\\[\\`\*_{}\[\]()#\+.!\-]/ ) ) - return [ 2, text[1] ]; - else - // Not an esacpe - return [ 1, "\\" ]; - }, - - "![": function image( text ) { - // ![Alt text](/path/to/img.jpg "Optional title") - // 1 2 3 4 <--- captures - var m = text.match( /^!\[(.*?)\][ \t]*\([ \t]*(\S*)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ ); - - if ( m ) { - if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' ) - m[2] = m[2].substring( 1, m[2].length - 1 ); - - m[2] == this.dialect.inline.__call__.call( this, m[2], /\\/ )[0]; - - var attrs = { alt: m[1], href: m[2] || "" }; - if ( m[4] !== undefined) - attrs.title = m[4]; - - return [ m[0].length, [ "img", attrs ] ]; - } - - // ![Alt text][id] - m = text.match( /^!\[(.*?)\][ \t]*\[(.*?)\]/ ); - - if ( m ) { - // We can't check if the reference is known here as it likely wont be - // found till after. Check it in md tree->hmtl tree conversion - return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), text: m[0] } ] ]; - } - - // Just consume the '![' - return [ 2, "![" ]; - }, - - "[": function link( text ) { - // [link text](/path/to/img.jpg "Optional title") - // 1 2 3 4 <--- captures - var m = text.match( /^\[([\s\S]*?)\][ \t]*\([ \t]*(\S+)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ ); - - if ( m ) { - if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' ) - m[2] = m[2].substring( 1, m[2].length - 1 ); - - // Process escapes only - m[2] = this.dialect.inline.__call__.call( this, m[2], /\\/ )[0]; - - var attrs = { href: m[2] || "" }; - if ( m[4] !== undefined) - attrs.title = m[4]; - - return [ m[0].length, [ "link", attrs, m[1] ] ]; - } - - // [Alt text][id] - // [Alt text] [id] - // [id] - m = text.match( /^\[([\s\S]*?)\](?: ?\[(.*?)\])?/ ); - - if ( m ) { - // [id] case, text == id - if ( m[2] === undefined || m[2] === "" ) m[2] = m[1]; - - // We can't check if the reference is known here as it likely wont be - // found till after. Check it in md tree->hmtl tree conversion. - // Store the original so that conversion can revert if the ref isn't found. - return [ - m[ 0 ].length, - [ - "link_ref", - { - ref: m[ 2 ].toLowerCase(), - original: m[ 0 ] - }, - m[ 1 ] - ] - ]; - } - - // Just consume the '[' - return [ 1, "[" ]; - }, - - - "<": function autoLink( text ) { - var m; - - if ( ( m = text.match( /^<(?:((https?|ftp|mailto):[^>]+)|(.*?@.*?\.[a-zA-Z]+))>/ ) ) != null ) { - if ( m[3] ) { - return [ m[0].length, [ "link", { href: "mailto:" + m[3] }, m[3] ] ]; - - } - else if ( m[2] == "mailto" ) { - return [ m[0].length, [ "link", { href: m[1] }, m[1].substr("mailto:".length ) ] ]; - } - else - return [ m[0].length, [ "link", { href: m[1] }, m[1] ] ]; - } - - return [ 1, "<" ]; - }, - - "`": function inlineCode( text ) { - // Inline code block. as many backticks as you like to start it - // Always skip over the opening ticks. - var m = text.match( /(`+)(([\s\S]*?)\1)/ ); - - if ( m && m[2] ) - return [ m[1].length + m[2].length, [ "inlinecode", m[3] ] ]; - else { - // TODO: No matching end code found - warn! - return [ 1, "`" ]; - } - }, - - " \n": function lineBreak( text ) { - return [ 3, [ "linebreak" ] ]; - } - -} - -// Meta Helper/generator method for em and strong handling -function strong_em( tag, md ) { - - var state_slot = tag + "_state", - other_slot = tag == "strong" ? "em_state" : "strong_state"; - - function CloseTag(len) { - this.len_after = len; - this.name = "close_" + md; - } - - return function ( text, orig_match ) { - - if (this[state_slot][0] == md) { - // Most recent em is of this type - //D:this.debug("closing", md); - this[state_slot].shift(); - - // "Consume" everything to go back to the recrusion in the else-block below - return[ text.length, new CloseTag(text.length-md.length) ]; - } - else { - // Store a clone of the em/strong states - var other = this[other_slot].slice(), - state = this[state_slot].slice(); - - this[state_slot].unshift(md); - - //D:this.debug_indent += " "; - - // Recurse - var res = this.processInline( text.substr( md.length ) ); - //D:this.debug_indent = this.debug_indent.substr(2); - - var last = res[res.length - 1]; - - //D:this.debug("processInline from", tag + ": ", uneval( res ) ); - - var check = this[state_slot].shift(); - if (last instanceof CloseTag) { - res.pop(); - // We matched! Huzzah. - var consumed = text.length - last.len_after; - return [ consumed, [ tag ].concat(res) ]; - } - else { - // Restore the state of the other kind. We might have mistakenly closed it. - this[other_slot] = other; - this[state_slot] = state; - - // We can't reuse the processed result as it could have wrong parsing contexts in it. - return [ md.length, md ]; - } - } - } // End returned function -} - -Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**"); -Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__"); -Markdown.dialects.Gruber.inline["*"] = strong_em("em", "*"); -Markdown.dialects.Gruber.inline["_"] = strong_em("em", "_"); - - -// Build default order from insertion order. -Markdown.buildBlockOrder = function(d) { - var ord = []; - for ( var i in d ) { - if ( i == "__order__" || i == "__call__" ) continue; - ord.push( i ); - } - d.__order__ = ord; -} - -// Build patterns for inline matcher -Markdown.buildInlinePatterns = function(d) { - var patterns = []; - - for ( var i in d ) { - if (i == "__call__") continue; - var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" ) - .replace( /\n/, "\\n" ); - patterns.push( i.length == 1 ? l : "(?:" + l + ")" ); - } - - patterns = patterns.join("|"); - //print("patterns:", uneval( patterns ) ); - - var fn = d.__call__; - d.__call__ = function(text, pattern) { - if (pattern != undefined) - return fn.call(this, text, pattern); - else - return fn.call(this, text, patterns); - } -} - -// Helper function to make sub-classing a dialect easier -Markdown.subclassDialect = function( d ) { - function Block() {}; - Block.prototype = d.block; - function Inline() {}; - Inline.prototype = d.inline; - - return { block: new Block(), inline: new Inline() }; -} - -Markdown.buildBlockOrder ( Markdown.dialects.Gruber.block ); -Markdown.buildInlinePatterns( Markdown.dialects.Gruber.inline ); - -Markdown.dialects.Maruku = Markdown.subclassDialect( Markdown.dialects.Gruber ); - -Markdown.dialects.Maruku.block.document_meta = function document_meta( block, next ) { - // we're only interested in the first block - if ( block.lineNumber > 1 ) return undefined; - - // document_meta blocks consist of one or more lines of `Key: Value\n` - if ( ! block.match( /^(?:\w+:.*\n)*\w+:.*$/ ) ) return undefined; - - // make an attribute node if it doesn't exist - if ( !extract_attr( this.tree ) ) { - this.tree.splice( 1, 0, {} ); - } - - var pairs = block.split( /\n/ ); - for ( p in pairs ) { - var m = pairs[ p ].match( /(\w+):\s*(.*)$/ ), - key = m[ 1 ].toLowerCase(), - value = m[ 2 ]; - - this.tree[ 1 ][ key ] = value; - } - - // document_meta produces no content! - return []; -} - -Markdown.dialects.Maruku.block.block_meta = function block_meta( block, next ) { - // check if the last line of the block is an meta hash - var m = block.match( /(^|\n) {0,3}\{:\s*((?:\\\}|[^\}])*)\s*\}$/ ); - if ( !m ) return undefined; - - // process the meta hash - var attr = process_meta_hash( m[ 2 ] ); - - // if we matched ^ then we need to apply meta to the previous block - if ( m[ 1 ] === "" ) { - var node = this.tree[ this.tree.length - 1 ], - hash = extract_attr( node ); - - // if the node is a string (rather than JsonML), bail - if ( typeof node === "string" ) return undefined; - - // create the attribute hash if it doesn't exist - if ( !hash ) { - hash = {}; - node.splice( 1, 0, hash ); - } - - // add the attributes in - for ( a in attr ) { - hash[ a ] = attr[ a ]; - } - - // return nothing so the meta hash is removed - return []; - } - - // pull the meta hash off the block and process what's left - var b = block.replace( /\n.*$/, "" ), - result = this.processBlock( b, [] ); - - // get or make the attributes hash - var hash = extract_attr( result[ 0 ] ); - if ( !hash ) { - hash = {}; - result[ 0 ].splice( 1, 0, hash ); - } - - // attach the attributes to the block - for ( a in attr ) { - hash[ a ] = attr[ a ]; - } - - return result; -} - -Markdown.dialects.Maruku.block.definition_list = function definition_list( block, next ) { - // one or more terms followed by one or more definitions, in a single block - var tight = /^((?:[^\s:].*\n)+):\s+([^]+)$/, - list = [ "dl" ]; - - // see if we're dealing with a tight or loose block - if ( ( m = block.match( tight ) ) ) { - // pull subsequent tight DL blocks out of `next` - var blocks = [ block ]; - while ( next.length && tight.exec( next[ 0 ] ) ) { - blocks.push( next.shift() ); - } - - for ( var b = 0; b < blocks.length; ++b ) { - var m = blocks[ b ].match( tight ), - terms = m[ 1 ].replace( /\n$/, "" ).split( /\n/ ), - defns = m[ 2 ].split( /\n:\s+/ ); - - // print( uneval( m ) ); - - for ( var i = 0; i < terms.length; ++i ) { - list.push( [ "dt", terms[ i ] ] ); - } - - for ( var i = 0; i < defns.length; ++i ) { - // run inline processing over the definition - list.push( [ "dd" ].concat( this.processInline( defns[ i ].replace( /(\n)\s+/, "$1" ) ) ) ); - } - } - } - else { - return undefined; - } - - return [ list ]; -} - -Markdown.dialects.Maruku.inline[ "{:" ] = function inline_meta( text, matches, out ) { - if ( !out.length ) { - return [ 2, "{:" ]; - } - - // get the preceeding element - var before = out[ out.length - 1 ]; - - if ( typeof before === "string" ) { - return [ 2, "{:" ]; - } - - // match a meta hash - var m = text.match( /^\{:\s*((?:\\\}|[^\}])*)\s*\}/ ); - - // no match, false alarm - if ( !m ) { - return [ 2, "{:" ]; - } - - // attach the attributes to the preceeding element - var meta = process_meta_hash( m[ 1 ] ), - attr = extract_attr( before ); - - if ( !attr ) { - attr = {}; - before.splice( 1, 0, attr ); - } - - for ( var k in meta ) { - attr[ k ] = meta[ k ]; - } - - // cut out the string and replace it with nothing - return [ m[ 0 ].length, "" ]; -} - -Markdown.buildBlockOrder ( Markdown.dialects.Maruku.block ); -Markdown.buildInlinePatterns( Markdown.dialects.Maruku.inline ); - -function extract_attr( jsonml ) { - return jsonml instanceof Array - && jsonml.length > 1 - && typeof jsonml[ 1 ] === "object" - && !( jsonml[ 1 ] instanceof Array ) - ? jsonml[ 1 ] - : undefined; -} - -function process_meta_hash( meta_string ) { - var meta = split_meta_hash( meta_string ), - attr = {}; - - for ( var i = 0; i < meta.length; ++i ) { - // id: #foo - if ( /^#/.test( meta[ i ] ) ) { - attr.id = meta[ i ].substring( 1 ); - } - // class: .foo - else if ( /^\./.test( meta[ i ] ) ) { - // if class already exists, append the new one - if ( attr['class'] ) { - attr['class'] = attr['class'] + meta[ i ].replace( /./, " " ); - } - else { - attr['class'] = meta[ i ].substring( 1 ); - } - } - // attribute: foo=bar - else if ( /=/.test( meta[ i ] ) ) { - var s = meta[ i ].split( /=/ ); - attr[ s[ 0 ] ] = s[ 1 ]; - } - } - - return attr; -} - -function split_meta_hash( meta_string ) { - var meta = meta_string.split( "" ), - parts = [ "" ], - in_quotes = false; - - while ( meta.length ) { - var letter = meta.shift(); - switch ( letter ) { - case " " : - // if we're in a quoted section, keep it - if ( in_quotes ) { - parts[ parts.length - 1 ] += letter; - } - // otherwise make a new part - else { - parts.push( "" ); - } - break; - case "'" : - case '"' : - // reverse the quotes and move straight on - in_quotes = !in_quotes; - break; - case "\\" : - // shift off the next letter to be used straight away. - // it was escaped so we'll keep it whatever it is - letter = meta.shift(); - default : - parts[ parts.length - 1 ] += letter; - break; - } - } - - return parts; -} - -/** - * renderJsonML( jsonml[, options] ) -> String - * - jsonml (Array): JsonML array to render to XML - * - options (Object): options - * - * Converts the given JsonML into well-formed XML. - * - * The options currently understood are: - * - * - root (Boolean): wether or not the root node should be included in the - * output, or just its children. The default `false` is to not include the - * root itself. - */ -expose.renderJsonML = function( jsonml, options ) { - options = options || {}; - // include the root element in the rendered output? - options.root = options.root || false; - - var content = []; - - if ( options.root ) { - content.push( render_tree( jsonml ) ); - } - else { - jsonml.shift(); // get rid of the tag - if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { - jsonml.shift(); // get rid of the attributes - } - - while ( jsonml.length ) { - content.push( render_tree( jsonml.shift() ) ); - } - } - - return content.join( "\n\n" ); -} - -function render_tree( jsonml ) { - // basic case - if ( typeof jsonml === "string" ) { - return jsonml.replace( /&/g, "&" ) - .replace( //g, ">" ); - } - - var tag = jsonml.shift(), - attributes = {}, - content = []; - - if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { - attributes = jsonml.shift(); - } - - while ( jsonml.length ) { - content.push( arguments.callee( jsonml.shift() ) ); - } - - var tag_attrs = ""; - for ( var a in attributes ) { - tag_attrs += " " + a + '="' + attributes[ a ] + '"'; - } - - // be careful about adding whitespace here for inline elements - return "<"+ tag + tag_attrs + ">" + content.join( "" ) + ""; -} - -function convert_tree_to_html( tree, references ) { - // shallow clone - var jsonml = tree.slice( 0 ); - - // Clone attributes if the exist - var attrs = extract_attr( jsonml ); - if ( attrs ) { - jsonml[ 1 ] = {}; - for ( var i in attrs ) { - jsonml[ 1 ][ i ] = attrs[ i ]; - } - attrs = jsonml[ 1 ]; - } - - // basic case - if ( typeof jsonml === "string" ) { - return jsonml; - } - - // convert this node - switch ( jsonml[ 0 ] ) { - case "header": - jsonml[ 0 ] = "h" + jsonml[ 1 ].level; - delete jsonml[ 1 ].level; - break; - case "bulletlist": - jsonml[ 0 ] = "ul"; - break; - case "numberlist": - jsonml[ 0 ] = "ol"; - break; - case "listitem": - jsonml[ 0 ] = "li"; - break; - case "para": - jsonml[ 0 ] = "p"; - break; - case "markdown": - jsonml[ 0 ] = "html"; - if ( attrs ) delete attrs.references; - break; - case "code_block": - jsonml[ 0 ] = "pre"; - var i = attrs ? 2 : 1; - var code = [ "code" ]; - code.push.apply( code, jsonml.splice( i ) ); - jsonml[ i ] = code; - break; - case "inlinecode": - jsonml[ 0 ] = "code"; - break; - case "img": - jsonml[ 1 ].src = jsonml[ 1 ].href; - delete jsonml[ 1 ].href; - break; - case "linebreak": - jsonml[0] = "br"; - break; - case "link": - jsonml[ 0 ] = "a"; - break; - case "link_ref": - jsonml[ 0 ] = "a"; - - // grab this ref and clean up the attribute node - var ref = references[ attrs.ref ]; - - // if the reference exists, make the link - if ( ref ) { - delete attrs.ref; - - // add in the href and title, if present - attrs.href = ref.href; - if ( ref.title ) { - attrs.title = ref.title; - } - - // get rid of the unneeded original text - delete attrs.original; - } - // the reference doesn't exist, so revert to plain text - else { - return attrs.original; - } - break; - } - - // convert all the children - var i = 1; - - // deal with the attribute node, if it exists - if ( attrs ) { - // if there are keys, skip over it - for ( var key in jsonml[ 1 ] ) { - i = 2; - } - // if there aren't, remove it - if ( i === 1 ) { - jsonml.splice( i, 1 ); - } - } - - for ( ; i < jsonml.length; ++i ) { - jsonml[ i ] = arguments.callee( jsonml[ i ], references ); - } - - return jsonml; -} - - -// merges adjacent text nodes into a single node -function merge_text_nodes( jsonml ) { - // skip the tag name and attribute hash - var i = extract_attr( jsonml ) ? 2 : 1; - - while ( i < jsonml.length ) { - // if it's a string check the next item too - if ( typeof jsonml[ i ] === "string" ) { - if ( i + 1 < jsonml.length && typeof jsonml[ i + 1 ] === "string" ) { - // merge the second string into the first and remove it - jsonml[ i ] += jsonml.splice( i + 1, 1 )[ 0 ]; - } - else { - ++i; - } - } - // if it's not a string recurse - else { - arguments.callee( jsonml[ i ] ); - ++i; - } - } -} - -} )( (function() { - if ( typeof exports === "undefined" ) { - window.markdown = {}; - return window.markdown; - } - else { - return exports; - } -} )() ); diff --git a/modules/goessner/json2xml.js b/modules/goessner/json2xml.js deleted file mode 100644 index 5283249f2..000000000 --- a/modules/goessner/json2xml.js +++ /dev/null @@ -1,70 +0,0 @@ -/* This work is licensed under Creative Commons GNU LGPL License. - - License: http://creativecommons.org/licenses/LGPL/2.1/ - Version: 0.9/modified to conform to commonjs modules pattern - Author: Stefan Goessner/2006 - Web: http://goessner.net/ -*/ - -var json2xml = (typeof exports === 'undefined')? {} : exports; // like commonjs - -(function() { - - json2xml.convert = function(o) { - var toXml = function(v, name, ind) { - var xml = ""; - if (v instanceof Array) { - for (var i=0, n=v.length; i\n" : "/>"; - if (hasChild) { - for (var m in v) { - if (m == "#text") - xml += makeSafe(v[m]); - else if (m == "#cdata") - xml += ""; - else if (m.charAt(0) != "@") - xml += toXml(v[m], m, ind+"\t"); - } - xml += (xml.charAt(xml.length-1)=="\n"?ind:"") + "\n"; - } - } - else { // added special-character transform, but this needs to be better handled [micmath] - xml += ind + "<" + name + ">" + makeSafe(v.toString()) + "\n"; - } - return xml; - }, - xml=""; - - for (var m in o) { - xml += toXml(o[m], m, ""); - } - - return xml; - } - - function lines(str) { - // normalise line endings, all in file will be unixy - str = str.replace(/\r\n/g, '\n'); - - return str; - } - - function makeSafe(str) { - // xml special charaters - str = str.replace(/ - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ -(function() { - - // requires docs to have been indexed: docs.index must be defined here - /** - Take a copy of the docs for borrowed symbols and attach them to the - docs for the borrowing symbol. This process changes the symbols involved, - moving docs from the "borrowed" array and into the general docs, then - deleting the "borrowed" array. - */ - exports.resolveBorrows = function(docs) { - if (!docs.index) { - throw 'Docs has not been indexed: docs.index must be defined here.'; - } - - docs.forEach(function(doc) { - if (doc.borrowed) { - doc.borrowed.forEach(function(b, i) { - var lent = docs.index[b.from], // lent is an array - asName = b['as'] || b.from; - - if (lent) { - var cloned = doop(lent); - - cloned.forEach(function(clone) { - asName = asName.replace(/^prototype\./, '#'); - var parts = asName.split('#'); - - if (parts.length === 2) clone.scope = 'instance'; - else clone.scope = 'static'; - - asName = parts.pop(); - clone.name = asName; - clone.memberof = doc.longname; - clone.longname = clone.memberof + (clone.scope === 'instance'? '#': '.') + clone.name; - docs.push(clone); - }); - - } - }); - - delete doc.borrowed; - } - }); - } - - /** - Deep clone a simple object. - @private - */ - function doop(o) { - if (o instanceof Object && o.constructor != Function) { - var clone = o instanceof Array ? [] : {}, prop; - - for (prop in o){ - if ( o.hasOwnProperty(prop) ) { - clone[prop] = (o[prop] instanceof Object)? doop(o[prop]) : o[prop]; - } - } - return clone; - } - return o; - }; - -})(); \ No newline at end of file diff --git a/modules/jsdoc/doclet.js b/modules/jsdoc/doclet.js deleted file mode 100644 index 4229ab0ea..000000000 --- a/modules/jsdoc/doclet.js +++ /dev/null @@ -1,296 +0,0 @@ -/** - @overview - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ - -/** - @module jsdoc/doclet - @requires jsdoc/tag - @requires jsdoc/name - @requires jsdoc/tag/dictionary - */ -(function() { - var jsdoc = { - tag: { - Tag: require('jsdoc/tag').Tag, - dictionary: require('jsdoc/tag/dictionary') - }, - name: require('jsdoc/name') - }; - - /** - @class - @classdesc Represents a single JSDoc comment. - @param {string} docletSrc - The raw source code of the jsdoc comment. - @param {object=} meta - Properties describing the code related to this comment. - */ - exports.Doclet = function(docletSrc, meta) { - var newTags = []; - - /** The original text of the comment from the source code. */ - this.comment = docletSrc; - this.setMeta(meta); - - docletSrc = unwrap(docletSrc); - docletSrc = fixDescription(docletSrc); - - newTags = toTags.call(this, docletSrc); - - for (var i = 0, leni = newTags.length; i < leni; i++) { - this.addTag(newTags[i].title, newTags[i].text); - } - - this.postProcess(); - } - - /** Called once after all tags have been added. */ - exports.Doclet.prototype.postProcess = function() { - if (!this.preserveName) { jsdoc.name.resolve(this); } - if (this.name && !this.longname) { - this.setLongname(this.name); - } - if (this.memberof === '') { - delete(this.memberof); - } - if (!this.kind && this.meta && this.meta.code) { - this.addTag( 'kind', codetypeToKind(this.meta.code.type) ); - } - } - - /** Add a tag to this doclet. - @param {string} title - The title of the tag being added. - @param {string} [text] - The text of the tag being added. - */ - exports.Doclet.prototype.addTag = function(title, text) { - var tagDef = jsdoc.tag.dictionary.lookUp(title), - newTag = new jsdoc.tag.Tag(title, text, this.meta); - - if (tagDef && tagDef.onTagged) { - tagDef.onTagged(this, newTag) - } - - if (!tagDef) { - this.tags = this.tags || []; - this.tags.push(newTag); - } - - applyTag.call(this, newTag); - } - - /** Set the `memberof` property of this doclet. - @param {string} sid - The longname of the symbol that this doclet is a member of. - */ - exports.Doclet.prototype.setMemberof = function(sid) { - if (/^\.?/.test(sid)) { sid = sid.replace(/^.?/, ''); } - /** - The longname of the symbol that contains this one, if any. - @type string - */ - this.memberof = sid.replace(/\.prototype/g, '#'); - } - - /** Set the `longname` property of this doclet. - @param {string} name - */ - exports.Doclet.prototype.setLongname = function(name) { - if (/^\.?/.test(name)) { name = name.replace(/^\.?/, ''); } - - /** - The fully resolved symbol name. - @type string - */ - this.longname = name; - if (jsdoc.tag.dictionary.isNamespace(this.kind)) { - this.longname = jsdoc.name.applyNamespace(this.longname, this.kind); - } - } - - /** Add a symbol to this doclet's `borrowed` array. - @param {string} source - The longname of the symbol that is the source. - @param {string} target - The name the symbol is being assigned to. - */ - exports.Doclet.prototype.borrow = function(source, target) { - var about = {from: source}; - if (target) about.as = target; - - if (!this.borrowed) { - /** - A list of symbols that are borrowed by this one, if any. - @type Array. - */ - this.borrowed = []; - } - this.borrowed.push(about); - } - - /** Add a symbol to this doclet's `augments` array. - @param {string} base - The longname of the base symbol. - */ - exports.Doclet.prototype.augment = function(base) { - if (!this.augments) { - /** - A list of symbols that are augmented by this one, if any. - @type Array. - */ - this.augments = []; - } - this.augments.push(base); - } - - /** - Set the `meta` property of this doclet. - @param {object} meta - */ - exports.Doclet.prototype.setMeta = function(meta) { - if (!this.meta) { - /** - Information about the source code associated with this doclet. - @namespace - */ - this.meta = {}; - } - - if (meta.lineno) { - /** - The line number of the code associated with this doclet. - @type number - */ - this.meta.lineno = meta.lineno; - } - - if (meta.lineno) { - /** - The name of the file containing the code associated with this doclet. - @type string - */ - this.meta.filename = meta.filename; - } - - /** - Information about the code symbol. - @namespace - */ - this.meta.code = (this.meta.code || {}); - if (meta.id) this.meta.code.id = meta.id; - if (meta.code) { - if (meta.code.name) { - /** The name of the symbol in the source code. */ - this.meta.code.name = meta.code.name; - } - if (meta.code.type) { - /** The type of the symbol in the source code. */ - this.meta.code.type = meta.code.type; - } - if (meta.code.node) { - this.meta.code.node = meta.code.node; - } - if (meta.code.funcscope) { - this.meta.code.funcscope = meta.code.funcscope; - } - if (meta.code.value) { - /** The value of the symbol in the source code. */ - this.meta.code.value = meta.code.value; - } - } - } - - function applyTag(tag) { - if (tag.title === 'name') { - this.name = tag.value; - } - - if (tag.title === 'kind') { - this.kind = tag.value; - } - - if (tag.title === 'description') { - this.description = tag.value; - } - - if (tag.title === 'scope') { - this.scope = tag.value; - } - } - - // use the meta info about the source code to guess what the doclet kind should be - function codetypeToKind(type) { - var kind = (type || '').toLowerCase(); - - if (kind !== 'function') { - return 'property'; - } - - return kind; - } - - /** - Convert the raw source of the doclet comment into an array of Tag objects. - @private - */ - function toTags(docletSrc) { - var tagSrcs, - tags = []; - - docletSrc = unwrap(docletSrc); - tagSrcs = split(docletSrc); - - for each(tagSrc in tagSrcs) { - tags.push( {title: tagSrc.title, text: tagSrc.text} ); - } - - return tags; - } - - function unwrap(docletSrc) { - if (!docletSrc) { return ''; } - - // note: keep trailing whitespace for @examples - // extra opening/closing stars are ignored - // left margin is considered a star and a space - // use the /m flag on regex to avoid having to guess what this platform's newline is - docletSrc = - docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars - .replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker - .replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker - .replace(/\s*\\Z$/g, ''); // remove end-marker - - return docletSrc; - } - - function fixDescription(docletSrc) { - if (!/^\s*@/.test(docletSrc)) { - docletSrc = '@description ' + docletSrc; - } - return docletSrc; - } - - function split(docletSrc) { - var tagSrcs = []; - - // split out the basic tags, keep surrounding whitespace - // like: @tagTitle tagBody - docletSrc - .replace(/^(\s*)@(\S)/gm, '$1\\@$2') // replace splitter ats with an arbitrary sequence - .split('\\@') // then split on that arbitrary sequence - .forEach(function($) { - if ($) { - var parsedTag = $.match(/^(\S+)(:?\s+(\S[\s\S]*))?/); - - if (parsedTag) { - var [, tagTitle, tagText] = parsedTag; - - if (tagTitle) { - tagSrcs.push({ - title: tagTitle, - text: tagText - }); - } - } - } - }); - - return tagSrcs; - } - -})(); \ No newline at end of file diff --git a/modules/jsdoc/name.js b/modules/jsdoc/name.js deleted file mode 100644 index f2f41b05b..000000000 --- a/modules/jsdoc/name.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - A collection of functions relating to JSDoc symbol name manipulation. - @module jsdoc/name - @requires jsdoc/tag/dictionary - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ -(function() { - var jsdoc = { - tagDictionary: require('jsdoc/tag/dictionary') - }; - - var puncToScope = { '.': 'static', '~': 'inner', '#': 'instance' }, - scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' }, - Token = Packages.org.mozilla.javascript.Token; - - /** - Resolves the longname, memberof, variation and name values of the given doclet. - @param {module:jsdoc/doclet.Doclet} doclet - */ - exports.resolve = function(doclet) { - - var name = doclet.name, - memberof = doclet.memberof || '', - about = {}, - parentDoc; - - name = name? (''+name).replace(/\.prototype\.?/g, '#') : ''; - - // member of a var in an outer scope? - if (name && !memberof && doclet.meta.code && doclet.meta.code.funcscope) { - name = doclet.longname = doclet.meta.code.funcscope + '~' + name; - } - - if (memberof) { // @memberof tag given - memberof = memberof.replace(/\.prototype\.?/g, '#'); - - // the name is a fullname, like @name foo.bar, @memberof foo - if (name && name.indexOf(memberof) === 0) { - about = exports.shorten(name); - } - else if (name && /([#.~])$/.test(memberof) ) { // like @memberof foo# or @memberof foo~ - about = exports.shorten(memberof + name); - } - else if (name && doclet.scope ) { // like @memberof foo# or @memberof foo~ - about = exports.shorten(memberof + scopeToPunc[doclet.scope] + name); - } - } - else { // no @memberof - about = exports.shorten(name); - } - - if (about.name) { - doclet.name = about.name; - } - - if (about.memberof) { - doclet.setMemberof(about.memberof); - } - - if (about.longname && !doclet.longname) { - doclet.setLongname(about.longname); - } - - if (doclet.scope === 'global') { // via @global tag? - doclet.setLongname(doclet.name); - delete doclet.memberof; - } - else if (about.scope) { - doclet.scope = puncToScope[about.scope]; - } - else { - if (doclet.name && doclet.memberof && !doclet.longname) { - doclet.scope = 'static'; // default scope when none is provided - - doclet.setLongname(doclet.memberof + scopeToPunc[doclet.scope] + doclet.name); - } - } - - if (about.variation) { - doclet.variation = about.variation; - } - -//dump('doclet', doclet); - } - - /** - @inner - @memberof module:jsdoc/name - @param {string} name - @param {string} kind - @returns {string} The name with unsafe names enclosed in quotes. - */ - function quoteUnsafe(name, kind) { // docspaced names may have unsafe characters which need to be quoted by us - if ( (jsdoc.tagDictionary.lookUp(kind).setsDocletDocspace) && /[^$_a-zA-Z0-9\/]/.test(name) ) { - if (!/^[a-z_$-\/]+:\"/i.test(name)) { - return '"' + name.replace(/\"/g, '"') + '"'; - } - } - - return name; - } - - RegExp.escape = RegExp.escape || function(str) { - var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\ - return str.replace(specials, "\\$&"); - } - - /** - @method module:jsdoc/name.applyNamespace - @param {string} longname The full longname of the symbol. - @param {string} ns The namespace to be applied. - @returns {string} The longname with the namespace applied. - */ - exports.applyNamespace = function(longname, ns) { - var nameParts = exports.shorten(longname), - name = nameParts.name, - longname = nameParts.longname; - - if ( !/^[a-zA-Z]+?:.+$/i.test(name) ) { - longname = longname.replace( new RegExp(RegExp.escape(name)+'$'), ns + ':' + name ); - } - - return longname; - } - - /** - Given a longname like "a.b#c(2)", slice it up into ["a.b", "#", 'c', '2'], - representing the memberof, the scope, the name, and variation. - @param {string} longname - @returns {object} Representing the properties of the given name. - */ - exports.shorten = function(longname) { - // quoted strings in a longname are atomic, convert to tokens - var atoms = [], token; - - // handle quoted names like foo["bar"] - longname = longname.replace(/(\[?".+?"\]?)/g, function($) { - var dot = ''; - if ( /^\[/.test($) ) { - dot = '.'; - $ = $.replace( /^\[/g, '' ).replace( /\]$/g, '' ); - } - - token = '@{' + atoms.length + '}@'; - atoms.push($); - - return dot + token; // foo["bar"] => foo.@{1}@ - }); - - longname = longname.replace( /\.prototype\.?/g, '#' ); - - var parts = longname? - (longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse() - : ['']; - - var name = parts[0], - scope = parts[1] || '', // ., ~, or # - memberof = parts[2] || '', - variation; - - // like /** @name foo.bar(2) */ - if ( /(.+)\(([^)]+)\)$/.test(name) ) { - name = RegExp.$1, variation = RegExp.$2; - } - - //// restore quoted strings back again - var i = atoms.length; - while (i--) { - longname = longname.replace('@{'+i+'}@', atoms[i]); - memberof = memberof.replace('@{'+i+'}@', atoms[i]); - scope = scope.replace('@{'+i+'}@', atoms[i]); - name = name.replace('@{'+i+'}@', atoms[i]); - } - - //// - return {longname: longname, memberof: memberof, scope: scope, name: name, variation: variation}; - } - -})(); \ No newline at end of file diff --git a/modules/jsdoc/opts/parser.js b/modules/jsdoc/opts/parser.js deleted file mode 100644 index 85b55ac67..000000000 --- a/modules/jsdoc/opts/parser.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - @module jsdoc/opts/parser - @requires common/args - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ -(function() { - var common = { - args: require('common/args') - }; - - var argParser = new common.args.ArgParser(), - ourOptions, - defaults = { - template: 'default', - destination: BASEDIR + 'out/' - }; - - argParser.addOption('t', 'template', true, 'The name of the template to use. Default: the "default" template'); - argParser.addOption('c', 'configure', true, 'The path to the configuration file. Default: jsdoc basedir + conf.json'); - argParser.addOption('e', 'encoding', true, 'Assume this encoding when reading all source files. Default: your system default encoding'); - argParser.addOption('n', 'nocode', false, 'Ignore doclets that don\'t explicitly provide a symbol name.'); - argParser.addOption('T', 'test', false, 'Run all tests and quit.'); - argParser.addOption('d', 'destination', true, 'The path to the output folder. Use "console" to dump data to the console. Default: console'); - argParser.addOption('V', 'validate', false, 'Validate the results produced by parsing the source code.'); - argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when scanning for source code files.'); - argParser.addOption('h', 'help', false, 'Print this message and quit.'); - argParser.addOption('X', 'expel', false, 'Dump all found doclet internals to console and quit.'); - argParser.addOption('q', 'query', true, 'Provide a querystring to define custom variable names/values to add to the options hash.'); - - -// TODO [-R, recurseonly] = a number representing the depth to recurse -// TODO [-f, filter] = a regex to filter on <-- this can be better defined in the configs? - - /** - Set the options for this app. - @throws {Error} Illegal arguments will throw errors. - @param {string|String[]} args The command line arguments for this app. - */ - exports.parse = function(args) { - args = args || []; - - if (typeof args === 'string' || args.constructor === String) { - args = (''+args).split(/\s+/g); - } - - ourOptions = argParser.parse(args, defaults); - - return ourOptions; - } - - /** - Display help message for options. - */ - exports.help = function() { - return argParser.help(); - } - - /** - Get a named option. - @param {string} name The name of the option. - @return {string} The value associated with the given name. - *//** - Get all the options for this app. - @return {Object} A collection of key/values representing all the options. - */ - exports.get = function(name) { - if (typeof name === 'undefined') { - return ourOptions; - } - else { - return ourOptions[name]; - } - } -})(); \ No newline at end of file diff --git a/modules/jsdoc/package.js b/modules/jsdoc/package.js deleted file mode 100644 index e4fcbf258..000000000 --- a/modules/jsdoc/package.js +++ /dev/null @@ -1,69 +0,0 @@ -/** - @overview - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ - -/** - @module jsdoc/package - @see http://wiki.commonjs.org/wiki/Packages/1.0 - */ -(function() { - /** - @class - @classdesc Represents a JavaScript package. - @param {string} json - The contents of package.json. - */ - exports.Package = function(json) { - /** The source files associated with this package. - @type {Array} - */ - this.files = []; - - /** The kind of this package. - @readonly - @default - @type {string} - */ - this.kind = 'package'; - - json = JSON.parse(json); - - /** The name of this package. - This value is found in the package.json file passed in as a command line option. - @type {string} - */ - this.name = json.name; - - /** The longname of this package. - @type {string} - */ - this.longname = this.kind + ':' + this.name; - - /** The description of this package. - @type {string} - */ - this.description = json.description; - - /** - The hash summary of the source file. - @type {string} - @since 3.2.0 - */ - this.version = json.version; - - /** - * The licenses of this package. - * @type {Array} - * @example - * "licenses": [ - * { - * "type": "GPLv2", - * "url": "http://www.example.com/licenses/gpl.html" - * } - * ] - */ - this.licenses = json.licenses; - } - -})(); \ No newline at end of file diff --git a/modules/jsdoc/schema.js b/modules/jsdoc/schema.js deleted file mode 100644 index be52250f7..000000000 --- a/modules/jsdoc/schema.js +++ /dev/null @@ -1,296 +0,0 @@ -/** - @overview Schema for validating JSON produced by JSDoc Toolkit. - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - @see - */ - -exports.jsdocSchema = { - "properties": { - "doc": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "author": { - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "path": { // unique identifier for each doc - "type": "string", - "maxItems": 1 - }, - "description": { // a description - "type": "string", - "optional": true, - "maxItems": 1 - }, - "classdesc": { // a description of the class that this constructor belongs to - "type": "string", - "optional": true, - "maxItems": 1 - }, - "name": { // probably a trailing substring of the path - "type": "string", - "maxItems": 1 - }, - "version": { // what is the version of this doc - "type": "string", - "optional": true, - "maxItems": 1 - }, - "since": { // at what previous version was this doc added? - "type": "string", - "optional": true, - "maxItems": 1 - }, - "see": { // some thing else to consider - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "deprecated": { // is usage of this symbol deprecated? - "type": ["string", "boolean"], - "optional": true - }, - "scope": { // how is this symbol attached to it's enclosing scope? - "type": "string", - "maxItems": 1, - "enum": ["global", "static", "instance", "inner"] - }, - "memberof": { // probably a leading substring of the path - "type": "string", - "optional": true, - "maxItems": 1 - }, - "extends": { // the path to another constructor - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "fires": { // the path to another doc object - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "requires": { // the symbol being documented requires another symbol - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "implements": { - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "kind": { // what kind of symbol is this? - "type": "string", - "maxItems": 1, - "enum": ["constructor", "module", "event", "namespace", "method", "property", "enum", "class", "interface", "constant", "mixin", "file", "version"] - }, - "refersto": { // the path to another doc: this doc is simply a renamed alias to that - "type": "string", - "optional": true, - "maxItems": 1 - }, - "access": { // what access priviledges are allowed - "type": "string", - "optional": true, - "maxItems": 1, - "enum": ["private", "protected", "public"] - }, - "attrib": { // other attributes, like "readonly" - "type": "string", - "optional": true - }, - "type": { // what type is the value that this doc is associated with, like "number" - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "exception" : { - "optional": true, - "type": "object", - "properties": { - "type": { // what is the type of the value thrown? - "type": "array", - "optional": true, - "items": { - "type": "string" - } - }, - "description": { // a description of the thrown value - "type": "string", - "optional": true - } - }, - "additionalProperties": false - }, - "returns" : { - "optional": true, - "type": "object", - "properties": { - "type": { // what is the type of the value returned? - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "description": { // a description of the returned value - "type": "string", - "optional": true - } - }, - "additionalProperties": false - }, - "param" : { // are there function parameters associated with this doc? - "type": "array", - "optional": true, - "items": { - "type": "object", - "properties": { - "type": { // what are the types of value expected for this parameter? - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "optional": { // is a value for this parameter optional? - "type": "boolean", - "optional": true, - "default": true - }, - "nullable": { // can the value for this parameter be null? - "type": "boolean", - "optional": true, - "default": true - }, - "defaultvalue": { // what is the default value for this parameter? - "type": "string", - "optional": true - }, - "name": { // what name does this parameter have within the function? - "type": "string" - }, - "description": { // a description of the parameter - "type": "string", - "optional": true - } - }, - "additionalProperties": false - } - }, - "thisobj": { - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "example": { // some thing else to consider - "type": ["string", "array"], - "optional": true, - "items": { - "type": "string" - } - }, - "tags": { // arbitrary tags associated with this doc - "type": "array", - "optional": true, - "additionalProperties": false, - "items": { - "type": "string" - } - }, - "meta": { // information about this doc - "type": "object", - "optional": true, - "maxItems": 1, - "properties": { - "file": { // what is the name of the file this doc appears in? - "type": "string", - "optional": true, - "maxItems": 1 - }, - "line": { // on what line of the file does this doc appear? - "type": "number", - "optional": true, - "maxItems": 1 - } - }, - "additionalProperties": false - } - } - } - }, - "meta": { // information about the generation for all the docs - "type": "object", - "optional": true, - "maxItems": 1, - "properties": { - "project": { // to what project does this doc belong? - "type": "object", - "optional": true, - "maxItems": 1, - "properties": { - "name": { // the name of the project - "type": "string", - "maxItems": 1 - }, - "uri": { // the URI of the project - "type": "string", - "maxItems": 1, - "format": "uri" - }, - "version": { // the version of the project - "type": "string", - "maxItems": 1 - }, - "lang": { // the programming language used in the project - "type": "string", - "maxItems": 1 - } - }, - "additionalProperties": false - }, - "generated": { // some information about the running of the doc generator - "type": "object", - "optional": true, - "maxItems": 1, - "properties": { - "date": { // on what date and time was the doc generated? - "type": "string", - "maxItems": 1, - "optional": true, - "format": "date-time" - }, - "parser": { // what tool was used to generate the doc? - "type": "string", - "maxItems": 1, - "optional": true - } - }, - "additionalProperties": false - } - } - } - } -}; \ No newline at end of file diff --git a/modules/jsdoc/src/handlers.js b/modules/jsdoc/src/handlers.js deleted file mode 100644 index 8443a73f2..000000000 --- a/modules/jsdoc/src/handlers.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - @module jsdoc/src/handlers - */ - -(function() { - var currentModule = null; - - /** - Attach these event handlers to a particular instance of a parser. - @param parser - */ - exports.attachTo = function(parser) { - var jsdoc = {doclet: require('jsdoc/doclet')}; - - // handles JSDoc comments that include a @name tag -- the code is ignored in such a case - parser.on('jsdocCommentFound', function(e) { - var newDoclet = new jsdoc.doclet.Doclet(e.comment, e); - - if (!newDoclet.name) { - return false; // only interested in virtual comments (with a @name) here - } - - addDoclet.call(this, newDoclet); - if (newDoclet.kind === 'module') { - currentModule = newDoclet.longname; - } - e.doclet = newDoclet; - }); - - // handles named symbols in the code, may or may not have a JSDoc comment attached - parser.on('symbolFound', function(e) { - var subDoclets = e.comment.split(/@also\b/g); - - for (var i = 0, l = subDoclets.length; i < l; i++) { - newSymbolDoclet.call(this, subDoclets[i], e); - } - }); - - function newSymbolDoclet(docletSrc, e) { - var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e); - - // an undocumented symbol right after a virtual comment? rhino mistakenly connected the two - if (newDoclet.name) { // there was a @name in comment - // try again, without the comment - e.comment = '@undocumented'; - newDoclet = new jsdoc.doclet.Doclet(e.comment, e); - } - - if (newDoclet.alias) { - newDoclet.addTag('name', newDoclet.alias); - newDoclet.postProcess(); - } - else if (e.code && e.code.name) { // we need to get the symbol name from code - newDoclet.addTag('name', e.code.name); - if (!newDoclet.memberof && e.astnode) { - var memberofName, - scope; - if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) { - var nameStartsWith = RegExp.$1; - - newDoclet.name = newDoclet.name.replace(/^(exports|this)(\.|$)/, ''); - - // like /** @module foo */ exports.bar = 1; - if (nameStartsWith === 'exports' && currentModule) { - memberofName = currentModule; - scope = 'static'; - } - else if (newDoclet.name === 'module.exports' && currentModule) { - newDoclet.addTag('name', currentModule); - newDoclet.postProcess(); - } - else { - // like /** @module foo */ exports = {bar: 1}; - // or /** blah */ this.foo = 1; - memberofName = this.resolveThis(e.astnode); - scope = nameStartsWith === 'exports'? 'static' : 'instance'; - - // like /** @module foo */ this.bar = 1; - if (nameStartsWith === 'this' && currentModule && !memberofName) { - memberofName = currentModule; - scope = 'static'; - } - } - - if (memberofName) { - if (newDoclet.name) { - newDoclet.name = memberofName + (scope === 'instance'? '#' : '.') + newDoclet.name; - } - else { newDoclet.name = memberofName; } - } - } - else { - memberofName = this.astnodeToMemberof(e.astnode); - } - - if (memberofName) { newDoclet.addTag( 'memberof', memberofName); } - } - - newDoclet.postProcess(); - } - else { - return false; - } - - addDoclet.call(this, newDoclet); - e.doclet = newDoclet; - } - - //parser.on('fileBegin', function(e) { }); - - parser.on('fileComplete', function(e) { - currentModule = null; - }); - - function addDoclet(newDoclet) { - if (newDoclet) { - e = { doclet: newDoclet }; - this.fire('newDoclet', e); - - if (!e.defaultPrevented) { - if ( !filter(newDoclet) ) { - this.addResult(newDoclet); - } - } - } - } - - function filter(doclet) { - // you can't document prototypes - if ( /#$/.test(doclet.longname) ) return true; - // you can't document symbols added by the parser with a dummy name - if (doclet.meta.code && doclet.meta.code.name === '____') return true; - - return false; - } - } -})(); \ No newline at end of file diff --git a/modules/jsdoc/src/parser.js b/modules/jsdoc/src/parser.js deleted file mode 100644 index 6f83a44fd..000000000 --- a/modules/jsdoc/src/parser.js +++ /dev/null @@ -1,495 +0,0 @@ -/** - * @module jsdoc/src/parser - * @requires common/util - * @requires common/fs - * @requires common/events - */ - -(function() { - var Token = Packages.org.mozilla.javascript.Token, - currentParser = null, - currentSourceName = ''; - - /** - * @class - * @mixes module:common/events - * - * @example Create a new parser. - * var jsdocParser = new (require('jsdoc/src/parser').Parser)(); - */ - exports.Parser = function() { - this._resultBuffer = []; - this.refs = {}; - } - require('common/util').mixin(exports.Parser.prototype, require('common/events')); - - /** - * Parse the given source files for JSDoc comments. - * @param {Array.} sourceFiles An array of filepaths to the JavaScript sources. - * @param {string} [encoding=utf8] - * - * @fires jsdocCommentFound - * @fires symbolFound - * @fires newDoclet - * @fires fileBegin - * @fires fileComplete - * - * @example Parse two source files. - * var myFiles = ['file1.js', 'file2.js']; - * var docs = jsdocParser.parse(myFiles); - */ - exports.Parser.prototype.parse = function(sourceFiles, encoding) { - const SCHEMA = 'javascript:'; - var sourceCode = '', - filename = ''; - - if (typeof sourceFiles === 'string') { sourceFiles = [sourceFiles]; } - - for (i = 0, leni = sourceFiles.length; i < leni; i++) { - if (sourceFiles[i].indexOf(SCHEMA) === 0) { - sourceCode = sourceFiles[i].substr(SCHEMA.length); - filename = '[[string' + i + ']]'; - } - else { - filename = sourceFiles[i]; - try { - sourceCode = require('common/fs').read(filename, encoding); - } - catch(e) { - print('FILE READ ERROR: in module:jsdoc/parser.parseFiles: "' + filename + '" ' + e); - continue; - } - } - - currentParser = this; - this._parseSourceCode(sourceCode, filename); - currentParser = null; - } - - return this._resultBuffer; - } - - /** - * @returns {Array} The accumulated results of any calls to parse. - */ - exports.Parser.prototype.results = function() { - return this._resultBuffer; - } - - /** - * @param {Object} o The parse result to add to the result buffer. - */ - exports.Parser.prototype.addResult = function(o) { - this._resultBuffer.push(o); - } - - /** - * Empty any accumulated results of calls to parse. - */ - exports.Parser.prototype.clear = function() { - currentParser = null; - currentSourceName = ''; - this._resultBuffer = []; - } - - /** @private */ - exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) { - currentSourceName = sourceName; - - sourceCode = pretreat(sourceCode); - - var ast = parserFactory().parse(sourceCode, sourceName, 1); - - var e = {filename: currentSourceName}; - this.fire('fileBegin', e); - - if (!e.defaultPrevented) { - ast.visit( - new Packages.org.mozilla.javascript.ast.NodeVisitor({ - visit: visitNode - }) - ); - } - - this.fire('fileComplete', e); - - currentSourceName = ''; - } - - function pretreat(code) { - return code - // merge adjacent doclets - .replace(/\*\/\/\*\*+/g, '@also') - // make lent objectliterals documentable by giving them a dummy name - .replace(/(\/\*\*[\s\S]*@lends\b[\s\S]*\*\/\s*)\{/g, '$1____ = {'); - } - - /** - * Given a node, determine what the node is a member of. - * @param {astnode} node - * @returns {string} The long name of the node that this is a member of. - */ - exports.Parser.prototype.astnodeToMemberof = function(node) { - var memberof = {}; - - if (node.type === Token.VAR || node.type === Token.FUNCTION) { - if (node.enclosingFunction) { // an inner var or func - memberof.id = 'astnode'+node.enclosingFunction.hashCode(); - memberof.doclet = this.refs[memberof.id]; - if (!memberof.doclet) { - return '~'; - } - return (memberof.doclet.longname||memberof.doclet.name) + '~'; - } - } - else { - memberof.id = 'astnode'+node.parent.hashCode(); - memberof.doclet = this.refs[memberof.id]; - if (!memberof.doclet) return ''; // global? - return memberof.doclet.longname||memberof.doclet.name; - } - } - - /** - * Resolve what "this" refers too, relative to a node. - * @param {astnode} node - The "this" node - * @returns {string} The longname of the enclosing node. - */ - exports.Parser.prototype.resolveThis = function(node) { - var memberof = {}; - - if (node.enclosingFunction) { - memberof.id = 'astnode'+node.enclosingFunction.hashCode(); - memberof.doclet = this.refs[memberof.id]; - - if (!memberof.doclet) { - return ''; // TODO handle global this? - } - - if (memberof.doclet['this']) { - return memberof.doclet['this']; - } - // like: Foo.constructor = function(n) { /** blah */ this.name = n; } - else if (memberof.doclet.kind === 'function' && memberof.doclet.memberof) { - return memberof.doclet.memberof; - } - // walk up to the closest class we can find - else if (memberof.doclet.kind === 'class' || memberof.doclet.kind === 'module') { - return memberof.doclet.longname||memberof.doclet.name; - } - else { - if (node.enclosingFunction){ - return this.resolveThis(node.enclosingFunction/*memberof.doclet.meta.code.val*/); - } - else return ''; // TODO handle global this? - } - } - else if (node.parent) { - var parent = node.parent; - if (parent.type === Token.COLON) parent = parent.parent; // go up one more - - memberof.id = 'astnode'+parent.hashCode(); - memberof.doclet = this.refs[memberof.id]; - - if (!memberof.doclet) return ''; // global? - - return memberof.doclet.longname||memberof.doclet.name; - } - else { - return ''; // global? - } - } - - /** - * Resolve what function a var is limited to. - * @param {astnode} node - * @param {string} basename The leftmost name in the long name: in foo.bar.zip the basename is foo. - */ - exports.Parser.prototype.resolveVar = function(node, basename) { - var doclet, - enclosingFunction = node.enclosingFunction; - - if (!enclosingFunction) { return ''; } // global - - doclet = this.refs['astnode'+enclosingFunction.hashCode()]; - - if ( doclet && doclet.meta.vars && ~doclet.meta.vars.indexOf(basename) ) { - return doclet.longname; - } - - return this.resolveVar(enclosingFunction, basename); - } - - /** @private */ - function visitNode(node) { - var e, - commentSrc; - - // look for stand-alone doc comments - if (node.type === Token.SCRIPT && node.comments) { - // note: ALL comments are seen in this block... - for each(var comment in node.comments.toArray()) { - if (comment.commentType !== Token.CommentType.JSDOC) { - continue; - } - - if (commentSrc = ''+comment.toSource()) { - - e = { - comment: commentSrc, - lineno: comment.getLineno(), - filename: currentSourceName - }; - - if ( isValidJsdoc(commentSrc) ) { - currentParser.fire('jsdocCommentFound', e, currentParser); - } - } - } - } - else if (node.type === Token.ASSIGN) { - e = { - id: 'astnode'+node.hashCode(), // the id of the ASSIGN node - comment: String(node.jsDoc||'@undocumented'), - lineno: node.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); - if (basename !== 'this') e.code.funcscope = currentParser.resolveVar(node, basename); - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - } - else if (node.type === Token.COLON) { // assignment within an object literal - e = { - id: 'astnode'+node.hashCode(), // the id of the COLON node - comment: String(node.left.jsDoc||'@undocumented'), - lineno: node.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - } - else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) { - - if (node.variables) { - return true; // we'll get each var separately on future visits - } - - if (node.parent.variables.toArray()[0] === node) { // like /** blah */ var a=1, b=2, c=3; - // the first var assignment gets any jsDoc before the whole var series - node.jsDoc = node.parent.jsDoc; - } - - e = { - id: 'astnode'+node.hashCode(), // the id of the VARIABLE node - comment: String(node.jsDoc||'@undocumented'), - lineno: node.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - // keep track of vars in a function scope - if (node.enclosingFunction) { - var func = 'astnode'+node.enclosingFunction.hashCode(), - funcDoc = currentParser.refs[func]; - - if (funcDoc) { - funcDoc.meta.vars = funcDoc.meta.vars || []; - funcDoc.meta.vars.push(e.code.name); - } - } - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - } - else if (node.type == Token.FUNCTION) { - e = { - id: 'astnode'+node.hashCode(), // the id of the COLON node - comment: String(node.jsDoc||'@undocumented'), - lineno: node.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - e.code.name = String(node.name) || ''; - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - else if (!currentParser.refs['astnode'+e.code.node.hashCode()]) { // keep references to undocumented anonymous functions too as they might have scoped vars - currentParser.refs['astnode'+e.code.node.hashCode()] = { - longname: '', - meta: { code: e.code } - }; - } - } - - return true; - } - - /** @private */ - function parserFactory() { - var cx = Packages.org.mozilla.javascript.Context.getCurrentContext(); - - var ce = new Packages.org.mozilla.javascript.CompilerEnvirons(); - ce.setRecordingComments(true); - ce.setRecordingLocalJsDocComments(true); - ce.setLanguageVersion(180); - - ce.initFromContext(cx); - return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter()); - } - - /** - * Attempts to find the name and type of the given node. - * @private - * @memberof module:src/parser.Parser - */ - function aboutNode(node) { - about = {}; - - if (node.type == Token.FUNCTION) { - about.name = '' + node.name; - - about.type = 'function'; - about.node = node; - - return about; - } - - if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) { - about.name = nodeToString(node.target); - if (node.initializer) { // like var i = 0; - about.node = node.initializer; - about.value = nodeToString(about.node); - about.type = getTypeName(node.initializer); - } - else { // like var i; - about.node = node.target; - about.value = nodeToString(about.node); - about.type = 'undefined'; - } - - return about; - } - - if (node.type === Token.ASSIGN || node.type === Token.COLON) { - about.name = nodeToString(node.left); - if (node.type === Token.COLON) { - - // objlit keys with unsafe variable-name characters must be quoted - if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) { - about.name = '"'+about.name.replace(/"/g, '\\"')+'"'; - } - } - about.node = node.right; - about.value = nodeToString(about.node); - about.type = getTypeName(node.right); - return about; - } - - // type 39 (NAME) - var string = nodeToString(node); - if (string) { - about.name = string; - return about; - } - - return about; - } - - /** @private - @memberof module:src/parser.Parser - */ - function nodeToString(node) { - var str; - - if (!node) return; - - if (node.type === Token.GETPROP) { - str = [nodeToString(node.target), node.property.string].join('.'); - } - else if (node.type === Token.VAR) { - str = nodeToString(node.target) - } - else if (node.type === Token.NAME) { - str = node.string; - } - else if (node.type === Token.STRING) { - str = node.value; - } - else if (node.type === Token.NUMBER) { - str = node.value; - } - else if (node.type === Token.THIS) { - str = 'this'; - } - else if (node.type === Token.GETELEM) { - str = node.toSource(); // like: Foo['Bar'] - } - else { - str = getTypeName(node); - } - - return '' + str; - }; - - /** @private - @memberof module:src/parser.Parser - */ - function getTypeName(node) { - var type = ''; - - if (node) { - type = ''+ Packages.org.mozilla.javascript.Token.typeToName(node.getType()); - } - - return type; - } - - /** @private - @memberof module:src/parser.Parser - */ - function isValidJsdoc(commentSrc) { - return commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/ - } - -})(); - -/** - Fired whenever the parser encounters a JSDoc comment in the current source code. - @event jsdocCommentFound - @memberof module:jsdoc/src/parser.Parser - @param {event} e - @param {string} e.comment The text content of the JSDoc comment - @param {number} e.lineno The line number associated with the found comment. - @param {string} e.filename The file name associated with the found comment. - */ \ No newline at end of file diff --git a/modules/jsdoc/src/scanner.js b/modules/jsdoc/src/scanner.js deleted file mode 100644 index 68f504906..000000000 --- a/modules/jsdoc/src/scanner.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - @module jsdoc/src/scanner - @requires module:common/fs - - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ - -(function() { - var common = { - fs: require('common/fs'), - mixin: require('common/util').mixin, - events: require('common/events') - }; - - /** - @constructor - @mixes module:common.events - */ - exports.Scanner = function() { - } - common.mixin(exports.Scanner.prototype, common.events); - - /** - Recursively searches the given searchPaths for js files. - @param {Array.} searchPaths - @param {number} [depth=1] - @fires sourceFileFound - */ - exports.Scanner.prototype.scan = function(searchPaths, depth, includeMatch, excludeMatch) { - var filePaths = [], - that = this; - - searchPaths = searchPaths || []; - depth = depth || 1; - - searchPaths.forEach(function($) { - filePaths = filePaths.concat(common.fs.ls($, depth)); - }); - - filePaths = filePaths.filter(function($) { - if (includeMatch && !includeMatch.test($)) { - return false - } - - if (excludeMatch && excludeMatch.test($)) { - return false - } - - return true; - }); - - filePaths = filePaths.filter(function($) { - var e = { fileName: $ }; - that.fire('sourceFileFound', e); - - return !e.defaultPrevented; - }); - - return filePaths; - } - -})(); \ No newline at end of file diff --git a/modules/jsdoc/tag.js b/modules/jsdoc/tag.js deleted file mode 100644 index c2173b539..000000000 --- a/modules/jsdoc/tag.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - @overview - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ - -/** - Functionality related to JSDoc tags. - @module jsdoc/tag - @requires jsdoc/tag/dictionary - @requires jsdoc/tag/validator - @requires jsdoc/tag/type - */ -(function() { - - var jsdoc = { - tag: { - dictionary: require('jsdoc/tag/dictionary'), - validator: require('jsdoc/tag/validator'), - type: require('jsdoc/tag/type') - } - }; - - /** - Constructs a new tag object. Calls the tag validator. - @class - @classdesc Represents a single doclet tag. - @param {string} tagTitle - @param {string=} tagBody - @param {object=} meta - */ - exports.Tag = function(tagTitle, tagBody, meta) { - var tagDef = jsdoc.tag.dictionary.lookUp(tagTitle), - meta = meta || {}; - - this.originalTitle = trim(tagTitle); - - /** The title part of the tag: @title text */ - this.title = jsdoc.tag.dictionary.normalise( this.originalTitle ); - - /** The text part of the tag: @title text */ - this.text = trim(tagBody, tagDef.keepsWhitespace); - - if (this.text) { - - if (tagDef.onTagText) { - this.text = tagDef.onTagText(this.text); - } - - if (tagDef.canHaveType) { - - /** The value propertiy represents the result of parsing the tag text. */ - this.value = {}; - - var [ - /*Array.*/ typeNames, - /*any*/ remainingText, - /*?boolean*/ optional, - /*?boolean*/ nullable, - /*?boolean*/ variable - ] = jsdoc.tag.type.parse(this.text); - - if (typeNames.length) { - this.value.type = { - names: typeNames, - optional: optional, - nullable: nullable, - variable: variable - }; - } - - if (remainingText) { - if (tagDef.canHaveName) { - var [paramName, paramDesc, paramOptional, paramDefault] - = parseParamText(remainingText); - - // note the dash is a special case: as a param name it means "no name" - if (paramName && paramName !== '-') { this.value.name = paramName; } - - if (paramDesc) { this.value.description = paramDesc; } - if (paramOptional) { this.value.optional = paramOptional; } - if (paramDefault) { this.value.defaultvalue = paramDefault; } - } - else { - this.value.description = remainingText; - } - } - } - else { - this.value = this.text; - } - } - - jsdoc.tag.validator.validate(this, meta); - } - - function trim(text, newlines) { - if (!text) { return ''; } - - if (newlines) { - return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, ''); - } - else { - return text.replace(/^\s+|\s+$/g, ''); - } - } - - /** - Parse the parameter name and parameter desc from the tag text. - @private - @method parseParamText - @param {string} tagText - @returns {Array.} [pname, pdesc, poptional, pdefault]. - */ - function parseParamText(tagText) { - var pname, pdesc, poptional, pdefault; - - // like: pname, pname pdesc, or name - pdesc - tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/); - pname = RegExp.$1; - pdesc = RegExp.$3; - - if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) { - pname = RegExp.$1; - poptional = true; - - if ( /^(.+?)\s*=\s*(.+)$/.test(pname) ) { - pname = RegExp.$1; - pdefault = RegExp.$2; - } - } - return [pname, pdesc, poptional, pdefault]; - } - -})(); \ No newline at end of file diff --git a/modules/jsdoc/tag/dictionary.js b/modules/jsdoc/tag/dictionary.js deleted file mode 100644 index b0484168d..000000000 --- a/modules/jsdoc/tag/dictionary.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - @overview - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ -(function() { - var _synonyms = {}, - _definitions = {}, - _namespaces = []; - - function _TagDefinition(title, etc) { - etc = etc || {}; - - this.title = dictionary.normalise(title); - - for (var p in etc) { - if (etc.hasOwnProperty(p)) { - this[p] = etc[p]; - } - } - } - - _TagDefinition.prototype.synonym = function(synonymName) { - _synonyms[synonymName.toLowerCase()] = this.title; - return this; // chainable - } - - /** @exports jsdoc/tag/dictionary */ - var dictionary = { - /** @function */ - defineTag: function(title, opts) { - _definitions[title] = new _TagDefinition(title, opts); - - if (opts.isNamespace) { - _namespaces.push(title); - } - - return _definitions[title]; - }, - - /** @function */ - lookUp: function(title) { - title = dictionary.normalise(title); - - if ( _definitions.hasOwnProperty(title) ) { - return _definitions[title]; - } - - return false; - }, - - /** @function */ - isNamespace: function(kind) { - return ( ~ _namespaces.indexOf(kind) ); - }, - - /** @function */ - normalise: function(title) { - canonicalName = title.toLowerCase(); - - if ( _synonyms.hasOwnProperty(canonicalName) ) { - return _synonyms[canonicalName]; - } - - return canonicalName; - } - }; - - require('jsdoc/tag/dictionary/definitions').defineTags(dictionary); - - module.exports = dictionary; - -})(); \ No newline at end of file diff --git a/modules/jsdoc/tag/dictionary/definitions.js b/modules/jsdoc/tag/dictionary/definitions.js deleted file mode 100644 index 958156939..000000000 --- a/modules/jsdoc/tag/dictionary/definitions.js +++ /dev/null @@ -1,538 +0,0 @@ -/** - Define tags that are known in JSDoc. - @module jsdoc/tag/dictionary/definitions - - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ -(function() { - /** Populate the given dictionary with all known JSDoc tag definitions. - @param {module:jsdoc/tag/dictionary} dictionary - */ - exports.defineTags = function(dictionary) { - - dictionary.defineTag('access', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - // only valid values are private and protected, public is default - if ( /^(private|protected)$/i.test(tag.value) ) { - doclet.access = tag.value.toLowerCase(); - } - else { - delete doclet.access; - } - } - }); - - dictionary.defineTag('alias', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.alias = tag.value; - } - }); - - dictionary.defineTag('lends', { - // mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.alias = tag.value || ''; - doclet.addTag('undocumented'); - } - }); - - dictionary.defineTag('author', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.author = tag.value; - } - }); - - // I add on to that - dictionary.defineTag('augments', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.augment( firstWordOf(tag.value) ); - } - }) - .synonym('extends'); - - // that adds on to me - dictionary.defineTag('borrows', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - var [target, source] = parseBorrows(doclet, tag); - doclet.borrow(target, source); - } - }) - .synonym('mixes'); - - dictionary.defineTag('class', { - onTagged: function(doclet, tag) { - doclet.addTag('kind', 'class'); - - // handle special case where both @class and @constructor tags exist in same doclet - if (tag.originalTitle === 'class') { - var looksLikeDesc = (tag.value || '').match(/\S+\s+\S+/); // multiple words after @class? - if ( looksLikeDesc || /@construct(s|or)\b/i.test(doclet.comment) ) { - doclet.classdesc = tag.value; // treat the @class tag as a @classdesc tag instead - return; - } - } - - setDocletNameToValue(doclet, tag); - } - }) - .synonym('constructor'); - - dictionary.defineTag('classdesc', { - onTagged: function(doclet, tag) { - doclet.classdesc = tag.value; - } - }); - - dictionary.defineTag('constant', { - canHaveType: true, - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - if (tag.value && tag.value.type) { - doclet.type = tag.value.type; - } - } - }) - .synonym('const'); - - dictionary.defineTag('copyright', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.copyright = tag.value; - } - }); - - dictionary.defineTag('constructs', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - var ownerClassName = firstWordOf(tag.value); - doclet.addTag('alias', ownerClassName); - doclet.addTag('kind', 'class'); - } - }); - - dictionary.defineTag('defaultvalue', { - onTagged: function(doclet, tag) { - if (tag.value) { - doclet.defaultvalue = tag.value; - } - else if (doclet.meta && doclet.meta.code && typeof doclet.meta.code.value !== 'undefined') { - if (doclet.meta.code.type && /STRING|NUMBER|NAME|TRUE|FALSE/.test(doclet.meta.code.type)) { - doclet.defaultvalue = doclet.meta.code.value; - if (doclet.meta.code.type === 'STRING') { - // TODO: handle escaped quotes in values - doclet.defaultvalue = '"'+doclet.defaultvalue.replace(/"/g, '\\"')+'"' - } - - if (doclet.defaultvalue === 'TRUE' || doclet.defaultvalue == 'FALSE') { - doclet.defaultvalue = doclet.defaultvalue.toLowerCase(); - } - } - else if (doclet.meta.code.type === 'NULL') { - // TODO: handle escaped quotes in values - doclet.defaultvalue = 'null' - } - } - } - }) - .synonym('default'); - - dictionary.defineTag('deprecated', { - // value is optional - onTagged: function(doclet, tag) { - doclet.deprecated = tag.value || true; - } - }); - - dictionary.defineTag('description', { - mustHaveValue: true - }) - .synonym('desc'); - - dictionary.defineTag('event', { - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - applyNamespace(doclet, tag); - } - }); - - dictionary.defineTag('example', { - keepsWhitespace: true, - mustHaveValue: true, - onTagged: function(doclet, tag) { - if (!doclet.examples) { doclet.examples = []; } - doclet.examples.push(tag.value); - } - }); - - dictionary.defineTag('exception', { - mustHaveValue: true, - canHaveType: true, - onTagged: function(doclet, tag) { - if (!doclet.exceptions) { doclet.exceptions = []; } - doclet.exceptions.push(tag.value); - if (tag.value && tag.value.type) { - doclet.type = tag.value.type; - } - } - }) - .synonym('throws'); - - dictionary.defineTag('exports', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - var modName = firstWordOf(tag.value); - - doclet.addTag('alias', modName); - doclet.addTag('kind', 'module'); - } - }); - - dictionary.defineTag('file', { - onTagged: function(doclet, tag) { - setNameToFile(doclet, tag); - setDocletKindToTitle(doclet, tag); - setDocletDescriptionToValue(doclet, tag); - - doclet.preserveName = true; - } - }) - .synonym('fileoverview') - .synonym('overview'); - - dictionary.defineTag('fires', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - if (!doclet.fires) { doclet.fires = []; } - doclet.fires.push(tag.value); - } - }); - - dictionary.defineTag('function', { - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - } - }) - .synonym('method'); - - dictionary.defineTag('global', { - mustNotHaveValue: true, - onTagged: function(doclet, tag) { - doclet.scope = 'global'; - delete doclet.memberof; - } - }); - - dictionary.defineTag('ignore', { - mustNotHaveValue: true, - onTagged: function(doclet, tag) { - doclet.ignore = true; - } - }); - - dictionary.defineTag('inner', { - onTagged: function(doclet, tag) { - setDocletScopeToTitle(doclet, tag); - } - }); - - dictionary.defineTag('instance', { - onTagged: function(doclet, tag) { - setDocletScopeToTitle(doclet, tag); - } - }); - - dictionary.defineTag('kind', { - mustHaveValue: true - }); - - dictionary.defineTag('license', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.license = tag.value; - } - }); - - dictionary.defineTag('memberof', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - setDocletMemberof(doclet, tag); - } - }) - .synonym('member'); - - dictionary.defineTag('mixin', { - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - } - }); - - dictionary.defineTag('module', { - canHaveType: true, - isNamespace: true, - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - doclet.name || setDocletNameToFilename(doclet, tag); - if (tag.value && tag.value.type) { - doclet.type = tag.value.type; - } - } - }); - - dictionary.defineTag('name', { - mustHaveValue: true - }); - - dictionary.defineTag('namespace', { - canHaveType: true, - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - if (tag.value && tag.value.type) { - doclet.type = tag.value.type; - } - } - }); - - dictionary.defineTag('param', { - mustHaveValue: true, - canHaveType: true, - canHaveName: true, - onTagged: function(doclet, tag) { - if (!doclet.params) { doclet.params = []; } - doclet.params.push(tag.value); - } - }) - .synonym('argument') - .synonym('arg'); - - dictionary.defineTag('private', { - mustNotHaveValue: true, - onTagged: function(doclet, tag) { - doclet.access = 'private'; - } - }); - - dictionary.defineTag('property', { - canHaveType: true, - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - setDocletNameToValue(doclet, tag); - if (tag.value && tag.value.type) { - doclet.type = tag.value.type; - } - } - }); - - dictionary.defineTag('protected', { - mustNotHaveValue: true, - onTagged: function(doclet, tag) { - doclet.access = 'protected'; - } - }); - - dictionary.defineTag('public', { - mustNotHaveValue: true, - onTagged: function(doclet, tag) { - delete doclet.access; // public is default - } - }); - - // use this instead of old deprecated @final tag - dictionary.defineTag('readonly', { - mustNotHaveValue: true, - onTagged: function(doclet, tag) { - doclet.readonly = true; - } - }); - - dictionary.defineTag('requires', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - var modName = firstWordOf(tag.value); - if (modName.indexOf('module:') !== 0) { - modName = 'module:'+modName; - } - if (!doclet.requires) { doclet.requires = []; } - doclet.requires.push(modName); - } - }); - - dictionary.defineTag('returns', { - mustHaveValue: true, - canHaveType: true, - onTagged: function(doclet, tag) { - if (!doclet.returns) { doclet.returns = []; } - doclet.returns.push(tag.value); - } - }) - .synonym('return'); - - dictionary.defineTag('see', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - if (!doclet.see) { doclet.see = []; } - doclet.see.push(tag.value); - } - }); - - dictionary.defineTag('since', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.since = tag.value; - } - }); - - dictionary.defineTag('summary', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.summary = tag.value; - } - }); - - dictionary.defineTag('this', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - if (!doclet.see) { doclet.see = []; } - doclet['this'] = firstWordOf(tag.value); - } - }); - - dictionary.defineTag('type', { - mustHaveValue: true, - canHaveType: true, - onTagText: function(text) { - // any text must be formatted as a type, but for back compat braces are optional - if ( ! /^\{.+\}$/.test(text) ) { - text = '{ '+text+' }'; - } - return text; - }, - onTagged: function(doclet, tag) { - if (tag.value && tag.value.type) { - doclet.type = tag.value.type; - if (doclet.kind === 'function') doclet.addTag('returns', tag.text); // for backwards compatibility we allow @type for functions to imply return type - } - } - }); - - dictionary.defineTag('typedef', { - canHaveType: true, - canHaveName: true, - onTagged: function(doclet, tag) { - setDocletKindToTitle(doclet, tag); - - if (tag.value) { - if (tag.value.name) { - doclet.addTag('name', tag.value.name); - } - if (tag.value.type) { - doclet.type = tag.value.type; - } - } - } - }); - - dictionary.defineTag('undocumented', { - mustNotHaveValue: true, - onTagged: function(doclet, tag) { - doclet.undocumented = true; - doclet.comment = ''; - } - }); - - dictionary.defineTag('variation', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.variation = tag.value; - } - }); - - dictionary.defineTag('version', { - mustHaveValue: true, - onTagged: function(doclet, tag) { - doclet.version = tag.value; - } - }); - } - - /** @private */ - function setDocletKindToTitle(doclet, tag) { - doclet.addTag( 'kind', tag.title ); - } - - function setDocletScopeToTitle(doclet, tag) { - doclet.addTag( 'scope', tag.title ); - } - - function setDocletNameToValue(doclet, tag) { - if (tag.value && tag.value.description) { // as in a long tag - doclet.addTag( 'name', tag.value.description); - } - else if (tag.text) { // or a short tag - doclet.addTag('name', tag.text); - } - } - - function setDocletDescriptionToValue(doclet, tag) { - if (tag.value) { - doclet.addTag( 'description', tag.value ); - } - } - - function setNameToFile(doclet, tag) { - if (doclet.meta.filename) { doclet.addTag( 'name', 'file:'+doclet.meta.filename ); } - } - - function setDocletMemberof(doclet, tag) { - doclet.setMemberof(tag.value); - } - - function applyNamespace(doclet, tag) { - if (!doclet.name) return; // error? - - //doclet.displayname = doclet.name; - doclet.longname = app.jsdoc.name.applyNamespace(doclet.name, tag.title) - } - - function setDocletNameToFilename(doclet, tag) { - var name = doclet.meta.filename; - name = name.replace(/\.js$/i, ''); - - for (var i = 0, len = env.opts._.length; i < len; i++) { - if (name.indexOf(env.opts._[i]) === 0) { - name = name.replace(env.opts._[0], ''); - break - } - } - doclet.name = name; - } - - function parseBorrows(doclet, tag) { - var m = /^(\S+)(?:\s+as\s+(\S+))?$/.exec(tag.text); - if (m) { - if (m[1] && m[2]) { - return [ m[1], m[2] ]; - } - else if (m[1]) { - return [ m[1] ]; - } - } - } - - function firstWordOf(string) { - var m = /^(\S+)/.exec(string); - if (m) { return m[1]; } - else { return ''; } - } -})(); \ No newline at end of file diff --git a/modules/jsdoc/tag/type.js b/modules/jsdoc/tag/type.js deleted file mode 100644 index 04bdc2801..000000000 --- a/modules/jsdoc/tag/type.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - @module jsdoc/tag/type - - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ - -(function() { - - /** - @param {string} tagValue - @returns {Array.} - */ - exports.parse = function(tagValue) { - if (typeof tagValue !== 'string') { tagValue = ''; } - var type = '', - text = '', - count = 0, - optional, - nullable, - variable; - - // type expressions start with '{' - if (tagValue[0] === '{') { - count++; - - // find matching closer '}' - for (var i = 1, leni = tagValue.length; i < leni; i++) { - if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character - - if (tagValue[i] === '{') { count++; } - else if (tagValue[i] === '}') { count--; } - - if (count === 0) { - type = trim(tagValue.slice(1, i)) - .replace(/\\\{/g, '{') // unescape escaped curly braces - .replace(/\\\}/g, '}'); - text = trim(tagValue.slice(i+1)); - break; - } - } - } - - if (type === '') { text = tagValue; } - - [type, optional] = parseOptional(type); - [type, nullable] = parseNullable(type); - [type, variable] = parseVariable(type); - - type = parseTypes(type); // make it into an array - - return [type, text, optional, nullable, variable]; - } - - function parseOptional(type) { - var optional = null; - - // {sometype=} means optional - if ( /(.+)=$/.test(type) ) { - type = RegExp.$1; - optional = true; - } - - return [type, optional]; - } - - function parseNullable(type) { - var nullable = null; - - // {?sometype} means nullable, {!sometype} means not-nullable - if ( /^([\?\!])(.+)$/.test(type) ) { - type = RegExp.$2; - nullable = (RegExp.$1 === '?')? true : false; - } - - return [type, nullable]; - } - - function parseVariable(type) { - var variable = null; - - // {...sometype} means variable number of that type - if ( /^(\.\.\.)(.+)$/.test(type) ) { - type = RegExp.$2; - variable = true; - } - - return [type, variable]; - } - - function parseTypes(type) { - var types = []; - - if ( ~type.indexOf('|') ) { - // remove optional parens, like: { ( string | number ) } - // see: http://code.google.com/closure/compiler/docs/js-for-compiler.html#types - if ( /^\s*\(\s*(.+)\s*\)\s*$/.test(type) ) { - type = RegExp.$1; - } - types = type.split(/\s*\|\s*/g); - } - else if (type) { - types = [type]; - } - - return types; - } - - /** @private */ - function trim(text) { - return text.replace(/^\s+|\s+$/g, ''); - } -})(); \ No newline at end of file diff --git a/modules/jsdoc/tag/validator.js b/modules/jsdoc/tag/validator.js deleted file mode 100644 index 7f714529b..000000000 --- a/modules/jsdoc/tag/validator.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - @module jsdoc/tag/validator - @requires jsdoc/tag/dictionary - - @author Michael Mathews - @license Apache License 2.0 - See file 'LICENSE.md' in this project. - */ -(function() { - - var dictionary = require('jsdoc/tag/dictionary'); - - /** - Validate the given tag. - */ - exports.validate = function(tag, meta) { - var tagDef = dictionary.lookUp(tag.title); - - if (!tagDef && !env.conf.tags.allowUnknownTags) { - throw new UnknownTagError(tag.title, meta); - } - - if (!tag.text) { - if (tagDef.mustHaveValue) { - throw new TagValueRequiredError(tag.title, meta); - } - } - else { - if (tagDef.mustNotHaveValue) { - throw new TagValueNotPermittedError(tag.title, meta); - } - } - } - - function UnknownTagError(tagName, meta) { - this.name = 'UnknownTagError'; - this.message = 'The @' + tagName + ' tag is not a known tag. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment; - } - UnknownTagError.prototype = Error.prototype; - - function TagValueRequiredError(tagName, meta) { - this.name = 'TagValueRequiredError'; - this.message = 'The @' + tagName + ' tag requires a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment; - } - TagValueRequiredError.prototype = Error.prototype; - - function TagValueNotPermittedError(tagName, message, meta) { - this.name = 'TagValueNotPermittedError'; - this.message = 'The @' + tagName + ' tag does not permit a value: "' + message + '". File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment; - } - TagValueNotPermittedError.prototype = Error.prototype; - -})(); \ No newline at end of file diff --git a/modules/sitepen/jsonschema.js b/modules/sitepen/jsonschema.js deleted file mode 100644 index 3d6f6625d..000000000 --- a/modules/sitepen/jsonschema.js +++ /dev/null @@ -1,233 +0,0 @@ -/** - * JSONSchema Validator - Validates JavaScript objects using JSON Schemas - * (http://www.json.com/json-schema-proposal/) - * - * Copyright (c) 2007 Kris Zyp SitePen (www.sitepen.com) - * Licensed under the MIT (MIT-LICENSE.txt) license. -To use the validator call JSONSchema.validate with an instance object and an optional schema object. -If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating), -that schema will be used to validate and the schema parameter is not necessary (if both exist, -both validations will occur). -The validate method will return an array of validation errors. If there are no errors, then an -empty list will be returned. A validation error will have two properties: -"property" which indicates which property had the error -"message" which indicates what the error was - */ - -// setup primitive classes to be JSON Schema types -String.type = "string"; -Boolean.type = "boolean"; -Number.type = "number"; -exports.Integer = {type:"integer"}; -Object.type = "object"; -Array.type = "array"; -Date.type = "string"; -Date.format = "date-time"; - -exports.validate = function(/*Any*/instance,/*Object*/schema) { - // Summary: - // To use the validator call JSONSchema.validate with an instance object and an optional schema object. - // If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating), - // that schema will be used to validate and the schema parameter is not necessary (if both exist, - // both validations will occur). - // The validate method will return an object with two properties: - // valid: A boolean indicating if the instance is valid by the schema - // errors: An array of validation errors. If there are no errors, then an - // empty list will be returned. A validation error will have two properties: - // property: which indicates which property had the error - // message: which indicates what the error was - // - return validate(instance,schema,false); - }; -exports.checkPropertyChange = function(/*Any*/value,/*Object*/schema, /*String*/ property) { - // Summary: - // The checkPropertyChange method will check to see if an value can legally be in property with the given schema - // This is slightly different than the validate method in that it will fail if the schema is readonly and it will - // not check for self-validation, it is assumed that the passed in value is already internally valid. - // The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for - // information. - // - return validate(value,schema, property || "property"); - }; -var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*Boolean*/ _changing) { - - var errors = []; - // validate a value against a property definition - function checkProp(value, schema, path,i){ - var l; - path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i; - function addError(message){ - errors.push({property:path,message:message}); - } - - if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function') && !(schema && schema.type)){ - if(typeof schema == 'function'){ - if(!(value instanceof schema)){ - addError("is not an instance of the class/constructor " + schema.name); - } - }else if(schema){ - addError("Invalid schema/property definition " + schema); - } - return null; - } - if(_changing && schema.readonly){ - addError("is a readonly field, it can not be changed"); - } - if(schema['extends']){ // if it extends another schema, it must pass that schema as well - checkProp(value,schema['extends'],path,i); - } - // validate a value against a type definition - function checkType(type,value){ - if(type){ - if(typeof type == 'string' && type != 'any' && - (type == 'null' ? value !== null : typeof value != type) && - !(value instanceof Array && type == 'array') && - !(type == 'integer' && value%1===0)){ - return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}]; - } - if(type instanceof Array){ - var unionErrors=[]; - for(var j = 0; j < type.length; j++){ // a union type - if(!(unionErrors=checkType(type[j],value)).length){ - break; - } - } - if(unionErrors.length){ - return unionErrors; - } - }else if(typeof type == 'object'){ - var priorErrors = errors; - errors = []; - checkProp(value,type,path); - var theseErrors = errors; - errors = priorErrors; - return theseErrors; - } - } - return []; - } - if(value === undefined){ - if(!schema.optional && !schema.get){ - addError("is missing and it is not optional"); - } - }else{ - errors = errors.concat(checkType(schema.type,value)); - if(schema.disallow && !checkType(schema.disallow,value).length){ - addError(" disallowed value was matched"); - } - if(value !== null){ - if(value instanceof Array){ - if(schema.items){ - if(schema.items instanceof Array){ - for(i=0,l=value.length; i schema.maxItems){ - addError("There must be a maximum of " + schema.maxItems + " in the array"); - } - }else if(schema.properties || schema.additionalProperties){ - errors.concat(checkObj(value, schema.properties, path, schema.additionalProperties)); - } - if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){ - addError("does not match the regex pattern " + schema.pattern); - } - if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){ - addError("may only be " + schema.maxLength + " characters long"); - } - if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){ - addError("must be at least " + schema.minLength + " characters long"); - } - if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum && - schema.minimum > value){ - addError("must have a minimum value of " + schema.minimum); - } - if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum && - schema.maximum < value){ - addError("must have a maximum value of " + schema.maximum); - } - if(schema['enum']){ - var enumer = schema['enum']; - l = enumer.length; - var found; - for(var j = 0; j < l; j++){ - if(enumer[j]===value){ - found=1; - break; - } - } - if(!found){ - addError("does not have a value in the enumeration " + enumer.join(", ")); - } - } - if(typeof schema.maxDecimal == 'number' && - (value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){ - addError("may only have " + schema.maxDecimal + " digits of decimal places"); - } - } - } - return null; - } - // validate an object against a schema - function checkObj(instance,objTypeDef,path,additionalProp){ - - if(typeof objTypeDef =='object'){ - if(typeof instance != 'object' || instance instanceof Array){ - errors.push({property:path,message:"an object is required"}); - } - - for(var i in objTypeDef){ - if(objTypeDef.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_')){ - var value = instance[i]; - var propDef = objTypeDef[i]; - checkProp(value,propDef,path,i); - } - } - } - for(i in instance){ - if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){ - errors.push({property:path,message:(typeof value) + "The property " + i + - " is not defined in the schema and the schema does not allow additional properties"}); - } - var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires; - if(requires && !(requires in instance)){ - errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"}); - } - value = instance[i]; - if(additionalProp && (!(objTypeDef && typeof objTypeDef == 'object') || !(i in objTypeDef))){ - checkProp(value,additionalProp,path,i); - } - if(!_changing && value && value.$schema){ - errors = errors.concat(checkProp(value,value.$schema,path,i)); - } - } - return errors; - } - if(schema){ - checkProp(instance,schema,'',_changing || ''); - } - if(!_changing && instance && instance.$schema){ - checkProp(instance,instance.$schema,'',''); - } - return {valid:!errors.length,errors:errors}; -}; -exports.mustBeValid = function(result){ - // summary: - // This checks to ensure that the result is valid and will throw an appropriate error message if it is not - // result: the result returned from checkPropertyChange or validate - if(!result.valid){ - throw new TypeError(result.errors.map(function(error){return "for property " + error.property + ': ' + error.message;}).join(", \n")); - } -} - /* will add this later - newFromSchema : function() { - } -*/ diff --git a/modules/underscore/template.js b/modules/underscore/template.js deleted file mode 100644 index 4f911b6b9..000000000 --- a/modules/underscore/template.js +++ /dev/null @@ -1,34 +0,0 @@ -// By default, Underscore uses ERB-style template delimiters, change the -// following template settings to use alternative delimiters. -exports.settings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g -}; - -// JavaScript micro-templating, similar to John Resig's implementation. -// Underscore templating handles arbitrary delimiters, preserves whitespace, -// and correctly escapes quotes within interpolated code. -exports.render = function(templateStr, data) { - var settings = exports.settings, - compiledTemplate, - renderFunction; - - compiledTemplate = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + - 'with(data||{}){__p.push(\'' + - templateStr.replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(settings.interpolate, function(match, code) { - return "'," + code.replace(/\\'/g, "'") + ",'"; - }) - .replace(settings.evaluate || null, function(match, code) { - return "');" + code.replace(/\\'/g, "'") - .replace(/[\r\n\t]/g, ' ') + "__p.push('"; - }) - .replace(/\r/g, '\\r') - .replace(/\n/g, '\\n') - .replace(/\t/g, '\\t') - + "');}return __p.join('');"; - - renderFunction = new Function('data', compiledTemplate); - return data ? renderFunction(data) : renderFunction; -}; \ No newline at end of file diff --git a/node_modules/.bin/nodeunit b/node_modules/.bin/nodeunit new file mode 120000 index 000000000..f31cdbe52 --- /dev/null +++ b/node_modules/.bin/nodeunit @@ -0,0 +1 @@ +../nodeunit/bin/nodeunit \ No newline at end of file diff --git a/node_modules/ejs/.gitignore b/node_modules/ejs/.gitignore new file mode 100644 index 000000000..99a4d6ef0 --- /dev/null +++ b/node_modules/ejs/.gitignore @@ -0,0 +1,3 @@ +# ignore any vim files: +*.sw[a-z] +vim/.netrwhist diff --git a/node_modules/ejs/.gitmodules b/node_modules/ejs/.gitmodules new file mode 100644 index 000000000..51ea138cb --- /dev/null +++ b/node_modules/ejs/.gitmodules @@ -0,0 +1,3 @@ +[submodule "support/expresso"] + path = support/expresso + url = http://github.com/visionmedia/expresso.git diff --git a/node_modules/ejs/History.md b/node_modules/ejs/History.md new file mode 100644 index 000000000..46160582c --- /dev/null +++ b/node_modules/ejs/History.md @@ -0,0 +1,60 @@ + +0.4.1 / 2011-04-21 +================== + + * Fixed error context + +0.4.0 / 2011-04-21 +================== + + * Added; ported jade's error reporting to ejs. [slaskis] + +0.3.1 / 2011-02-23 +================== + + * Fixed optional `compile()` options + +0.3.0 / 2011-02-14 +================== + + * Added 'json' filter [Yuriy Bogdanov] + * Use exported version of parse function to allow monkey-patching [Anatoliy Chakkaev] + +0.2.1 / 2010-10-07 +================== + + * Added filter support + * Fixed _cache_ option. ~4x performance increase + +0.2.0 / 2010-08-05 +================== + + * Added support for global tag config + * Added custom tag support. Closes #5 + * Fixed whitespace bug. Closes #4 + +0.1.0 / 2010-08-04 +================== + + * Faster implementation [ashleydev] + +0.0.4 / 2010-08-02 +================== + + * Fixed single quotes for content outside of template tags. [aniero] + * Changed; `exports.compile()` now expects only "locals" + +0.0.3 / 2010-07-15 +================== + + * Fixed single quotes + +0.0.2 / 2010-07-09 +================== + + * Fixed newline preservation + +0.0.1 / 2010-07-09 +================== + + * Initial release diff --git a/node_modules/ejs/Makefile b/node_modules/ejs/Makefile new file mode 100644 index 000000000..f971a5931 --- /dev/null +++ b/node_modules/ejs/Makefile @@ -0,0 +1,5 @@ + +test: + @./support/expresso/bin/expresso -I lib test/*.test.js + +.PHONY: test \ No newline at end of file diff --git a/node_modules/ejs/Readme.md b/node_modules/ejs/Readme.md new file mode 100644 index 000000000..85409ab44 --- /dev/null +++ b/node_modules/ejs/Readme.md @@ -0,0 +1,137 @@ + +# EJS + +Embedded JavaScript templates. + +## Installation + + $ npm install ejs + +## Features + + * Complies with the [Express](http://expressjs.com) view system + * Static caching of intermediate JavaScript + * Unbuffered code for conditionals etc `<% code %>` + * Escapes html by default with `<%= code %>` + * Unescaped buffering with `<%- code %>` + * Supports tag customization + * Filter support for designer-friendly templates + +## Example + + <% if (user) { %> +

    <%= user.name %>

    + <% } %> + +## Usage + + ejs.compile(str, options); + // => Function + + ejs.render(str, options); + // => str + +## Options + + - `locals` Local variables object + - `cache` Compiled functions are cached, requires `filename` + - `filename` Used by `cache` to key caches + - `scope` Function execution context + - `debug` Output generated function body + - `open` Open tag, defaulting to "<%" + - `close` Closing tag, defaulting to "%>" + +## Custom Tags + +Custom tags can also be applied globally: + + var ejs = require('ejs'); + ejs.open = '{{'; + ejs.close = '}}'; + +Which would make the following a valid template: + +

    {{= title }}

    + +## Filters + +EJS conditionally supports the concept of "filters". A "filter chain" +is a designer friendly api for manipulating data, without writing JavaScript. + +Filters can be applied by supplying the _:_ modifier, so for example if we wish to take the array `[{ name: 'tj' }, { name: 'mape' }, { name: 'guillermo' }]` and output a list of names we can do this simply with filters: + +Template: + +

    <%=: users | map:'name' | join %>

    + +Output: + +

    Tj, Mape, Guillermo

    + +Render call: + + ejs.render(str, { + locals: { + users: [ + { name: 'tj' }, + { name: 'mape' }, + { name: 'guillermo' } + ] + } + }); + +Or perhaps capitalize the first user's name for display: + +

    <%=: users | first | capitalize %>

    + +## Filter List + +Currently these filters are available: + + - first + - last + - capitalize + - downcase + - upcase + - sort + - sort_by:'prop' + - size + - length + - plus:n + - minus:n + - times:n + - divided_by:n + - join:'val' + - truncate:n + - truncate_words:n + - replace:pattern,substitution + - prepend:val + - append:val + - map:'prop' + - reverse + - get:'prop' + +## License + +(The MIT License) + +Copyright (c) 2009-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. diff --git a/node_modules/ejs/benchmark.js b/node_modules/ejs/benchmark.js new file mode 100644 index 000000000..7b267e160 --- /dev/null +++ b/node_modules/ejs/benchmark.js @@ -0,0 +1,14 @@ + + +var ejs = require('./lib/ejs'), + str = '<% if (foo) { %>

    <%= foo %>

    <% } %>', + times = 50000; + +console.log('rendering ' + times + ' times'); + +var start = new Date; +while (times--) { + ejs.render(str, { cache: true, filename: 'test', locals: { foo: 'bar' }}); +} + +console.log('took ' + (new Date - start) + 'ms'); \ No newline at end of file diff --git a/node_modules/ejs/examples/list.ejs b/node_modules/ejs/examples/list.ejs new file mode 100644 index 000000000..d571330ae --- /dev/null +++ b/node_modules/ejs/examples/list.ejs @@ -0,0 +1,7 @@ +<% if (names.length) { %> +
      + <% names.forEach(function(name){ %> +
    • <%= name %>
    • + <% }) %> +
    +<% } %> \ No newline at end of file diff --git a/node_modules/ejs/examples/list.js b/node_modules/ejs/examples/list.js new file mode 100644 index 000000000..9cd71683b --- /dev/null +++ b/node_modules/ejs/examples/list.js @@ -0,0 +1,16 @@ + +/** + * Module dependencies. + */ + +var ejs = require('../') + , fs = require('fs') + , str = fs.readFileSync(__dirname + '/list.ejs', 'utf8'); + +var ret = ejs.render(str, { + locals: { + names: ['foo', 'bar', 'baz'] + } +}); + +console.log(ret); \ No newline at end of file diff --git a/node_modules/ejs/index.js b/node_modules/ejs/index.js new file mode 100644 index 000000000..20bf71a3f --- /dev/null +++ b/node_modules/ejs/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/ejs'); \ No newline at end of file diff --git a/node_modules/ejs/lib/ejs.js b/node_modules/ejs/lib/ejs.js new file mode 100644 index 000000000..c03244ed2 --- /dev/null +++ b/node_modules/ejs/lib/ejs.js @@ -0,0 +1,249 @@ + +/*! + * EJS + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var sys = require('sys') + , utils = require('./utils'); + +/** + * Library version. + */ + +exports.version = '0.4.1'; + +/** + * Filters. + * + * @type Object + */ + +var filters = exports.filters = require('./filters'); + +/** + * Intermediate js cache. + * + * @type Object + */ + +var cache = {}; + +/** + * Clear intermediate js cache. + * + * @api public + */ + +exports.clearCache = function(){ + cache = {}; +}; + +/** + * Translate filtered code into function calls. + * + * @param {String} js + * @return {String} + * @api private + */ + +function filtered(js) { + return js.substr(1).split('|').reduce(function(js, filter){ + var parts = filter.split(':') + , name = parts.shift() + , args = parts.shift() || ''; + if (args) args = ', ' + args; + return 'filters.' + name + '(' + js + args + ')'; + }); +}; + +/** + * Re-throw the given `err` in context to the + * `str` of ejs, `filename`, and `lineno`. + * + * @param {Error} err + * @param {String} str + * @param {String} filename + * @param {String} lineno + * @api private + */ + +function rethrow(err, str, filename, lineno){ + var lines = str.split('\n') + , start = Math.max(lineno - 3, 0) + , end = Math.min(lines.length, lineno + 3); + + // Error context + var context = lines.slice(start, end).map(function(line, i){ + var curr = i + start + 1; + return (curr == lineno ? ' >> ' : ' ') + + curr + + '| ' + + line; + }).join('\n'); + + // Alter exception message + err.path = filename; + err.message = (filename || 'ejs') + ':' + + lineno + '\n' + + context + '\n\n' + + err.message; + + throw err; +} + +/** + * Parse the given `str` of ejs, returning the function body. + * + * @param {String} str + * @return {String} + * @api public + */ + +var parse = exports.parse = function(str, options){ + var options = options || {} + , open = options.open || exports.open || '<%' + , close = options.close || exports.close || '%>'; + + var buf = [ + "var buf = [];" + , "\nwith (locals) {" + , "\n buf.push('" + ]; + + var lineno = 1; + + for (var i = 0, len = str.length; i < len; ++i) { + if (str.slice(i, open.length + i) == open) { + i += open.length + + var prefix, postfix, line = '__stack.lineno=' + lineno; + switch (str[i]) { + case '=': + prefix = "', escape((" + line + ', '; + postfix = ")), '"; + ++i; + break; + case '-': + prefix = "', (" + line + ', '; + postfix = "), '"; + ++i; + break; + default: + prefix = "');" + line + ';'; + postfix = "; buf.push('"; + } + + var start = i; + var end = str.indexOf(close, i); + var js = str.substring(i, end); + if (js[0] == ':') js = filtered(js); + buf.push(prefix, js, postfix); + i += end - start + close.length - 1; + + } else if (str[i] == "\\") { + buf.push("\\\\"); + } else if (str[i] == "'") { + buf.push("\\'"); + } else if (str[i] == "\r") { + buf.push(" "); + } else if (str[i] == "\n") { + buf.push("\\n"); + lineno++; + } else { + buf.push(str[i]); + } + } + + buf.push("');\n}\nreturn buf.join('');"); + return buf.join(''); +}; + +/** + * Compile the given `str` of ejs into a `Function`. + * + * @param {String} str + * @param {Object} options + * @return {Function} + * @api public + */ + +var compile = exports.compile = function(str, options){ + options = options || {}; + + var input = JSON.stringify(str) + , filename = options.filename + ? JSON.stringify(options.filename) + : 'undefined'; + + // Adds the fancy stack trace meta info + str = [ + 'var __stack = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };', + rethrow.toString(), + 'try {', + exports.parse(str, options), + '} catch (err) {', + ' rethrow(err, __stack.input, __stack.filename, __stack.lineno);', + '}' + ].join("\n"); + + if (options.debug) sys.puts(str); + var fn = new Function('locals, filters, escape', str); + return function(locals){ + return fn.call(this, locals, filters, utils.escape); + } +}; + +/** + * Render the given `str` of ejs. + * + * Options: + * + * - `locals` Local variables object + * - `cache` Compiled functions are cached, requires `filename` + * - `filename` Used by `cache` to key caches + * - `scope` Function execution context + * - `debug` Output generated function body + * - `open` Open tag, defaulting to "<%" + * - `close` Closing tag, defaulting to "%>" + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api public + */ + +exports.render = function(str, options){ + var fn + , options = options || {}; + if (options.cache) { + if (options.filename) { + fn = cache[options.filename] || (cache[options.filename] = compile(str, options)); + } else { + throw new Error('"cache" option requires "filename".'); + } + } else { + fn = compile(str, options); + } + return fn.call(options.scope, options.locals || {}); +}; + +/** + * Expose to require(). + */ + +if (require.extensions) { + require.extensions['.ejs'] = function(module, filename) { + source = require('fs').readFileSync(filename, 'utf-8'); + module._compile(compile(source, {}), filename); + }; +} else if (require.registerExtension) { + require.registerExtension('.ejs', function(src) { + return compile(src, {}); + }); +} diff --git a/node_modules/ejs/lib/filters.js b/node_modules/ejs/lib/filters.js new file mode 100644 index 000000000..d425c8d89 --- /dev/null +++ b/node_modules/ejs/lib/filters.js @@ -0,0 +1,198 @@ + +/*! + * EJS - Filters + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * First element of the target `obj`. + */ + +exports.first = function(obj) { + return obj[0]; +}; + +/** + * Last element of the target `obj`. + */ + +exports.last = function(obj) { + return obj[obj.length - 1]; +}; + +/** + * Capitalize the first letter of the target `str`. + */ + +exports.capitalize = function(str){ + str = String(str); + return str[0].toUpperCase() + str.substr(1, str.length); +}; + +/** + * Downcase the target `str`. + */ + +exports.downcase = function(str){ + return String(str).toLowerCase(); +}; + +/** + * Uppercase the target `str`. + */ + +exports.upcase = function(str){ + return String(str).toUpperCase(); +}; + +/** + * Sort the target `obj`. + */ + +exports.sort = function(obj){ + return Object.create(obj).sort(); +}; + +/** + * Sort the target `obj` by the given `prop` ascending. + */ + +exports.sort_by = function(obj, prop){ + return Object.create(obj).sort(function(a, b){ + a = a[prop], b = b[prop]; + if (a > b) return 1; + if (a < b) return -1; + return 0; + }); +}; + +/** + * Size or length of the target `obj`. + */ + +exports.size = exports.length = function(obj) { + return obj.length; +}; + +/** + * Add `a` and `b`. + */ + +exports.plus = function(a, b){ + return Number(a) + Number(b); +}; + +/** + * Subtract `b` from `a`. + */ + +exports.minus = function(a, b){ + return Number(a) - Number(b); +}; + +/** + * Multiply `a` by `b`. + */ + +exports.times = function(a, b){ + return Number(a) * Number(b); +}; + +/** + * Divide `a` by `b`. + */ + +exports.divided_by = function(a, b){ + return Number(a) / Number(b); +}; + +/** + * Join `obj` with the given `str`. + */ + +exports.join = function(obj, str){ + return obj.join(str || ', '); +}; + +/** + * Truncate `str` to `len`. + */ + +exports.truncate = function(str, len){ + str = String(str); + return str.substr(0, len); +}; + +/** + * Truncate `str` to `n` words. + */ + +exports.truncate_words = function(str, n){ + var str = String(str) + , words = str.split(/ +/); + return words.slice(0, n).join(' '); +}; + +/** + * Replace `pattern` with `substitution` in `str`. + */ + +exports.replace = function(str, pattern, substitution){ + return String(str).replace(pattern, substitution || ''); +}; + +/** + * Prepend `val` to `obj`. + */ + +exports.prepend = function(obj, val){ + return Array.isArray(obj) + ? [val].concat(obj) + : val + obj; +}; + +/** + * Append `val` to `obj`. + */ + +exports.append = function(obj, val){ + return Array.isArray(obj) + ? obj.concat(val) + : obj + val; +}; + +/** + * Map the given `prop`. + */ + +exports.map = function(arr, prop){ + return arr.map(function(obj){ + return obj[prop]; + }); +}; + +/** + * Reverse the given `obj`. + */ + +exports.reverse = function(obj){ + return Array.isArray(obj) + ? obj.reverse() + : String(obj).split('').reverse().join(''); +}; + +/** + * Get `prop` of the given `obj`. + */ + +exports.get = function(obj, prop){ + return obj[prop]; +}; + +/** + * Packs the given `obj` into json string + */ +exports.json = function(obj){ + return JSON.stringify(obj); +}; \ No newline at end of file diff --git a/node_modules/ejs/lib/utils.js b/node_modules/ejs/lib/utils.js new file mode 100644 index 000000000..8d569d6f2 --- /dev/null +++ b/node_modules/ejs/lib/utils.js @@ -0,0 +1,23 @@ + +/*! + * EJS + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * 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/node_modules/ejs/package.json b/node_modules/ejs/package.json new file mode 100644 index 000000000..357e89ed6 --- /dev/null +++ b/node_modules/ejs/package.json @@ -0,0 +1,8 @@ +{ + "name": "ejs", + "description": "Embedded JavaScript templates", + "version": "0.4.1", + "author": "TJ Holowaychuk ", + "keywords": ["template", "engine", "ejs"], + "main": "./lib/ejs.js" +} \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/.gitignore b/node_modules/ejs/support/expresso/.gitignore new file mode 100644 index 000000000..432563ff5 --- /dev/null +++ b/node_modules/ejs/support/expresso/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +lib-cov +*.seed \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/.gitmodules b/node_modules/ejs/support/expresso/.gitmodules new file mode 100644 index 000000000..191ddebd4 --- /dev/null +++ b/node_modules/ejs/support/expresso/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/jscoverage"] + path = deps/jscoverage + url = git://github.com/visionmedia/node-jscoverage.git diff --git a/node_modules/ejs/support/expresso/History.md b/node_modules/ejs/support/expresso/History.md new file mode 100644 index 000000000..e3b014127 --- /dev/null +++ b/node_modules/ejs/support/expresso/History.md @@ -0,0 +1,87 @@ + +0.6.2 / 2010-08-03 +================== + + * Added `assert.type()` + * Renamed `assert.isNotUndefined()` to `assert.isDefined()` + * Fixed `assert.includes()` param ordering + +0.6.0 / 2010-07-31 +================== + + * Added _docs/api.html_ + * Added -w, --watch + * Added `Array` support to `assert.includes()` + * Added; outputting exceptions immediately. Closes #19 + * Fixed `assert.includes()` param ordering + * Fixed `assert.length()` param ordering + * Fixed jscoverage links + +0.5.0 / 2010-07-16 +================== + + * Added support for async exports + * Added timeout support to `assert.response()`. Closes #3 + * Added 4th arg callback support to `assert.response()` + * Added `assert.length()` + * Added `assert.match()` + * Added `assert.isUndefined()` + * Added `assert.isNull()` + * Added `assert.includes()` + * Added growlnotify support via -g, --growl + * Added -o, --only TESTS. Ex: --only "test foo()" --only "test foo(), test bar()" + * Removed profanity + +0.4.0 / 2010-07-09 +================== + + * Added reporting source coverage (respects --boring for color haters) + * Added callback to assert.response(). Closes #12 + * Fixed; putting exceptions to stderr. Closes #13 + +0.3.1 / 2010-06-28 +================== + + * Faster assert.response() + +0.3.0 / 2010-06-28 +================== + + * Added -p, --port NUM flags + * Added assert.response(). Closes #11 + +0.2.1 / 2010-06-25 +================== + + * Fixed issue with reporting object assertions + +0.2.0 / 2010-06-21 +================== + + * Added `make uninstall` + * Added better readdir() failure message + * Fixed `make install` for kiwi + +0.1.0 / 2010-06-15 +================== + + * Added better usage docs via --help + * Added better conditional color support + * Added pre exit assertion support + +0.0.3 / 2010-06-02 +================== + + * Added more room for filenames in test coverage + * Added boring output support via --boring (suppress colored output) + * Fixed async failure exit status + +0.0.2 / 2010-05-30 +================== + + * Fixed exit status for CI support + +0.0.1 / 2010-05-30 +================== + + * Initial release \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/Makefile b/node_modules/ejs/support/expresso/Makefile new file mode 100644 index 000000000..9348bbd30 --- /dev/null +++ b/node_modules/ejs/support/expresso/Makefile @@ -0,0 +1,50 @@ + +BIN = bin/expresso +PREFIX = /usr/local +JSCOV = deps/jscoverage/node-jscoverage +DOCS = docs/index.md +HTMLDOCS = $(DOCS:.md=.html) + +test: $(BIN) + @./$(BIN) -I lib --growl $(TEST_FLAGS) test/*.test.js + +test-cov: + @./$(BIN) -I lib --cov $(TEST_FLAGS) test/*.test.js + +install: install-jscov install-expresso + +uninstall: + rm -f $(PREFIX)/bin/expresso + rm -f $(PREFIX)/bin/node-jscoverage + +install-jscov: $(JSCOV) + install $(JSCOV) $(PREFIX)/bin + +install-expresso: + install $(BIN) $(PREFIX)/bin + +$(JSCOV): + cd deps/jscoverage && ./configure && make && mv jscoverage node-jscoverage + +clean: + @cd deps/jscoverage && git clean -fd + +docs: docs/api.html $(HTMLDOCS) + +%.html: %.md + @echo "... $< > $@" + @ronn -5 --pipe --fragment $< \ + | cat docs/layout/head.html - docs/layout/foot.html \ + > $@ + +docs/api.html: bin/expresso + dox \ + --title "Expresso" \ + --ribbon "http://github.com/visionmedia/expresso" \ + --desc "Insanely fast TDD framework for [node](http://nodejs.org) featuring code coverage reporting." \ + $< > $@ + +docclean: + rm -f docs/*.html + +.PHONY: test test-cov install uninstall install-expresso install-jscov clean docs docclean \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/Readme.md b/node_modules/ejs/support/expresso/Readme.md new file mode 100644 index 000000000..dcc1c85c7 --- /dev/null +++ b/node_modules/ejs/support/expresso/Readme.md @@ -0,0 +1,39 @@ + +# Expresso + + TDD framework for [nodejs](http://nodejs.org). + +## Features + + - light-weight + - intuitive async support + - intuitive test runner executable + - test coverage support and reporting + - uses the _assert_ module + - `assert.eql()` alias of `assert.deepEqual()` + - `assert.response()` http response utility + - `assert.includes()` + - `assert.type()` + - `assert.isNull()` + - `assert.isUndefined()` + - `assert.isNotNull()` + - `assert.isDefined()` + - `assert.match()` + - `assert.length()` + +## Installation + +To install both expresso _and_ node-jscoverage run: + + $ make install + +To install expresso alone (no build required) run: + + $ make install-expresso + +Install via npm: + + $ npm install expresso + + + diff --git a/node_modules/ejs/support/expresso/bin/expresso b/node_modules/ejs/support/expresso/bin/expresso new file mode 100755 index 000000000..96c7ff399 --- /dev/null +++ b/node_modules/ejs/support/expresso/bin/expresso @@ -0,0 +1,775 @@ +#!/usr/bin/env node + +/*! + * Expresso + * Copyright(c) TJ Holowaychuk + * (MIT Licensed) + */ + +/** + * Module dependencies. + */ + +var assert = require('assert'), + childProcess = require('child_process'), + http = require('http'), + path = require('path'), + sys = require('sys'), + cwd = process.cwd(), + fs = require('fs'), + defer; + +/** + * Expresso version. + */ + +var version = '0.6.1'; + +/** + * Failure count. + */ + +var failures = 0; + + +/** + * Number of tests executed. + */ + +var testcount = 0; + +/** + * Whitelist of tests to run. + */ + +var only = []; + +/** + * Boring output. + */ + +var boring = false; + +/** + * Growl notifications. + */ + +var growl = false; + +/** + * Server port. + */ + +var port = 5555; + +/** + * Watch mode. + */ + +var watch = false; + +/** + * Usage documentation. + */ + +var usage = '' + + '[bold]{Usage}: expresso [options] ' + + '\n' + + '\n[bold]{Options}:' + + '\n -w, --watch Watch for modifications and re-execute tests' + + '\n -g, --growl Enable growl notifications' + + '\n -c, --coverage Generate and report test coverage' + + '\n -r, --require PATH Require the given module path' + + '\n -o, --only TESTS Execute only the comma sperated TESTS (can be set several times)' + + '\n -I, --include PATH Unshift the given path to require.paths' + + '\n -p, --port NUM Port number for test servers, starts at 5555' + + '\n -b, --boring Suppress ansi-escape colors' + + '\n -v, --version Output version number' + + '\n -h, --help Display help information' + + '\n'; + +// Parse arguments + +var files = [], + args = process.argv.slice(2); + +while (args.length) { + var arg = args.shift(); + switch (arg) { + case '-h': + case '--help': + print(usage + '\n'); + process.exit(1); + break; + case '-v': + case '--version': + sys.puts(version); + process.exit(1); + break; + case '-i': + case '-I': + case '--include': + if (arg = args.shift()) { + require.paths.unshift(arg); + } else { + throw new Error('--include requires a path'); + } + break; + case '-o': + case '--only': + if (arg = args.shift()) { + only = only.concat(arg.split(/ *, */)); + } else { + throw new Error('--only requires comma-separated test names'); + } + break; + case '-p': + case '--port': + if (arg = args.shift()) { + port = parseInt(arg, 10); + } else { + throw new Error('--port requires a number'); + } + break; + case '-r': + case '--require': + if (arg = args.shift()) { + require(arg); + } else { + throw new Error('--require requires a path'); + } + break; + case '-c': + case '--cov': + case '--coverage': + defer = true; + childProcess.exec('rm -fr lib-cov && node-jscoverage lib lib-cov', function(err){ + if (err) throw err; + require.paths.unshift('lib-cov'); + run(files); + }) + break; + case '-b': + case '--boring': + boring = true; + break; + case '-w': + case '--watch': + watch = true; + break; + case '--g': + case '--growl': + growl = true; + break; + default: + if (/\.js$/.test(arg)) { + files.push(arg); + } + break; + } +} + +/** + * Colorized sys.error(). + * + * @param {String} str + */ + +function print(str){ + sys.error(colorize(str)); +} + +/** + * Colorize the given string using ansi-escape sequences. + * Disabled when --boring is set. + * + * @param {String} str + * @return {String} + */ + +function colorize(str){ + var colors = { bold: 1, red: 31, green: 32, yellow: 33 }; + return str.replace(/\[(\w+)\]\{([^]*?)\}/g, function(_, color, str){ + return boring + ? str + : '\x1B[' + colors[color] + 'm' + str + '\x1B[0m'; + }); +} + +// Alias deepEqual as eql for complex equality + +assert.eql = assert.deepEqual; + +/** + * Assert that `val` is null. + * + * @param {Mixed} val + * @param {String} msg + */ + +assert.isNull = function(val, msg) { + assert.strictEqual(null, val, msg); +}; + +/** + * Assert that `val` is not null. + * + * @param {Mixed} val + * @param {String} msg + */ + +assert.isNotNull = function(val, msg) { + assert.notStrictEqual(null, val, msg); +}; + +/** + * Assert that `val` is undefined. + * + * @param {Mixed} val + * @param {String} msg + */ + +assert.isUndefined = function(val, msg) { + assert.strictEqual(undefined, val, msg); +}; + +/** + * Assert that `val` is not undefined. + * + * @param {Mixed} val + * @param {String} msg + */ + +assert.isDefined = function(val, msg) { + assert.notStrictEqual(undefined, val, msg); +}; + +/** + * Assert that `obj` is `type`. + * + * @param {Mixed} obj + * @param {String} type + * @api public + */ + +assert.type = function(obj, type, msg){ + var real = typeof obj; + msg = msg || 'typeof ' + sys.inspect(obj) + ' is ' + real + ', expected ' + type; + assert.ok(type === real, msg); +}; + +/** + * Assert that `str` matches `regexp`. + * + * @param {String} str + * @param {RegExp} regexp + * @param {String} msg + */ + +assert.match = function(str, regexp, msg) { + msg = msg || sys.inspect(str) + ' does not match ' + sys.inspect(regexp); + assert.ok(regexp.test(str), msg); +}; + +/** + * Assert that `val` is within `obj`. + * + * Examples: + * + * assert.includes('foobar', 'bar'); + * assert.includes(['foo', 'bar'], 'foo'); + * + * @param {String|Array} obj + * @param {Mixed} val + * @param {String} msg + */ + +assert.includes = function(obj, val, msg) { + msg = msg || sys.inspect(obj) + ' does not include ' + sys.inspect(val); + assert.ok(obj.indexOf(val) >= 0, msg); +}; + +/** + * Assert length of `val` is `n`. + * + * @param {Mixed} val + * @param {Number} n + * @param {String} msg + */ + +assert.length = function(val, n, msg) { + msg = msg || sys.inspect(val) + ' has length of ' + val.length + ', expected ' + n; + assert.equal(n, val.length, msg); +}; + +/** + * Assert response from `server` with + * the given `req` object and `res` assertions object. + * + * @param {Server} server + * @param {Object} req + * @param {Object|Function} res + * @param {String} msg + */ + +assert.response = function(server, req, res, msg){ + // Callback as third or fourth arg + var callback = typeof res === 'function' + ? res + : typeof msg === 'function' + ? msg + : function(){}; + + // Default messate to test title + if (typeof msg === 'function') msg = null; + msg = msg || assert.testTitle; + msg += '. '; + + // Pending responses + server.__pending = server.__pending || 0; + server.__pending++; + + // Create client + if (!server.fd) { + server.listen(server.__port = port++); + server.client = http.createClient(server.__port); + } + + // Issue request + var timer, + client = server.client, + method = req.method || 'GET', + status = res.status || res.statusCode, + data = req.data || req.body, + timeout = req.timeout || 0; + + var request = client.request(method, req.url, req.headers); + + // Timeout + if (timeout) { + timer = setTimeout(function(){ + --server.__pending || server.close(); + delete req.timeout; + assert.fail(msg + 'Request timed out after ' + timeout + 'ms.'); + }, timeout); + } + + if (data) request.write(data); + request.addListener('response', function(response){ + response.body = ''; + response.setEncoding('utf8'); + response.addListener('data', function(chunk){ response.body += chunk; }); + response.addListener('end', function(){ + --server.__pending || server.close(); + if (timer) clearTimeout(timer); + + // Assert response body + if (res.body !== undefined) { + assert.equal( + response.body, + res.body, + msg + 'Invalid response body.\n' + + ' Expected: ' + sys.inspect(res.body) + '\n' + + ' Got: ' + sys.inspect(response.body) + ); + } + + // Assert response status + if (typeof status === 'number') { + assert.equal( + response.statusCode, + status, + msg + colorize('Invalid response status code.\n' + + ' Expected: [green]{' + status + '}\n' + + ' Got: [red]{' + response.statusCode + '}') + ); + } + + // Assert response headers + if (res.headers) { + var keys = Object.keys(res.headers); + for (var i = 0, len = keys.length; i < len; ++i) { + var name = keys[i], + actual = response.headers[name.toLowerCase()], + expected = res.headers[name]; + assert.equal( + actual, + expected, + msg + colorize('Invalid response header [bold]{' + name + '}.\n' + + ' Expected: [green]{' + expected + '}\n' + + ' Got: [red]{' + actual + '}') + ); + } + } + + // Callback + callback(response); + }); + }); + request.end(); +}; + +/** + * Pad the given string to the maximum width provided. + * + * @param {String} str + * @param {Number} width + * @return {String} + */ + +function lpad(str, width) { + str = String(str); + var n = width - str.length; + if (n < 1) return str; + while (n--) str = ' ' + str; + return str; +} + +/** + * Pad the given string to the maximum width provided. + * + * @param {String} str + * @param {Number} width + * @return {String} + */ + +function rpad(str, width) { + str = String(str); + var n = width - str.length; + if (n < 1) return str; + while (n--) str = str + ' '; + return str; +} + +/** + * Report test coverage. + * + * @param {Object} cov + */ + +function reportCoverage(cov) { + populateCoverage(cov); + // Stats + print('\n [bold]{Test Coverage}\n'); + var sep = ' +------------------------------------------+----------+------+------+--------+', + lastSep = ' +----------+------+------+--------+'; + sys.puts(sep); + sys.puts(' | filename | coverage | LOC | SLOC | missed |'); + sys.puts(sep); + for (var name in cov) { + var file = cov[name]; + if (Array.isArray(file)) { + sys.print(' | ' + rpad(name, 40)); + sys.print(' | ' + lpad(file.coverage.toFixed(2), 8)); + sys.print(' | ' + lpad(file.LOC, 4)); + sys.print(' | ' + lpad(file.SLOC, 4)); + sys.print(' | ' + lpad(file.totalMisses, 6)); + sys.print(' |\n'); + } + } + sys.puts(sep); + sys.print(' ' + rpad('', 40)); + sys.print(' | ' + lpad(cov.coverage.toFixed(2), 8)); + sys.print(' | ' + lpad(cov.LOC, 4)); + sys.print(' | ' + lpad(cov.SLOC, 4)); + sys.print(' | ' + lpad(cov.totalMisses, 6)); + sys.print(' |\n'); + sys.puts(lastSep); + // Source + for (var name in cov) { + if (name.match(/\.js$/)) { + var file = cov[name]; + print('\n [bold]{' + name + '}:'); + print(file.source); + sys.print('\n'); + } + } +} + +/** + * Populate code coverage data. + * + * @param {Object} cov + */ + +function populateCoverage(cov) { + cov.LOC = + cov.SLOC = + cov.totalFiles = + cov.totalHits = + cov.totalMisses = + cov.coverage = 0; + for (var name in cov) { + var file = cov[name]; + if (Array.isArray(file)) { + // Stats + ++cov.totalFiles; + cov.totalHits += file.totalHits = coverage(file, true); + cov.totalMisses += file.totalMisses = coverage(file, false); + file.totalLines = file.totalHits + file.totalMisses; + cov.SLOC += file.SLOC = file.totalLines; + cov.LOC += file.LOC = file.source.length; + file.coverage = (file.totalHits / file.totalLines) * 100; + // Source + var width = file.source.length.toString().length; + file.source = file.source.map(function(line, i){ + ++i; + var hits = file[i] === 0 ? 0 : (file[i] || ' '); + if (!boring) { + if (hits === 0) { + hits = '\x1b[31m' + hits + '\x1b[0m'; + line = '\x1b[41m' + line + '\x1b[0m'; + } else { + hits = '\x1b[32m' + hits + '\x1b[0m'; + } + } + return '\n ' + lpad(i, width) + ' | ' + hits + ' | ' + line; + }).join(''); + } + } + cov.coverage = (cov.totalHits / cov.SLOC) * 100; +} + +/** + * Total coverage for the given file data. + * + * @param {Array} data + * @return {Type} + */ + +function coverage(data, val) { + var n = 0; + for (var i = 0, len = data.length; i < len; ++i) { + if (data[i] !== undefined && data[i] == val) ++n; + } + return n; +} + +/** + * Run the given test `files`, or try _test/*_. + * + * @param {Array} files + */ + +function run(files) { + if (!files.length) { + try { + files = fs.readdirSync('test').map(function(file){ + return 'test/' + file; + }); + } catch (err) { + print('\n failed to load tests in [bold]{./test}\n'); + ++failures; + process.exit(1); + } + } + if (watch) watchFiles(files); + runFiles(files); +} + +/** + * Show the cursor when `show` is true, otherwise hide it. + * + * @param {Boolean} show + */ + +function cursor(show) { + if (show) { + sys.print('\x1b[?25h'); + } else { + sys.print('\x1b[?25l'); + } +} + +/** + * Run the given test `files`. + * + * @param {Array} files + */ + +function runFiles(files) { + files.forEach(runFile); +} + +/** + * Run tests for the given `file`. + * + * @param {String} file + */ + +function runFile(file) { + if (file.match(/\.js$/)) { + var title = path.basename(file), + file = path.join(cwd, file), + mod = require(file.replace(/\.js$/, '')); + (function check(){ + var len = Object.keys(mod).length; + if (len) { + runSuite(title, mod); + } else { + setTimeout(check, 20); + } + })(); + } +} + +/** + * Clear the module cache for the given `file`. + * + * @param {String} file + */ + +function clearCache(file) { + var keys = Object.keys(module.moduleCache); + for (var i = 0, len = keys.length; i < len; ++i) { + var key = keys[i]; + if (key.indexOf(file) === key.length - file.length) { + delete module.moduleCache[key]; + } + } +} + +/** + * Watch the given `files` for changes. + * + * @param {Array} files + */ + +function watchFiles(files) { + var p = 0, + c = ['▫ ', '▫▫ ', '▫▫▫ ', ' ▫▫▫', + ' ▫▫', ' ▫', ' ▫', ' ▫▫', + '▫▫▫ ', '▫▫ ', '▫ '], + l = c.length; + cursor(false); + setInterval(function(){ + sys.print(colorize(' [green]{' + c[p++ % l] + '} watching\r')); + }, 100); + files.forEach(function(file){ + fs.watchFile(file, { interval: 100 }, function(curr, prev){ + if (curr.mtime > prev.mtime) { + print(' [yellow]{◦} ' + file); + clearCache(file); + runFile(file); + } + }); + }); +} + +/** + * Report `err` for the given `test` and `suite`. + * + * @param {String} suite + * @param {String} test + * @param {Error} err + */ + +function error(suite, test, err) { + ++failures; + var name = err.name, + stack = err.stack.replace(err.name, ''), + label = test === 'uncaught' + ? test + : suite + ' ' + test; + print('\n [bold]{' + label + '}: [red]{' + name + '}' + stack + '\n'); + if (watch) notify(label + ' failed'); +} + +/** + * Run the given tests. + * + * @param {String} title + * @param {Object} tests + */ + +function runSuite(title, tests) { + var keys = only.length + ? only.slice(0) + : Object.keys(tests); + (function next(){ + if (keys.length) { + var key, + test = tests[key = keys.shift()]; + if (test) { + try { + ++testcount; + assert.testTitle = key; + test(assert, function(fn){ + process.addListener('beforeExit', function(){ + try { + fn(); + } catch (err) { + error(title, key, err); + } + }); + }); + } catch (err) { + error(title, key, err); + } + } + next(); + } + })(); +} + +/** + * Report exceptions. + */ + +function report() { + process.emit('beforeExit'); + if (failures) { + print('\n [bold]{Failures}: [red]{' + failures + '}\n\n'); + notify('Failures: ' + failures); + } else { + print('\n [green]{100%} ' + testcount + ' tests\n'); + notify('100% ok'); + } + if (typeof _$jscoverage === 'object') { + reportCoverage(_$jscoverage); + } +} + +/** + * Growl notify the given `msg`. + * + * @param {String} msg + */ + +function notify(msg) { + if (growl) { + childProcess.exec('growlnotify -name Expresso -m "' + msg + '"'); + } +} + +// Report uncaught exceptions + +process.addListener('uncaughtException', function(err){ + error('uncaught', 'uncaught', err); +}); + +// Show cursor + +['INT', 'TERM', 'QUIT'].forEach(function(sig){ + process.addListener('SIG' + sig, function(){ + cursor(true); + process.exit(1); + }); +}); + +// Report test coverage when available +// and emit "beforeExit" event to perform +// final assertions + +var orig = process.emit; +process.emit = function(event){ + if (event === 'exit') { + report(); + process.reallyExit(failures); + } + orig.apply(this, arguments); +}; + +// Run test files + +if (!defer) run(files); diff --git a/node_modules/ejs/support/expresso/docs/api.html b/node_modules/ejs/support/expresso/docs/api.html new file mode 100644 index 000000000..4496371fc --- /dev/null +++ b/node_modules/ejs/support/expresso/docs/api.html @@ -0,0 +1,989 @@ +Fork me on GitHub + + Expresso + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Expresso

    Insanely fast TDD framework for node featuring code coverage reporting.

    expresso

    bin/expresso
    +

    !/usr/bin/env node

    +
    +
    !
    + * Expresso
    + * Copyright(c) TJ Holowaychuk &lt;tj@vision-media.ca&gt;
    + * (MIT Licensed)
    + 
    +
    +

    Module dependencies. +

    +
    +
    var assert = require('assert'),
    +    childProcess = require('child_process'),
    +    http = require('http'),
    +    path = require('path'),
    +    sys = require('sys'),
    +    cwd = process.cwd(),
    +    fs = require('fs'),
    +    defer;
    +
    +

    Expresso version. +

    +
    +
    var version = '0.6.0';
    +
    +

    Failure count. +

    +
    +
    var failures = 0;
    +
    +

    Whitelist of tests to run. +

    +
    +
    var only = [];
    +
    +

    Boring output. +

    +
    +
    var boring = false;
    +
    +

    Growl notifications. +

    +
    +
    var growl = false;
    +
    +

    Server port. +

    +
    +
    var port = 5555;
    +
    +

    Watch mode. +

    +
    +
    var watch = false;
    +
    +

    Usage documentation. +

    +
    +
    var usage = ''
    +    + '[bold]{Usage}: expresso [options] <file ...>'
    +    + '\n'
    +    + '\n[bold]{Options}:'
    +    + '\n  -w, --watch          Watch for modifications and re-execute tests'
    +    + '\n  -g, --growl          Enable growl notifications'
    +    + '\n  -c, --coverage       Generate and report test coverage'
    +    + '\n  -r, --require PATH   Require the given module path'
    +    + '\n  -o, --only TESTS     Execute only the comma sperated TESTS (can be set several times)'
    +    + '\n  -I, --include PATH   Unshift the given path to require.paths'
    +    + '\n  -p, --port NUM       Port number for test servers, starts at 5555'
    +    + '\n  -b, --boring         Suppress ansi-escape colors'
    +    + '\n  -v, --version        Output version number'
    +    + '\n  -h, --help           Display help information'
    +    + '\n';
    +
    +// Parse arguments
    +
    +var files = [],
    +    args = process.argv.slice(2);
    +
    +while (args.length) {
    +    var arg = args.shift();
    +    switch (arg) {
    +        case '-h':
    +        case '--help':
    +            print(usage + '\n');
    +            process.exit(1);
    +            break;
    +        case '-v':
    +        case '--version':
    +            sys.puts(version);
    +            process.exit(1);
    +            break;
    +        case '-i':
    +        case '-I':
    +        case '--include':
    +            if (arg = args.shift()) {
    +                require.paths.unshift(arg);
    +            } else {
    +                throw new Error('--include requires a path');
    +            }
    +            break;
    +        case '-o':
    +        case '--only':
    +            if (arg = args.shift()) {
    +                only = only.concat(arg.split(/ *, */));
    +            } else {
    +                throw new Error('--only requires comma-separated test names');
    +            }
    +            break;
    +        case '-p':
    +        case '--port':
    +            if (arg = args.shift()) {
    +                port = parseInt(arg, 10);
    +            } else {
    +                throw new Error('--port requires a number');
    +            }
    +            break;
    +        case '-r':
    +        case '--require':
    +            if (arg = args.shift()) {
    +                require(arg);
    +            } else {
    +                throw new Error('--require requires a path');
    +            }
    +            break;
    +        case '-c':
    +        case '--cov':
    +        case '--coverage':
    +            defer = true;
    +            childProcess.exec('rm -fr lib-cov && node-jscoverage lib lib-cov', function(err){
    +                if (err) throw err;
    +                require.paths.unshift('lib-cov');
    +                run(files);
    +            })
    +            break;
    +        case '-b':
    +        case '--boring':
    +        	boring = true;
    +        	break;
    +        case '-w':
    +        case '--watch':
    +            watch = true;
    +            break;
    +        case '--g':
    +        case '--growl':
    +            growl = true;
    +            break;
    +        default:
    +            if (/\.js$/.test(arg)) {
    +                files.push(arg);
    +            }
    +            break;
    +    }
    +}
    +
    +

    Colorized sys.error().

    + +

    + +
    • param: String str

    +
    +
    function print(str){
    +    sys.error(colorize(str));
    +}
    +
    +

    Colorize the given string using ansi-escape sequences. +Disabled when --boring is set.

    + +

    + +
    • param: String str

    • return: String

    +
    +
    function colorize(str){
    +    var colors = { bold: 1, red: 31, green: 32, yellow: 33 };
    +    return str.replace(/\[(\w+)\]\{([^]*?)\}/g, function(_, color, str){
    +        return boring
    +            ? str
    +            : '\x1B[' + colors[color] + 'm' + str + '\x1B[0m';
    +    });
    +}
    +
    +// Alias deepEqual as eql for complex equality
    +
    +assert.eql = assert.deepEqual;
    +
    +

    Assert that val is null.

    + +

    + +
    • param: Mixed val

    • param: String msg

    +
    +
    assert.isNull = function(val, msg) {
    +    assert.strictEqual(null, val, msg);
    +};
    +
    +

    Assert that val is not null.

    + +

    + +
    • param: Mixed val

    • param: String msg

    +
    +
    assert.isNotNull = function(val, msg) {
    +    assert.notStrictEqual(null, val, msg);
    +};
    +
    +

    Assert that val is undefined.

    + +

    + +
    • param: Mixed val

    • param: String msg

    +
    +
    assert.isUndefined = function(val, msg) {
    +    assert.strictEqual(undefined, val, msg);
    +};
    +
    +

    Assert that val is not undefined.

    + +

    + +
    • param: Mixed val

    • param: String msg

    +
    +
    assert.isDefined = function(val, msg) {
    +    assert.notStrictEqual(undefined, val, msg);
    +};
    +
    +

    Assert that obj is type.

    + +

    + +
    • param: Mixed obj

    • param: String type

    • api: public

    +
    +
    assert.type = function(obj, type, msg){
    +    var real = typeof obj;
    +    msg = msg || 'typeof ' + sys.inspect(obj) + ' is ' + real + ', expected ' + type;
    +    assert.ok(type === real, msg);
    +};
    +
    +

    Assert that str matches regexp.

    + +

    + +
    • param: String str

    • param: RegExp regexp

    • param: String msg

    +
    +
    assert.match = function(str, regexp, msg) {
    +    msg = msg || sys.inspect(str) + ' does not match ' + sys.inspect(regexp);
    +    assert.ok(regexp.test(str), msg);
    +};
    +
    +

    Assert that val is within obj.

    + +

    Examples

    + +

    assert.includes('foobar', 'bar'); + assert.includes(['foo', 'bar'], 'foo');

    + +

    + +
    • param: String | Array obj

    • param: Mixed val

    • param: String msg

    +
    +
    assert.includes = function(obj, val, msg) {
    +    msg = msg || sys.inspect(obj) + ' does not include ' + sys.inspect(val);
    +    assert.ok(obj.indexOf(val) &gt;= 0, msg);
    +};
    +
    +

    Assert length of val is n.

    + +

    + +
    • param: Mixed val

    • param: Number n

    • param: String msg

    +
    +
    assert.length = function(val, n, msg) {
    +    msg = msg || sys.inspect(val) + ' has length of ' + val.length + ', expected ' + n;
    +    assert.equal(n, val.length, msg);
    +};
    +
    +

    Assert response from server with +the given req object and res assertions object.

    + +

    + +
    • param: Server server

    • param: Object req

    • param: Object | Function res

    • param: String msg

    +
    +
    assert.response = function(server, req, res, msg){
    +    // Callback as third or fourth arg
    +    var callback = typeof res === 'function'
    +        ? res
    +        : typeof msg === 'function'
    +            ? msg
    +            : function(){};
    +
    +    // Default messate to test title
    +    msg = msg || assert.testTitle;
    +    msg += '. ';
    +
    +    // Pending responses
    +    server.__pending = server.__pending || 0;
    +    server.__pending++;
    +
    +    // Create client
    +    if (!server.fd) {
    +        server.listen(server.__port = port++);
    +        server.client = http.createClient(server.__port);
    +    }
    +
    +    // Issue request
    +    var timer,
    +        client = server.client,
    +        method = req.method || 'GET',
    +        status = res.status || res.statusCode,
    +        data = req.data || req.body,
    +        timeout = req.timeout || 0;
    +
    +    var request = client.request(method, req.url, req.headers);
    +
    +    // Timeout
    +    if (timeout) {
    +        timer = setTimeout(function(){
    +            --server.__pending || server.close();
    +            delete req.timeout;
    +            assert.fail(msg + 'Request timed out after ' + timeout + 'ms.');
    +        }, timeout);
    +    }
    +
    +    if (data) request.write(data);
    +    request.addListener('response', function(response){
    +        response.body = '';
    +        response.setEncoding('utf8');
    +        response.addListener('data', function(chunk){ response.body += chunk; });
    +        response.addListener('end', function(){
    +            --server.__pending || server.close();
    +            if (timer) clearTimeout(timer);
    +
    +            // Assert response body
    +            if (res.body !== undefined) {
    +                assert.equal(
    +                    response.body,
    +                    res.body,
    +                    msg + 'Invalid response body.\n'
    +                        + '    Expected: ' + sys.inspect(res.body) + '\n'
    +                        + '    Got: ' + sys.inspect(response.body)
    +                );
    +            }
    +
    +            // Assert response status
    +            if (typeof status === 'number') {
    +                assert.equal(
    +                    response.statusCode,
    +                    status,
    +                    msg + colorize('Invalid response status code.\n'
    +                        + '    Expected: [green]{' + status + '}\n'
    +                        + '    Got: [red]{' + response.statusCode + '}')
    +                );
    +            }
    +
    +            // Assert response headers
    +            if (res.headers) {
    +                var keys = Object.keys(res.headers);
    +                for (var i = 0, len = keys.length; i &lt; len; ++i) {
    +                    var name = keys[i],
    +                        actual = response.headers[name.toLowerCase()],
    +                        expected = res.headers[name];
    +                    assert.equal(
    +                        actual,
    +                        expected,
    +                        msg + colorize('Invalid response header [bold]{' + name + '}.\n'
    +                            + '    Expected: [green]{' + expected + '}\n'
    +                            + '    Got: [red]{' + actual + '}')
    +                    );
    +                }
    +            }
    +
    +            // Callback
    +            callback(response);
    +        });
    +    });
    +    request.end();
    +};
    +
    +

    Pad the given string to the maximum width provided.

    + +

    + +
    • param: String str

    • param: Number width

    • return: String

    +
    +
    function lpad(str, width) {
    +    str = String(str);
    +    var n = width - str.length;
    +    if (n &lt; 1) return str;
    +    while (n--) str = ' ' + str;
    +    return str;
    +}
    +
    +

    Pad the given string to the maximum width provided.

    + +

    + +
    • param: String str

    • param: Number width

    • return: String

    +
    +
    function rpad(str, width) {
    +    str = String(str);
    +    var n = width - str.length;
    +    if (n &lt; 1) return str;
    +    while (n--) str = str + ' ';
    +    return str;
    +}
    +
    +

    Report test coverage.

    + +

    + +
    • param: Object cov

    +
    +
    function reportCoverage(cov) {
    +    populateCoverage(cov);
    +    // Stats
    +    print('\n   [bold]{Test Coverage}\n');
    +    var sep = '   +------------------------------------------+----------+------+------+--------+',
    +        lastSep = '                                              +----------+------+------+--------+';
    +    sys.puts(sep);
    +    sys.puts('   | filename                                 | coverage | LOC  | SLOC | missed |');
    +    sys.puts(sep);
    +    for (var name in cov) {
    +        var file = cov[name];
    +        if (Array.isArray(file)) {
    +            sys.print('   | ' + rpad(name, 40));
    +            sys.print(' | ' + lpad(file.coverage.toFixed(2), 8));
    +            sys.print(' | ' + lpad(file.LOC, 4));
    +            sys.print(' | ' + lpad(file.SLOC, 4));
    +            sys.print(' | ' + lpad(file.totalMisses, 6));
    +            sys.print(' |\n');
    +        }
    +    }
    +    sys.puts(sep);
    +    sys.print('     ' + rpad('', 40));
    +    sys.print(' | ' + lpad(cov.coverage.toFixed(2), 8));
    +    sys.print(' | ' + lpad(cov.LOC, 4));
    +    sys.print(' | ' + lpad(cov.SLOC, 4));
    +    sys.print(' | ' + lpad(cov.totalMisses, 6));
    +    sys.print(' |\n');
    +    sys.puts(lastSep);
    +    // Source
    +    for (var name in cov) {
    +        if (name.match(/\.js$/)) {
    +            var file = cov[name];
    +            print('\n   [bold]{' + name + '}:');
    +            print(file.source);
    +            sys.print('\n');
    +        }
    +    }
    +}
    +
    +

    Populate code coverage data.

    + +

    + +
    • param: Object cov

    +
    +
    function populateCoverage(cov) {
    +    cov.LOC = 
    +    cov.SLOC =
    +    cov.totalFiles =
    +    cov.totalHits =
    +    cov.totalMisses = 
    +    cov.coverage = 0;
    +    for (var name in cov) {
    +        var file = cov[name];
    +        if (Array.isArray(file)) {
    +            // Stats
    +            ++cov.totalFiles;
    +            cov.totalHits += file.totalHits = coverage(file, true);
    +            cov.totalMisses += file.totalMisses = coverage(file, false);
    +            file.totalLines = file.totalHits + file.totalMisses;
    +            cov.SLOC += file.SLOC = file.totalLines;
    +            cov.LOC += file.LOC = file.source.length;
    +            file.coverage = (file.totalHits / file.totalLines) * 100;
    +            // Source
    +            var width = file.source.length.toString().length;
    +            file.source = file.source.map(function(line, i){
    +                ++i;
    +                var hits = file[i] === 0 ? 0 : (file[i] || ' ');
    +                if (!boring) {
    +                    if (hits === 0) {
    +                        hits = '\x1b[31m' + hits + '\x1b[0m';
    +                        line = '\x1b[41m' + line + '\x1b[0m';
    +                    } else {
    +                        hits = '\x1b[32m' + hits + '\x1b[0m';
    +                    }
    +                }
    +                return '\n     ' + lpad(i, width) + ' | ' + hits + ' | ' + line;
    +            }).join('');
    +        }
    +    }
    +    cov.coverage = (cov.totalHits / cov.SLOC) * 100;
    +}
    +
    +

    Total coverage for the given file data.

    + +

    + +
    • param: Array data

    • return: Type

    +
    +
    function coverage(data, val) {
    +    var n = 0;
    +    for (var i = 0, len = data.length; i &lt; len; ++i) {
    +        if (data[i] !== undefined &amp;&amp; data[i] == val) ++n;
    +    }
    +    return n;  
    +}
    +
    +

    Run the given test files, or try test/*.

    + +

    + +
    • param: Array files

    +
    +
    function run(files) {
    +    if (!files.length) {
    +        try {
    +            files = fs.readdirSync('test').map(function(file){
    +                return 'test/' + file;
    +            });
    +        } catch (err) {
    +            print('\n  failed to load tests in [bold]{./test}\n');
    +            ++failures;
    +            process.exit(1);
    +        }
    +    }
    +    if (watch) watchFiles(files);
    +    runFiles(files);
    +}
    +
    +

    Show the cursor when show is true, otherwise hide it.

    + +

    + +
    • param: Boolean show

    +
    +
    function cursor(show) {
    +    if (show) {
    +        sys.print('\x1b[?25h');
    +    } else {
    +        sys.print('\x1b[?25l');
    +    }
    +}
    +
    +

    Run the given test files.

    + +

    + +
    • param: Array files

    +
    +
    function runFiles(files) {
    +    files.forEach(runFile);
    +}
    +
    +

    Run tests for the given file.

    + +

    + +
    • param: String file

    +
    +
    function runFile(file) {
    +    if (file.match(/\.js$/)) {
    +        var title = path.basename(file),
    +            file = path.join(cwd, file),
    +            mod = require(file.replace(/\.js$/, ''));
    +        (function check(){
    +           var len = Object.keys(mod).length;
    +           if (len) {
    +               runSuite(title, mod);
    +           } else {
    +               setTimeout(check, 20);
    +           }
    +        })();
    +    }
    +}
    +
    +

    Clear the module cache for the given file.

    + +

    + +
    • param: String file

    +
    +
    function clearCache(file) {
    +    var keys = Object.keys(module.moduleCache);
    +    for (var i = 0, len = keys.length; i &lt; len; ++i) {
    +        var key = keys[i];
    +        if (key.indexOf(file) === key.length - file.length) {
    +            delete module.moduleCache[key];
    +        }
    +    }
    +}
    +
    +

    Watch the given files for changes.

    + +

    + +
    • param: Array files

    +
    +
    function watchFiles(files) {
    +    var p = 0,
    +        c = ['▫   ', '▫▫  ', '▫▫▫ ', ' ▫▫▫',
    +             '  ▫▫', '   ▫', '   ▫', '  ▫▫',
    +             '▫▫▫ ', '▫▫  ', '▫   '],
    +        l = c.length;
    +    cursor(false);
    +    setInterval(function(){
    +        sys.print(colorize('  [green]{' + c[p++ % l] + '} watching\r'));
    +    }, 100);
    +    files.forEach(function(file){
    +        fs.watchFile(file, { interval: 100 }, function(curr, prev){
    +            if (curr.mtime &gt; prev.mtime) {
    +                print('  [yellow]{◦} ' + file);
    +                clearCache(file);
    +                runFile(file);
    +            }
    +        });
    +    });
    +}
    +
    +

    Report err for the given test and suite.

    + +

    + +
    • param: String suite

    • param: String test

    • param: Error err

    +
    +
    function error(suite, test, err) {
    +    ++failures;
    +    var name = err.name,
    +        stack = err.stack.replace(err.name, ''),
    +        label = test === 'uncaught'
    +            ? test
    +            : suite + ' ' + test;
    +    print('\n   [bold]{' + label + '}: [red]{' + name + '}' + stack + '\n');
    +    if (watch) notify(label + ' failed');
    +}
    +
    +

    Run the given tests.

    + +

    + +
    • param: String title

    • param: Object tests

    +
    +
    function runSuite(title, tests) {
    +    var keys = only.length
    +        ? only.slice(0)
    +        : Object.keys(tests);
    +    (function next(){
    +        if (keys.length) {
    +            var key,
    +                test = tests[key = keys.shift()];
    +            if (test) {
    +                try {
    +                    assert.testTitle = key;
    +                    test(assert, function(fn){
    +                        process.addListener('beforeExit', function(){
    +                            try {
    +                                fn();
    +                            } catch (err) {
    +                                error(title, key, err);
    +                            }
    +                        });
    +                    });
    +                } catch (err) {
    +                    error(title, key, err);
    +                }
    +            }
    +            next();
    +        }
    +    })();
    +}
    +
    +

    Report exceptions. +

    +
    +
    function report() {
    +    process.emit('beforeExit');
    +    if (failures) {
    +        print('\n   [bold]{Failures}: [red]{' + failures + '}\n\n');
    +        notify('Failures: ' + failures);
    +    } else {
    +    	print('\n   [green]{100%} ok\n');
    +    	notify('100% ok');
    +    }
    +    if (typeof _$jscoverage === 'object') {
    +        reportCoverage(_$jscoverage);
    +    }
    +}
    +
    +

    Growl notify the given msg.

    + +

    + +
    • param: String msg

    +
    +
    function notify(msg) {
    +    if (growl) {
    +        childProcess.exec('growlnotify -name Expresso -m "' + msg + '"');
    +    }
    +}
    +
    +// Report uncaught exceptions
    +
    +process.addListener('uncaughtException', function(err){
    +    error('uncaught', 'uncaught', err);
    +});
    +
    +// Show cursor
    +
    +['INT', 'TERM', 'QUIT'].forEach(function(sig){
    +    process.addListener('SIG' + sig, function(){
    +        cursor(true);
    +        process.exit(1);
    +    });
    +});
    +
    +// Report test coverage when available
    +// and emit "beforeExit" event to perform
    +// final assertions
    +
    +var orig = process.emit;
    +process.emit = function(event){
    +    if (event === 'exit') {
    +        report();
    +        process.reallyExit(failures);
    +    }
    +    orig.apply(this, arguments);
    +};
    +
    +// Run test files
    +
    +if (!defer) run(files);
    +
    +
    \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/docs/index.html b/node_modules/ejs/support/expresso/docs/index.html new file mode 100644 index 000000000..5ae18ab10 --- /dev/null +++ b/node_modules/ejs/support/expresso/docs/index.html @@ -0,0 +1,380 @@ + + + Expresso - TDD Framework For Node + + + + + Fork me on GitHub + + +
    +

    Expresso

    +
    +

    Expresso is a JavaScript TDD framework written for nodejs. Expresso is extremely fast, and is packed with features such as additional assertion methods, code coverage reporting, CI support, and more.

    + +

    Features

    + +
      +
    • light-weight
    • +
    • intuitive async support
    • +
    • intuitive test runner executable
    • +
    • test coverage support and reporting via node-jscoverage
    • +
    • uses and extends the core assert module
    • +
    • assert.eql() alias of assert.deepEqual()
    • +
    • assert.response() http response utility
    • +
    • assert.includes()
    • +
    • assert.isNull()
    • +
    • assert.isUndefined()
    • +
    • assert.isNotNull()
    • +
    • assert.isDefined()
    • +
    • assert.match()
    • +
    • assert.length()
    • +
    + + +

    Installation

    + +

    To install both expresso and node-jscoverage run +the command below, which will first compile node-jscoverage:

    + +
    $ make install
    +
    + +

    To install expresso alone without coverage reporting run:

    + +
    $ make install-expresso
    +
    + +

    Install via npm:

    + +
    $ npm install expresso
    +
    + +

    Examples

    + +

    Examples

    + +

    To define tests we simply export several functions:

    + +
    exports['test String#length'] = function(assert){
    +    assert.equal(6, 'foobar'.length);
    +};
    +
    + +

    Alternatively for large numbers of tests you may want to +export your own object containing the tests, however this +is essentially the as above:

    + +
    module.exports = {
    +    'test String#length': function(assert){
    +        assert.equal(6, 'foobar'.length);
    +    }
    +};
    +
    + +

    If you prefer not to use quoted keys:

    + +
    exports.testsStringLength = function(assert){
    +    assert.equal(6, 'foobar'.length);
    +};
    +
    + +

    The second argument passed to each callback is beforeExit, +which is typically used to assert that callbacks have been +invoked.

    + +
    exports.testAsync = function(assert, beforeExit){
    +    var n = 0;
    +    setTimeout(function(){
    +        ++n;
    +        assert.ok(true);
    +    }, 200);
    +    setTimeout(function(){
    +        ++n;
    +        assert.ok(true);
    +    }, 200);
    +    beforeExit(function(){
    +        assert.equal(2, n, 'Ensure both timeouts are called');
    +    });
    +};
    +
    + +

    Assert Utilities

    + +

    assert.isNull(val[, msg])

    + +

    Asserts that the given val is null.

    + +
    assert.isNull(null);
    +
    + +

    assert.isNotNull(val[, msg])

    + +

    Asserts that the given val is not null.

    + +
    assert.isNotNull(undefined);
    +assert.isNotNull(false);
    +
    + +

    assert.isUndefined(val[, msg])

    + +

    Asserts that the given val is undefined.

    + +
    assert.isUndefined(undefined);
    +
    + +

    assert.isDefined(val[, msg])

    + +

    Asserts that the given val is not undefined.

    + +
    assert.isDefined(null);
    +assert.isDefined(false);
    +
    + +

    assert.match(str, regexp[, msg])

    + +

    Asserts that the given str matches regexp.

    + +
    assert.match('foobar', /^foo(bar)?/);
    +assert.match('foo', /^foo(bar)?/);
    +
    + +

    assert.length(val, n[, msg])

    + +

    Assert that the given val has a length of n.

    + +
    assert.length([1,2,3], 3);
    +assert.length('foo', 3);
    +
    + +

    assert.type(obj, type[, msg])

    + +

    Assert that the given obj is typeof type.

    + +
    assert.type(3, 'number');
    +
    + +

    assert.eql(a, b[, msg])

    + +

    Assert that object b is equal to object a. This is an +alias for the core assert.deepEqual() method which does complex +comparisons, opposed to assert.equal() which uses ==.

    + +
    assert.eql('foo', 'foo');
    +assert.eql([1,2], [1,2]);
    +assert.eql({ foo: 'bar' }, { foo: 'bar' });
    +
    + +

    assert.includes(obj, val[, msg])

    + +

    Assert that obj is within val. This method supports Array_s +and Strings_s.

    + +
    assert.includes([1,2,3], 3);
    +assert.includes('foobar', 'foo');
    +assert.includes('foobar', 'bar');
    +
    + +

    assert.response(server, req, res|fn[, msg|fn])

    + +

    Performs assertions on the given server, which should not call +listen(), as this is handled internally by expresso and the server +is killed after all responses have completed. This method works with +any http.Server instance, so Connect and Express servers will work +as well.

    + +

    The req object may contain:

    + +
      +
    • url request url
    • +
    • timeout timeout in milliseconds
    • +
    • method HTTP method
    • +
    • data request body
    • +
    • headers headers object
    • +
    + + +

    The res object may be a callback function which +receives the response for assertions, or an object +which is then used to perform several assertions +on the response with the following properties:

    + +
      +
    • body assert response body
    • +
    • status assert response status code
    • +
    • header assert that all given headers match (unspecified are ignored)
    • +
    + + +

    When providing res you may then also pass a callback function +as the fourth argument for additional assertions.

    + +

    Below are some examples:

    + +
    assert.response(server, {
    +    url: '/', timeout: 500
    +}, {
    +    body: 'foobar'
    +});
    +
    +assert.response(server, {
    +    url: '/',
    +    method: 'GET'
    +},{
    +    body: '{"name":"tj"}',
    +    status: 200,
    +    headers: {
    +        'Content-Type': 'application/json; charset=utf8',
    +        'X-Foo': 'bar'
    +    }
    +});
    +
    +assert.response(server, {
    +    url: '/foo',
    +    method: 'POST',
    +    data: 'bar baz'
    +},{
    +    body: '/foo bar baz',
    +    status: 200
    +}, 'Test POST');
    +
    +assert.response(server, {
    +    url: '/foo',
    +    method: 'POST',
    +    data: 'bar baz'
    +},{
    +    body: '/foo bar baz',
    +    status: 200
    +}, function(res){
    +    // All done, do some more tests if needed
    +});
    +
    +assert.response(server, {
    +    url: '/'
    +}, function(res){
    +    assert.ok(res.body.indexOf('tj') >= 0, 'Test assert.response() callback');
    +});
    +
    + +

    expresso(1)

    + +

    To run a single test suite (file) run:

    + +
    $ expresso test/a.test.js
    +
    + +

    To run several suites we may simply append another:

    + +
    $ expresso test/a.test.js test/b.test.js
    +
    + +

    We can also pass a whitelist of tests to run within all suites:

    + +
    $ expresso --only "foo()" --only "bar()"
    +
    + +

    Or several with one call:

    + +
    $ expresso --only "foo(), bar()"
    +
    + +

    Globbing is of course possible as well:

    + +
    $ expresso test/*
    +
    + +

    When expresso is called without any files, test/* is the default, +so the following is equivalent to the command above:

    + +
    $ expresso
    +
    + +

    If you wish to unshift a path to require.paths before +running tests, you may use the -I or --include flag.

    + +
    $ expresso --include lib test/*
    +
    + +

    The previous example is typically what I would recommend, since expresso +supports test coverage via node-jscoverage (bundled with expresso), +so you will need to expose an instrumented version of you library.

    + +

    To instrument your library, simply run node-jscoverage, +passing the src and dest directories:

    + +
    $ node-jscoverage lib lib-cov
    +
    + +

    Now we can run our tests again, using the lib-cov directory that has been +instrumented with coverage statements:

    + +
    $ expresso -I lib-cov test/*
    +
    + +

    The output will look similar to below, depending on your test coverage of course :)

    + +

    node coverage

    + +

    To make this process easier expresso has the -c or --cov which essentially +does the same as the two commands above. The following two commands will +run the same tests, however one will auto-instrument, and unshift lib-cov, +and the other will run tests normally:

    + +
    $ expresso -I lib test/*
    +$ expresso -I lib --cov test/*
    +
    + +

    Currently coverage is bound to the lib directory, however in the +future --cov will most likely accept a path.

    + +

    Async Exports

    + +

    Sometimes it is useful to postpone running of tests until a callback or event has fired, currently the exports.foo = function(){}; syntax is supported for this:

    + +
    setTimeout(function(){
    +    exports['test async exports'] = function(assert){
    +        assert.ok('wahoo');
    +    };
    +}, 100);
    +
    + +
    +
    + + \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/docs/index.md b/node_modules/ejs/support/expresso/docs/index.md new file mode 100644 index 000000000..169b33565 --- /dev/null +++ b/node_modules/ejs/support/expresso/docs/index.md @@ -0,0 +1,292 @@ + +[Expresso](http://github.com/visionmedia/expresso) is a JavaScript [TDD](http://en.wikipedia.org/wiki/Test-driven_development) framework written for [nodejs](http://nodejs.org). Expresso is extremely fast, and is packed with features such as additional assertion methods, code coverage reporting, CI support, and more. + +## Features + + - light-weight + - intuitive async support + - intuitive test runner executable + - test coverage support and reporting via [node-jscoverage](http://github.com/visionmedia/node-jscoverage) + - uses and extends the core _assert_ module + - `assert.eql()` alias of `assert.deepEqual()` + - `assert.response()` http response utility + - `assert.includes()` + - `assert.isNull()` + - `assert.isUndefined()` + - `assert.isNotNull()` + - `assert.isDefined()` + - `assert.match()` + - `assert.length()` + +## Installation + +To install both expresso _and_ node-jscoverage run +the command below, which will first compile node-jscoverage: + + $ make install + +To install expresso alone without coverage reporting run: + + $ make install-expresso + +Install via npm: + + $ npm install expresso + +## Examples + +## Examples + +To define tests we simply export several functions: + + exports['test String#length'] = function(assert){ + assert.equal(6, 'foobar'.length); + }; + +Alternatively for large numbers of tests you may want to +export your own object containing the tests, however this +is essentially the as above: + + module.exports = { + 'test String#length': function(assert){ + assert.equal(6, 'foobar'.length); + } + }; + +If you prefer not to use quoted keys: + + exports.testsStringLength = function(assert){ + assert.equal(6, 'foobar'.length); + }; + +The second argument passed to each callback is _beforeExit_, +which is typically used to assert that callbacks have been +invoked. + + exports.testAsync = function(assert, beforeExit){ + var n = 0; + setTimeout(function(){ + ++n; + assert.ok(true); + }, 200); + setTimeout(function(){ + ++n; + assert.ok(true); + }, 200); + beforeExit(function(){ + assert.equal(2, n, 'Ensure both timeouts are called'); + }); + }; + +## Assert Utilities + +### assert.isNull(val[, msg]) + +Asserts that the given _val_ is _null_. + + assert.isNull(null); + +### assert.isNotNull(val[, msg]) + +Asserts that the given _val_ is not _null_. + + assert.isNotNull(undefined); + assert.isNotNull(false); + +### assert.isUndefined(val[, msg]) + +Asserts that the given _val_ is _undefined_. + + assert.isUndefined(undefined); + +### assert.isDefined(val[, msg]) + +Asserts that the given _val_ is not _undefined_. + + assert.isDefined(null); + assert.isDefined(false); + +### assert.match(str, regexp[, msg]) + +Asserts that the given _str_ matches _regexp_. + + assert.match('foobar', /^foo(bar)?/); + assert.match('foo', /^foo(bar)?/); + +### assert.length(val, n[, msg]) + +Assert that the given _val_ has a length of _n_. + + assert.length([1,2,3], 3); + assert.length('foo', 3); + +### assert.type(obj, type[, msg]) + +Assert that the given _obj_ is typeof _type_. + + assert.type(3, 'number'); + +### assert.eql(a, b[, msg]) + +Assert that object _b_ is equal to object _a_. This is an +alias for the core _assert.deepEqual()_ method which does complex +comparisons, opposed to _assert.equal()_ which uses _==_. + + assert.eql('foo', 'foo'); + assert.eql([1,2], [1,2]); + assert.eql({ foo: 'bar' }, { foo: 'bar' }); + +### assert.includes(obj, val[, msg]) + +Assert that _obj_ is within _val_. This method supports _Array_s +and _Strings_s. + + assert.includes([1,2,3], 3); + assert.includes('foobar', 'foo'); + assert.includes('foobar', 'bar'); + +### assert.response(server, req, res|fn[, msg|fn]) + +Performs assertions on the given _server_, which should _not_ call +listen(), as this is handled internally by expresso and the server +is killed after all responses have completed. This method works with +any _http.Server_ instance, so _Connect_ and _Express_ servers will work +as well. + +The _req_ object may contain: + + - _url_ request url + - _timeout_ timeout in milliseconds + - _method_ HTTP method + - _data_ request body + - _headers_ headers object + +The _res_ object may be a callback function which +receives the response for assertions, or an object +which is then used to perform several assertions +on the response with the following properties: + + - _body_ assert response body + - _status_ assert response status code + - _header_ assert that all given headers match (unspecified are ignored) + +When providing _res_ you may then also pass a callback function +as the fourth argument for additional assertions. + +Below are some examples: + + assert.response(server, { + url: '/', timeout: 500 + }, { + body: 'foobar' + }); + + assert.response(server, { + url: '/', + method: 'GET' + },{ + body: '{"name":"tj"}', + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf8', + 'X-Foo': 'bar' + } + }); + + assert.response(server, { + url: '/foo', + method: 'POST', + data: 'bar baz' + },{ + body: '/foo bar baz', + status: 200 + }, 'Test POST'); + + assert.response(server, { + url: '/foo', + method: 'POST', + data: 'bar baz' + },{ + body: '/foo bar baz', + status: 200 + }, function(res){ + // All done, do some more tests if needed + }); + + assert.response(server, { + url: '/' + }, function(res){ + assert.ok(res.body.indexOf('tj') >= 0, 'Test assert.response() callback'); + }); + + +## expresso(1) + +To run a single test suite (file) run: + + $ expresso test/a.test.js + +To run several suites we may simply append another: + + $ expresso test/a.test.js test/b.test.js + +We can also pass a whitelist of tests to run within all suites: + + $ expresso --only "foo()" --only "bar()" + +Or several with one call: + + $ expresso --only "foo(), bar()" + +Globbing is of course possible as well: + + $ expresso test/* + +When expresso is called without any files, _test/*_ is the default, +so the following is equivalent to the command above: + + $ expresso + +If you wish to unshift a path to `require.paths` before +running tests, you may use the `-I` or `--include` flag. + + $ expresso --include lib test/* + +The previous example is typically what I would recommend, since expresso +supports test coverage via [node-jscoverage](http://github.com/visionmedia/node-jscoverage) (bundled with expresso), +so you will need to expose an instrumented version of you library. + +To instrument your library, simply run [node-jscoverage](http://github.com/visionmedia/node-jscoverage), +passing the _src_ and _dest_ directories: + + $ node-jscoverage lib lib-cov + +Now we can run our tests again, using the _lib-cov_ directory that has been +instrumented with coverage statements: + + $ expresso -I lib-cov test/* + +The output will look similar to below, depending on your test coverage of course :) + +![node coverage](http://dl.dropbox.com/u/6396913/cov.png) + +To make this process easier expresso has the _-c_ or _--cov_ which essentially +does the same as the two commands above. The following two commands will +run the same tests, however one will auto-instrument, and unshift _lib-cov_, +and the other will run tests normally: + + $ expresso -I lib test/* + $ expresso -I lib --cov test/* + +Currently coverage is bound to the _lib_ directory, however in the +future `--cov` will most likely accept a path. + +## Async Exports + +Sometimes it is useful to postpone running of tests until a callback or event has fired, currently the _exports.foo = function(){};_ syntax is supported for this: + + setTimeout(function(){ + exports['test async exports'] = function(assert){ + assert.ok('wahoo'); + }; + }, 100); diff --git a/node_modules/ejs/support/expresso/docs/layout/foot.html b/node_modules/ejs/support/expresso/docs/layout/foot.html new file mode 100644 index 000000000..44d85e96e --- /dev/null +++ b/node_modules/ejs/support/expresso/docs/layout/foot.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/docs/layout/head.html b/node_modules/ejs/support/expresso/docs/layout/head.html new file mode 100644 index 000000000..b2d42c361 --- /dev/null +++ b/node_modules/ejs/support/expresso/docs/layout/head.html @@ -0,0 +1,47 @@ + + + Expresso - TDD Framework For Node + + + + + Fork me on GitHub + + +
    +

    Expresso

    diff --git a/node_modules/ejs/support/expresso/lib/bar.js b/node_modules/ejs/support/expresso/lib/bar.js new file mode 100644 index 000000000..e15aad41f --- /dev/null +++ b/node_modules/ejs/support/expresso/lib/bar.js @@ -0,0 +1,4 @@ + +exports.bar = function(msg){ + return msg || 'bar'; +}; \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/lib/foo.js b/node_modules/ejs/support/expresso/lib/foo.js new file mode 100644 index 000000000..15701a540 --- /dev/null +++ b/node_modules/ejs/support/expresso/lib/foo.js @@ -0,0 +1,16 @@ + +exports.foo = function(msg){ + if (msg) { + return msg; + } else { + return generateFoo(); + } +}; + +function generateFoo() { + return 'foo'; +} + +function Foo(msg){ + this.msg = msg || 'foo'; +} diff --git a/node_modules/ejs/support/expresso/package.json b/node_modules/ejs/support/expresso/package.json new file mode 100644 index 000000000..ef89b5efd --- /dev/null +++ b/node_modules/ejs/support/expresso/package.json @@ -0,0 +1,9 @@ +{ "name": "expresso", + "version": "0.6.1", + "description": "TDD framework, light-weight, fast, CI-friendly", + "author": "TJ Holowaychuk ", + "bin": { "expresso": "./bin/expresso" }, + "scripts": { + "install": "make install-jscov" + } +} \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/test/assert.test.js b/node_modules/ejs/support/expresso/test/assert.test.js new file mode 100644 index 000000000..6a5e7649a --- /dev/null +++ b/node_modules/ejs/support/expresso/test/assert.test.js @@ -0,0 +1,84 @@ +module.exports = { + 'assert.eql()': function(assert){ + assert.equal(assert.deepEqual, assert.eql); + }, + + 'assert.type()': function(assert){ + assert.type('foobar', 'string'); + assert.type(2, 'number'); + assert.throws(function(){ + assert.type([1,2,3], 'string'); + }); + }, + + 'assert.includes()': function(assert){ + assert.includes('some random string', 'dom'); + assert.throws(function(){ + assert.include('some random string', 'foobar'); + }); + + assert.includes(['foo', 'bar'], 'bar'); + assert.includes(['foo', 'bar'], 'foo'); + assert.includes([1,2,3], 3); + assert.includes([1,2,3], 2); + assert.includes([1,2,3], 1); + assert.throws(function(){ + assert.includes(['foo', 'bar'], 'baz'); + }); + + assert.throws(function(){ + assert.includes({ wrong: 'type' }, 'foo'); + }); + }, + + 'assert.isNull()': function(assert){ + assert.isNull(null); + assert.throws(function(){ + assert.isNull(undefined); + }); + assert.throws(function(){ + assert.isNull(false); + }); + }, + + 'assert.isUndefined()': function(assert){ + assert.isUndefined(undefined); + assert.throws(function(){ + assert.isUndefined(null); + }); + assert.throws(function(){ + assert.isUndefined(false); + }); + }, + + 'assert.isNotNull()': function(assert){ + assert.isNotNull(false); + assert.isNotNull(undefined); + assert.throws(function(){ + assert.isNotNull(null); + }); + }, + + 'assert.isDefined()': function(assert){ + assert.isDefined(false); + assert.isDefined(null); + assert.throws(function(){ + assert.isDefined(undefined); + }); + }, + + 'assert.match()': function(assert){ + assert.match('foobar', /foo(bar)?/); + assert.throws(function(){ + assert.match('something', /rawr/); + }); + }, + + 'assert.length()': function(assert){ + assert.length('test', 4); + assert.length([1,2,3,4], 4); + assert.throws(function(){ + assert.length([1,2,3], 4); + }); + } +}; \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/test/async.test.js b/node_modules/ejs/support/expresso/test/async.test.js new file mode 100644 index 000000000..0dc9016b7 --- /dev/null +++ b/node_modules/ejs/support/expresso/test/async.test.js @@ -0,0 +1,6 @@ + +setTimeout(function(){ + exports['test async exports'] = function(assert){ + assert.ok('wahoo'); + }; +}, 100); \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/test/bar.test.js b/node_modules/ejs/support/expresso/test/bar.test.js new file mode 100644 index 000000000..68e8d48b9 --- /dev/null +++ b/node_modules/ejs/support/expresso/test/bar.test.js @@ -0,0 +1,12 @@ + +/** + * Module dependencies. + */ + +var bar = require('bar'); + +module.exports = { + 'bar()': function(assert){ + assert.equal('bar', bar.bar()); + } +}; \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/test/foo.test.js b/node_modules/ejs/support/expresso/test/foo.test.js new file mode 100644 index 000000000..5d9d94eaf --- /dev/null +++ b/node_modules/ejs/support/expresso/test/foo.test.js @@ -0,0 +1,13 @@ + +/** + * Module dependencies. + */ + +var foo = require('foo'); + +module.exports = { + 'foo()': function(assert){ + assert.equal('foo', foo.foo()); + assert.equal('foo', foo.foo()); + } +}; \ No newline at end of file diff --git a/node_modules/ejs/support/expresso/test/http.test.js b/node_modules/ejs/support/expresso/test/http.test.js new file mode 100644 index 000000000..8eff2b7c8 --- /dev/null +++ b/node_modules/ejs/support/expresso/test/http.test.js @@ -0,0 +1,76 @@ + +/** + * Module dependencies. + */ + +var http = require('http'); + +var server = http.createServer(function(req, res){ + if (req.method === 'GET') { + if (req.url === '/delay') { + setTimeout(function(){ + res.writeHead(200, {}); + res.end('delayed'); + }, 200); + } else { + var body = JSON.stringify({ name: 'tj' }); + res.writeHead(200, { + 'Content-Type': 'application/json; charset=utf8', + 'Content-Length': body.length + }); + res.end(body); + } + } else { + var body = ''; + req.setEncoding('utf8'); + req.addListener('data', function(chunk){ body += chunk }); + req.addListener('end', function(){ + res.writeHead(200, {}); + res.end(req.url + ' ' + body); + }); + } +}); + +module.exports = { + 'test assert.response()': function(assert, beforeExit){ + var called = 0; + + assert.response(server, { + url: '/', + method: 'GET' + },{ + body: '{"name":"tj"}', + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf8' + } + }); + + assert.response(server, { + url: '/foo', + method: 'POST', + data: 'bar baz' + },{ + body: '/foo bar baz', + status: 200 + }, function(res){ + ++called; + assert.ok(res); + }); + + assert.response(server, { + url: '/foo' + }, function(res){ + ++called; + assert.ok(res.body.indexOf('tj') >= 0, 'Test assert.response() callback'); + }); + + assert.response(server, + { url: '/delay', timeout: 300 }, + { body: 'delayed' }); + + beforeExit(function(){ + assert.equal(2, called); + }); + } +}; \ No newline at end of file diff --git a/node_modules/ejs/test/ejs.test.js b/node_modules/ejs/test/ejs.test.js new file mode 100644 index 000000000..e1b5a0cb5 --- /dev/null +++ b/node_modules/ejs/test/ejs.test.js @@ -0,0 +1,245 @@ + +/** + * Module dependencies. + */ + +var ejs = require('ejs'); + +module.exports = { + 'test .version': function(assert){ + assert.ok(/^\d+\.\d+\.\d+$/.test(ejs.version), 'Test .version format'); + }, + + 'test html': function(assert){ + assert.equal('

    yay

    ', ejs.render('

    yay

    ')); + }, + + 'test buffered code': function(assert){ + var html = '

    tj

    ', + str = '

    <%= name %>

    ', + locals = { name: 'tj' }; + assert.equal(html, ejs.render(str, { locals: locals })); + }, + + 'test unbuffered code': function(assert){ + var html = '

    tj

    ', + str = '<% if (name) { %>

    <%= name %>

    <% } %>', + locals = { name: 'tj' }; + assert.equal(html, ejs.render(str, { locals: locals })); + }, + + 'test `scope` option': function(assert){ + var html = '

    tj

    ', + str = '

    <%= this %>

    '; + assert.equal(html, ejs.render(str, { scope: 'tj' })); + }, + + 'test escaping': function(assert){ + assert.equal('<script>', ejs.render('<%= " **blah** + + + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.html new file mode 100644 index 000000000..57871fb55 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.html @@ -0,0 +1,3 @@ +

    Alternative delimiters for link definitions are allowed -- as of +Markdown 1.0.2, I think. Hence, this link and this link work +too.

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.json new file mode 100644 index 000000000..e35e754ad --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.json @@ -0,0 +1,13 @@ +["html", ["p", "Alternative delimiters for ", ["a", { + "href": "http://daringfireball.net/projects/markdown/syntax#link", + "title": "link syntax" +}, +"link definitions"], " are allowed -- as of\u000aMarkdown 1.0.2, I think. Hence, ", ["a", { + "href": "http://daringfireball.net/projects/markdown/syntax#link", + "title": "link syntax" +}, +"this link"], " and ", ["a", { + "href": "http://daringfireball.net/projects/markdown/syntax#link", + "title": "link syntax" +}, +"this link"], " work\u000atoo."]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.text new file mode 100644 index 000000000..3d62b11d7 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_defn_alt_title_delims.text @@ -0,0 +1,7 @@ +Alternative delimiters for [link definitions][link1] are allowed -- as of +Markdown 1.0.2, I think. Hence, [this link][link2] and [this link][link3] work +too. + +[link1]: http://daringfireball.net/projects/markdown/syntax#link "link syntax" +[link2]: http://daringfireball.net/projects/markdown/syntax#link 'link syntax' +[link3]: http://daringfireball.net/projects/markdown/syntax#link (link syntax) diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.html new file mode 100644 index 000000000..6c98ea972 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.html @@ -0,0 +1 @@ +

    Recipe 123 and Komodo bug 234 are related.

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.json new file mode 100644 index 000000000..c7b0dcb4f --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.json @@ -0,0 +1,7 @@ +["html", ["p", ["a", { + "href": "http://code.activestate.com/recipes/123/" +}, +"Recipe 123"], " and ", ["a", { + "href": "http://bugs.activestate.com/show_bug.cgi?id=234" +}, +"Komodo bug 234"], " are related."]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.opts b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.opts new file mode 100644 index 000000000..bd85ea915 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.opts @@ -0,0 +1,7 @@ +{"extras": ["link-patterns"], + "link_patterns": [ + (re.compile("recipe\s+(\d+)", re.I), r"http://code.activestate.com/recipes/\1/"), + (re.compile("(?:komodo\s+)?bug\s+(\d+)", re.I), r"http://bugs.activestate.com/show_bug.cgi?id=\1"), + ], +} + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.text new file mode 100644 index 000000000..d8a408138 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns.text @@ -0,0 +1 @@ +Recipe 123 and Komodo bug 234 are related. diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.html new file mode 100644 index 000000000..4402dcf6a --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.html @@ -0,0 +1 @@ +

    There once was a Mozilla bug 123 and a Komodo bug 123.

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.json new file mode 100644 index 000000000..52163772f --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.json @@ -0,0 +1,7 @@ +["html", ["p", "There once was a ", ["a", { + "href": "http://bugzilla.mozilla.org/show_bug.cgi?id=123" +}, +"Mozilla bug 123"], " and a ", ["a", { + "href": "http://bugs.activestate.com/show_bug.cgi?id=123" +}, +"Komodo bug 123"], "."]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.opts b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.opts new file mode 100644 index 000000000..d64982d8a --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.opts @@ -0,0 +1,7 @@ +{"extras": ["link-patterns"], + "link_patterns": [ + (re.compile(r'mozilla\s+bug\s+(\d+)', re.I), r'http://bugzilla.mozilla.org/show_bug.cgi?id=\1'), + (re.compile("(?:komodo\s+)?bug\s+(\d+)", re.I), r"http://bugs.activestate.com/show_bug.cgi?id=\1"), + ], +} + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.text new file mode 100644 index 000000000..29868b8d1 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_double_hit.text @@ -0,0 +1 @@ +There once was a Mozilla bug 123 and a Komodo bug 123. diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.html new file mode 100644 index 000000000..ffd0a72e3 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.html @@ -0,0 +1 @@ +

    Blah 123 becomes a line with two underscores.

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.json new file mode 100644 index 000000000..ad16ccfaf --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.json @@ -0,0 +1,4 @@ +["html", ["p", ["a", { + "href": "http://foo.com/blah_blah_blah/123" +}, +"Blah 123"], " becomes a line with two underscores."]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.opts b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.opts new file mode 100644 index 000000000..99ce0f79e --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.opts @@ -0,0 +1,6 @@ +{"extras": ["link-patterns"], + "link_patterns": [ + (re.compile("Blah\s+(\d+)", re.I), r"http://foo.com/blah_blah_blah/\1"), + ], +} + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.text new file mode 100644 index 000000000..d1fb62cf1 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/link_patterns_edge_cases.text @@ -0,0 +1 @@ +Blah 123 becomes a line with two underscores. diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.html new file mode 100644 index 000000000..f34c900b4 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.html @@ -0,0 +1,15 @@ +

    count:

    + +
      +
    • one
    • +
    • two
    • +
    • three
    • +
    + +

    count in spanish:

    + +
      +
    1. uno
    2. +
    3. dos
    4. +
    5. tres
    6. +
    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.json new file mode 100644 index 000000000..2c1c55dd0 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.json @@ -0,0 +1,8 @@ +["html", ["p", "count:"], + ["ul", ["li", "one"], + ["li", "two"], + ["li", "three"]], + ["p", "count in spanish:"], + ["ol", ["li", "uno"], + ["li", "dos"], + ["li", "tres"]]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.text new file mode 100644 index 000000000..8d2b43c64 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/lists.text @@ -0,0 +1,11 @@ +count: + +* one +* two +* three + +count in spanish: + +1. uno +2. dos +3. tres diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.html new file mode 100644 index 000000000..7ac7bfd53 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.html @@ -0,0 +1,16 @@ +

    This is sentence has a footnote foo1 and whamo[^whamo].

    + +

    This is another para with a numbered footnote2.

    + +
    +
    +
      +
    1. +

      Here is the body of the footnote foo. 

      +
    2. + +
    3. +

      Here is the body of the footnote 6. 

      +
    4. +
    +
    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.json new file mode 100644 index 000000000..69e09d87d --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.json @@ -0,0 +1,36 @@ +["html", ["p", "This is sentence has a footnote foo", ["sup", { + "class": "footnote-ref", + "id": "fnref-foo" +}, +["a", { + "href": "#fn-foo" +}, +"1"]], " and whamo[^whamo]."], + ["p", "This is another para with a numbered footnote", ["sup", { + "class": "footnote-ref", + "id": "fnref-6" + }, + ["a", { + "href": "#fn-6" + }, + "2"]], "."], "\u000a\u000a", ["div", { + "class": "footnotes" +}, +"\u000a", ["hr"], "\u000a", ["ol", ["li", { + "id": "fn-foo" +}, +["p", "Here is the body of the footnote foo.", ["a", { + "href": "#fnref-foo", + "class": "footnoteBackLink", + "title": "Jump back to footnote 1 in the text." +}, +"↩"]]], + ["li", { + "id": "fn-6" + }, + ["p", "Here is the body of the footnote 6.", ["a", { + "href": "#fnref-6", + "class": "footnoteBackLink", + "title": "Jump back to footnote 2 in the text." + }, + "↩"]]]]], "\u000a"] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.opts b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.opts new file mode 100644 index 000000000..9dfee9e21 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.opts @@ -0,0 +1 @@ +{"extras": ["footnotes"]} diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.text new file mode 100644 index 000000000..d67919237 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/mismatched_footnotes.text @@ -0,0 +1,9 @@ +This is sentence has a footnote foo[^foo] and whamo[^whamo]. + +This is another para with a numbered footnote[^6]. + + +[^foo]: Here is the body of the footnote foo. +[^bar]: Here is the body of the footnote bar. +[^6]: Here is the body of the footnote 6. + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.html new file mode 100644 index 000000000..2f8a9e8a9 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.html @@ -0,0 +1 @@ +

    This is a [missing link][missing] and a used link.

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.json new file mode 100644 index 000000000..b2bc9d44c --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.json @@ -0,0 +1,4 @@ +["html", ["p", "This is a [missing link][missing] and a ", ["a", { + "href": "http://foo.com" +}, +"used link"], "."]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.text new file mode 100644 index 000000000..817677f93 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/missing_link_defn.text @@ -0,0 +1,7 @@ + +This is a [missing link][missing] and a [used link][used]. + + +[used]: http://foo.com +[unused]: http://foo.com + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.html new file mode 100644 index 000000000..c0617e425 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.html @@ -0,0 +1,15 @@ +

    shopping list:

    + +
      +
    • veggies +
        +
      • carrots
      • +
      • lettuce
      • +
    • +
    • fruits +
        +
      • oranges
      • +
      • apples
      • +
      • peaches
      • +
    • +
    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.json new file mode 100644 index 000000000..6879f11ca --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.json @@ -0,0 +1,6 @@ +["html", ["p", "shopping list:"], + ["ul", ["li", "veggies\u000a", ["ul", ["li", "carrots"], + ["li", "lettuce"]]], + ["li", "fruits\u000a", ["ul", ["li", "oranges"], + ["li", "apples"], + ["li", ["em", "peaches"]]]]]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.text new file mode 100644 index 000000000..a37a19549 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list.text @@ -0,0 +1,9 @@ +shopping list: + +- veggies + + carrots + + lettuce +- fruits + + oranges + + apples + + *peaches* diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.html new file mode 100644 index 000000000..c0617e425 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.html @@ -0,0 +1,15 @@ +

    shopping list:

    + +
      +
    • veggies +
        +
      • carrots
      • +
      • lettuce
      • +
    • +
    • fruits +
        +
      • oranges
      • +
      • apples
      • +
      • peaches
      • +
    • +
    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.json new file mode 100644 index 000000000..6879f11ca --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.json @@ -0,0 +1,6 @@ +["html", ["p", "shopping list:"], + ["ul", ["li", "veggies\u000a", ["ul", ["li", "carrots"], + ["li", "lettuce"]]], + ["li", "fruits\u000a", ["ul", ["li", "oranges"], + ["li", "apples"], + ["li", ["em", "peaches"]]]]]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.opts b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.opts new file mode 100644 index 000000000..ccb6a09bd --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.opts @@ -0,0 +1 @@ +{'safe_mode': True} diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.text new file mode 100644 index 000000000..a37a19549 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/nested_list_safe_mode.text @@ -0,0 +1,9 @@ +shopping list: + +- veggies + + carrots + + lettuce +- fruits + + oranges + + apples + + *peaches* diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.html new file mode 100644 index 000000000..6535e80d2 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.html @@ -0,0 +1 @@ +

    Inline link 4 with non-escaped parens.

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.json new file mode 100644 index 000000000..2d9e60bde --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.json @@ -0,0 +1,5 @@ +["html", ["p", ["a", { + "href": "/url(test)", + "title": "title" +}, +"Inline link 4 with non-escaped parens"], "."]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.text new file mode 100644 index 000000000..5dfb9ab39 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/parens_in_url_4.text @@ -0,0 +1 @@ +[Inline link 4 with non-escaped parens]( "title"). diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.html new file mode 100644 index 000000000..ec135c6ee --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.html @@ -0,0 +1,5 @@ +

    Hi, there. blah

    + +
    + **ack** +
    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.json new file mode 100644 index 000000000..ff401bbf7 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.json @@ -0,0 +1,4 @@ +["html", ["p", "Hi, ", ["span", { + "foo": "*bar*" +}, +["em", "there"]], ". ", " blah"], "\u000a\u000a", ["div", "\u000a **ack**\u000a"], "\u000a"] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.text new file mode 100644 index 000000000..13cde3785 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/raw_html.text @@ -0,0 +1,6 @@ + +Hi, *there*. blah + +
    + **ack** +
    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.html new file mode 100644 index 000000000..406f8b54f --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.html @@ -0,0 +1 @@ +

    Google is fast star.

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.json new file mode 100644 index 000000000..95493a806 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.json @@ -0,0 +1,7 @@ +["html", ["p", ["a", { + "href": "http://www.google.com/" +}, +"Google"], " is fast ", ["img", { + "src": "/img/star.png", + "alt": "star" +}], "."]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.text new file mode 100644 index 000000000..4df8c01e0 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/ref_links.text @@ -0,0 +1,6 @@ +[Google][] is fast ![star][]. + +[google]: http://www.google.com/ +[star]: /img/star.png + + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.html new file mode 100644 index 000000000..a2f551b10 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.html @@ -0,0 +1,17 @@ +

    Some quick thoughts from a coder's perspective:

    + +
      +
    • The source will be available in a Mercurial ...

    • +
    • Komodo is a Mozilla-based application...

      + +
        +
      • Get a slightly tweaked mozilla build (C++, JavaScript, XUL).
      • +
      • Get a slightly tweaks Python build (C).
      • +
      • Add a bunch of core logic (Python)...
      • +
      • Add Komodo chrome (XUL, JavaScript, CSS, DTDs).
      • +
      + +

      What this means is that work on and add significant functionality...

    • +
    • Komodo uses the same extension mechanisms as Firefox...

    • +
    • Komodo builds and runs on Windows, Linux and ...

    • +

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.json new file mode 100644 index 000000000..24adae887 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.json @@ -0,0 +1,10 @@ +["html", ["p", "Some quick thoughts from a coder's perspective:"], + ["ul", ["li", ["p", "The source will be available in a Mercurial ..."]], + ["li", ["p", "Komodo is a Mozilla-based application..."], + ["ul", ["li", "Get a slightly tweaked mozilla build (C++, JavaScript, XUL)."], + ["li", "Get a slightly tweaks Python build (C)."], + ["li", "Add a bunch of core logic (Python)..."], + ["li", "Add Komodo chrome (XUL, JavaScript, CSS, DTDs)."]], + ["p", ["p", "What this means is that work on and add significant functionality..."]]], + ["li", ["p", "Komodo uses the same extension mechanisms as Firefox..."]], + ["li", ["p", "Komodo builds and runs on Windows, Linux and ..."]]]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.text new file mode 100644 index 000000000..7bf07ba3f --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/sublist-para.text @@ -0,0 +1,17 @@ +Some quick thoughts from a coder's perspective: + +- The source will be available in a Mercurial ... + +- Komodo is a Mozilla-based application... + + - Get a slightly tweaked mozilla build (C++, JavaScript, XUL). + - Get a slightly tweaks Python build (C). + - Add a bunch of core logic (Python)... + - Add Komodo chrome (XUL, JavaScript, CSS, DTDs). + + What this means is that work on and add significant functionality... + +- Komodo uses the same extension mechanisms as Firefox... + +- Komodo builds and runs on Windows, Linux and ... + diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.html new file mode 100644 index 000000000..2e172c139 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.html @@ -0,0 +1,15 @@ +

    Here is some sample code:

    + +
    import sys
    +def main(argv=sys.argv):
    +    logging.basicConfig()
    +    log.info('hi')
    +
    + +

    and:

    + +
    use 'zlib'
    +sub main(argv)
    +    puts 'hi'
    +end
    +
    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.json new file mode 100644 index 000000000..9c1f198e6 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.json @@ -0,0 +1,118 @@ +["html", ["p", "Here is some sample code:"], "\u000a\u000a", ["div", { + "class": "codehilite" +}, +["pre", ["code", ["span", { + "class": "k" +}, +"import"], " ", ["span", { + "class": "nn" +}, +"sys"], "\u000a", ["span", { + "class": "k" +}, +"def"], " ", ["span", { + "class": "nf" +}, +"main"], + ["span", { + "class": "p" + }, + "("], + ["span", { + "class": "n" + }, + "argv"], + ["span", { + "class": "o" + }, + "="], + ["span", { + "class": "n" + }, + "sys"], + ["span", { + "class": "o" + }, + "."], + ["span", { + "class": "n" + }, + "argv"], + ["span", { + "class": "p" + }, + "):"], "\u000a ", ["span", { + "class": "n" +}, +"logging"], + ["span", { + "class": "o" + }, + "."], + ["span", { + "class": "n" + }, + "basicConfig"], + ["span", { + "class": "p" + }, + "()"], "\u000a ", ["span", { + "class": "n" +}, +"log"], + ["span", { + "class": "o" + }, + "."], + ["span", { + "class": "n" + }, + "info"], + ["span", { + "class": "p" + }, + "("], + ["span", { + "class": "s" + }, + "'hi'"], + ["span", { + "class": "p" + }, + ")"], "\u000a"]]], "\u000a\u000a", ["p", "and:"], "\u000a\u000a", ["div", { + "class": "codehilite" +}, +["pre", ["code", ["span", { + "class": "n" +}, +"use"], " ", ["span", { + "class": "s1" +}, +"'zlib'"], "\u000a", ["span", { + "class": "nb" +}, +"sub"], " ", ["span", { + "class": "n" +}, +"main"], + ["span", { + "class": "p" + }, + "("], + ["span", { + "class": "n" + }, + "argv"], + ["span", { + "class": "p" + }, + ")"], "\u000a ", ["span", { + "class": "nb" +}, +"puts"], " ", ["span", { + "class": "s1" +}, +"'hi'"], "\u000a", ["span", { + "class": "k" +}, +"end"], "\u000a"]]], "\u000a"] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.opts b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.opts new file mode 100644 index 000000000..95dfd4185 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.opts @@ -0,0 +1 @@ +{"extras": ["code-color"]} diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.text new file mode 100644 index 000000000..b4b30e55d --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/syntax_color.text @@ -0,0 +1,15 @@ +Here is some sample code: + + :::python + import sys + def main(argv=sys.argv): + logging.basicConfig() + log.info('hi') + +and: + + :::ruby + use 'zlib' + sub main(argv) + puts 'hi' + end diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.html new file mode 100644 index 000000000..0ec6204f1 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.html @@ -0,0 +1,11 @@ +

    with [brackets][] in text

    + +

    with [[brackets][]] in text

    + +

    full link [like](/this/) in text

    + +

    full link to img like is ok

    + +

    [only open bracket(/in/) text

    + +

    only close bracket text](/url/)

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.json new file mode 100644 index 000000000..cd92d155b --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.json @@ -0,0 +1,29 @@ +["html", ["p", ["a", { + "href": "/url/" +}, +"with [brackets][] in text"]], + ["p", ["a", { + "href": "/url/", + "title": "a title" + }, + "with [[brackets][]] in text"]], + ["p", ["a", { + "href": "/url/" + }, + "full link [like](/this/) in text"]], + ["p", ["a", { + "href": "/url/" + }, + "full link to img ", ["img", { + "src": "/this/", + "alt": "like" + }], " is ok"]], + ["p", "[only open ", ["a", { + "href": "/url/", + "title": "a title" + }, + "bracket(/in/) text"]], + ["p", ["a", { + "href": "/in/" + }, + "only close bracket"], " text](/url/)"]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.text new file mode 100644 index 000000000..217f6855c --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/tricky_anchors.text @@ -0,0 +1,11 @@ +[with [brackets][] in text](/url/) + +[with [[brackets][]] in text](/url/ "a title") + +[full link [like](/this/) in text](/url/) + +[full link to img ![like](/this/) is ok](/url/) + +[only open [bracket(/in/) text](/url/ 'a title') + +[only close bracket](/in/) text](/url/) diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.html b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.html new file mode 100644 index 000000000..9fef884ff --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.html @@ -0,0 +1,2 @@ +

    Eric wrote up a (long) intro to writing UDL definitions a while back on +his blog: http://blogs.activestate.com/ericp/2007/01/kid_adding_a_ne.html

    diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.json b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.json new file mode 100644 index 000000000..71dba0139 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.json @@ -0,0 +1,4 @@ +["html", ["p", "Eric wrote up a (long) intro to writing UDL definitions a while back on\u000ahis blog: ", ["a", { + "href": "http://blogs.activestate.com/ericp/2007/01/kid_adding_a_ne.html" +}, +"http://blogs.activestate.com/ericp/2007/01/kid_adding_a_ne.html"]]] diff --git a/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.text b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.text new file mode 100644 index 000000000..58ae3e0d1 --- /dev/null +++ b/node_modules/markdown/test/fixtures/docs-pythonmarkdown2-tm-cases-pass/underline_in_autolink.text @@ -0,0 +1,2 @@ +Eric wrote up a (long) intro to writing UDL definitions a while back on +his blog: diff --git a/node_modules/markdown/test/interface.t.js b/node_modules/markdown/test/interface.t.js new file mode 100644 index 000000000..5e571d891 --- /dev/null +++ b/node_modules/markdown/test/interface.t.js @@ -0,0 +1,28 @@ +var markdown = require('Markdown'); + +function clone_array( input ) { + eval( "var tmp = " + input.toSource() ); + return tmp; +} + +tests = { + test_arguments_untouched: function() { + var input = "A [link][id] by id.\n\n[id]: http://google.com", + tree = markdown.parse( input ), + clone = clone_array( tree ); + + + var output = markdown.toHTML( tree ); + + asserts.same( tree, clone, "tree isn't modified" ); + // We had a problem where we would acidentally remove the references + // property from the root. We want to check the output is the same when + // called twice. + asserts.same( markdown.toHTML( tree ), output, "output is consistent" ); + } +} + +if (require.main === module) { + var asserts = require('test').asserts; + require('test').runner(tests); +} diff --git a/node_modules/markdown/test/regressions.t.js b/node_modules/markdown/test/regressions.t.js new file mode 100644 index 000000000..1602de511 --- /dev/null +++ b/node_modules/markdown/test/regressions.t.js @@ -0,0 +1,510 @@ +var Markdown = require('Markdown').Markdown, + mk_block = Markdown.mk_block; + +var tests = { + meta: function(fn) { + return function() { fn( new Markdown ) } + } +}; + +tests = { + test_split_block: tests.meta(function(md) { + asserts.same( + md.split_blocks( "# h1 #\n\npara1\npara1L2\n \n\n\n\npara2\n" ), + [mk_block( "# h1 #", "\n\n", 1 ), + mk_block( "para1\npara1L2", "\n \n\n\n\n", 3 ), + mk_block( "para2", "\n", 9 ) + ], + "split_block should record trailing newlines"); + + asserts.same( + md.split_blocks( "\n\n# heading #\n\npara\n" ), + [mk_block( "# heading #", "\n\n", 3 ), + mk_block( "para", "\n", 5 ) + ], + "split_block should ignore leading newlines"); + }), + + test_headers: tests.meta(function(md) { + var h1 = md.dialect.block.atxHeader( "# h1 #\n\n", [] ), + h2; + + asserts.same( + h1, + md.dialect.block.setextHeader( "h1\n===\n\n", [] ), + "Atx and Setext style H1s should produce the same output" ); + + asserts.same( + md.dialect.block.atxHeader("# h1\n\n"), + h1, + "Closing # optional on atxHeader"); + + asserts.same( + h2 = md.dialect.block.atxHeader( "## h2\n\n", [] ), + [["header", {level: 2}, "h2"]], + "Atx h2 has right level"); + + asserts.same( + h2, + md.dialect.block.setextHeader( "h2\n---\n\n", [] ), + "Atx and Setext style H2s should produce the same output" ); + + }), + + test_code: tests.meta(function(md) { + var code = md.dialect.block.code, + next = [ mk_block("next") ]; + + asserts.same( + code.call( md, mk_block(" foo\n bar"), next ), + [["code_block", "foo\nbar" ]], + "Code block correct"); + + asserts.same( + next, [mk_block("next")], + "next untouched when its not code"); + + next = []; + asserts.same( + code.call( md, mk_block(" foo\n bar"), next ), + [["code_block", "foo" ]], + "Code block correct for abutting para"); + + asserts.same( + next, [mk_block(" bar")], + "paragraph put back into next block"); + + asserts.same( + code.call( md, mk_block(" foo"), [mk_block(" bar"), ] ), + [["code_block", "foo\n\nbar" ]], + "adjacent code blocks "); + + asserts.same( + code.call( md, mk_block(" foo","\n \n \n"), [mk_block(" bar"), ] ), + [["code_block", "foo\n\n\nbar" ]], + "adjacent code blocks preserve correct number of empty lines"); + + }), + + test_bulletlist: tests.meta(function(md) { + var bl = function() { return md.dialect.block.lists.apply(md, arguments) }; + + asserts.same( + bl( mk_block("* foo\n* bar"), [] ), + [ [ "bulletlist", [ "listitem", "foo" ], [ "listitem", "bar" ] ] ], + "single line bullets"); + + asserts.same( + bl( mk_block("* [text](url)" ), [] ), + [ [ "bulletlist", [ "listitem", [ "link", { href: "url" }, "text" ] ] ] ], + "link in bullet"); + + asserts.same( + bl( mk_block("* foo\nbaz\n* bar\nbaz"), [] ), + [ [ "bulletlist", [ "listitem", "foo\nbaz" ], [ "listitem", "bar\nbaz" ] ] ], + "multiline lazy bullets"); + + asserts.same( + bl( mk_block("* foo\n baz\n* bar\n baz"), [] ), + [ [ "bulletlist", [ "listitem", "foo\nbaz" ], [ "listitem", "bar\nbaz" ] ] ], + "multiline tidy bullets"); + + asserts.same( + bl( mk_block("* foo\n baz"), [] ), + [ [ "bulletlist", [ "listitem", "foo\n baz" ] ] ], + "only trim 4 spaces from the start of the line"); + + /* Test wrong: should end up with 3 nested lists here + asserts.same( + bl( mk_block(" * one\n * two\n * three" ), [] ), + [ [ "bulletlist", [ "listitem", "one" ], [ "listitem", "two" ], [ "listitem", "three" ] ] ], + "bullets can be indented up to three spaces"); + */ + + asserts.same( + bl( mk_block(" * one"), [ mk_block(" two") ] ), + [ [ "bulletlist", [ "listitem", [ "para", "one" ], [ "para", "two" ] ] ] ], + "loose bullet lists can have multiple paragraphs"); + + /* Case: no space after bullet - not a list + | *↵ + |foo + */ + asserts.same( + bl( mk_block(" *\nfoo") ), + undefined, + "Space required after bullet to trigger list"); + + /* Case: note the space after the bullet + | *␣ + |foo + |bar + */ + asserts.same( + bl( mk_block(" * \nfoo\nbar"), [ ] ), + [ [ "bulletlist", [ "listitem", "foo\nbar" ] ] ], + "space+continuation lines"); + + + /* Case I: + | * foo + | * bar + | * baz + */ + asserts.same( + bl( mk_block(" * foo\n" + + " * bar\n" + + " * baz"), + [] ), + [ [ "bulletlist", + [ "listitem", + "foo", + [ "bulletlist", + [ "listitem", + "bar", + [ "bulletlist", + [ "listitem", "baz" ] + ] + ] + ] + ] + ] ], + "Interesting indented lists I"); + + /* Case II: + | * foo + | * bar + | * baz + */ + asserts.same( + bl( mk_block(" * foo\n * bar\n * baz"), [] ), + [ [ "bulletlist", + [ "listitem", + "foo", + [ "bulletlist", + [ "listitem", "bar" ] + ] + ], + [ "listitem", "baz" ] + ] ], + "Interesting indented lists II"); + + /* Case III: + | * foo + | * bar + |* baz + | * fnord + */ + asserts.same( + bl( mk_block(" * foo\n * bar\n* baz\n * fnord"), [] ), + [ [ "bulletlist", + [ "listitem", + "foo", + [ "bulletlist", + [ "listitem", "bar" ], + [ "listitem", "baz" ], + [ "listitem", "fnord" ] + ] + ] + ] ], + "Interesting indented lists III"); + + /* Case IV: + | * foo + | + | 1. bar + */ + asserts.same( + bl( mk_block(" * foo"), [ mk_block(" 1. bar\n") ] ), + [ [ "bulletlist", + ["listitem", ["para", "foo"] ], + ["listitem", ["para", "bar"] ] + ] ], + "Different lists at same indent IV"); + + /* Case V: + | * foo + | * bar + | * baz + */ + asserts.same( + bl( mk_block(" * foo\n * bar\n * baz"), [] ), + [ [ "bulletlist", + [ "listitem", + "foo", + [ "bulletlist", + ["listitem", "bar"], + ["listitem", "baz"], + ] + ] + ] ], + "Indenting Case V") + + /* Case VI: deep nesting + |* one + | * two + | * three + | * four + */ + asserts.same( + bl( mk_block("* one\n * two\n * three\n * four"), [] ), + [ [ "bulletlist", + [ "listitem", + "one", + [ "bulletlist", + [ "listitem", + "two", + [ "bulletlist", + [ "listitem", + "three", + [ "bulletlist", + [ "listitem", "four" ] + ] + ] + ] + ] + ] + ] + ] ], + "deep nested lists VI") + + /* Case VII: This one is just fruity! + | * foo + | * bar + | * baz + |* HATE + | * flibble + | * quxx + | * nest? + | * where + | * am + | * i? + */ + asserts.same( + bl( mk_block(" * foo\n" + + " * bar\n" + + " * baz\n" + + "* HATE\n" + + " * flibble\n" + + " * quxx\n" + + " * nest?\n" + + " * where\n" + + " * am\n" + + " * i?"), + [] ), + [ [ "bulletlist", + [ "listitem", + "foo", + [ "bulletlist", + ["listitem", "bar"], + ["listitem", "baz"], + ["listitem", "HATE"], + ["listitem", "flibble"] + ] + ], + [ "listitem", + "quxx", + [ "bulletlist", + [ "listitem", + "nest?", + [ "bulletlist", + ["listitem", "where"], + ["listitem", "am"], + ["listitem", "i?"] + ] + ] + ] + ] + ] ], + "Indenting Case VII"); + + /* Case VIII: Deep nesting + code block + | * one + | * two + | * three + | * four + | + | foo + */ + asserts.same( + bl( mk_block(" * one\n" + + " 1. two\n" + + " * three\n" + + " * four", + "\n\n"), + [ mk_block(" foo") ] ), + [ [ "bulletlist", + [ "listitem", + ["para", "one"], + [ "numberlist", + [ "listitem", + ["para", "two"], + [ "bulletlist", + [ "listitem", + [ "para", "three\n * four"], + ["code_block", "foo"] + ] + ] + ] + ] + ] + ] ], + "Case VIII: Deep nesting and code block"); + + }), + + test_horizRule: tests.meta(function(md) { + var hr = md.dialect.block.horizRule, + strs = ["---", "_ __", "** ** **", "--- "]; + strs.forEach( function(s) { + asserts.same( + hr.call( md, mk_block(s), [] ), + [ [ "hr" ] ], + "simple hr from " + uneval(s)); + }); + }), + + test_blockquote: tests.meta(function(md) { + var bq = md.dialect.block.blockquote; + asserts.same( + bq.call( md, mk_block("> foo\n> bar"), [] ), + [ ["blockquote", ["para", "foo\nbar"] ] ], + "simple blockquote"); + + // Note: this tests horizRule as well through block processing. + asserts.same( + bq.call( md, mk_block("> foo\n> bar\n>\n>- - - "), [] ), + [ ["blockquote", + ["para", "foo\nbar"], + ["hr"] + ] ], + "blockquote with interesting content"); + + }), + + test_referenceDefn: tests.meta(function(md) { + var rd = md.dialect.block.referenceDefn; + + [ '[id]: http://example.com/ "Optional Title Here"', + "[id]: http://example.com/ 'Optional Title Here'", + '[id]: http://example.com/ (Optional Title Here)' + ].forEach( function(s) { + md.tree = ["markdown"]; + + asserts.same(rd.call( md, mk_block(s) ), [], "ref processed"); + + asserts.same(md.tree[ 1 ].references, + { "id": { href: "http://example.com/", title: "Optional Title Here" } }, + "reference extracted"); + }); + + // Check a para abbuting a ref works right + md.tree = ["markdown"]; + var next = []; + asserts.same(rd.call( md, mk_block("[id]: example.com\npara"), next ), [], "ref processed"); + asserts.same(md.tree[ 1 ].references, { "id": { href: "example.com" } }, "reference extracted"); + asserts.same(next, [ mk_block("para") ], "paragraph put back into blocks"); + + }), + + test_inline_br: tests.meta(function(md) { + asserts.same( + md.processInline("foo \n\\[bar"), + [ "foo", ["linebreak"], "[bar" ], "linebreak+escape"); + }), + + test_inline_escape: tests.meta(function(md) { + asserts.same( md.processInline("\\bar"), [ "\\bar" ], "invalid escape" ); + asserts.same( md.processInline("\\*foo*"), [ "*foo*" ], "escaped em" ); + }), + + test_inline_code: tests.meta(function(md) { + asserts.same( md.processInline("`bar`"), [ ["inlinecode", "bar" ] ], "code I" ); + asserts.same( md.processInline("``b`ar``"), [ ["inlinecode", "b`ar" ] ], "code II" ); + asserts.same( md.processInline("```bar``` baz"), [ ["inlinecode", "bar" ], " baz" ], "code III" ); + }), + + test_inline_strong_em: tests.meta(function(md) { + // Yay for horrible edge cases >_< + asserts.same( md.processInline("foo *abc* bar"), [ "foo ", ["em", "abc" ], " bar" ], "strong/em I" ); + asserts.same( md.processInline("*abc `code`"), [ "*abc ", ["inlinecode", "code" ] ], "strong/em II" ); + asserts.same( md.processInline("*abc**def* after"), [ ["em", "abc**def" ], " after" ], "strong/em III" ); + asserts.same( md.processInline("*em **strong * wtf**"), [ ["em", "em **strong " ], " wtf**" ], "strong/em IV" ); + asserts.same( md.processInline("*foo _b*a*r baz"), [ [ "em", "foo _b" ], "a*r baz" ], "strong/em V" ); + }), + + test_inline_img: tests.meta(function(md) { + + asserts.same( md.processInline( "![alt] (url)" ), + [ [ "img", { href: "url", alt: "alt" } ] ], + "inline img I" ); + + asserts.same( md.processInline( "![alt](url 'title')" ), + [ [ "img", { href: "url", alt: "alt", title: "title" } ] ], + "inline img II" ); + + asserts.same( md.processInline( "![alt] (url 'tit'le') after')" ), + [ [ "img", { href: "url", alt: "alt", title: "tit'le" } ], " after')" ], + "inline img III" ); + + asserts.same( md.processInline( "![alt] (url \"title\")" ), + [ [ "img", { href: "url", alt: "alt", title: "title" } ] ], + "inline img IV" ); + + asserts.same( md.processInline( "![alt][id]" ), + [ [ "img_ref", { ref: "id", alt: "alt", text: "![alt][id]" } ] ], + "ref img I" ); + + asserts.same( md.processInline( "![alt] [id]" ), + [ [ "img_ref", { ref: "id", alt: "alt", text: "![alt] [id]" } ] ], + "ref img II" ); + }), + + test_inline_link: tests.meta(function(md) { + + asserts.same( md.processInline( "[text] (url)" ), + [ [ "link", { href: "url" }, "text" ] ], + "inline link I" ); + + asserts.same( md.processInline( "[text](url 'title')" ), + [ [ "link", { href: "url", title: "title" }, "text" ] ], + "inline link II" ); + + asserts.same( md.processInline( "[text](url 'tit'le') after')" ), + [ [ "link", { href: "url", title: "tit'le" }, "text" ], " after')" ], + "inline link III" ); + + asserts.same( md.processInline( "[text](url \"title\")" ), + [ [ "link", { href: "url", title: "title" }, "text" ] ], + "inline link IV" ); + + asserts.same( md.processInline( "[text][id]" ), + [ [ "link_ref", { ref: "id", original: "[text][id]" }, "text" ] ], + "ref link I" ); + + asserts.same( md.processInline( "[text] [id]" ), + [ [ "link_ref", { ref: "id", original: "[text] [id]" }, "text" ] ], + "ref link II" ); + }), + + test_inline_autolink: tests.meta(function(md) { + + asserts.same( md.processInline( "" ), + [ [ "link", { href: "http://foo.com" }, "http://foo.com" ] ], + "autolink I" ); + + asserts.same( md.processInline( "" ), + [ [ "link", { href: "mailto:foo@bar.com" }, "foo@bar.com" ] ], + "autolink II" ); + + asserts.same( md.processInline( "" ), + [ [ "link", { href: "mailto:foo@bar.com" }, "foo@bar.com" ] ], + "autolink III" ); + }), +} + + +if (require.main === module) { + var asserts = require('test').asserts; + require('test').runner(tests); +} diff --git a/node_modules/narcissus.js b/node_modules/narcissus.js new file mode 100644 index 000000000..ea9c48f0a --- /dev/null +++ b/node_modules/narcissus.js @@ -0,0 +1,31 @@ +/* + * Include the Mozilla Narcissus JavaScript parser. + */ + +(function(global) { + + if (typeof require !== 'undefined') { + if (typeof Narcissus === 'undefined') { + Narcissus = {}; + + require("narcissus/lib/jsdefs"); + + // workaround for rhino, which will throw an exception when trying + // to eval a const declaration + if (Narcissus.definitions.consts) try { + eval(Narcissus.definitions.consts); + } + catch (e) { + Narcissus.definitions.consts = Narcissus.definitions.consts.replace('const ', 'var '); + } + + //require("jsecma5"); + //require("jsmods.js"); + require("narcissus/lib/jslex"); + require("narcissus/lib/jsparse"); + } + + exports.Narcissus = Narcissus; + } + +})(global|this); \ No newline at end of file diff --git a/node_modules/narcissus/LICENSE b/node_modules/narcissus/LICENSE new file mode 100644 index 000000000..173bdc47a --- /dev/null +++ b/node_modules/narcissus/LICENSE @@ -0,0 +1,5 @@ +This software is available under your choice of the following licenses: + + * MPL 1.1 or later: http://www.mozilla.org/MPL/ + * GPL 2.0 or later: http://www.gnu.org/licenses/gpl.html + * LGPL 2.1 or later: http://www.gnu.org/licenses/lgpl.html diff --git a/node_modules/narcissus/README.md b/node_modules/narcissus/README.md new file mode 100644 index 000000000..02e7daa11 --- /dev/null +++ b/node_modules/narcissus/README.md @@ -0,0 +1,21 @@ +# Narcissus + +Narcissus is a JavaScript interpreter written in pure JavaScript (i.e., a [meta-circular evaluator](http://en.wikipedia.org/wiki/Meta-circular_evaluator)), using the [SpiderMonkey](http://www.mozilla.org/js/spidermonkey/) engine. + +Originally a proof-of-concept by [Brendan Eich](http://brendaneich.com/), Narcissus is being revived as a test-bed for rapidly prototyping new language features for the JavaScript language (as well as the ECMAScript standard). + +# Documentation + +Documentation can be found on the [Narcissus wiki](https://github.com/mozilla/narcissus/wiki). + +# Contributors + +* Tom Austin +* Brendan Eich +* Andreas Gal +* Shu-yu Guo +* Dave Herman +* Bruno Jouhier +* Gregor Richards +* Dimitris Vardoulakis +* Patrick Walton diff --git a/node_modules/narcissus/harmony-tests/README.txt b/node_modules/narcissus/harmony-tests/README.txt new file mode 100644 index 000000000..f353a9255 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/README.txt @@ -0,0 +1,17 @@ +This directory contains tests for experimental Harmony features. + +NOTE: Please don't fork this test suite without talking to me first! + +I purposefully created almost no test harness whatsoever -- just a stupid shell +script that uses directory structure to figure out expected results. This is +because: + +a) I don't want to write Yet Another Test Harness; and + +b) I'd like these tests to get eaten up by some other test suite eventually. + +So *please* talk to me first before forking; it's probably much better to +adapt these tests directly. + +Dave Herman +dherman@mozilla.com diff --git a/node_modules/narcissus/harmony-tests/fail-execute/eval-export.js b/node_modules/narcissus/harmony-tests/fail-execute/eval-export.js new file mode 100644 index 000000000..83f8c2490 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-execute/eval-export.js @@ -0,0 +1 @@ +eval("var x = 42; export x;"); diff --git a/node_modules/narcissus/harmony-tests/fail-execute/eval-resolve1.js b/node_modules/narcissus/harmony-tests/fail-execute/eval-resolve1.js new file mode 100644 index 000000000..0bdcc1a1d --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-execute/eval-resolve1.js @@ -0,0 +1 @@ +eval("xyzzx") diff --git a/node_modules/narcissus/harmony-tests/fail-execute/eval-resolve2.js b/node_modules/narcissus/harmony-tests/fail-execute/eval-resolve2.js new file mode 100644 index 000000000..d6474648c --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-execute/eval-resolve2.js @@ -0,0 +1 @@ +(function(x) { return eval("xyzzx") })("foo"); diff --git a/node_modules/narcissus/harmony-tests/fail-execute/module-uninit-read.js b/node_modules/narcissus/harmony-tests/fail-execute/module-uninit-read.js new file mode 100644 index 000000000..8cca9092d --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-execute/module-uninit-read.js @@ -0,0 +1,8 @@ +module A { + import B.foo; + var x = foo; +} + +module B { + export var foo = 12; +} diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle1.js b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle1.js new file mode 100644 index 000000000..b7d4f86d8 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle1.js @@ -0,0 +1,3 @@ +module M { + export M.foo; +} diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle2.js b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle2.js new file mode 100644 index 000000000..7caf42bff --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle2.js @@ -0,0 +1,7 @@ +module M { + export N.foo; +} + +module N { + export M.foo; +} diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle3.js b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle3.js new file mode 100644 index 000000000..b4b35cbc3 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle3.js @@ -0,0 +1,7 @@ +module M { + export { foo: N.bar }; +} + +module N { + export { bar: M.foo }; +} diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle4.js b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle4.js new file mode 100644 index 000000000..db014b14d --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/export-cycle4.js @@ -0,0 +1,15 @@ +module M { + export { foo: N.bar }; +} + +module N { + export { bar: O.baz }; +} + +module O { + export { baz: P.buz }; +} + +module P { + export { buz: M.foo }; +} diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/export-unbound-var.js b/node_modules/narcissus/harmony-tests/fail-resolve/export-unbound-var.js new file mode 100644 index 000000000..148ff7469 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/export-unbound-var.js @@ -0,0 +1 @@ +module M { export x } diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/import-eval1.js b/node_modules/narcissus/harmony-tests/fail-resolve/import-eval1.js new file mode 100644 index 000000000..d8848d7b9 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/import-eval1.js @@ -0,0 +1,2 @@ +eval("module M { export var foo = 42 }"); +import M.foo; diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/import-eval2.js b/node_modules/narcissus/harmony-tests/fail-resolve/import-eval2.js new file mode 100644 index 000000000..4c3a726de --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/import-eval2.js @@ -0,0 +1,2 @@ +eval("module M { export var foo = 42 }"); +var x = M.foo; diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/module-rebind-assignment1.js b/node_modules/narcissus/harmony-tests/fail-resolve/module-rebind-assignment1.js new file mode 100644 index 000000000..7ee5e17b6 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/module-rebind-assignment1.js @@ -0,0 +1,5 @@ +module P { export module A { export var a = 12 } } + +module B = P.A + +B.a = 13; diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/unbound-read.js b/node_modules/narcissus/harmony-tests/fail-resolve/unbound-read.js new file mode 100644 index 000000000..ebfbbb3a0 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/unbound-read.js @@ -0,0 +1 @@ +var foo = someUnboundVariable; diff --git a/node_modules/narcissus/harmony-tests/fail-resolve/unbound-write.js b/node_modules/narcissus/harmony-tests/fail-resolve/unbound-write.js new file mode 100644 index 000000000..8f9dd8fca --- /dev/null +++ b/node_modules/narcissus/harmony-tests/fail-resolve/unbound-write.js @@ -0,0 +1 @@ +someUnboundVariable = 12; diff --git a/node_modules/narcissus/harmony-tests/run.sh b/node_modules/narcissus/harmony-tests/run.sh new file mode 100755 index 000000000..ec27881dc --- /dev/null +++ b/node_modules/narcissus/harmony-tests/run.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +HERE=`dirname $0` +ROOT=$HERE/.. + +cd $ROOT + +FAILURES= + +################################################################################ + +echo Running harmony-tests/succeed... + +SUCCEED_PASS=0 +SUCCEED_FAIL=0 + +for f in harmony-tests/succeed/*.js ; do + ./njs -H -f $f >/dev/null 2>&1 + if [ $? -eq 0 ]; then + SUCCEED_PASS=$(($SUCCEED_PASS + 1)) + else + SUCCEED_FAIL=$(($SUCCEED_FAIL + 1)) + FAILURES="$FAILURES $f" + fi +done + +echo "==> $SUCCEED_PASS passed, $SUCCEED_FAIL failed." + +################################################################################ + +echo +echo Running harmony-tests/fail-resolve... + +FAIL_RESOLVE_PASS=0 +FAIL_RESOLVE_FAIL=0 + +for f in harmony-tests/fail-resolve/*.js ; do + ./njs -H -E "Narcissus.resolver.resolve(Narcissus.parser.parse(snarf('$f')),Narcissus.interpreter.globalStaticEnv)" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + FAIL_RESOLVE_FAIL=$((FAIL_RESOLVE_FAIL + 1)) + FAILURES="$FAILURES $f" + else + FAIL_RESOLVE_PASS=$((FAIL_RESOLVE_PASS + 1)) + fi +done + +echo "==> $FAIL_RESOLVE_PASS passed, $FAIL_RESOLVE_FAIL failed." + +################################################################################ + +echo +echo Running harmony-tests/fail-execute... + +FAIL_EXECUTE_PASS=0 +FAIL_EXECUTE_FAIL=0 + +for f in harmony-tests/fail-execute ; do + ./njs -H -f $f >/dev/null 2>&1 + if [ $? -eq 0 ]; then + FAIL_EXECUTE_FAIL=$(($FAIL_EXECUTE_FAIL + 1)) + FAILURES="$FAILURES $f" + else + FAIL_EXECUTE_PASS=$(($FAIL_EXECUTE_PASS + 1)) + fi +done + +echo "==> $FAIL_EXECUTE_PASS passed, $FAIL_EXECUTE_FAIL failed." + +################################################################################ + +echo +echo TOTAL: + +TOTAL_PASS=$(($SUCCEED_PASS + $FAIL_EXECUTE_PASS + $FAIL_RESOLVE_PASS)) +TOTAL_FAIL=$(($SUCCEED_FAIL + $FAIL_EXECUTE_FAIL + $FAIL_RESOLVE_FAIL)) + +echo "==> $TOTAL_PASS passed, $TOTAL_FAIL failed." + +if [ $TOTAL_FAIL -gt 0 ]; then + echo + echo Failures: + for f in $FAILURES ; do + echo " $f" + done + exit 1 +else + exit 0 +fi diff --git a/node_modules/narcissus/harmony-tests/succeed/eval-import1.js b/node_modules/narcissus/harmony-tests/succeed/eval-import1.js new file mode 100644 index 000000000..64f51a29c --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/eval-import1.js @@ -0,0 +1,5 @@ +module M { + export var foo = 42; +} + +assertEq(eval("import M.foo; foo"), 42); diff --git a/node_modules/narcissus/harmony-tests/succeed/eval-import2.js b/node_modules/narcissus/harmony-tests/succeed/eval-import2.js new file mode 100644 index 000000000..af6d8eebe --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/eval-import2.js @@ -0,0 +1,5 @@ +module M { + export var foo = "foo"; +} + +assertEq(eval("module M { export var bar = 'bar' } import M.bar; bar"), "bar"); diff --git a/node_modules/narcissus/harmony-tests/succeed/eval-module1.js b/node_modules/narcissus/harmony-tests/succeed/eval-module1.js new file mode 100644 index 000000000..ba97819ee --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/eval-module1.js @@ -0,0 +1,9 @@ +module M { + export var foo = "foo"; +} + +var foo = M.foo; +var bar = eval("module M { export var bar = 'bar'; } M.bar"); + +assertEq(foo, "foo"); +assertEq(bar, "bar"); diff --git a/node_modules/narcissus/harmony-tests/succeed/eval-module2.js b/node_modules/narcissus/harmony-tests/succeed/eval-module2.js new file mode 100644 index 000000000..be217222d --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/eval-module2.js @@ -0,0 +1,4 @@ +var M1 = eval("module M { } M"); +var M2 = eval("module M { } M"); + +assertEq(M1 === M2, false); diff --git a/node_modules/narcissus/harmony-tests/succeed/eval-resolve1.js b/node_modules/narcissus/harmony-tests/succeed/eval-resolve1.js new file mode 100644 index 000000000..461432047 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/eval-resolve1.js @@ -0,0 +1,5 @@ +var foo = 42; + +var myfoo = eval("foo"); + +assertEq(foo, myfoo); diff --git a/node_modules/narcissus/harmony-tests/succeed/eval-resolve2.js b/node_modules/narcissus/harmony-tests/succeed/eval-resolve2.js new file mode 100644 index 000000000..69a4023fd --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/eval-resolve2.js @@ -0,0 +1,3 @@ +var foo = (function(x) { return eval("x") })("foo"); + +assertEq(foo, "foo"); diff --git a/node_modules/narcissus/harmony-tests/succeed/export-bound-var.js b/node_modules/narcissus/harmony-tests/succeed/export-bound-var.js new file mode 100644 index 000000000..1fea449c6 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/export-bound-var.js @@ -0,0 +1,12 @@ +module M { + import N.x; + export x; +} + +module N { + var x = 42; + export x; +} + +assertEq(M.x, 42); +assertEq(N.x, 42); diff --git a/node_modules/narcissus/harmony-tests/succeed/export-var.js b/node_modules/narcissus/harmony-tests/succeed/export-var.js new file mode 100644 index 000000000..bf1a9eb1a --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/export-var.js @@ -0,0 +1,5 @@ +module M { + export var foo = 42; +} + +assertEq(M.foo, 42); diff --git a/node_modules/narcissus/harmony-tests/succeed/global-this.js b/node_modules/narcissus/harmony-tests/succeed/global-this.js new file mode 100644 index 000000000..4f9f9d33e --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/global-this.js @@ -0,0 +1,3 @@ +var global = this; + +assertEq("global" in global, true); diff --git a/node_modules/narcissus/harmony-tests/succeed/incomplete-module1.js b/node_modules/narcissus/harmony-tests/succeed/incomplete-module1.js new file mode 100644 index 000000000..dd3aceea9 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/incomplete-module1.js @@ -0,0 +1,8 @@ +var saved; + +module M { + saved = M; + export var foo = 42; +} + +assertEq("foo" in saved, true); diff --git a/node_modules/narcissus/harmony-tests/succeed/incomplete-module2.js b/node_modules/narcissus/harmony-tests/succeed/incomplete-module2.js new file mode 100644 index 000000000..dfa890bbc --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/incomplete-module2.js @@ -0,0 +1,9 @@ +var saved; + +module M { + export var foo = 42; + var tmp = M; + saved = tmp.foo; +} + +assertEq(saved, 42); diff --git a/node_modules/narcissus/harmony-tests/succeed/module-completion.js b/node_modules/narcissus/harmony-tests/succeed/module-completion.js new file mode 100644 index 000000000..edb66130d --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/module-completion.js @@ -0,0 +1 @@ +assertEq(eval("module M { export var foo = 42 }").foo, 42); diff --git a/node_modules/narcissus/harmony-tests/succeed/module-rebind1.js b/node_modules/narcissus/harmony-tests/succeed/module-rebind1.js new file mode 100644 index 000000000..8313b9783 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/module-rebind1.js @@ -0,0 +1,8 @@ +module P { + module A { export var a = 12 } + export A +} + +module B = P.A + +assertEq(B.a, 12); diff --git a/node_modules/narcissus/harmony-tests/succeed/module-rebind2.js b/node_modules/narcissus/harmony-tests/succeed/module-rebind2.js new file mode 100644 index 000000000..e96dbbd97 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/module-rebind2.js @@ -0,0 +1,10 @@ +module P { + module A { export var a = 12 } + export A +} + +module B = P.A, C = P.A, D = P.A; + +assertEq(B.a, 12); +assertEq(C.a, 12); +assertEq(D.a, 12); diff --git a/node_modules/narcissus/harmony-tests/succeed/module-rebind3.js b/node_modules/narcissus/harmony-tests/succeed/module-rebind3.js new file mode 100644 index 000000000..fec298fdb --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/module-rebind3.js @@ -0,0 +1,9 @@ +module M { + export var foo = 42; + export module N { + module M; + export var foo = M.foo + } +} + +assertEq(M.foo, M.N.foo); diff --git a/node_modules/narcissus/harmony-tests/succeed/module-this.js b/node_modules/narcissus/harmony-tests/succeed/module-this.js new file mode 100644 index 000000000..0e20dda1f --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/module-this.js @@ -0,0 +1,8 @@ +var saved; + +module M { + saved = this; + export var foo = 42; +} + +assertEq("foo" in saved, true); diff --git a/node_modules/narcissus/harmony-tests/succeed/re-export1.js b/node_modules/narcissus/harmony-tests/succeed/re-export1.js new file mode 100644 index 000000000..7886c7926 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/re-export1.js @@ -0,0 +1,18 @@ +module M { + export module N { + export var foo = 42; + export var bar = "hello world"; + } +} + +module Q { + export { + foo: M.N.bar, + bar: M.N.foo + } +} + +assertEq(Q.foo, "hello world"); +assertEq(Q.bar, 42); +assertEq(Q.foo, M.N.bar); +assertEq(Q.bar, M.N.foo); diff --git a/node_modules/narcissus/harmony-tests/succeed/re-export2.js b/node_modules/narcissus/harmony-tests/succeed/re-export2.js new file mode 100644 index 000000000..f115ac6ea --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/re-export2.js @@ -0,0 +1,19 @@ +module M { + export module N { + export var foo = 42; + export var bar = "hello world"; + } +} + +module Q { + module MN = M.N; + export { + foo: MN.bar, + bar: MN.foo + } +} + +assertEq(Q.foo, "hello world"); +assertEq(Q.bar, 42); +assertEq(Q.foo, M.N.bar); +assertEq(Q.bar, M.N.foo); diff --git a/node_modules/narcissus/harmony-tests/succeed/uninitialized-module1.js b/node_modules/narcissus/harmony-tests/succeed/uninitialized-module1.js new file mode 100644 index 000000000..4996d82bd --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/uninitialized-module1.js @@ -0,0 +1,7 @@ +var saved = M; + +module M { + export var foo = 42; +} + +assertEq(saved.foo, 42); diff --git a/node_modules/narcissus/harmony-tests/succeed/uninitialized-module2.js b/node_modules/narcissus/harmony-tests/succeed/uninitialized-module2.js new file mode 100644 index 000000000..047170593 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/uninitialized-module2.js @@ -0,0 +1,9 @@ +var saved = M.N; + +module M { + export module N { + export var foo = 42; + } +} + +assertEq(saved.foo, 42); diff --git a/node_modules/narcissus/harmony-tests/succeed/uninitialized-module3.js b/node_modules/narcissus/harmony-tests/succeed/uninitialized-module3.js new file mode 100644 index 000000000..e535558c9 --- /dev/null +++ b/node_modules/narcissus/harmony-tests/succeed/uninitialized-module3.js @@ -0,0 +1,14 @@ +var saved; + +module M { + module N { + saved = M.O.P; + } + export module O { + export module P { + export var foo = 42; + } + } +} + +assertEq(saved.foo, 42); diff --git a/node_modules/narcissus/jstests b/node_modules/narcissus/jstests new file mode 100755 index 000000000..c62465bf1 --- /dev/null +++ b/node_modules/narcissus/jstests @@ -0,0 +1,44 @@ +#!/bin/sh + +# Root of the narcissus tree +NJS_HOME=`dirname $0` +NJS_HOME=`(cd $NJS_HOME; pwd)` + +# Fake information for the test harness's |xulRuntime| configuration object. +XUL_INFO=none:none:true + +if [ $# -eq 1 -a "$1" = "-h" ]; then + echo "usage: jstests [-h | -a | ...]" 1>&2 + echo " -h display this usage information and quit" 1>&2 + echo " -a run all but the slowest tests (those in xfail/narcissus-slow.txt)" 1>&2 + echo " path to individual test (relative to test directory)" 1>&2 + echo "" 1>&2 + echo "With no arguments, jstests runs all tests except those listed in" 1>&2 + echo "xfail/narcissus-failures.txt, which includes all the tests listed in" 1>&2 + echo "xfail/narcissus-slow.txt." 1>&2 + echo "" 1>&2 + echo "The test directory is searched for either in NJS_TESTS or in" 1>&2 + echo "a tests/ subdirectory of the Narcissus home directory." + exit +elif [ $# -gt 0 -a "$1" = "-a" ]; then + shift + XFAIL=narcissus-slow.txt +else + XFAIL=narcissus-failures.txt +fi + +if [ ! -z $NJS_TESTS -a -d $NJS_TESTS ]; then + cd $NJS_TESTS +elif [ -d $NJS_HOME/tests ]; then + cd $NJS_HOME/tests +else + echo 'Expected a test directory in $NJS_TESTS or '"$NJS_HOME/tests." 1>&2 + echo "Run jstests -h for more information." 1>&2 + exit 1 +fi + +if [ $# -gt 0 ]; then + exec python jstests.py --xul-info=$XUL_INFO -s -o -d -j 4 $NJS_HOME/njs $* +else + exec python jstests.py --xul-info=$XUL_INFO -d -j 4 $NJS_HOME/njs -x $NJS_HOME/xfail/$XFAIL +fi diff --git a/node_modules/narcissus/lib.zip b/node_modules/narcissus/lib.zip new file mode 100644 index 000000000..9602252e0 Binary files /dev/null and b/node_modules/narcissus/lib.zip differ diff --git a/node_modules/narcissus/lib/decompiler.js b/node_modules/narcissus/lib/decompiler.js new file mode 100644 index 000000000..929464588 --- /dev/null +++ b/node_modules/narcissus/lib/decompiler.js @@ -0,0 +1,50 @@ +/* vim: set sw=4 ts=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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +require("./jsdefs"); +require("./jslex"); +require("./jsparse"); +require("./jsdecomp"); + +for (var exp in Narcissus.decompiler) + exports[exp] = Narcissus.decompiler[exp]; diff --git a/node_modules/narcissus/lib/definitions.js b/node_modules/narcissus/lib/definitions.js new file mode 100644 index 000000000..4412b4a17 --- /dev/null +++ b/node_modules/narcissus/lib/definitions.js @@ -0,0 +1,47 @@ +/* vim: set sw=4 ts=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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +require("./jsdefs"); + +for (var exp in Narcissus.definitions) + exports[exp] = Narcissus.definitions[exp]; diff --git a/node_modules/narcissus/lib/jsbrowser.js b/node_modules/narcissus/lib/jsbrowser.js new file mode 100644 index 000000000..eb03e05a8 --- /dev/null +++ b/node_modules/narcissus/lib/jsbrowser.js @@ -0,0 +1,65 @@ +/* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; -*- + * vim: set sw=4 ts=8 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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Browser-specific tweaks needed for Narcissus to execute properly + */ + +// Prevent setTimeout from breaking out to SpiderMonkey +Narcissus.interpreter.globalBase.setTimeout = function(code, delay) { + var timeoutCode = (typeof code === "string") ? + function() { Narcissus.interpreter.evaluate(code); } : + code; + return setTimeout(timeoutCode, delay); +}; + +// Prevent setInterval from breaking out to SpiderMonkey +Narcissus.interpreter.globalBase.setInterval = function(code, delay) { + var timeoutCode = (typeof code === "string") ? + function() { Narcissus.interpreter.evaluate(code); } : + code; + return setInterval(timeoutCode, delay); +}; + +// Hack to avoid problems with the Image constructor in Narcissus. +Narcissus.interpreter.globalBase.Image = function() {}; + + diff --git a/node_modules/narcissus/lib/jsdecomp.js b/node_modules/narcissus/lib/jsdecomp.js new file mode 100644 index 000000000..8e1780781 --- /dev/null +++ b/node_modules/narcissus/lib/jsdecomp.js @@ -0,0 +1,528 @@ +/* vim: set sw=4 ts=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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shu-Yu Guo + * Bruno Jouhier + * Gregor Richards + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Decompiler and pretty-printer. + */ + +Narcissus.decompiler = (function() { + + const parser = Narcissus.parser; + const definitions = Narcissus.definitions; + const tokens = definitions.tokens; + + // Set constants in the local scope. + eval(definitions.consts); + + function indent(n, s) { + var ss = "", d = true; + + for (var i = 0, j = s.length; i < j; i++) { + if (d) + for (var k = 0; k < n; k++) + ss += " "; + ss += s[i]; + d = s[i] === '\n'; + } + + return ss; + } + + function isBlock(n) { + return n && (n.type === BLOCK); + } + + function isNonEmptyBlock(n) { + return isBlock(n) && n.children.length > 0; + } + + function nodeStr(n) { + return '"' + + n.value.replace(/\\/g, "\\\\") + .replace(/"/g, "\\\"") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + + '"'; + } + + function pp(n, d, inLetHead) { + var topScript = false; + + if (!n) + return ""; + if (!(n instanceof Object)) + return n; + if (!d) { + topScript = true; + d = 1; + } + + var p = ""; + + if (n.parenthesized) + p += "("; + + switch (n.type) { + case FUNCTION: + case GETTER: + case SETTER: + if (n.type === FUNCTION) + p += "function"; + else if (n.type === GETTER) + p += "get"; + else + p += "set"; + + p += (n.name ? " " + n.name : "") + "("; + for (var i = 0, j = n.params.length; i < j; i++) + p += (i > 0 ? ", " : "") + pp(n.params[i], d); + p += ") " + pp(n.body, d); + break; + + case SCRIPT: + case BLOCK: + var nc = n.children; + if (topScript) { + // No indentation. + for (var i = 0, j = nc.length; i < j; i++) { + if (i > 0) + p += "\n"; + p += pp(nc[i], d); + var eoc = p[p.length - 1]; + if (eoc != ";") + p += ";"; + } + + break; + } + + p += "{"; + if (n.id !== undefined) + p += " /* " + n.id + " */"; + p += "\n"; + for (var i = 0, j = nc.length; i < j; i++) { + if (i > 0) + p += "\n"; + p += indent(4, pp(nc[i], d)); + var eoc = p[p.length - 1]; + if (eoc != ";") + p += ";"; + } + p += "\n}"; + break; + + case LET_BLOCK: + p += "let (" + pp(n.variables, d, true) + ") "; + if (n.expression) + p += pp(n.expression, d); + else + p += pp(n.block, d); + break; + + case IF: + p += "if (" + pp(n.condition, d) + ") "; + + var tp = n.thenPart, ep = n.elsePart; + var b = isBlock(tp) || isBlock(ep); + if (!b) + p += "{\n"; + p += (b ? pp(tp, d) : indent(4, pp(tp, d))) + "\n"; + + if (ep) { + if (!b) + p += "} else {\n"; + else + p += " else "; + + p += (b ? pp(ep, d) : indent(4, pp(ep, d))) + "\n"; + } + if (!b) + p += "}"; + break; + + case SWITCH: + p += "switch (" + pp(n.discriminant, d) + ") {\n"; + for (var i = 0, j = n.cases.length; i < j; i++) { + var ca = n.cases[i]; + if (ca.type === CASE) + p += " case " + pp(ca.caseLabel, d) + ":\n"; + else + p += " default:\n"; + ps = pp(ca.statements, d); + p += ps.slice(2, ps.length - 2) + "\n"; + } + p += "}"; + break; + + case FOR: + p += "for (" + pp(n.setup, d) + "; " + + pp(n.condition, d) + "; " + + pp(n.update, d) + ") "; + + var pb = pp(n.body, d); + if (!isBlock(n.body)) + p += "{\n" + indent(4, pb) + ";\n}"; + else if (n.body) + p += pb; + break; + + case WHILE: + p += "while (" + pp(n.condition, d) + ") "; + + var pb = pp(n.body, d); + if (!isBlock(n.body)) + p += "{\n" + indent(4, pb) + ";\n}"; + else + p += pb; + break; + + case FOR_IN: + var u = n.varDecl; + p += n.isEach ? "for each (" : "for ("; + p += (u ? pp(u, d) : pp(n.iterator, d)) + " in " + + pp(n.object, d) + ") "; + + var pb = pp(n.body, d); + if (!isBlock(n.body)) + p += "{\n" + indent(4, pb) + ";\n}"; + else if (n.body) + p += pb; + break; + + case DO: + p += "do " + pp(n.body, d); + p += " while (" + pp(n.condition, d) + ");"; + break; + + case BREAK: + p += "break" + (n.label ? " " + n.label : "") + ";"; + break; + + case CONTINUE: + p += "continue" + (n.label ? " " + n.label : "") + ";"; + break; + + case TRY: + p += "try "; + p += pp(n.tryBlock, d); + for (var i = 0, j = n.catchClauses.length; i < j; i++) { + var t = n.catchClauses[i]; + p += " catch (" + pp(t.varName, d) + + (t.guard ? " if " + pp(t.guard, d) : "") + + ") "; + p += pp(t.block, d); + } + if (n.finallyBlock) { + p += " finally "; + p += pp(n.finallyBlock, d); + } + break; + + case THROW: + p += "throw " + pp(n.exception, d); + break; + + case RETURN: + p += "return"; + if (n.value) + p += " " + pp(n.value, d); + break; + + case YIELD: + p += "yield"; + if (n.value.type) + p += " " + pp(n.value, d); + break; + + case GENERATOR: + p += pp(n.expression, d) + " " + pp(n.tail, d); + break; + + case WITH: + p += "with (" + pp(n.object, d) + ") "; + p += pp(n.body, d); + break; + + case LET: + case VAR: + case CONST: + var nc = n.children; + if (!inLetHead) { + p += tokens[n.type] + " "; + } + for (var i = 0, j = nc.length; i < j; i++) { + if (i > 0) + p += ", "; + var u = nc[i]; + p += pp(u.name, d); + if (u.initializer) + p += " = " + pp(u.initializer, d); + } + break; + + case DEBUGGER: + p += "debugger NYI\n"; + break; + + case SEMICOLON: + if (n.expression) { + p += pp(n.expression, d) + ";"; + } + break; + + case LABEL: + p += n.label + ":\n" + pp(n.statement, d); + break; + + case COMMA: + case LIST: + var nc = n.children; + for (var i = 0, j = nc.length; i < j; i++) { + if (i > 0) + p += ", "; + p += pp(nc[i], d); + } + break; + + case ASSIGN: + var nc = n.children; + var t = n.assignOp; + p += pp(nc[0], d) + " " + (t ? tokens[t] : "") + "=" + + " " + pp(nc[1], d); + break; + + case HOOK: + var nc = n.children; + p += "(" + pp(nc[0], d) + " ? " + + pp(nc[1], d) + " : " + + pp(nc[2], d); + p += ")"; + break; + + case OR: + case AND: + var nc = n.children; + p += "(" + pp(nc[0], d) + " " + tokens[n.type] + " " + + pp(nc[1], d); + p += ")"; + break; + + case BITWISE_OR: + case BITWISE_XOR: + case BITWISE_AND: + case EQ: + case NE: + case STRICT_EQ: + case STRICT_NE: + case LT: + case LE: + case GE: + case GT: + case IN: + case INSTANCEOF: + case LSH: + case RSH: + case URSH: + case PLUS: + case MINUS: + case MUL: + case DIV: + case MOD: + var nc = n.children; + p += "(" + pp(nc[0], d) + " " + tokens[n.type] + " " + + pp(nc[1], d) + ")"; + break; + + case DELETE: + case VOID: + case TYPEOF: + p += tokens[n.type] + " " + pp(n.children[0], d); + break; + + case NOT: + case BITWISE_NOT: + p += tokens[n.type] + pp(n.children[0], d); + break; + + case UNARY_PLUS: + p += "+" + pp(n.children[0], d); + break; + + case UNARY_MINUS: + p += "-" + pp(n.children[0], d); + break; + + case INCREMENT: + case DECREMENT: + if (n.postfix) { + p += pp(n.children[0], d) + tokens[n.type]; + } else { + p += tokens[n.type] + pp(n.children[0], d); + } + break; + + case DOT: + var nc = n.children; + p += pp(nc[0], d) + "." + pp(nc[1], d); + break; + + case INDEX: + var nc = n.children; + p += pp(nc[0], d) + "[" + pp(nc[1], d) + "]"; + break; + + case CALL: + var nc = n.children; + p += pp(nc[0], d) + "(" + pp(nc[1], d) + ")"; + break; + + case NEW: + case NEW_WITH_ARGS: + var nc = n.children; + p += "new " + pp(nc[0], d); + if (nc[1]) + p += "(" + pp(nc[1], d) + ")"; + break; + + case ARRAY_INIT: + p += "["; + var nc = n.children; + for (var i = 0, j = nc.length; i < j; i++) { + if(nc[i]) + p += pp(nc[i], d); + p += "," + } + p += "]"; + break; + + case ARRAY_COMP: + p += "[" + pp (n.expression, d) + " "; + p += pp(n.tail, d); + p += "]"; + break; + + case COMP_TAIL: + var nc = n.children; + for (var i = 0, j = nc.length; i < j; i++) { + if (i > 0) + p += " "; + p += pp(nc[i], d); + } + if (n.guard) + p += " if (" + pp(n.guard, d) + ")"; + break; + + case OBJECT_INIT: + var nc = n.children; + if (nc[0] && nc[0].type === PROPERTY_INIT) + p += "{\n"; + else + p += "{"; + for (var i = 0, j = nc.length; i < j; i++) { + if (i > 0) { + p += ",\n"; + } + + var t = nc[i]; + if (t.type === PROPERTY_INIT) { + var tc = t.children; + var l; + // see if the left needs to be a string + if (tc[0].value === "" || /[^A-Za-z0-9_$]/.test(tc[0].value)) { + l = nodeStr(tc[0]); + } else { + l = pp(tc[0], d); + } + p += indent(4, l) + ": " + + indent(4, pp(tc[1], d)).substring(4); + } else { + p += indent(4, pp(t, d)); + } + } + p += "\n}"; + break; + + case NULL: + p += "null"; + break; + + case THIS: + p += "this"; + break; + + case TRUE: + p += "true"; + break; + + case FALSE: + p += "false"; + break; + + case IDENTIFIER: + case NUMBER: + case REGEXP: + p += n.value; + break; + + case STRING: + p += nodeStr(n); + break; + + case GROUP: + p += "(" + pp(n.children[0], d) + ")"; + break; + + default: + throw "PANIC: unknown operation " + tokens[n.type] + " " + n.toSource(); + } + + if (n.parenthesized) + p += ")"; + + return p; + } + + return { + pp: pp + }; + +}()); diff --git a/node_modules/narcissus/lib/jsdefs.js b/node_modules/narcissus/lib/jsdefs.js new file mode 100644 index 000000000..bb1a81038 --- /dev/null +++ b/node_modules/narcissus/lib/jsdefs.js @@ -0,0 +1,675 @@ +/* vim: set sw=4 ts=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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Well-known constants and lookup tables. Many consts are generated from the + * tokens table via eval to minimize redundancy, so consumers must be compiled + * separately to take advantage of the simple switch-case constant propagation + * done by SpiderMonkey. + */ + +(function() { + + var narcissus = { + options: { + version: 185, + }, + hostGlobal: this + }; + Narcissus = narcissus; +})(); + +Narcissus.definitions = (function() { + + var tokens = [ + // End of source. + "END", + + // Operators and punctuators. Some pair-wise order matters, e.g. (+, -) + // and (UNARY_PLUS, UNARY_MINUS). + "\n", ";", + ",", + "=", + "?", ":", "CONDITIONAL", + "||", + "&&", + "|", + "^", + "&", + "==", "!=", "===", "!==", + "<", "<=", ">=", ">", + "<<", ">>", ">>>", + "+", "-", + "*", "/", "%", + "!", "~", "UNARY_PLUS", "UNARY_MINUS", + "++", "--", + ".", + "[", "]", + "{", "}", + "(", ")", + + // Nonterminal tree node type codes. + "SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX", + "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER", + "GROUP", "LIST", "LET_BLOCK", "ARRAY_COMP", "GENERATOR", "COMP_TAIL", + + // Terminals. + "IDENTIFIER", "NUMBER", "STRING", "REGEXP", + + // Keywords. + "break", + "case", "catch", "const", "continue", + "debugger", "default", "delete", "do", + "else", "export", + "false", "finally", "for", "function", + "if", "import", "in", "instanceof", + "let", "module", + "new", "null", + "return", + "switch", + "this", "throw", "true", "try", "typeof", + "var", "void", + "yield", + "while", "with", + ]; + + var statementStartTokens = [ + "break", + "const", "continue", + "debugger", "do", + "for", + "if", + "return", + "switch", + "throw", "try", + "var", + "yield", + "while", "with", + ]; + + // Whitespace characters (see ECMA-262 7.2) + var whitespaceChars = [ + // normal whitespace: + "\u0009", "\u000B", "\u000C", "\u0020", "\u00A0", "\uFEFF", + + // high-Unicode whitespace: + "\u1680", "\u180E", + "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006", + "\u2007", "\u2008", "\u2009", "\u200A", + "\u202F", "\u205F", "\u3000" + ]; + + var whitespace = {}; + for (var i = 0; i < whitespaceChars.length; i++) { + whitespace[whitespaceChars[i]] = true; + } + + // Operator and punctuator mapping from token to tree node type name. + // NB: because the lexer doesn't backtrack, all token prefixes must themselves + // be valid tokens (e.g. !== is acceptable because its prefixes are the valid + // tokens != and !). + var opTypeNames = { + '\n': "NEWLINE", + ';': "SEMICOLON", + ',': "COMMA", + '?': "HOOK", + ':': "COLON", + '||': "OR", + '&&': "AND", + '|': "BITWISE_OR", + '^': "BITWISE_XOR", + '&': "BITWISE_AND", + '===': "STRICT_EQ", + '==': "EQ", + '=': "ASSIGN", + '!==': "STRICT_NE", + '!=': "NE", + '<<': "LSH", + '<=': "LE", + '<': "LT", + '>>>': "URSH", + '>>': "RSH", + '>=': "GE", + '>': "GT", + '++': "INCREMENT", + '--': "DECREMENT", + '+': "PLUS", + '-': "MINUS", + '*': "MUL", + '/': "DIV", + '%': "MOD", + '!': "NOT", + '~': "BITWISE_NOT", + '.': "DOT", + '[': "LEFT_BRACKET", + ']': "RIGHT_BRACKET", + '{': "LEFT_CURLY", + '}': "RIGHT_CURLY", + '(': "LEFT_PAREN", + ')': "RIGHT_PAREN" + }; + + // Hash of keyword identifier to tokens index. NB: we must null __proto__ to + // avoid toString, etc. namespace pollution. + var keywords = {__proto__: null}; + + // Define const END, etc., based on the token names. Also map name to index. + var tokenIds = {}; + + // Building up a string to be eval'd in different contexts. + var consts = "const "; + for (var i = 0, j = tokens.length; i < j; i++) { + if (i > 0) + consts += ", "; + var t = tokens[i]; + var name; + if (/^[a-z]/.test(t)) { + name = t.toUpperCase(); + keywords[t] = i; + } else { + name = (/^\W/.test(t) ? opTypeNames[t] : t); + } + consts += name + " = " + i; + tokenIds[name] = i; + tokens[t] = i; + } + consts += ";"; + + var isStatementStartCode = {__proto__: null}; + for (i = 0, j = statementStartTokens.length; i < j; i++) + isStatementStartCode[keywords[statementStartTokens[i]]] = true; + + // Map assignment operators to their indexes in the tokens array. + var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%']; + + for (i = 0, j = assignOps.length; i < j; i++) { + t = assignOps[i]; + assignOps[t] = tokens[t]; + } + + function defineGetter(obj, prop, fn, dontDelete, dontEnum) { + Object.defineProperty(obj, prop, + { get: fn, configurable: !dontDelete, enumerable: !dontEnum }); + } + + function defineGetterSetter(obj, prop, getter, setter, dontDelete, dontEnum) { + Object.defineProperty(obj, prop, { + get: getter, + set: setter, + configurable: !dontDelete, + enumerable: !dontEnum + }); + } + + function defineMemoGetter(obj, prop, fn, dontDelete, dontEnum) { + Object.defineProperty(obj, prop, { + get: function() { + var val = fn(); + defineProperty(obj, prop, val, dontDelete, true, dontEnum); + return val; + }, + configurable: true, + enumerable: !dontEnum + }); + } + + function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) { + Object.defineProperty(obj, prop, + { value: val, writable: !readOnly, configurable: !dontDelete, + enumerable: !dontEnum }); + } + + // Returns true if fn is a native function. (Note: SpiderMonkey specific.) + function isNativeCode(fn) { + // Relies on the toString method to identify native code. + return ((typeof fn) === "function") && fn.toString().match(/\[native code\]/); + } + + function getPropertyDescriptor(obj, name) { + while (obj) { + if (({}).hasOwnProperty.call(obj, name)) + return Object.getOwnPropertyDescriptor(obj, name); + obj = Object.getPrototypeOf(obj); + } + } + + function getPropertyNames(obj) { + var table = Object.create(null, {}); + while (obj) { + var names = Object.getOwnPropertyNames(obj); + for (var i = 0, n = names.length; i < n; i++) + table[names[i]] = true; + obj = Object.getPrototypeOf(obj); + } + return Object.keys(table); + } + + function getOwnProperties(obj) { + var map = {}; + for (var name in Object.getOwnPropertyNames(obj)) + map[name] = Object.getOwnPropertyDescriptor(obj, name); + return map; + } + + function blacklistHandler(target, blacklist) { + var mask = Object.create(null, {}); + var redirect = StringMap.create(blacklist).mapObject(function(name) { return mask; }); + return mixinHandler(redirect, target); + } + + function whitelistHandler(target, whitelist) { + var catchall = Object.create(null, {}); + var redirect = StringMap.create(whitelist).mapObject(function(name) { return target; }); + return mixinHandler(redirect, catchall); + } + + function mirrorHandler(target, writable) { + var handler = makePassthruHandler(target); + + var defineProperty = handler.defineProperty; + handler.defineProperty = function(name, desc) { + if (!desc.enumerable) + throw new Error("mirror property must be enumerable"); + if (!desc.configurable) + throw new Error("mirror property must be configurable"); + if (desc.writable !== writable) + throw new Error("mirror property must " + (writable ? "" : "not ") + "be writable"); + defineProperty(name, desc); + }; + + handler.fix = function() { }; + handler.getOwnPropertyDescriptor = handler.getPropertyDescriptor; + handler.getOwnPropertyNames = getPropertyNames.bind(handler, target); + handler.keys = handler.enumerate; + handler['delete'] = function() { return false; }; + handler.hasOwn = handler.has; + return handler; + } + + /* + * Mixin proxies break the single-inheritance model of prototypes, so + * the handler treats all properties as own-properties: + * + * X + * | + * +------------+------------+ + * | O | + * | | | + * | O O O | + * | | | | | + * | O O O O | + * | | | | | | + * | O O O O O | + * | | | | | | | + * +-(*)--(w)--(x)--(y)--(z)-+ + */ + + function mixinHandler(redirect, catchall) { + function targetFor(name) { + return hasOwn(redirect, name) ? redirect[name] : catchall; + } + + function getMuxPropertyDescriptor(name) { + var desc = getPropertyDescriptor(targetFor(name), name); + if (desc) + desc.configurable = true; + return desc; + } + + function getMuxPropertyNames() { + var names1 = Object.getOwnPropertyNames(redirect).filter(function(name) { + return name in redirect[name]; + }); + var names2 = getPropertyNames(catchall).filter(function(name) { + return !hasOwn(redirect, name); + }); + return names1.concat(names2); + } + + function enumerateMux() { + var result = Object.getOwnPropertyNames(redirect).filter(function(name) { + return name in redirect[name]; + }); + for (name in catchall) { + if (!hasOwn(redirect, name)) + result.push(name); + }; + return result; + } + + function hasMux(name) { + return name in targetFor(name); + } + + return { + getOwnPropertyDescriptor: getMuxPropertyDescriptor, + getPropertyDescriptor: getMuxPropertyDescriptor, + getOwnPropertyNames: getMuxPropertyNames, + defineProperty: function(name, desc) { + Object.defineProperty(targetFor(name), name, desc); + }, + "delete": function(name) { + var target = targetFor(name); + return delete target[name]; + }, + // FIXME: ha ha ha + fix: function() { }, + has: hasMux, + hasOwn: hasMux, + get: function(receiver, name) { + var target = targetFor(name); + return target[name]; + }, + set: function(receiver, name, val) { + var target = targetFor(name); + target[name] = val; + return true; + }, + enumerate: enumerateMux, + keys: enumerateMux + }; + } + + function makePassthruHandler(obj) { + // Handler copied from + // http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy + return { + getOwnPropertyDescriptor: function(name) { + var desc = Object.getOwnPropertyDescriptor(obj, name); + + // a trapping proxy's properties must always be configurable + desc.configurable = true; + return desc; + }, + getPropertyDescriptor: function(name) { + var desc = getPropertyDescriptor(obj, name); + + // a trapping proxy's properties must always be configurable + desc.configurable = true; + return desc; + }, + getOwnPropertyNames: function() { + return Object.getOwnPropertyNames(obj); + }, + defineProperty: function(name, desc) { + Object.defineProperty(obj, name, desc); + }, + "delete": function(name) { return delete obj[name]; }, + fix: function() { + if (Object.isFrozen(obj)) { + return getOwnProperties(obj); + } + + // As long as obj is not frozen, the proxy won't allow itself to be fixed. + return undefined; // will cause a TypeError to be thrown + }, + + has: function(name) { return name in obj; }, + hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); }, + get: function(receiver, name) { return obj[name]; }, + + // bad behavior when set fails in non-strict mode + set: function(receiver, name, val) { obj[name] = val; return true; }, + enumerate: function() { + var result = []; + for (name in obj) { result.push(name); }; + return result; + }, + keys: function() { return Object.keys(obj); } + }; + } + + // default function used when looking for a property in the global object + function noPropFound() { return undefined; } + + var hasOwnProperty = ({}).hasOwnProperty; + + function hasOwn(obj, name) { + return hasOwnProperty.call(obj, name); + } + + function StringMap(table, size) { + this.table = table || Object.create(null, {}); + this.size = size || 0; + } + + StringMap.create = function(table) { + var init = Object.create(null, {}); + var size = 0; + var names = Object.getOwnPropertyNames(table); + for (var i = 0, n = names.length; i < n; i++) { + var name = names[i]; + init[name] = table[name]; + size++; + } + return new StringMap(init, size); + }; + + StringMap.prototype = { + has: function(x) { return hasOwnProperty.call(this.table, x); }, + set: function(x, v) { + if (!hasOwnProperty.call(this.table, x)) + this.size++; + this.table[x] = v; + }, + get: function(x) { return this.table[x]; }, + getDef: function(x, thunk) { + if (!hasOwnProperty.call(this.table, x)) { + this.size++; + this.table[x] = thunk(); + } + return this.table[x]; + }, + forEach: function(f) { + var table = this.table; + for (var key in table) + f.call(this, key, table[key]); + }, + map: function(f) { + var table1 = this.table; + var table2 = Object.create(null, {}); + this.forEach(function(key, val) { + table2[key] = f.call(this, val, key); + }); + return new StringMap(table2, this.size); + }, + mapObject: function(f) { + var table1 = this.table; + var table2 = Object.create(null, {}); + this.forEach(function(key, val) { + table2[key] = f.call(this, val, key); + }); + return table2; + }, + toObject: function() { + return this.mapObject(function(val) { return val; }); + }, + choose: function() { + return Object.getOwnPropertyNames(this.table)[0]; + }, + remove: function(x) { + if (hasOwnProperty.call(this.table, x)) { + this.size--; + delete this.table[x]; + } + }, + copy: function() { + var table = Object.create(null, {}); + for (var key in this.table) + table[key] = this.table[key]; + return new StringMap(table, this.size); + }, + toString: function() { return "[object StringMap]" } + }; + + // an object-key table with poor asymptotics (replace with WeakMap when possible) + function ObjectMap(array) { + this.array = array || []; + } + + function searchMap(map, key, found, notFound) { + var a = map.array; + for (var i = 0, n = a.length; i < n; i++) { + var pair = a[i]; + if (pair.key === key) + return found(pair, i); + } + return notFound(); + } + + ObjectMap.prototype = { + has: function(x) { + return searchMap(this, x, function() { return true }, function() { return false }); + }, + set: function(x, v) { + var a = this.array; + searchMap(this, x, + function(pair) { pair.value = v }, + function() { a.push({ key: x, value: v }) }); + }, + get: function(x) { + return searchMap(this, x, + function(pair) { return pair.value }, + function() { return null }); + }, + getDef: function(x, thunk) { + var a = this.array; + return searchMap(this, x, + function(pair) { return pair.value }, + function() { + var v = thunk(); + a.push({ key: x, value: v }); + return v; + }); + }, + forEach: function(f) { + var a = this.array; + for (var i = 0, n = a.length; i < n; i++) { + var pair = a[i]; + f.call(this, pair.key, pair.value); + } + }, + choose: function() { + return this.array[0].key; + }, + get size() { + return this.array.length; + }, + remove: function(x) { + var a = this.array; + searchMap(this, x, + function(pair, i) { a.splice(i, 1) }, + function() { }); + }, + copy: function() { + return new ObjectMap(this.array.map(function(pair) { + return { key: pair.key, value: pair.value } + })); + }, + clear: function() { + this.array = []; + }, + toString: function() { return "[object ObjectMap]" } + }; + + // non-destructive stack + function Stack(elts) { + this.elts = elts || null; + } + + Stack.prototype = { + push: function(x) { + return new Stack({ top: x, rest: this.elts }); + }, + top: function() { + if (!this.elts) + throw new Error("empty stack"); + return this.elts.top; + }, + isEmpty: function() { + return this.top === null; + }, + find: function(test) { + for (var elts = this.elts; elts; elts = elts.rest) { + if (test(elts.top)) + return elts.top; + } + return null; + }, + has: function(x) { + return Boolean(this.find(function(elt) { return elt === x })); + }, + forEach: function(f) { + for (var elts = this.elts; elts; elts = elts.rest) { + f(elts.top); + } + } + }; + + return { + tokens: tokens, + whitespace: whitespace, + opTypeNames: opTypeNames, + keywords: keywords, + isStatementStartCode: isStatementStartCode, + tokenIds: tokenIds, + consts: consts, + assignOps: assignOps, + defineGetter: defineGetter, + defineGetterSetter: defineGetterSetter, + defineMemoGetter: defineMemoGetter, + defineProperty: defineProperty, + isNativeCode: isNativeCode, + mirrorHandler: mirrorHandler, + mixinHandler: mixinHandler, + whitelistHandler: whitelistHandler, + blacklistHandler: blacklistHandler, + makePassthruHandler: makePassthruHandler, + noPropFound: noPropFound, + StringMap: StringMap, + ObjectMap: ObjectMap, + Stack: Stack + }; +}()); diff --git a/node_modules/narcissus/lib/jsexec.js b/node_modules/narcissus/lib/jsexec.js new file mode 100644 index 000000000..ea1a7eb5a --- /dev/null +++ b/node_modules/narcissus/lib/jsexec.js @@ -0,0 +1,1484 @@ +/* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; -*- + * vim: set sw=4 ts=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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Execution of parse trees. + * + * Standard classes except for eval, Function, Array, and String are borrowed + * from the host JS environment. Function is metacircular. Array and String + * are reflected via wrapping the corresponding native constructor and adding + * an extra level of prototype-based delegation. + */ + +Narcissus.interpreter = (function() { + + var parser = Narcissus.parser; + var definitions = Narcissus.definitions; + var resolver = Narcissus.resolver; + var hostGlobal = Narcissus.hostGlobal; + + // Set constants in the local scope. + eval(definitions.consts); + + const StringMap = definitions.StringMap; + const ObjectMap = definitions.ObjectMap; + const StaticEnv = resolver.StaticEnv; + const Def = resolver.Def; + + const GLOBAL_CODE = 0, EVAL_CODE = 1, FUNCTION_CODE = 2, MODULE_CODE = 3; + + function ExecutionContext(type, version) { + this.type = type; + this.version = version; + // In Harmony, the global scope record is not exposed to the program. + if (type === GLOBAL_CODE && version === "harmony") { + this.scope = {object: globalScope, parent: null}; + this.thisObject = globalMirror; + } + } + + function isStackOverflow(e) { + var re = /InternalError: (script stack space quota is exhausted|too much recursion)/; + return re.test(e.toString()); + } + + // The underlying global object for narcissus. + var globalBase = { + // Value properties. + NaN: NaN, Infinity: Infinity, undefined: undefined, + + // Function properties. + eval: function eval(s) { + if (typeof s !== "string") + return s; + + var x = ExecutionContext.current; + var x2 = new ExecutionContext(EVAL_CODE, x.version); + x2.thisObject = x.thisObject; + x2.thisModule = x.thisModule; + x2.caller = x.caller; + x2.callee = x.callee; + x2.scope = x.version === "harmony" ? { object: new Object, parent: x.scope } : x.scope; + try { + var ast = parser.parse(s); + if (x.version === "harmony") { + resolver.resolve(ast, new StaticEnv(x.staticEnv)); + instantiateModules(ast, x2.scope); + } + x2.execute(ast); + return x2.result; + } catch (e if e instanceof SyntaxError || isStackOverflow(e)) { + /* + * If we get an internal error during parsing we need to reify + * the exception as a Narcissus THROW. + * + * See bug 152646. + */ + x.result = e; + throw THROW; + } + }, + + // Class constructors. Where ECMA-262 requires C.length === 1, we declare + // a dummy formal parameter. + Function: function Function(dummy) { + var p = "", b = "", n = arguments.length; + if (n) { + var m = n - 1; + if (m) { + p += arguments[0]; + for (var k = 1; k < m; k++) + p += "," + arguments[k]; + } + b += arguments[m]; + } + + // XXX We want to pass a good file and line to the tokenizer. + // Note the anonymous name to maintain parity with Spidermonkey. + var t = new parser.Tokenizer("anonymous(" + p + ") {" + b + "}"); + + // NB: Use the STATEMENT_FORM constant since we don't want to push this + // function onto the fake compilation context. + var f = parser.FunctionDefinition(t, null, false, parser.STATEMENT_FORM); + var s = {object: global, parent: null}; + return newFunction(f,{scope:s}); + }, + Array: function (dummy) { + // Array when called as a function acts as a constructor. + return Array.apply(this, arguments); + }, + String: function String(s) { + // Called as function or constructor: convert argument to string type. + s = arguments.length ? "" + s : ""; + if (this instanceof String) { + // Called as constructor: save the argument as the string value + // of this String object and return this object. + this.value = s; + return this; + } + return s; + }, + + // Don't want to proxy RegExp or some features won't work + RegExp: RegExp, + + // Extensions to ECMA. + load: function load(s) { + if (typeof s !== "string") + return s; + + evaluate(snarf(s), s, 1) + }, + version: function() { return ExecutionContext.current.version; }, + quit: function() { throw END; }, + assertEq: function() { + try { + return assertEq.apply(null, arguments); + } catch (e) { + ExecutionContext.current.result = e; + throw THROW; + } + } + }; + + function wrapNative(name, val) { + if (!definitions.isNativeCode(val)) + return val; + return Proxy.createFunction( + definitions.makePassthruHandler(val), + function() { return val.apply(hostGlobal, arguments); }, + function() { + var a = arguments; + switch (a.length) { + case 0: + return new val(); + case 1: + return new val(a[0]); + case 2: + return new val(a[0], a[1]); + case 3: + return new val(a[0], a[1], a[2]); + default: + var argStr = ""; + for (var i = 0; i < a.length; i++) + argStr += 'a[' + i + '],'; + return eval('new ' + name + '(' + argStr.slice(0,-1) + ');'); + } + }); + } + + var hostHandler = definitions.blacklistHandler(hostGlobal, { Narcissus: true }); + var hostHandlerGet = hostHandler.get; + hostHandler.get = function(receiver, name) { + return wrapNative(name, hostHandlerGet(receiver, name)); + }; + var hostProxy = Proxy.create(hostHandler); + + var globalStaticEnv; // global static scope + var moduleInstances = new ObjectMap(); // maps module instance objects -> module instances + var global = Object.create(hostProxy, {}); // exposed global object (legacy) + + // unexposed global scope record (Harmony) + var globalScope = Object.create(hostProxy, {}); + + // exposed global scope mirror (Harmony) + var globalMirror = Proxy.create(definitions.mirrorHandler(globalScope, true)); + + function resetEnvironment() { + ExecutionContext.current = new ExecutionContext(GLOBAL_CODE, Narcissus.options.version); + let names = Object.getOwnPropertyNames(global); + for (let i = 0, n = names.length; i < n; i++) { + delete global[names[i]]; + } + for (let key in globalScope) { + delete globalScope[key]; + } + moduleInstances.clear(); + globalStaticEnv = new StaticEnv(); + + let names = Object.getOwnPropertyNames(hostProxy); + for (let i = 0, n = names.length; i < n; i++) { + globalStaticEnv.bind(names[i], new Def()); + } + for (let key in globalBase) { + let val = globalBase[key]; + global[key] = val; + globalScope[key] = val; + // NB: this assumes globalBase never contains module or import bindings + globalStaticEnv.bind(key, new Def()); + } + } + resetEnvironment(); + + // Helper to avoid Object.prototype.hasOwnProperty polluting scope objects. + function hasDirectProperty(o, p) { + return Object.prototype.hasOwnProperty.call(o, p); + } + + // Reflect a host class into the target global environment by delegation. + function reflectClass(name, proto) { + var gctor = global[name]; + definitions.defineProperty(gctor, "prototype", proto, true, true, true); + definitions.defineProperty(proto, "constructor", gctor, false, false, true); + return proto; + } + + // Reflect Array -- note that all Array methods are generic. + reflectClass('Array', new Array); + + // Reflect String, overriding non-generic methods. + var gSp = reflectClass('String', new String); + gSp.toSource = function () { return this.value.toSource(); }; + gSp.toString = function () { return this.value; }; + gSp.valueOf = function () { return this.value; }; + global.String.fromCharCode = String.fromCharCode; + + ExecutionContext.current = null; + + ExecutionContext.prototype = { + caller: null, + callee: null, + scope: {object: global, parent: null}, + thisObject: global, + thisModule: null, + result: undefined, + target: null, + ecma3OnlyMode: false, + + // Execute a node in this execution context. + execute: function(n) { + var prev = ExecutionContext.current; + ExecutionContext.current = this; + try { + execute(n, this); + } catch (e if e === THROW) { + // Propagate the throw to the previous context if it exists. + if (prev) { + prev.result = this.result; + throw THROW; + } + // Otherwise reflect the throw into host JS. + throw this.result; + } finally { + ExecutionContext.current = prev; + } + } + }; + + function Reference(base, propertyName, node) { + this.base = base; + this.propertyName = propertyName; + this.node = node; + } + + Reference.prototype.toString = function () { return this.node.getSource(); } + + function getValue(v) { + if (v instanceof Reference) { + if (!v.base) { + throw new ReferenceError(v.propertyName + " is not defined", + v.node.filename, v.node.lineno); + } + return v.base[v.propertyName]; + } + return v; + } + + function putValue(v, w, vn) { + if (v instanceof Reference) + return (v.base || global)[v.propertyName] = w; + throw new ReferenceError("Invalid assignment left-hand side", + vn.filename, vn.lineno); + } + + function isPrimitive(v) { + var t = typeof v; + return (t === "object") ? v === null : t !== "function"; + } + + function isObject(v) { + var t = typeof v; + return (t === "object") ? v !== null : t === "function"; + } + + // If r instanceof Reference, v === getValue(r); else v === r. If passed, rn + // is the node whose execute result was r. + function toObject(v, r, rn) { + switch (typeof v) { + case "boolean": + return new global.Boolean(v); + case "number": + return new global.Number(v); + case "string": + return new global.String(v); + case "function": + return v; + case "object": + if (v !== null) + return v; + } + var message = r + " (type " + (typeof v) + ") has no properties"; + throw rn ? new TypeError(message, rn.filename, rn.lineno) + : new TypeError(message); + } + + // reifyModule :: (Module) -> module instance object + function reifyModule(mod) { + return mod.instance.proxy; + } + + function bindImports(impDecls, x) { + for (var i = 0; i < impDecls.length; i++) { + var list = impDecls[i].pathList; + for (var j = 0; j < list.length; j++) { + bindImport(list[j], x); + } + } + } + + function bindImport(decl, x) { + var t = x.scope.object; + var lhs = decl.children[0]; + var rhs = decl.children[1]; + var mod = lhs.denotedModule; + + function bind(importID, exportID) { + definitions.defineGetter(t, importID, function() { + var m = reifyModule(mod); + return m[exportID]; + }, true); + } + + if (rhs.type === IDENTIFIER) { + if (rhs.value === "*") { + mod.exports.forEach(function(exportID, exp) { + if (!mod.exportedModules.has(exportID)) + bind(exportID, exportID); + }); + } else { + bind(rhs.value, rhs.value); + } + return; + } + + for (var i = 0; i < rhs.children.length; i++) { + var pair = rhs.children[i]; + bind(pair.children[1].value, pair.children[0].value); + } + } + + function executeModule(n, x) { + var m = x.scope.object[n.name]; + var inst = moduleInstances.get(m); + var x2 = new ExecutionContext(MODULE_CODE, x.version); + x2.scope = inst.scope; + x2.thisObject = m; + x2.thisModule = m; + x2.execute(n.body); + return m; + } + + function execute(n, x) { + var a, c, f, i, j, r, s, t, u, v; + + switch (n.type) { + case MODULE: + if (n.body) + x.result = executeModule(n, x); + break; + + case IMPORT: + case EXPORT: + break; + + case FUNCTION: + if (n.functionForm !== parser.DECLARED_FORM) { + if (!n.name || n.functionForm === parser.STATEMENT_FORM) { + v = newFunction(n, x); + if (n.functionForm === parser.STATEMENT_FORM) + definitions.defineProperty(x.scope.object, n.name, v, true); + } else { + t = new Object; + x.scope = {object: t, parent: x.scope}; + try { + v = newFunction(n, x); + definitions.defineProperty(t, n.name, v, true, true); + } finally { + x.scope = x.scope.parent; + } + } + } + break; + + case SCRIPT: + t = x.scope.object; + n.modAssns.forEach(function(name, node) { + definitions.defineMemoGetter(t, name, function() { + return reifyModule(node.initializer.denotedModule); + }, true); + }); + bindImports(n.impDecls, x); + a = n.funDecls; + for (i = 0, j = a.length; i < j; i++) { + s = a[i].name; + f = newFunction(a[i], x); + // ECMA-262 says variable bindings created by `eval' are deleteable. + definitions.defineProperty(t, s, f, x.type !== EVAL_CODE); + } + a = n.varDecls; + var defineVar; + if (x.thisModule) { + defineVar = function(obj, prop) { + // start out as a getter/setter that throws on get + definitions.defineGetterSetter(obj, prop, function() { + throw new ReferenceError(prop + " is not initialized"); + }, function(val) { + // on first set, replace with ordinary property + definitions.defineProperty(obj, prop, val, false); + return val; + }, false); + }; + } else { + defineVar = function(obj, prop) { + // ECMA-262 says variable bindings created by `eval' are deleteable. + definitions.defineProperty(obj, prop, undefined, x.type !== EVAL_CODE, false); + }; + } + for (i = 0, j = a.length; i < j; i++) { + u = a[i]; + s = u.name; + if (u.readOnly && hasDirectProperty(t, s)) { + throw new TypeError("Redeclaration of const " + s, + u.filename, u.lineno); + } + if (u.readOnly || !hasDirectProperty(t, s)) { + // Does not correctly handle 'const x;' -- see bug 592335. + defineVar(t, s); + } + } + // FALL THROUGH + + case BLOCK: + c = n.children; + for (i = 0, j = c.length; i < j; i++) + execute(c[i], x); + break; + + case IMPORT: + case EXPORT: + break; + + case IF: + if (getValue(execute(n.condition, x))) + execute(n.thenPart, x); + else if (n.elsePart) + execute(n.elsePart, x); + break; + + case SWITCH: + s = getValue(execute(n.discriminant, x)); + a = n.cases; + var matchDefault = false; + switch_loop: + for (i = 0, j = a.length; ; i++) { + if (i === j) { + if (n.defaultIndex >= 0) { + i = n.defaultIndex - 1; // no case matched, do default + matchDefault = true; + continue; + } + break; // no default, exit switch_loop + } + t = a[i]; // next case (might be default!) + if (t.type === CASE) { + u = getValue(execute(t.caseLabel, x)); + } else { + if (!matchDefault) // not defaulting, skip for now + continue; + u = s; // force match to do default + } + if (u === s) { + for (;;) { // this loop exits switch_loop + if (t.statements.children.length) { + try { + execute(t.statements, x); + } catch (e if e === BREAK && x.target === n) { + break switch_loop; + } + } + if (++i === j) + break switch_loop; + t = a[i]; + } + // NOT REACHED + } + } + break; + + case FOR: + n.setup && getValue(execute(n.setup, x)); + // FALL THROUGH + case WHILE: + while (!n.condition || getValue(execute(n.condition, x))) { + try { + execute(n.body, x); + } catch (e if e === BREAK && x.target === n) { + break; + } catch (e if e === CONTINUE && x.target === n) { + // Must run the update expression. + } + n.update && getValue(execute(n.update, x)); + } + break; + + case FOR_IN: + u = n.varDecl; + if (u) + execute(u, x); + r = n.iterator; + s = execute(n.object, x); + v = getValue(s); + + // ECMA deviation to track extant browser JS implementation behavior. + t = ((v === null || v === undefined) && !x.ecma3OnlyMode) + ? v + : toObject(v, s, n.object); + a = []; + for (i in t) + a.push(i); + for (i = 0, j = a.length; i < j; i++) { + putValue(execute(r, x), a[i], r); + try { + execute(n.body, x); + } catch (e if e === BREAK && x.target === n) { + break; + } catch (e if e === CONTINUE && x.target === n) { + continue; + } + } + break; + + case DO: + do { + try { + execute(n.body, x); + } catch (e if e === BREAK && x.target === n) { + break; + } catch (e if e === CONTINUE && x.target === n) { + continue; + } + } while (getValue(execute(n.condition, x))); + break; + + case BREAK: + case CONTINUE: + x.target = n.target; + throw n.type; + + case TRY: + try { + execute(n.tryBlock, x); + } catch (e if e === THROW && (j = n.catchClauses.length)) { + e = x.result; + x.result = undefined; + for (i = 0; ; i++) { + if (i === j) { + x.result = e; + throw THROW; + } + t = n.catchClauses[i]; + x.scope = {object: {}, parent: x.scope}; + definitions.defineProperty(x.scope.object, t.varName, e, true); + try { + if (t.guard && !getValue(execute(t.guard, x))) + continue; + execute(t.block, x); + break; + } finally { + x.scope = x.scope.parent; + } + } + } finally { + if (n.finallyBlock) + execute(n.finallyBlock, x); + } + break; + + case THROW: + x.result = getValue(execute(n.exception, x)); + throw THROW; + + case RETURN: + // Check for returns with no return value + x.result = n.value ? getValue(execute(n.value, x)) : undefined; + throw RETURN; + + case WITH: + r = execute(n.object, x); + t = toObject(getValue(r), r, n.object); + x.scope = {object: t, parent: x.scope}; + try { + execute(n.body, x); + } finally { + x.scope = x.scope.parent; + } + break; + + case VAR: + case CONST: + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + u = c[i].initializer; + if (!u) + continue; + t = c[i].name; + for (s = x.scope; s; s = s.parent) { + if (hasDirectProperty(s.object, t)) + break; + } + u = getValue(execute(u, x)); + if (n.type === CONST) + definitions.defineProperty(s.object, t, u, x.type !== EVAL_CODE, true); + else + s.object[t] = u; + } + break; + + case DEBUGGER: + throw "NYI: " + definitions.tokens[n.type]; + + case SEMICOLON: + if (n.expression) + x.result = getValue(execute(n.expression, x)); + break; + + case LABEL: + try { + execute(n.statement, x); + } catch (e if e === BREAK && x.target === n.target) { + } + break; + + case COMMA: + c = n.children; + for (i = 0, j = c.length; i < j; i++) + v = getValue(execute(c[i], x)); + break; + + case ASSIGN: + c = n.children; + r = execute(c[0], x); + t = n.assignOp; + if (t) + u = getValue(r); + v = getValue(execute(c[1], x)); + if (t) { + switch (t) { + case BITWISE_OR: v = u | v; break; + case BITWISE_XOR: v = u ^ v; break; + case BITWISE_AND: v = u & v; break; + case LSH: v = u << v; break; + case RSH: v = u >> v; break; + case URSH: v = u >>> v; break; + case PLUS: v = u + v; break; + case MINUS: v = u - v; break; + case MUL: v = u * v; break; + case DIV: v = u / v; break; + case MOD: v = u % v; break; + } + } + putValue(r, v, c[0]); + break; + + case HOOK: + c = n.children; + v = getValue(execute(c[0], x)) ? getValue(execute(c[1], x)) + : getValue(execute(c[2], x)); + break; + + case OR: + c = n.children; + v = getValue(execute(c[0], x)) || getValue(execute(c[1], x)); + break; + + case AND: + c = n.children; + v = getValue(execute(c[0], x)) && getValue(execute(c[1], x)); + break; + + case BITWISE_OR: + c = n.children; + v = getValue(execute(c[0], x)) | getValue(execute(c[1], x)); + break; + + case BITWISE_XOR: + c = n.children; + v = getValue(execute(c[0], x)) ^ getValue(execute(c[1], x)); + break; + + case BITWISE_AND: + c = n.children; + v = getValue(execute(c[0], x)) & getValue(execute(c[1], x)); + break; + + case EQ: + c = n.children; + v = getValue(execute(c[0], x)) == getValue(execute(c[1], x)); + break; + + case NE: + c = n.children; + v = getValue(execute(c[0], x)) != getValue(execute(c[1], x)); + break; + + case STRICT_EQ: + c = n.children; + v = getValue(execute(c[0], x)) === getValue(execute(c[1], x)); + break; + + case STRICT_NE: + c = n.children; + v = getValue(execute(c[0], x)) !== getValue(execute(c[1], x)); + break; + + case LT: + c = n.children; + v = getValue(execute(c[0], x)) < getValue(execute(c[1], x)); + break; + + case LE: + c = n.children; + v = getValue(execute(c[0], x)) <= getValue(execute(c[1], x)); + break; + + case GE: + c = n.children; + v = getValue(execute(c[0], x)) >= getValue(execute(c[1], x)); + break; + + case GT: + c = n.children; + v = getValue(execute(c[0], x)) > getValue(execute(c[1], x)); + break; + + case IN: + c = n.children; + v = getValue(execute(c[0], x)) in getValue(execute(c[1], x)); + break; + + case INSTANCEOF: + c = n.children; + t = getValue(execute(c[0], x)); + u = getValue(execute(c[1], x)); + if (isObject(u) && typeof u.__hasInstance__ === "function") + v = u.__hasInstance__(t); + else + v = t instanceof u; + break; + + case LSH: + c = n.children; + v = getValue(execute(c[0], x)) << getValue(execute(c[1], x)); + break; + + case RSH: + c = n.children; + v = getValue(execute(c[0], x)) >> getValue(execute(c[1], x)); + break; + + case URSH: + c = n.children; + v = getValue(execute(c[0], x)) >>> getValue(execute(c[1], x)); + break; + + case PLUS: + c = n.children; + v = getValue(execute(c[0], x)) + getValue(execute(c[1], x)); + break; + + case MINUS: + c = n.children; + v = getValue(execute(c[0], x)) - getValue(execute(c[1], x)); + break; + + case MUL: + c = n.children; + v = getValue(execute(c[0], x)) * getValue(execute(c[1], x)); + break; + + case DIV: + c = n.children; + v = getValue(execute(c[0], x)) / getValue(execute(c[1], x)); + break; + + case MOD: + c = n.children; + v = getValue(execute(c[0], x)) % getValue(execute(c[1], x)); + break; + + case DELETE: + t = execute(n.children[0], x); + v = !(t instanceof Reference) || delete t.base[t.propertyName]; + break; + + case VOID: + getValue(execute(n.children[0], x)); + break; + + case TYPEOF: + t = execute(n.children[0], x); + if (t instanceof Reference) + t = t.base ? t.base[t.propertyName] : undefined; + v = typeof t; + break; + + case NOT: + v = !getValue(execute(n.children[0], x)); + break; + + case BITWISE_NOT: + v = ~getValue(execute(n.children[0], x)); + break; + + case UNARY_PLUS: + v = +getValue(execute(n.children[0], x)); + break; + + case UNARY_MINUS: + v = -getValue(execute(n.children[0], x)); + break; + + case INCREMENT: + case DECREMENT: + t = execute(n.children[0], x); + u = Number(getValue(t)); + if (n.postfix) + v = u; + putValue(t, (n.type === INCREMENT) ? ++u : --u, n.children[0]); + if (!n.postfix) + v = u; + break; + + case DOT: + c = n.children; + r = execute(c[0], x); + t = getValue(r); + u = c[1].value; + v = new Reference(toObject(t, r, c[0]), u, n); + break; + + case INDEX: + c = n.children; + r = execute(c[0], x); + t = getValue(r); + u = getValue(execute(c[1], x)); + v = new Reference(toObject(t, r, c[0]), String(u), n); + break; + + case LIST: + // Curse ECMA for specifying that arguments is not an Array object! + v = {}; + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + u = getValue(execute(c[i], x)); + definitions.defineProperty(v, i, u, false, false, true); + } + definitions.defineProperty(v, "length", i, false, false, true); + break; + + case CALL: + c = n.children; + r = execute(c[0], x); + a = execute(c[1], x); + f = getValue(r); + x.staticEnv = n.staticEnv; + if (isPrimitive(f) || typeof f.__call__ !== "function") { + throw new TypeError(r + " is not callable", c[0].filename, c[0].lineno); + } + t = (r instanceof Reference) ? r.base : null; + if (t instanceof Activation) + t = null; + v = f.__call__(t, a, x); + break; + + case NEW: + case NEW_WITH_ARGS: + c = n.children; + r = execute(c[0], x); + f = getValue(r); + if (n.type === NEW) { + a = {}; + definitions.defineProperty(a, "length", 0, false, false, true); + } else { + a = execute(c[1], x); + } + if (isPrimitive(f) || typeof f.__construct__ !== "function") { + throw new TypeError(r + " is not a constructor", c[0].filename, c[0].lineno); + } + v = f.__construct__(a, x); + break; + + case ARRAY_INIT: + v = []; + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + if (c[i]) + v[i] = getValue(execute(c[i], x)); + } + v.length = j; + break; + + case OBJECT_INIT: + v = {}; + c = n.children; + for (i = 0, j = c.length; i < j; i++) { + t = c[i]; + if (t.type === PROPERTY_INIT) { + let c2 = t.children; + v[c2[0].value] = getValue(execute(c2[1], x)); + } else { + f = newFunction(t, x); + u = (t.type === GETTER) ? '__defineGetter__' + : '__defineSetter__'; + v[u](t.name, thunk(f, x)); + } + } + break; + + case NULL: + v = null; + break; + + case THIS: + v = x.thisObject; + break; + + case TRUE: + v = true; + break; + + case FALSE: + v = false; + break; + + case IDENTIFIER: + for (s = x.scope; s; s = s.parent) { + if (n.value in s.object) + break; + } + v = new Reference(s && s.object, n.value, n); + break; + + case NUMBER: + case STRING: + case REGEXP: + v = n.value; + break; + + case GROUP: + v = execute(n.children[0], x); + break; + + default: + throw "PANIC: unknown operation " + n.type + ": " + uneval(n); + } + + return v; + } + + function Activation(f, a) { + for (var i = 0, j = f.params.length; i < j; i++) + definitions.defineProperty(this, f.params[i], a[i], true); + definitions.defineProperty(this, "arguments", a, true); + } + + // Null Activation.prototype's proto slot so that Object.prototype.* does not + // pollute the scope of heavyweight functions. Also delete its 'constructor' + // property so that it doesn't pollute function scopes. + + Activation.prototype.__proto__ = null; + delete Activation.prototype.constructor; + + function FunctionObject(node, scope) { + this.node = node; + this.scope = scope; + definitions.defineProperty(this, "length", node.params.length, true, true, true); + var proto = {}; + definitions.defineProperty(this, "prototype", proto, true); + definitions.defineProperty(proto, "constructor", this, false, false, true); + } + + /* + * ModuleInstance :: (Module, scope) -> ModuleInstance + * + * Dynamic semantic representation of a module. + */ + function ModuleInstance(mod, scope) { + this.module = mod; + this.scope = scope; + } + + /* + * newModule :: (Module, scope) -> module instance object + * + * Instantiates a module node, producing a module instance object. + */ + function newModule(mod, scope) { + var exports = mod.exports; + + // the module instance + mod.instance = new ModuleInstance(mod, {object: new Object, parent: scope}); + + function keys() { + var result = []; + exports.forEach(function(name, exp) { + result.push(name); + }); + return result; + } + + function getExportDescriptor(name) { + if (exports.has(name)) { + var exp = exports.get(name); + var inst = exp.resolved.module.instance; + + return { + value: inst.scope.object[exp.resolved.internalID], + writable: false, + enumerable: true, + configurable: true + }; + } + + throw new ReferenceError("no such export: " + name); + } + + function getExportValue(receiver, name) { + return getExportDescriptor(name).value; + } + + function hasExport(name) { + return exports.has(name); + } + + function refuse() { } + + // the module instance proxy + var instObj = Proxy.create({ + getOwnPropertyDescriptor: getExportDescriptor, + getPropertyDescriptor: getExportDescriptor, + getOwnPropertyNames: keys, + defineProperty: refuse, + "delete": refuse, + fix: refuse, + has: hasExport, + hasOwn: hasExport, + get: getExportValue, + set: refuse, + enumerate: keys, + keys: keys + }); + + // associate the instance with the instance proxy + moduleInstances.set(instObj, mod.instance); + mod.instance.proxy = instObj; + + return instObj; + } + + function instantiateModules(n, scope) { + n.modDefns.forEach(function(name, defn) { + var m = defn.module; + var instObj = newModule(m, scope); + var inst = moduleInstances.get(instObj); + definitions.defineProperty(scope.object, name, instObj, true, true); + instantiateModules(m.node.body, inst.scope); + }); + } + + function getPropertyDescriptor(obj, name) { + while (obj) { + if (({}).hasOwnProperty.call(obj, name)) + return Object.getOwnPropertyDescriptor(obj, name); + obj = Object.getPrototypeOf(obj); + } + } + + function getOwnProperties(obj) { + var map = {}; + for (var name in Object.getOwnPropertyNames(obj)) + map[name] = Object.getOwnPropertyDescriptor(obj, name); + return map; + } + + // Returns a new function wrapped with a Proxy. + function newFunction(n, x) { + var fobj = new FunctionObject(n, x.scope); + var handler = definitions.makePassthruHandler(fobj); + var p = Proxy.createFunction(handler, + function() { return fobj.__call__(this, arguments, x); }, + function() { return fobj.__construct__(arguments, x); }); + return p; + } + + var FOp = FunctionObject.prototype = { + + // Internal methods. + __call__: function (t, a, x) { + var x2 = new ExecutionContext(FUNCTION_CODE, x.version); + x2.thisObject = t || global; + x2.thisModule = null; + x2.caller = x; + x2.callee = this; + definitions.defineProperty(a, "callee", this, false, false, true); + var f = this.node; + x2.scope = {object: new Activation(f, a), parent: this.scope}; + + try { + x2.execute(f.body); + } catch (e if e === RETURN) { + return x2.result; + } + return undefined; + }, + + __construct__: function (a, x) { + var o = new Object; + var p = this.prototype; + if (isObject(p)) + o.__proto__ = p; + // else o.__proto__ defaulted to Object.prototype + + var v = this.__call__(o, a, x); + if (isObject(v)) + return v; + return o; + }, + + __hasInstance__: function (v) { + if (isPrimitive(v)) + return false; + var p = this.prototype; + if (isPrimitive(p)) { + throw new TypeError("'prototype' property is not an object", + this.node.filename, this.node.lineno); + } + var o; + while ((o = v.__proto__)) { + if (o === p) + return true; + v = o; + } + return false; + }, + + // Standard methods. + toString: function () { + return this.node.getSource(); + }, + + apply: function (t, a) { + // Curse ECMA again! + if (typeof this.__call__ !== "function") { + throw new TypeError("Function.prototype.apply called on" + + " uncallable object"); + } + + if (t === undefined || t === null) + t = global; + else if (typeof t !== "object") + t = toObject(t, t); + + if (a === undefined || a === null) { + a = {}; + definitions.defineProperty(a, "length", 0, false, false, true); + } else if (a instanceof Array) { + var v = {}; + for (var i = 0, j = a.length; i < j; i++) + definitions.defineProperty(v, i, a[i], false, false, true); + definitions.defineProperty(v, "length", i, false, false, true); + a = v; + } else if (!(a instanceof Object)) { + // XXX check for a non-arguments object + throw new TypeError("Second argument to Function.prototype.apply" + + " must be an array or arguments object", + this.node.filename, this.node.lineno); + } + + return this.__call__(t, a, ExecutionContext.current); + }, + + call: function (t) { + // Curse ECMA a third time! + var a = Array.prototype.splice.call(arguments, 1); + return this.apply(t, a); + } + }; + + // Connect Function.prototype and Function.prototype.constructor in global. + reflectClass('Function', FOp); + + // Help native and host-scripted functions be like FunctionObjects. + var Fp = Function.prototype; + var REp = RegExp.prototype; + + if (!('__call__' in Fp)) { + definitions.defineProperty(Fp, "__call__", + function (t, a, x) { + // Curse ECMA yet again! + a = Array.prototype.splice.call(a, 0, a.length); + return this.apply(t, a); + }, true, true, true); + definitions.defineProperty(REp, "__call__", + function (t, a, x) { + a = Array.prototype.splice.call(a, 0, a.length); + return this.exec.apply(this, a); + }, true, true, true); + definitions.defineProperty(Fp, "__construct__", + function (a, x) { + a = Array.prototype.splice.call(a, 0, a.length); + switch (a.length) { + case 0: + return new this(); + case 1: + return new this(a[0]); + case 2: + return new this(a[0], a[1]); + case 3: + return new this(a[0], a[1], a[2]); + default: + var argStr = ""; + for (var i=0; i@"); + line = line.replace(/@(.*\/|\\)?([^\/\\]+:[0-9]+)/, " at $2"); + print(" in " + line); + } + } + } + + // A read-eval-print-loop that roughly tracks the behavior of the js shell. + function repl() { + + // Display a value similarly to the js shell. + function display(x) { + if (typeof x === "object") { + // At the js shell, objects with no |toSource| don't print. + if (x !== null && "toSource" in x) { + try { + print(x.toSource()); + } catch (e) { + } + } else { + print("null"); + } + } else if (typeof x === "string") { + print(uneval(x)); + } else if (typeof x !== "undefined") { + // Since x must be primitive, String can't throw. + print(String(x)); + } + } + + // String conversion that never throws. + function string(x) { + try { + return String(x); + } catch (e) { + return "unknown (can't convert to string)"; + } + } + + const BREAK_INTERACTION = {}; + + // isCommand :: (string) -> boolean + function isCommand(line) { + switch (line.trim()) { + case ".help": + print(".begin Begin multiline input mode."); + print(".break Sometimes you get stuck in a place you can't get out... This will get you out."); + print(".clear Break, and also clear the global environment."); + print(".end End multiline input mode."); + print(".exit Exit the prompt."); + print(".help Show repl options."); + return true; + + case ".clear": + resetEnvironment(); + // FALL THROUGH + + case ".break": + throw BREAK_INTERACTION; + + case ".exit": + throw END; + } + return false; + } + + var x = new ExecutionContext(GLOBAL_CODE, Narcissus.options.version); + + // Line number in/out parameter to parser.parseStdin. + var ln = {value: 0}; + + ExecutionContext.current = x; + for (;;) { + x.result = undefined; + putstr("njs> "); + var src = readline(); + + // If readline receives EOF it returns null. + if (src === null) { + print(""); + break; + } + ++ln.value; + + try { + var ast = parser.parseStdin(src, ln, "... ", isCommand); + if (x.version === "harmony") { + resolveGlobal(ast); + instantiateModules(ast, x.scope); + } + execute(ast, x); + display(x.result); + } catch (e if e === THROW) { + print("uncaught exception: " + string(x.result)); + } catch (e if e === END) { + break; + } catch (e if e === BREAK_INTERACTION) { + continue; + } catch (e if e instanceof SyntaxError) { + const PREFIX = (e.filename || "stdin") + ":" + e.lineNumber + ": "; + print(PREFIX + e.toString()); + print(PREFIX + e.source); + print(PREFIX + ".".repeat(e.cursor) + "^"); + } catch (e if e instanceof Error) { + print((e.filename || "stdin") + ":" + e.lineNumber + ": " + e.toString()); + if (e.stack) + printStackTrace(e.stack); + } catch (e) { + print("unexpected Narcissus exception (" + e + ")"); + throw e; + } + } + ExecutionContext.current = null; + } + + function test(thunk) { + try { + thunk(); + } catch (e) { + print(e.fileName + ":" + e.lineNumber + ": " + e.name + ": " + e.message); + printStackTrace(e.stack); + return false; + } + return true; + } + + return { + // resetEnvironment wipes any properties added externally to global, + // but properties added to globalBase will persist. + global: global, + globalBase: globalBase, + resetEnvironment: resetEnvironment, + evaluate: evaluate, + repl: repl, + test: test + }; + +}()); diff --git a/node_modules/narcissus/lib/jslex.js b/node_modules/narcissus/lib/jslex.js new file mode 100644 index 000000000..ae7235874 --- /dev/null +++ b/node_modules/narcissus/lib/jslex.js @@ -0,0 +1,508 @@ +/* vim: set sw=4 ts=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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Lexical scanner. + */ + +Narcissus.lexer = (function() { + + var definitions = Narcissus.definitions; + + // Set constants in the local scope. + eval(definitions.consts); + + // Build up a trie of operator tokens. + var opTokens = {}; + for (var op in definitions.opTypeNames) { + if (op === '\n' || op === '.') + continue; + + var node = opTokens; + for (var i = 0; i < op.length; i++) { + var ch = op[i]; + if (!(ch in node)) + node[ch] = {}; + node = node[ch]; + node.op = op; + } + } + + /* + * Tokenizer :: (source, filename, line number) -> Tokenizer + */ + function Tokenizer(s, f, l) { + this.cursor = 0; + this.source = String(s); + this.tokens = []; + this.tokenIndex = 0; + this.lookahead = 0; + this.scanNewlines = false; + this.unexpectedEOF = false; + this.filename = f || ""; + this.lineno = l || 1; + this.jsdocs = []; + } + + Tokenizer.prototype = { + get done() { + // We need to set scanOperand to true here because the first thing + // might be a regexp. + return this.peek(true) === END; + }, + + get token() { + return this.tokens[this.tokenIndex]; + }, + + match: function (tt, scanOperand) { + return this.get(scanOperand) === tt || this.unget(); + }, + + mustMatch: function (tt) { + if (!this.match(tt)) { + throw this.newSyntaxError("Missing " + + definitions.tokens[tt].toLowerCase()); + } + return this.token; + }, + + peek: function (scanOperand) { + var tt, next; + if (this.lookahead) { + next = this.tokens[(this.tokenIndex + this.lookahead) & 3]; + tt = (this.scanNewlines && next.lineno !== this.lineno) + ? NEWLINE + : next.type; + } else { + tt = this.get(scanOperand); + this.unget(); + } + return tt; + }, + + peekOnSameLine: function (scanOperand) { + this.scanNewlines = true; + var tt = this.peek(scanOperand); + this.scanNewlines = false; + return tt; + }, + + // Eat comments and whitespace. + skip: function () { + var input = this.source, + jsdocComment = null; + for (;;) { + var ch = input[this.cursor++]; + var next = input[this.cursor]; + // handle \r, \r\n and (always preferable) \n + if (ch === '\r') { + // if the next character is \n, we don't care about this at all + if (next === '\n') continue; + + // otherwise, we want to consider this as a newline + ch = '\n'; + } + + if (ch === '\n' && !this.scanNewlines) { + this.lineno++; + } else if (ch === '/' && next === '*') { + this.cursor++; + if (input[this.cursor] === '*') { // like /** + this.cursor++; + jsdocComment = ''; + } + for (;;) { + ch = input[this.cursor++]; + if (ch === undefined) + throw this.newSyntaxError("Unterminated comment"); + + if (ch === '*') { + next = input[this.cursor]; + if (next === '/') { + if (jsdocComment) this.jsdocs.push(jsdocComment); + jsdocComment = null; + + this.cursor++; + if (typeof input[this.cursor+1] === 'undefined') { // eof + if (typeof this.onJsDoc === 'function') { + while(this.jsdocs.length) { + this.onJsDoc(this.jsdocs.shift()); + } + } + } + break; + } + } else if (ch === '\n') { + this.lineno++; + } + if (typeof jsdocComment === 'string') { jsdocComment += ch; } + } + } else if (ch === '/' && next === '/') { + this.cursor++; + for (;;) { + ch = input[this.cursor++]; + next = input[this.cursor]; + if (ch === undefined) + return; + + if (ch === '\r') { + // check for \r\n + if (next !== '\n') ch = '\n'; + } + + if (ch === '\n') { + if (this.scanNewlines) { + this.cursor--; + } else { + this.lineno++; + } + break; + } + } + } else if (!(ch in definitions.whitespace)) { + this.cursor--; + return; + } + } + }, + + // Lex the exponential part of a number, if present. Return true iff an + // exponential part was found. + lexExponent: function() { + var input = this.source; + var next = input[this.cursor]; + if (next === 'e' || next === 'E') { + this.cursor++; + ch = input[this.cursor++]; + if (ch === '+' || ch === '-') + ch = input[this.cursor++]; + + if (ch < '0' || ch > '9') + throw this.newSyntaxError("Missing exponent"); + + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '9'); + this.cursor--; + + return true; + } + + return false; + }, + + lexZeroNumber: function (ch) { + var token = this.token, input = this.source; + token.type = NUMBER; + + ch = input[this.cursor++]; + if (ch === '.') { + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '9'); + this.cursor--; + + this.lexExponent(); + token.value = parseFloat(token.start, this.cursor); + } else if (ch === 'x' || ch === 'X') { + do { + ch = input[this.cursor++]; + } while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F')); + this.cursor--; + + token.value = parseInt(input.substring(token.start, this.cursor)); + } else if (ch >= '0' && ch <= '7') { + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '7'); + this.cursor--; + + token.value = parseInt(input.substring(token.start, this.cursor)); + } else { + this.cursor--; + this.lexExponent(); // 0E1, &c. + token.value = 0; + } + }, + + lexNumber: function (ch) { + var token = this.token, input = this.source; + token.type = NUMBER; + + var floating = false; + do { + ch = input[this.cursor++]; + if (ch === '.' && !floating) { + floating = true; + ch = input[this.cursor++]; + } + } while (ch >= '0' && ch <= '9'); + + this.cursor--; + + var exponent = this.lexExponent(); + floating = floating || exponent; + + var str = input.substring(token.start, this.cursor); + token.value = floating ? parseFloat(str) : parseInt(str); + }, + + lexDot: function (ch) { + var token = this.token, input = this.source; + var next = input[this.cursor]; + if (next >= '0' && next <= '9') { + do { + ch = input[this.cursor++]; + } while (ch >= '0' && ch <= '9'); + this.cursor--; + + this.lexExponent(); + + token.type = NUMBER; + token.value = parseFloat(token.start, this.cursor); + } else { + token.type = DOT; + token.assignOp = null; + token.value = '.'; + } + }, + + lexString: function (ch) { + var token = this.token, input = this.source; + token.type = STRING; + + var hasEscapes = false; + var delim = ch; + while ((ch = input[this.cursor++]) !== delim) { + if (this.cursor == input.length) + throw this.newSyntaxError("Unterminated string literal"); + if (ch === '\\') { + hasEscapes = true; + if (++this.cursor == input.length) + throw this.newSyntaxError("Unterminated string literal"); + } + } + + token.value = hasEscapes + ? eval(input.substring(token.start, this.cursor)) + : input.substring(token.start + 1, this.cursor - 1); + }, + + lexRegExp: function (ch) { + var token = this.token, input = this.source; + token.type = REGEXP; + + do { + ch = input[this.cursor++]; + if (ch === '\\') { + this.cursor++; + } else if (ch === '[') { + do { + if (ch === undefined) + throw this.newSyntaxError("Unterminated character class"); + + if (ch === '\\') + this.cursor++; + + ch = input[this.cursor++]; + } while (ch !== ']'); + } else if (ch === undefined) { + throw this.newSyntaxError("Unterminated regex"); + } + } while (ch !== '/'); + + do { + ch = input[this.cursor++]; + } while (ch >= 'a' && ch <= 'z'); + + this.cursor--; + + token.value = eval(input.substring(token.start, this.cursor)); + }, + + lexOp: function (ch) { + var token = this.token, input = this.source; + + // A bit ugly, but it seems wasteful to write a trie lookup routine + // for only 3 characters... + var node = opTokens[ch]; + var next = input[this.cursor]; + if (next in node) { + node = node[next]; + this.cursor++; + next = input[this.cursor]; + if (next in node) { + node = node[next]; + this.cursor++; + next = input[this.cursor]; + } + } + + var op = node.op; + if (definitions.assignOps[op] && input[this.cursor] === '=') { + this.cursor++; + token.type = ASSIGN; + token.assignOp = definitions.tokenIds[definitions.opTypeNames[op]]; + op += '='; + } else { + token.type = definitions.tokenIds[definitions.opTypeNames[op]]; + token.assignOp = null; + } + + token.value = op; + }, + + // FIXME: Unicode escape sequences + // FIXME: Unicode identifiers + lexIdent: function (ch) { + var token = this.token, input = this.source; + + do { + ch = input[this.cursor++]; + } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || ch === '$' || ch === '_'); + + this.cursor--; // Put the non-word character back. + + var id = input.substring(token.start, this.cursor); + token.type = definitions.keywords[id] || IDENTIFIER; + token.value = id; + }, + + /* + * Tokenizer.get :: void -> token type + * + * Consume input *only* if there is no lookahead. + * Dispatch to the appropriate lexing function depending on the input. + */ + get: function (scanOperand) { + var token; + while (this.lookahead) { + --this.lookahead; + this.tokenIndex = (this.tokenIndex + 1) & 3; + token = this.tokens[this.tokenIndex]; + if (token.type !== NEWLINE || this.scanNewlines) + return token.type; + } + + this.skip(); + + this.tokenIndex = (this.tokenIndex + 1) & 3; + token = this.tokens[this.tokenIndex]; + if (!token) + this.tokens[this.tokenIndex] = token = {}; + if (this.jsdocs && this.jsdocs.length) { + while (this.jsdocs.length > 1) { // orphaned jsdocComments have no code token + if (typeof this.onJsDoc === 'function' && this.jsdocs.length) { + this.onJsDoc(this.jsdocs.shift()); + } + else { // discard? + this.jsdocs.shift(); + } + } + token.jsdoc = this.jsdocs.pop(); + } + + var input = this.source; + if (this.cursor >= input.length) + return token.type = END; + + token.start = this.cursor; + token.lineno = this.lineno; + + var ch = input[this.cursor++]; + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '$' || ch === '_') { + this.lexIdent(ch); + } else if (scanOperand && ch === '/') { + this.lexRegExp(ch); + } else if (ch in opTokens) { + this.lexOp(ch); + } else if (ch === '.') { + this.lexDot(ch); + } else if (ch >= '1' && ch <= '9') { + this.lexNumber(ch); + } else if (ch === '0') { + this.lexZeroNumber(ch); + } else if (ch === '"' || ch === "'") { + this.lexString(ch); + } else if (this.scanNewlines && (ch === '\n' || ch === '\r')) { + // if this was a \r, look for \r\n + if (ch === '\r' && input[this.cursor] === '\n') this.cursor++; + token.type = NEWLINE; + token.value = '\n'; + this.lineno++; + } else { + throw this.newSyntaxError("Illegal token"); + } + + token.end = this.cursor; + return token.type; + }, + + /* + * Tokenizer.unget :: void -> undefined + * + * Match depends on unget returning undefined. + */ + unget: function () { + if (++this.lookahead === 4) throw "PANIC: too much lookahead!"; + this.tokenIndex = (this.tokenIndex - 1) & 3; + }, + + newSyntaxError: function (m) { + var e = new SyntaxError(m, this.filename, this.lineno); + e.source = this.source; + e.cursor = this.lookahead + ? this.tokens[(this.tokenIndex + this.lookahead) & 3].start + : this.cursor; + return e; + }, + }; + + return { Tokenizer: Tokenizer }; + +}()); diff --git a/node_modules/narcissus/lib/jsparse.js b/node_modules/narcissus/lib/jsparse.js new file mode 100644 index 000000000..5961b7200 --- /dev/null +++ b/node_modules/narcissus/lib/jsparse.js @@ -0,0 +1,1813 @@ +/* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; -*- + * vim: set sw=4 ts=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 the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Tom Austin + * Brendan Eich + * Shu-Yu Guo + * Dave Herman + * Dimitris Vardoulakis + * Patrick Walton + * + * Alternatively, the contents of this file may be used under the terms of + * either 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 ***** */ + +/* + * Narcissus - JS implemented in JS. + * + * Parser. + */ + +Narcissus.parser = (function() { + + var lexer = Narcissus.lexer; + var definitions = Narcissus.definitions; + + const StringMap = definitions.StringMap; + const Stack = definitions.Stack; + + // Set constants in the local scope. + eval(definitions.consts); + + // Banned statement types by language version. + const blackLists = { 185: {}, harmony: {} }; + blackLists[185][IMPORT] = true; + blackLists[185][EXPORT] = true; + blackLists[185][MODULE] = true; + blackLists.harmony[WITH] = true; + + /* + * pushDestructuringVarDecls :: (node, hoisting node) -> void + * + * Recursively add all destructured declarations to varDecls. + */ + function pushDestructuringVarDecls(n, s) { + for (var i in n) { + var sub = n[i]; + if (sub.type === IDENTIFIER) { + s.varDecls.push(sub); + } else { + pushDestructuringVarDecls(sub, s); + } + } + } + + function StaticContext(parentScript, parentBlock, inModule, inFunction) { + this.parentScript = parentScript; + this.parentBlock = parentBlock || parentScript; + this.inModule = inModule || false; + this.inFunction = inFunction || false; + this.inForLoopInit = false; + this.topLevel = true; + this.allLabels = new Stack(); + this.currentLabels = new Stack(); + this.labeledTargets = new Stack(); + this.defaultLoopTarget = null; + this.defaultTarget = null; + this.blackList = blackLists[Narcissus.options.version]; + Narcissus.options.ecma3OnlyMode && (this.ecma3OnlyMode = true); + Narcissus.options.parenFreeMode && (this.parenFreeMode = true); + } + + StaticContext.prototype = { + ecma3OnlyMode: false, + parenFreeMode: false, + // non-destructive update via prototype extension + update: function(ext) { + var desc = {}; + for (var key in ext) { + desc[key] = { + value: ext[key], + writable: true, + enumerable: true, + configurable: true + } + } + return Object.create(this, desc); + }, + pushLabel: function(label) { + return this.update({ currentLabels: this.currentLabels.push(label), + allLabels: this.allLabels.push(label) }); + }, + pushTarget: function(target) { + var isDefaultLoopTarget = target.isLoop; + var isDefaultTarget = isDefaultLoopTarget || target.type === SWITCH; + + if (this.currentLabels.isEmpty()) { + if (isDefaultLoopTarget) this.update({ defaultLoopTarget: target }); + if (isDefaultTarget) this.update({ defaultTarget: target }); + return this; + } + + target.labels = new StringMap(); + this.currentLabels.forEach(function(label) { + target.labels.set(label, true); + }); + return this.update({ currentLabels: new Stack(), + labeledTargets: this.labeledTargets.push(target), + defaultLoopTarget: isDefaultLoopTarget + ? target + : this.defaultLoopTarget, + defaultTarget: isDefaultTarget + ? target + : this.defaultTarget }); + }, + nest: function() { + return this.topLevel ? this.update({ topLevel: false }) : this; + }, + allow: function(type) { + switch (type) { + case EXPORT: + if (!this.inModule || this.inFunction || !this.topLevel) + return false; + // FALL THROUGH + + case IMPORT: + return !this.inFunction && this.topLevel; + + case MODULE: + return !this.inFunction && this.topLevel; + + default: + return true; + } + } + }; + + /* + * Script :: (tokenizer, boolean, boolean) -> node + * + * Parses the toplevel and module/function bodies. + */ + function Script(t, inModule, inFunction) { + var n = new Node(t, scriptInit()); + Statements(t, new StaticContext(n, n, inModule, inFunction), n); + return n; + } + + // We extend Array slightly with a top-of-stack method. + definitions.defineProperty(Array.prototype, "top", + function() { + return this.length && this[this.length-1]; + }, false, false, true); + + /* + * Node :: (tokenizer, optional init object) -> node + */ + function Node(t, init) { + var token = t.token; + if (token) { + // If init.type exists it will override token.type. + this.type = token.type; + this.value = token.value; + this.lineno = token.lineno; + if (token.jsdoc) { + this.jsdoc = token.jsdoc; + delete token.jsdoc; + } + + // Start and end are file positions for error handling. + this.start = token.start; + this.end = token.end; + } else { + this.lineno = t.lineno; + } + + // Node uses a tokenizer for debugging (getSource, filename getter). + this.tokenizer = t; + this.children = []; + + for (var prop in init) + this[prop] = init[prop]; + } + + var Np = Node.prototype = {}; + Np.constructor = Node; + Np.toSource = Object.prototype.toSource; + + // Always use push to add operands to an expression, to update start and end. + Np.push = function (kid) { + // kid can be null e.g. [1, , 2]. + if (kid !== null) { + if (kid.start < this.start) + this.start = kid.start; + if (this.end < kid.end) + this.end = kid.end; + } + return this.children.push(kid); + } + + Node.indentLevel = 0; + + function tokenString(tt) { + var t = definitions.tokens[tt]; + return /^\W/.test(t) ? definitions.opTypeNames[t] : t.toUpperCase(); + } + + Np.toString = function () { + var a = []; + for (var i in this) { + if (this.hasOwnProperty(i) && i !== 'type' && i !== 'target') + a.push({id: i, value: this[i]}); + } + a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; }); + const INDENTATION = " "; + var n = ++Node.indentLevel; + var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenString(this.type); + for (i = 0; i < a.length; i++) + s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value; + n = --Node.indentLevel; + s += "\n" + INDENTATION.repeat(n) + "}"; + return s; + } + + Np.getSource = function () { + return this.tokenizer.source.slice(this.start, this.end); + }; + + /* + * Helper init objects for common nodes. + */ + + const LOOP_INIT = { isLoop: true }; + + function blockInit() { + return { type: BLOCK, varDecls: [] }; + } + + function scriptInit() { + return { type: SCRIPT, + funDecls: [], + varDecls: [], + modDefns: new StringMap(), + modAssns: new StringMap(), + modDecls: new StringMap(), + modLoads: new StringMap(), + impDecls: [], + expDecls: [], + exports: new StringMap(), + hasEmptyReturn: false, + hasReturnWithValue: false, + isGenerator: false }; + } + + definitions.defineGetter(Np, "filename", + function() { + return this.tokenizer.filename; + }); + + definitions.defineGetter(Np, "length", + function() { + throw new Error("Node.prototype.length is gone; " + + "use n.children.length instead"); + }); + + definitions.defineProperty(String.prototype, "repeat", + function(n) { + var s = "", t = this + s; + while (--n >= 0) + s += t; + return s; + }, false, false, true); + + function MaybeLeftParen(t, x) { + if (x.parenFreeMode) + return t.match(LEFT_PAREN) ? LEFT_PAREN : END; + return t.mustMatch(LEFT_PAREN).type; + } + + function MaybeRightParen(t, p) { + if (p === LEFT_PAREN) + t.mustMatch(RIGHT_PAREN); + } + + /* + * Statements :: (tokenizer, compiler context, node) -> void + * + * Parses a sequence of Statements. + */ + function Statements(t, x, n) { + try { + while (!t.done && t.peek(true) !== RIGHT_CURLY) + n.push(Statement(t, x)); + } catch (e) { + if (t.done) + t.unexpectedEOF = true; + throw e; + } + } + + function Block(t, x) { + t.mustMatch(LEFT_CURLY); + var n = new Node(t, blockInit()); + Statements(t, x.update({ parentBlock: n }).pushTarget(n), n); + t.mustMatch(RIGHT_CURLY); + return n; + } + + const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2; + + /* + * Export :: (binding node, boolean) -> Export + * + * Static semantic representation of a module export. + */ + function Export(node, isDefinition) { + this.node = node; // the AST node declaring this individual export + this.isDefinition = isDefinition; // is the node an 'export'-annotated definition? + this.resolved = null; // resolved pointer to the target of this export + } + + /* + * registerExport :: (StringMap, EXPORT node) -> void + */ + function registerExport(exports, decl) { + function register(name, exp) { + if (exports.has(name)) + throw new SyntaxError("multiple exports of " + name); + exports.set(name, exp); + } + + switch (decl.type) { + case MODULE: + case FUNCTION: + register(decl.name, new Export(decl, true)); + break; + + case VAR: + for (var i = 0; i < decl.children.length; i++) + register(decl.children[i].name, new Export(decl.children[i], true)); + break; + + case LET: + case CONST: + throw new Error("NYI: " + definitions.tokens[decl.type]); + + case EXPORT: + for (var i = 0; i < decl.pathList.length; i++) { + var path = decl.pathList[i]; + switch (path.type) { + case OBJECT_INIT: + for (var j = 0; j < path.children.length; j++) { + // init :: IDENTIFIER | PROPERTY_INIT + var init = path.children[j]; + if (init.type === IDENTIFIER) + register(init.value, new Export(init, false)); + else + register(init.children[0].value, new Export(init.children[1], false)); + } + break; + + case DOT: + register(path.children[1].value, new Export(path, false)); + break; + + case IDENTIFIER: + register(path.value, new Export(path, false)); + break; + + default: + throw new Error("unexpected export path: " + definitions.tokens[path.type]); + } + } + break; + + default: + throw new Error("unexpected export decl: " + definitions.tokens[exp.type]); + } + } + + /* + * Module :: (node) -> Module + * + * Static semantic representation of a module. + */ + function Module(node) { + var exports = node.body.exports; + var modDefns = node.body.modDefns; + + var exportedModules = new StringMap(); + + exports.forEach(function(name, exp) { + var node = exp.node; + if (node.type === MODULE) { + exportedModules.set(name, node); + } else if (!exp.isDefinition && node.type === IDENTIFIER && modDefns.has(node.value)) { + var mod = modDefns.get(node.value); + exportedModules.set(name, mod); + } + }); + + this.node = node; + this.exports = exports; + this.exportedModules = exportedModules; + } + + /* + * Statement :: (tokenizer, compiler context) -> node + * + * Parses a Statement. + */ + function Statement(t, x) { + var i, label, n, n2, p, c, ss, tt = t.get(true), tt2, x2, x3; + + if (x.blackList[tt]) + throw t.newSyntaxError(definitions.tokens[tt] + " statements only allowed in Harmony"); + if (!x.allow(tt)) + throw t.newSyntaxError(definitions.tokens[tt] + " statement in illegal context"); + + // Cases for statements ending in a right curly return early, avoiding the + // common semicolon insertion magic after this switch. + switch (tt) { + case IMPORT: + n = new Node(t); + n.pathList = ImportPathList(t, x); + x.parentScript.impDecls.push(n); + break; + + case EXPORT: + switch (t.peek()) { + case MODULE: + case FUNCTION: + case LET: + case VAR: + case CONST: + n = Statement(t, x); + n.exported = true; + x.parentScript.expDecls.push(n); + registerExport(x.parentScript.exports, n); + return n; + + default: + n = new Node(t); + n.pathList = ExportPathList(t, x); + break; + } + x.parentScript.expDecls.push(n); + registerExport(x.parentScript.exports, n); + break; + + case MODULE: + n = new Node(t); + t.mustMatch(IDENTIFIER); + label = t.token.value; + + if (t.match(LEFT_CURLY)) { + n.name = label; + n.body = Script(t, true, false); + n.module = new Module(n); + t.mustMatch(RIGHT_CURLY); + x.parentScript.modDefns.set(n.name, n); + return n; + } + + t.unget(); + ModuleVariables(t, x, n); + return n; + + case FUNCTION: + // DECLARED_FORM extends funDecls of x, STATEMENT_FORM doesn't. + return FunctionDefinition(t, x, true, x.topLevel ? DECLARED_FORM : STATEMENT_FORM); + + case LEFT_CURLY: + n = new Node(t, blockInit()); + Statements(t, x.update({ parentBlock: n }).pushTarget(n).nest(), n); + t.mustMatch(RIGHT_CURLY); + return n; + + case IF: + n = new Node(t); + n.condition = HeadExpression(t, x); + x2 = x.pushTarget(n).nest(); + n.thenPart = Statement(t, x2); + n.elsePart = t.match(ELSE, true) ? Statement(t, x2) : null; + return n; + + case SWITCH: + // This allows CASEs after a DEFAULT, which is in the standard. + n = new Node(t, { cases: [], defaultIndex: -1 }); + n.discriminant = HeadExpression(t, x); + x2 = x.pushTarget(n).nest(); + t.mustMatch(LEFT_CURLY); + while ((tt = t.get()) !== RIGHT_CURLY) { + switch (tt) { + case DEFAULT: + if (n.defaultIndex >= 0) + throw t.newSyntaxError("More than one switch default"); + // FALL THROUGH + case CASE: + n2 = new Node(t); + if (tt === DEFAULT) + n.defaultIndex = n.cases.length; + else + n2.caseLabel = Expression(t, x2, COLON); + break; + + default: + throw t.newSyntaxError("Invalid switch case"); + } + t.mustMatch(COLON); + n2.statements = new Node(t, blockInit()); + while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT && + tt !== RIGHT_CURLY) + n2.statements.push(Statement(t, x2)); + n.cases.push(n2); + } + return n; + + case FOR: + n = new Node(t, LOOP_INIT); + if (t.match(IDENTIFIER)) { + if (t.token.value === "each") + n.isEach = true; + else + t.unget(); + } + if (!x.parenFreeMode) + t.mustMatch(LEFT_PAREN); + x2 = x.pushTarget(n).nest(); + x3 = x.update({ inForLoopInit: true }); + n2 = null; + if ((tt = t.peek(true)) !== SEMICOLON) { + if (tt === VAR || tt === CONST) { + t.get(); + n2 = Variables(t, x3); + } else if (tt === LET) { + t.get(); + if (t.peek() === LEFT_PAREN) { + n2 = LetBlock(t, x3, false); + } else { + // Let in for head, we need to add an implicit block + // around the rest of the for. + x3.parentBlock = n; + n.varDecls = []; + n2 = Variables(t, x3); + } + } else { + n2 = Expression(t, x3); + } + } + if (n2 && t.match(IN)) { + n.type = FOR_IN; + n.object = Expression(t, x3); + if (n2.type === VAR || n2.type === LET) { + c = n2.children; + + // Destructuring turns one decl into multiples, so either + // there must be only one destructuring or only one + // decl. + if (c.length !== 1 && n2.destructurings.length !== 1) { + throw new SyntaxError("Invalid for..in left-hand side", + t.filename, n2.lineno); + } + if (n2.destructurings.length > 0) { + n.iterator = n2.destructurings[0]; + } else { + n.iterator = c[0]; + } + n.varDecl = n2; + } else { + if (n2.type === ARRAY_INIT || n2.type === OBJECT_INIT) { + n2.destructuredNames = checkDestructuring(t, x3, n2); + } + n.iterator = n2; + } + } else { + x3.inForLoopInit = false; + n.setup = n2; + t.mustMatch(SEMICOLON); + if (n.isEach) + throw t.newSyntaxError("Invalid for each..in loop"); + n.condition = (t.peek(true) === SEMICOLON) + ? null + : Expression(t, x3); + t.mustMatch(SEMICOLON); + tt2 = t.peek(true); + n.update = (x.parenFreeMode + ? tt2 === LEFT_CURLY || definitions.isStatementStartCode[tt2] + : tt2 === RIGHT_PAREN) + ? null + : Expression(t, x3); + } + if (!x.parenFreeMode) + t.mustMatch(RIGHT_PAREN); + n.body = Statement(t, x2); + return n; + + case WHILE: + n = new Node(t, { isLoop: true }); + n.condition = HeadExpression(t, x); + n.body = Statement(t, x.pushTarget(n).nest()); + return n; + + case DO: + n = new Node(t, { isLoop: true }); + n.body = Statement(t, x.pushTarget(n).nest()); + t.mustMatch(WHILE); + n.condition = HeadExpression(t, x); + if (!x.ecmaStrictMode) { + // + + + + +

    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 completed = []; + + 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, function (fn) { + fn(); + }); + }; + + addListener(function () { + if (completed.length === keys.length) { + callback(null); + } + }); + + _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 { + completed.push(k); + taskComplete(); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && _indexOf(completed, x) !== -1); + }, true); + }; + if (ready()) { + task[task.length - 1](taskCallback); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback); + } + }; + addListener(listener); + } + }); + }; + + async.waterfall = function (tasks, callback) { + if (!tasks.length) { + return callback(); + } + callback = callback || function () {}; + 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 || null); + }); + } + }, 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 || null); + }); + } + }, 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 tasks = []; + var q = { + concurrency: concurrency, + push: function (data, callback) { + tasks.push({data: data, callback: callback}); + async.nextTick(q.process); + }, + process: function () { + if (workers < q.concurrency && tasks.length) { + var task = tasks.splice(0, 1)[0]; + workers += 1; + worker(task.data, function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + q.process(); + }); + } + }, + length: function () { + return tasks.length; + } + }; + 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 = {}; + hasher = hasher || function (x) { + return x; + }; + return 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 { + fn.apply(null, args.concat([function () { + memo[key] = arguments; + callback.apply(null, arguments); + }])); + } + }; + }; + +}()); diff --git a/node_modules/nodeunit/deps/ejs.js b/node_modules/nodeunit/deps/ejs.js new file mode 100644 index 000000000..f6abf29f6 --- /dev/null +++ b/node_modules/nodeunit/deps/ejs.js @@ -0,0 +1,125 @@ + +/*! + * EJS + * Copyright(c) 2010 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var sys = require('sys'); + +/** + * Library version. + */ + +exports.version = '0.0.3'; + +/** + * Intermediate js cache. + * + * @type Object + */ + +var cache = {}; + +/** + * Clear intermediate js cache. + * + * @api public + */ + +exports.clearCache = function(){ + cache = {}; +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +function escape(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +/** + * Parse the given `str` of ejs, returning the function body. + * + * @param {String} str + * @return {String} + * @api public + */ + +var parse = exports.parse = function(str){ + return 'var buf = [];\n' + + "with (locals) {\nbuf.push('" + + String(str) + .replace(/[\r\t]/g, " ") + .replace(/\n/g, "\\n") + .split("<%").join("\t") + .replace(/((^|%>)[^\t]*)'/g, "$1\r") + .replace(/\t=(.*?)%>/g, "', escape($1) ,'") + .replace(/\t-(.*?)%>/g, "', $1 ,'") + .split("\t").join("');") + .split("%>").join("buf.push('") + .split("\r").join("\\'") + + "');\n}\nreturn buf.join('');"; +}; + +/** + * Compile the given `str` of ejs into a `Function`. + * + * @param {String} str + * @param {Object} options + * @return {Function} + * @api public + */ + +var compile = exports.compile = function(str, options){ + if (options.debug) sys.puts(parse(str)); + return new Function('locals, escape', parse(str)); +}; + +/** + * Render the given `str` of ejs. + * + * Options: + * + * - `locals` Local variables object + * - `cache` Compiled functions are cached, requires `filename` + * - `filename` Used by `cache` to key caches + * - `context|scope` Function execution context + * - `debug` Output generated function body + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api public + */ + +exports.render = function(str, options){ + var fn, + options = options || {}; + if (options.cache) { + if (options.filename) { + fn = cache[options.filename] = compile(str, options); + } else { + throw new Error('"cache" option requires "filename".'); + } + } else { + fn = compile(str, options); + } + return fn.call( + options.context || options.scope, + options.locals || {}, + escape); +}; \ No newline at end of file diff --git a/node_modules/nodeunit/deps/json2.js b/node_modules/nodeunit/deps/json2.js new file mode 100644 index 000000000..22b44d961 --- /dev/null +++ b/node_modules/nodeunit/deps/json2.js @@ -0,0 +1,483 @@ +/* + http://www.JSON.org/json2.js + 2010-11-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + 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 && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + 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.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.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'); + }; + } +}()); diff --git a/node_modules/nodeunit/doc/nodeunit.md b/node_modules/nodeunit/doc/nodeunit.md new file mode 100644 index 000000000..efde75cde --- /dev/null +++ b/node_modules/nodeunit/doc/nodeunit.md @@ -0,0 +1,60 @@ +nodeunit(1) -- simple node.js unit testing tool +=============================================== + +## SYNOPSIS + + nodeunit [options] [ ...] + +## DESCRIPTION + +Nodeunit is a simple unit testing tool based on the node.js assert module. + +* Simple to use +* Just export the tests from a module +* Helps you avoid common pitfalls when testing asynchronous code +* Easy to add test cases with setUp and tearDown functions if you wish +* Allows the use of mocks and stubs + +## OPTIONS + + __--config FILE__: + Load config options from a JSON file, allows the customisation + of color schemes for the default test reporter etc. + See bin/nodeunit.json for current available options. + + __--reporter FILE__: + You can set the test reporter to a custom module or on of the modules + in nodeunit/lib/reporters, when omitted, the default test runner is used. + + __--list-reporters__: + List available build-in reporters. + + __-h__, __--help__: + Display the help and exit. + + __-v__, __--version__: + Output version information and exit. + + ____: + You can run nodeunit on specific files or on all *\*.js* files inside + a directory. + +## AUTHORS + +Written by Caolan McMahon and other nodeunit contributors. +Contributors list: . + +## REPORTING BUGS + +Report nodeunit bugs to . + +## COPYRIGHT + +Copyright © 2010 Caolan McMahon. +Nodeunit has been released under the MIT license: +. + +## SEE ALSO + +node(1) + diff --git a/node_modules/nodeunit/examples/browser/nodeunit.js b/node_modules/nodeunit/examples/browser/nodeunit.js new file mode 100644 index 000000000..8c12b0f88 --- /dev/null +++ b/node_modules/nodeunit/examples/browser/nodeunit.js @@ -0,0 +1,1757 @@ +/*! + * Nodeunit + * https://github.com/caolan/nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * json2.js + * http://www.JSON.org/json2.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ +nodeunit = (function(){ +/* + http://www.JSON.org/json2.js + 2010-11-17 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false, regexp: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + "use strict"; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + 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 && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + 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.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.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'); + }; + } +}()); +var assert = {}; +var types = {}; +var core = {}; +var nodeunit = {}; +var reporter = {}; +(function(){ + + var async = {}; + + // global on the server, window in the browser + var root = this; + var 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 b ? 1 : 0; + }), function(x){return x.value;})); + }) + }; + + async.auto = function(tasks, callback){ + callback = callback || function(){}; + var keys = _keys(tasks); + if(!keys.length) return callback(null); + + var completed = []; + + var listeners = []; + var addListener = function(fn){ + listeners.unshift(fn); + }; + var removeListener = function(fn){ + for(var i=0; i +// +// 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 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. + + +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({message: message, actual: actual, expected: expected}) + +assert.AssertionError = function AssertionError (options) { + this.name = "AssertionError"; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +// code from util.inherits in node +assert.AssertionError.super_ = Error; + + +// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call +// TODO: test what effect this may have +var ctor = function () { this.constructor = assert.AssertionError; }; +ctor.prototype = Error.prototype; +assert.AssertionError.prototype = new ctor(); + + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name+":", this.message].join(' '); + } else { + return [ this.name+":" + , JSON.stringify(this.expected ) + , this.operator + , JSON.stringify(this.actual) + ].join(" "); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, "==", assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, "==", assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + } else if (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 _deepEqual(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 (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as determined by !==. +// assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } +}; + +function _throws (shouldThrow, block, err, message) { + var exception = null, + threw = false, + typematters = true; + + message = message || ""; + + //handle optional arguments + if (arguments.length == 3) { + if (typeof(err) == "string") { + message = err; + typematters = false; + } + } else if (arguments.length == 2) { + typematters = false; + } + + try { + block(); + } catch (e) { + threw = true; + exception = e; + } + + if (shouldThrow && !threw) { + fail( "Missing expected exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if (!shouldThrow && threw && typematters && exception instanceof err) { + fail( "Got unwanted exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if ((shouldThrow && threw && typematters && !(exception instanceof err)) || + (!shouldThrow && threw)) { + throw exception; + } +}; + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function (err) { if (err) {throw err;}}; +})(assert); +(function(exports){ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + + + +/** + * Creates assertion objects representing the result of an assert call. + * Accepts an object or AssertionError as its argument. + * + * @param {object} obj + * @api public + */ + +exports.assertion = function (obj) { + return { + method: obj.method || '', + message: obj.message || (obj.error && obj.error.message) || '', + error: obj.error, + passed: function () { + return !this.error; + }, + failed: function () { + return Boolean(this.error); + } + }; +}; + +/** + * Creates an assertion list object representing a group of assertions. + * Accepts an array of assertion objects. + * + * @param {Array} arr + * @param {Number} duration + * @api public + */ + +exports.assertionList = function (arr, duration) { + var that = arr || []; + that.failures = function () { + var failures = 0; + for (var i=0; i'; +}; + + +/** + * Run all tests within each module, reporting the results + * + * @param {Array} files + * @api public + */ + +exports.run = function (modules, options) { + var start = new Date().getTime(); + exports.addStyles(); + + var html = ''; + nodeunit.runModules(modules, { + moduleStart: function (name) { + html += '

    ' + name + '

    '; + html += '
      '; + }, + testDone: function (name, assertions) { + if (!assertions.failures()) { + html += '
    1. ' + name + '
    2. '; + } + else { + html += '
    3. ' + name; + for (var i=0; i'; + } + html += '
      ';
      +                        html += a.error.stack || a.error;
      +                        html += '
      '; + } + }; + html += '
    4. '; + } + }, + moduleDone: function () { + html += '
    '; + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + if (assertions.failures()) { + html += '

    FAILURES: ' + assertions.failures() + + '/' + assertions.length + ' assertions failed (' + + assertions.duration + 'ms)

    '; + } + else { + html += '

    OK: ' + assertions.length + + ' assertions (' + assertions.duration + 'ms)

    '; + } + document.body.innerHTML += html; + } + }); +}; +})(reporter); +nodeunit = core; +nodeunit.assert = assert; +nodeunit.reporter = reporter; +nodeunit.run = reporter.run; +return nodeunit; })(); diff --git a/node_modules/nodeunit/examples/browser/suite1.js b/node_modules/nodeunit/examples/browser/suite1.js new file mode 100644 index 000000000..0d5fc90ee --- /dev/null +++ b/node_modules/nodeunit/examples/browser/suite1.js @@ -0,0 +1,12 @@ +this.suite1 = { + 'test one': function (test) { + test.ok(true, 'everythings ok'); + setTimeout(function () { + test.done(); + }, 10); + }, + 'apples and oranges': function (test) { + test.equal('apples', 'oranges', 'comparing apples and oranges'); + test.done(); + } +}; diff --git a/node_modules/nodeunit/examples/browser/suite2.js b/node_modules/nodeunit/examples/browser/suite2.js new file mode 100644 index 000000000..c7288e8d8 --- /dev/null +++ b/node_modules/nodeunit/examples/browser/suite2.js @@ -0,0 +1,13 @@ +this.suite2 = { + 'another test': function (test) { + setTimeout(function () { + // lots of assertions + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.ok(true, 'everythings ok'); + test.done(); + }, 10); + } +}; diff --git a/node_modules/nodeunit/examples/browser/test.html b/node_modules/nodeunit/examples/browser/test.html new file mode 100644 index 000000000..e9f8180f8 --- /dev/null +++ b/node_modules/nodeunit/examples/browser/test.html @@ -0,0 +1,16 @@ + + + Example tests + + + + + + + + diff --git a/node_modules/nodeunit/img/example_fail.png b/node_modules/nodeunit/img/example_fail.png new file mode 100644 index 000000000..78ff4258c Binary files /dev/null and b/node_modules/nodeunit/img/example_fail.png differ diff --git a/node_modules/nodeunit/img/example_pass.png b/node_modules/nodeunit/img/example_pass.png new file mode 100644 index 000000000..069d71698 Binary files /dev/null and b/node_modules/nodeunit/img/example_pass.png differ diff --git a/node_modules/nodeunit/index.js b/node_modules/nodeunit/index.js new file mode 100644 index 000000000..07867d011 --- /dev/null +++ b/node_modules/nodeunit/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/nodeunit'); diff --git a/node_modules/nodeunit/lib/assert.js b/node_modules/nodeunit/lib/assert.js new file mode 100644 index 000000000..7f7bbdf40 --- /dev/null +++ b/node_modules/nodeunit/lib/assert.js @@ -0,0 +1,316 @@ +/** + * This file is based on the node.js assert module, but with some small + * changes for browser-compatibility + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + */ + + +/** + * Added for browser compatibility + */ + +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; +}; + + + +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Originally from narwhal.js (http://narwhaljs.org) +// Copyright (c) 2009 Thomas Robinson <280north.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 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. + + +var pSlice = Array.prototype.slice; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = exports; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({message: message, actual: actual, expected: expected}) + +assert.AssertionError = function AssertionError (options) { + this.name = "AssertionError"; + this.message = options.message; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } +}; +// code from util.inherits in node +assert.AssertionError.super_ = Error; + + +// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call +// TODO: test what effect this may have +var ctor = function () { this.constructor = assert.AssertionError; }; +ctor.prototype = Error.prototype; +assert.AssertionError.prototype = new ctor(); + + +assert.AssertionError.prototype.toString = function() { + if (this.message) { + return [this.name+":", this.message].join(' '); + } else { + return [ this.name+":" + , JSON.stringify(this.expected ) + , this.operator + , JSON.stringify(this.actual) + ].join(" "); + } +}; + +// assert.AssertionError instanceof Error + +assert.AssertionError.__proto__ = Error.prototype; + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +assert.ok = function ok(value, message) { + if (!!!value) fail(value, true, message, "==", assert.ok); +}; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, "==", assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + 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 _deepEqual(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 (!_deepEqual(a[key], b[key] )) + return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as determined by !==. +// assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } +}; + +function _throws (shouldThrow, block, err, message) { + var exception = null, + threw = false, + typematters = true; + + message = message || ""; + + //handle optional arguments + if (arguments.length == 3) { + if (typeof(err) == "string") { + message = err; + typematters = false; + } + } else if (arguments.length == 2) { + typematters = false; + } + + try { + block(); + } catch (e) { + threw = true; + exception = e; + } + + if (shouldThrow && !threw) { + fail( "Missing expected exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if (!shouldThrow && threw && typematters && exception instanceof err) { + fail( "Got unwanted exception" + + (err && err.name ? " ("+err.name+")." : '.') + + (message ? " " + message : "") + ); + } + if ((shouldThrow && threw && typematters && !(exception instanceof err)) || + (!shouldThrow && threw)) { + throw exception; + } +}; + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert['throws'] = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function (err) { if (err) {throw err;}}; diff --git a/node_modules/nodeunit/lib/core.js b/node_modules/nodeunit/lib/core.js new file mode 100644 index 000000000..981d7c63b --- /dev/null +++ b/node_modules/nodeunit/lib/core.js @@ -0,0 +1,236 @@ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +/** + * Module dependencies + */ + +var async = require('../deps/async'), //@REMOVE_LINE_FOR_BROWSER + types = require('./types'); //@REMOVE_LINE_FOR_BROWSER + + +/** + * Added for browser compatibility + */ + +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; +}; + + +/** + * Runs a test function (fn) from a loaded module. After the test function + * calls test.done(), the callback is executed with an assertionList as its + * second argument. + * + * @param {String} name + * @param {Function} fn + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runTest = function (name, fn, opt, callback) { + var options = types.options(opt); + + options.testStart(name); + var start = new Date().getTime(); + var test = types.test(name, start, options, callback); + + try { + fn(test); + } + catch (e) { + test.done(e); + } +}; + +/** + * Takes an object containing test functions or other test suites as properties + * and runs each in series. After all tests have completed, the callback is + * called with a list of all assertions as the second argument. + * + * If a name is passed to this function it is prepended to all test and suite + * names that run within it. + * + * @param {String} name + * @param {Object} suite + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runSuite = function (name, suite, opt, callback) { + var keys = _keys(suite); + + async.concatSeries(keys, function (k, cb) { + var prop = suite[k], _name; + + _name = name ? [].concat(name, k) : [k]; + + _name.toString = function () { + // fallback for old one + return this.join(' - '); + }; + + if (typeof prop === 'function') { + exports.runTest(_name, suite[k], opt, cb); + } + else { + exports.runSuite(_name, suite[k], opt, cb); + } + }, callback); +}; + +/** + * Run each exported test function or test suite from a loaded module. + * + * @param {String} name + * @param {Object} mod + * @param {Object} opt + * @param {Function} callback + * @api public + */ + +exports.runModule = function (name, mod, opt, callback) { + var options = types.options(opt); + + options.moduleStart(name); + var start = new Date().getTime(); + + exports.runSuite(null, mod, opt, function (err, a_list) { + var end = new Date().getTime(); + var assertion_list = types.assertionList(a_list, end - start); + options.moduleDone(name, assertion_list); + callback(null, a_list); + }); +}; + +/** + * Treats an object literal as a list of modules keyed by name. Runs each + * module and finished with calling 'done'. You can think of this as a browser + * safe alternative to runFiles in the nodeunit module. + * + * @param {Object} modules + * @param {Object} opt + * @api public + */ + +// TODO: add proper unit tests for this function +exports.runModules = function (modules, opt) { + var all_assertions = []; + var options = types.options(opt); + var start = new Date().getTime(); + + async.concatSeries(_keys(modules), function (k, cb) { + exports.runModule(k, modules[k], options, cb); + }, + function (err, all_assertions) { + var end = new Date().getTime(); + options.done(types.assertionList(all_assertions, end - start)); + }); +}; + + +/** + * Wraps a test function with setUp and tearDown functions. + * Used by testCase. + * + * @param {Function} setUp + * @param {Function} tearDown + * @param {Function} fn + * @api private + */ + +var wrapTest = function (setUp, tearDown, fn) { + return function (test) { + var context = {}; + if (tearDown) { + var done = test.done; + test.done = function (err) { + try { + tearDown.call(context, function (err2) { + if (err && err2) { + test._assertion_list.push( + types.assertion({error: err}) + ); + return done(err2); + } + done(err || err2); + }); + } + catch (e) { + done(e); + } + }; + } + if (setUp) { + setUp.call(context, function (err) { + if (err) { + return test.done(err); + } + fn.call(context, test); + }); + } + else { + fn.call(context, test); + } + } +}; + + +/** + * Wraps a group of tests with setUp and tearDown functions. + * Used by testCase. + * + * @param {Function} setUp + * @param {Function} tearDown + * @param {Object} group + * @api private + */ + +var wrapGroup = function (setUp, tearDown, group) { + var tests = {}; + var keys = _keys(group); + for (var i=0; i(' + + '' + assertions.failures() + ', ' + + '' + assertions.passes() + ', ' + + assertions.length + + ')'; + test.className = assertions.failures() ? 'fail': 'pass'; + test.appendChild(strong); + + var aList = document.createElement('ol'); + aList.style.display = 'none'; + test.onclick = function () { + var d = aList.style.display; + aList.style.display = (d == 'none') ? 'block': 'none'; + }; + for (var i=0; i' + (a.error.stack || a.error) + ''; + li.className = 'fail'; + } + else { + li.innerHTML = a.message || a.method || 'no message'; + li.className = 'pass'; + } + aList.appendChild(li); + } + test.appendChild(aList); + tests.appendChild(test); + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + + var failures = assertions.failures(); + banner.className = failures ? 'fail': 'pass'; + + result.innerHTML = 'Tests completed in ' + duration + + ' milliseconds.
    ' + + assertions.passes() + ' assertions of ' + + '' + assertions.length + ' passed, ' + + assertions.failures() + ' failed.'; + } + }); +}; diff --git a/node_modules/nodeunit/lib/reporters/default.js b/node_modules/nodeunit/lib/reporters/default.js new file mode 100644 index 000000000..683b66ded --- /dev/null +++ b/node_modules/nodeunit/lib/reporters/default.js @@ -0,0 +1,131 @@ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + */ + +/** + * Module dependencies + */ + +var nodeunit = require('../nodeunit'), + utils = require('../utils'), + fs = require('fs'), + sys = require('sys'), + track = require('../track'), + path = require('path'); + AssertionError = require('../assert').AssertionError; + +/** + * Reporter info string + */ + +exports.info = "Default tests reporter"; + + +/** + * Run all tests within each module, reporting the results to the command-line. + * + * @param {Array} files + * @api public + */ + +exports.run = function (files, options) { + + if (!options) { + // load default options + var content = fs.readFileSync( + __dirname + '/../../bin/nodeunit.json', 'utf8' + ); + options = JSON.parse(content); + } + + var error = function (str) { + return options.error_prefix + str + options.error_suffix; + }; + var ok = function (str) { + return options.ok_prefix + str + options.ok_suffix; + }; + var bold = function (str) { + return options.bold_prefix + str + options.bold_suffix; + }; + var assertion_message = function (str) { + return options.assertion_prefix + str + options.assertion_suffix; + }; + + var start = new Date().getTime(); + var paths = files.map(function (p) { + return path.join(process.cwd(), p); + }); + var tracker = track.createTracker(function (tracker) { + if (tracker.unfinished()) { + sys.puts(''); + sys.puts(error(bold( + 'FAILURES: Undone tests (or their setups/teardowns): ' + ))); + var names = tracker.names(); + for (var i = 0; i < names.length; i += 1) { + sys.puts('- ' + names[i]); + } + sys.puts(''); + sys.puts('To fix this, make sure all tests call test.done()'); + process.reallyExit(tracker.unfinished()); + } + }); + + nodeunit.runFiles(paths, { + moduleStart: function (name) { + sys.puts('\n' + bold(name)); + }, + testDone: function (name, assertions) { + tracker.remove(name); + + if (!assertions.failures()) { + sys.puts('✔ ' + name); + } + else { + sys.puts(error('✖ ' + name) + '\n'); + assertions.forEach(function (a) { + if (a.failed()) { + a = utils.betterErrors(a); + if (a.error instanceof AssertionError && a.message) { + sys.puts( + 'Assertion Message: ' + + assertion_message(a.message) + ); + } + sys.puts(a.error.stack + '\n'); + } + }); + } + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + if (assertions.failures()) { + sys.puts( + '\n' + bold(error('FAILURES: ')) + assertions.failures() + + '/' + assertions.length + ' assertions failed (' + + assertions.duration + 'ms)' + ); + } + else { + sys.puts( + '\n' + bold(ok('OK: ')) + assertions.length + + ' assertions (' + assertions.duration + 'ms)' + ); + } + // alexgorbatchev 2010-11-10 :: should be able to flush stdout + // here, but doesn't seem to work, instead delay the exit to give + // enough to time flush. + // process.stdout.flush() + // process.stdout.end() + setTimeout(function () { + process.reallyExit(assertions.failures()); + }, 10); + }, + testStart: function(name) { + tracker.put(name); + } + }); +}; diff --git a/node_modules/nodeunit/lib/reporters/html.js b/node_modules/nodeunit/lib/reporters/html.js new file mode 100644 index 000000000..a693c2d17 --- /dev/null +++ b/node_modules/nodeunit/lib/reporters/html.js @@ -0,0 +1,112 @@ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + */ + +/** + * Module dependencies + */ + +var nodeunit = require('../nodeunit'), + utils = require('../utils'), + fs = require('fs'), + sys = require('sys'), + path = require('path'), + AssertionError = require('assert').AssertionError; + +/** + * Reporter info string + */ + +exports.info = "Report tests result as HTML"; + +/** + * Run all tests within each module, reporting the results to the command-line. + * + * @param {Array} files + * @api public + */ + +exports.run = function (files, options) { + + var start = new Date().getTime(); + var paths = files.map(function (p) { + return path.join(process.cwd(), p); + }); + + sys.puts(''); + sys.puts(''); + sys.puts(''); + sys.puts(''); + sys.puts(''); + sys.puts(''); + nodeunit.runFiles(paths, { + moduleStart: function (name) { + sys.puts('

    ' + name + '

    '); + sys.puts('
      '); + }, + testDone: function (name, assertions) { + if (!assertions.failures()) { + sys.puts('
    1. ' + name + '
    2. '); + } + else { + sys.puts('
    3. ' + name); + assertions.forEach(function (a) { + if (a.failed()) { + a = utils.betterErrors(a); + if (a.error instanceof AssertionError && a.message) { + sys.puts('
      ' + + 'Assertion Message: ' + a.message + + '
      '); + } + sys.puts('
      ');
      +                        sys.puts(a.error.stack);
      +                        sys.puts('
      '); + } + }); + sys.puts('
    4. '); + } + }, + moduleDone: function () { + sys.puts('
    '); + }, + done: function (assertions) { + var end = new Date().getTime(); + var duration = end - start; + if (assertions.failures()) { + sys.puts( + '

    FAILURES: ' + assertions.failures() + + '/' + assertions.length + ' assertions failed (' + + assertions.duration + 'ms)

    ' + ); + } + else { + sys.puts( + '

    OK: ' + assertions.length + + ' assertions (' + assertions.duration + 'ms)

    ' + ); + } + sys.puts(''); + // should be able to flush stdout here, but doesn't seem to work, + // instead delay the exit to give enough to time flush. + setTimeout(function () { + process.reallyExit(assertions.failures()); + }, 10); + } + }); + +}; diff --git a/node_modules/nodeunit/lib/reporters/index.js b/node_modules/nodeunit/lib/reporters/index.js new file mode 100644 index 000000000..bbaf800d0 --- /dev/null +++ b/node_modules/nodeunit/lib/reporters/index.js @@ -0,0 +1,9 @@ +module.exports = { + 'junit': require('./junit'), + 'default': require('./default'), + 'skip_passed': require('./skip_passed'), + 'minimal': require('./minimal'), + 'html': require('./html') + // browser test reporter is not listed because it cannot be used + // with the command line tool, only inside a browser. +}; diff --git a/node_modules/nodeunit/lib/reporters/junit.js b/node_modules/nodeunit/lib/reporters/junit.js new file mode 100644 index 000000000..7ff8a7d52 --- /dev/null +++ b/node_modules/nodeunit/lib/reporters/junit.js @@ -0,0 +1,186 @@ +/*! + * Nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + */ + +/** + * Module dependencies + */ + +var nodeunit = require('../nodeunit'), + utils = require('../utils'), + fs = require('fs'), + sys = require('sys'), + path = require('path'), + async = require('../../deps/async'), + AssertionError = require('assert').AssertionError, + child_process = require('child_process'), + ejs = require('../../deps/ejs'); + + +/** + * Reporter info string + */ + +exports.info = "jUnit XML test reports"; + + +/** + * Ensures a directory exists using mkdir -p. + * + * @param {String} path + * @param {Function} callback + * @api private + */ + +var ensureDir = function (path, callback) { + var mkdir = child_process.spawn('mkdir', ['-p', path]); + mkdir.on('error', function (err) { + callback(err); + callback = function(){}; + }); + mkdir.on('exit', function (code) { + if (code === 0) callback(); + else callback(new Error('mkdir exited with code: ' + code)); + }); +}; + + +/** + * Returns absolute version of a path. Relative paths are interpreted + * relative to process.cwd() or the cwd parameter. Paths that are already + * absolute are returned unaltered. + * + * @param {String} p + * @param {String} cwd + * @return {String} + * @api public + */ + +var abspath = function (p, /*optional*/cwd) { + if (p[0] === '/') return p; + cwd = cwd || process.cwd(); + return path.normalize(path.join(cwd, p)); +}; + + +/** + * Run all tests within each module, reporting the results to the command-line, + * then writes out junit-compatible xml documents. + * + * @param {Array} files + * @api public + */ + +exports.run = function (files, opts, callback) { + if (!opts.output) { + console.error( + 'Error: No output directory defined.\n' + + '\tEither add an "output" property to your nodeunit.json config ' + + 'file, or\n\tuse the --output command line option.' + ); + return; + } + opts.output = abspath(opts.output); + var error = function (str) { + return opts.error_prefix + str + opts.error_suffix; + }; + var ok = function (str) { + return opts.ok_prefix + str + opts.ok_suffix; + }; + var bold = function (str) { + return opts.bold_prefix + str + opts.bold_suffix; + }; + + var start = new Date().getTime(); + var paths = files.map(function (p) { + return path.join(process.cwd(), p); + }); + + var modules = {} + var curModule; + + nodeunit.runFiles(paths, { + moduleStart: function (name) { + curModule = { + errorCount: 0, + failureCount: 0, + tests: 0, + testcases: [], + name: name + }; + modules[name] = curModule; + }, + testDone: function (name, assertions) { + var testcase = {name: name}; + for (var i=0; i [ \.\.\.] +. +.fi +. +.SH "DESCRIPTION" +Nodeunit is a simple unit testing tool based on the node\.js assert module\. +. +.IP "\(bu" 4 +Simple to use +. +.IP "\(bu" 4 +Just export the tests from a module +. +.IP "\(bu" 4 +Helps you avoid common pitfalls when testing asynchronous code +. +.IP "\(bu" 4 +Easy to add test cases with setUp and tearDown functions if you wish +. +.IP "\(bu" 4 +Allows the use of mocks and stubs +. +.IP "" 0 +. +.SH "OPTIONS" + \fB\-\-config FILE\fR: +. +.br + Load config options from a JSON file, allows the customisation + of color schemes for the default test reporter etc\. + See bin/nodeunit\.json for current available options\. +. +.P + \fB\-\-reporter FILE\fR: +. +.br + You can set the test reporter to a custom module or on of the modules + in nodeunit/lib/reporters, when omitted, the default test runner is used\. +. +.P + \fB\-\-list\-reporters\fR: +. +.br + List available build\-in reporters\. +. +.P + \fB\-h\fR, \fB\-\-help\fR: +. +.br + Display the help and exit\. +. +.P + \fB\-v\fR, \fB\-\-version\fR: +. +.br + Output version information and exit\. +. +.P + \fB\fR: + You can run nodeunit on specific files or on all \fI*\.js\fR files inside +. +.br + a directory\. +. +.SH "AUTHORS" +Written by Caolan McMahon and other nodeunit contributors\. +. +.br +Contributors list: \fIhttp://github\.com/caolan/nodeunit/contributors\fR\|\. +. +.SH "REPORTING BUGS" +Report nodeunit bugs to \fIhttp://github\.com/caolan/nodeunit/issues\fR\|\. +. +.SH "COPYRIGHT" +Copyright © 2010 Caolan McMahon\. +. +.br +Nodeunit has been released under the MIT license: +. +.br +\fIhttp://github\.com/caolan/nodeunit/raw/master/LICENSE\fR\|\. +. +.SH "SEE ALSO" +node(1) diff --git a/node_modules/nodeunit/nodelint.cfg b/node_modules/nodeunit/nodelint.cfg new file mode 100644 index 000000000..457a967e0 --- /dev/null +++ b/node_modules/nodeunit/nodelint.cfg @@ -0,0 +1,4 @@ +var options = { + indent: 4, + onevar: false +}; diff --git a/node_modules/nodeunit/package.json b/node_modules/nodeunit/package.json new file mode 100644 index 000000000..da62137e1 --- /dev/null +++ b/node_modules/nodeunit/package.json @@ -0,0 +1,53 @@ +{ "name": "nodeunit" +, "description": "Easy unit testing for node.js and the browser." +, "maintainers": + [ { "name": "Caolan McMahon" + , "web": "https://github.com/caolan" + } + ] +, "contributors" : + [ { "name": "Alex Gorbatchev" + , "web": "https://github.com/alexgorbatchev" + } + , { "name": "Alex Wolfe" + , "web": "https://github.com/alexkwolfe" + } + , { "name": "Carl Fürstenberg" + , "web": "https://github.com/azatoth" + } + , { "name": "Gerad Suyderhoud" + , "web": "https://github.com/gerad" + } + , { "name": "Kadir Pekel" + , "web": "https://github.com/coffeemate" + } + , { "name": "Oleg Efimov" + , "web": "https://github.com/Sannis" + } + , { "name": "Orlando Vazquez" + , "web": "https://github.com/orlandov" + } + , { "name": "Ryan Dahl" + , "web": "https://github.com/ry" + } + , { "name": "Sam Stephenson" + , "web": "https://github.com/sstephenson" + } + , { "name": "Thomas Mayfield" + , "web": "https://github.com/thegreatape" + } + ] +, "version": "0.5.1" +, "repository" : + { "type" : "git" + , "url" : "http://github.com/caolan/nodeunit.git" + } +, "bugs" : { "web" : "http://github.com/caolan/nodeunit/issues" } +, "licenses" : + [ { "type" : "MIT" + , "url" : "http://github.com/caolan/nodeunit/raw/master/LICENSE" + } + ] +, "directories" : { "lib": "./lib", "doc" : "./doc", "man" : "./man1" } +, "bin" : { "nodeunit" : "./bin/nodeunit" } +} diff --git a/node_modules/nodeunit/share/junit.xml.ejs b/node_modules/nodeunit/share/junit.xml.ejs new file mode 100644 index 000000000..c1db5bbec --- /dev/null +++ b/node_modules/nodeunit/share/junit.xml.ejs @@ -0,0 +1,19 @@ + +<% for (var i=0; i < suites.length; i++) { %> + <% var suite=suites[i]; %> + + <% for (var j=0; j < suite.testcases.length; j++) { %> + <% var testcase=suites[i].testcases[j]; %> + + <% if (testcase.failure) { %> + + <% if (testcase.failure.backtrace) { %><%= testcase.failure.backtrace %><% } %> + + <% } %> + + <% } %> + +<% } %> diff --git a/node_modules/nodeunit/share/license.js b/node_modules/nodeunit/share/license.js new file mode 100644 index 000000000..f0f326f33 --- /dev/null +++ b/node_modules/nodeunit/share/license.js @@ -0,0 +1,11 @@ +/*! + * Nodeunit + * https://github.com/caolan/nodeunit + * Copyright (c) 2010 Caolan McMahon + * MIT Licensed + * + * json2.js + * http://www.JSON.org/json2.js + * Public Domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ diff --git a/node_modules/nodeunit/share/nodeunit.css b/node_modules/nodeunit/share/nodeunit.css new file mode 100644 index 000000000..274434a4a --- /dev/null +++ b/node_modules/nodeunit/share/nodeunit.css @@ -0,0 +1,70 @@ +/*! + * Styles taken from qunit.css + */ + +h1#nodeunit-header, h1.nodeunit-header { + padding: 15px; + font-size: large; + background-color: #06b; + color: white; + font-family: 'trebuchet ms', verdana, arial; + margin: 0; +} + +h1#nodeunit-header a { + color: white; +} + +h2#nodeunit-banner { + height: 2em; + border-bottom: 1px solid white; + background-color: #eee; + margin: 0; + font-family: 'trebuchet ms', verdana, arial; +} +h2#nodeunit-banner.pass { + background-color: green; +} +h2#nodeunit-banner.fail { + background-color: red; +} + +h2#nodeunit-userAgent, h2.nodeunit-userAgent { + padding: 10px; + background-color: #eee; + color: black; + margin: 0; + font-size: small; + font-weight: normal; + font-family: 'trebuchet ms', verdana, arial; + font-size: 10pt; +} + +div#nodeunit-testrunner-toolbar { + background: #eee; + border-top: 1px solid black; + padding: 10px; + font-family: 'trebuchet ms', verdana, arial; + margin: 0; + font-size: 10pt; +} + +ol#nodeunit-tests { + font-family: 'trebuchet ms', verdana, arial; + font-size: 10pt; +} +ol#nodeunit-tests li strong { + cursor:pointer; +} +ol#nodeunit-tests .pass { + color: green; +} +ol#nodeunit-tests .fail { + color: red; +} + +p#nodeunit-testresult { + margin-left: 1em; + font-size: 10pt; + font-family: 'trebuchet ms', verdana, arial; +} diff --git a/node_modules/nodeunit/test/fixtures/coffee/mock_coffee_module.coffee b/node_modules/nodeunit/test/fixtures/coffee/mock_coffee_module.coffee new file mode 100644 index 000000000..a1c069b57 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/coffee/mock_coffee_module.coffee @@ -0,0 +1,4 @@ +j = 0 +j += i for i in [0..5] + +exports.name = "mock_coffee_#{j}" diff --git a/node_modules/nodeunit/test/fixtures/dir/mock_module3.js b/node_modules/nodeunit/test/fixtures/dir/mock_module3.js new file mode 100644 index 000000000..3021776c8 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/dir/mock_module3.js @@ -0,0 +1 @@ +exports.name = 'mock_module3'; diff --git a/node_modules/nodeunit/test/fixtures/dir/mock_module4.js b/node_modules/nodeunit/test/fixtures/dir/mock_module4.js new file mode 100644 index 000000000..876f9ca07 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/dir/mock_module4.js @@ -0,0 +1 @@ +exports.name = 'mock_module4'; diff --git a/node_modules/nodeunit/test/fixtures/mock_module1.js b/node_modules/nodeunit/test/fixtures/mock_module1.js new file mode 100644 index 000000000..4c093ad16 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/mock_module1.js @@ -0,0 +1 @@ +exports.name = 'mock_module1'; diff --git a/node_modules/nodeunit/test/fixtures/mock_module2.js b/node_modules/nodeunit/test/fixtures/mock_module2.js new file mode 100644 index 000000000..a63d01226 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/mock_module2.js @@ -0,0 +1 @@ +exports.name = 'mock_module2'; diff --git a/node_modules/nodeunit/test/fixtures/raw_jscode1.js b/node_modules/nodeunit/test/fixtures/raw_jscode1.js new file mode 100644 index 000000000..2ef711524 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/raw_jscode1.js @@ -0,0 +1,3 @@ +function hello_world(arg) { + return "_" + arg + "_"; +} diff --git a/node_modules/nodeunit/test/fixtures/raw_jscode2.js b/node_modules/nodeunit/test/fixtures/raw_jscode2.js new file mode 100644 index 000000000..55a764ef6 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/raw_jscode2.js @@ -0,0 +1,3 @@ +function get_a_variable() { + return typeof a_variable; +} diff --git a/node_modules/nodeunit/test/fixtures/raw_jscode3.js b/node_modules/nodeunit/test/fixtures/raw_jscode3.js new file mode 100644 index 000000000..1fd1e7889 --- /dev/null +++ b/node_modules/nodeunit/test/fixtures/raw_jscode3.js @@ -0,0 +1 @@ +var t=t?t+1:1; diff --git a/node_modules/nodeunit/test/test-base.js b/node_modules/nodeunit/test/test-base.js new file mode 100644 index 000000000..64b8c8bbd --- /dev/null +++ b/node_modules/nodeunit/test/test-base.js @@ -0,0 +1,219 @@ +/* + * This module is not a plain nodeunit test suite, but instead uses the + * assert module to ensure a basic level of functionality is present, + * allowing the rest of the tests to be written using nodeunit itself. + * + * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +var assert = require('assert'), // @REMOVE_LINE_FOR_BROWSER + async = require('../deps/async'), // @REMOVE_LINE_FOR_BROWSER + nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER + + +// NOT A TEST - util function to make testing faster. +// retries the assertion until it passes or the timeout is reached, +// at which point it throws the assertion error +var waitFor = function (fn, timeout, callback, start) { + start = start || new Date().getTime(); + callback = callback || function () {}; + try { + fn(); + callback(); + } + catch (e) { + if (e instanceof assert.AssertionError) { + var now = new Date().getTime(); + if (now - start >= timeout) { + throw e; + } + else { + async.nextTick(function () { + waitFor(fn, timeout, callback, start); + }); + } + } + else { + throw e; + } + } +}; + + +// TESTS: + +// Are exported tests actually run? - store completed tests in this variable +// for checking later +var tests_called = {}; + +// most basic test that should run, the tests_called object is tested +// at the end of this module to ensure the tests were actually run by nodeunit +exports.testCalled = function (test) { + tests_called.testCalled = true; + test.done(); +}; + +// generates test functions for nodeunit assertions +var makeTest = function (method, args_pass, args_fail) { + return function (test) { + var test1_called = false; + var test2_called = false; + + // test pass + nodeunit.runTest( + 'testname', + function (test) { + test[method].apply(test, args_pass); + test.done(); + }, + {testDone: function (name, assertions) { + assert.equal(assertions.length, 1); + assert.equal(assertions.failures(), 0); + }}, + function () { + test1_called = true; + } + ); + + // test failure + nodeunit.runTest( + 'testname', + function (test) { + test[method].apply(test, args_fail); + test.done(); + }, + {testDone: function (name, assertions) { + assert.equal(assertions.length, 1); + assert.equal(assertions.failures(), 1); + }}, + function () { + test2_called = true; + } + ); + + // ensure tests were run + waitFor(function () { + assert.ok(test1_called); + assert.ok(test2_called); + tests_called[method] = true; + }, 500, test.done); + }; +}; + +// ensure basic assertions are working: +exports.testOk = makeTest('ok', [true], [false]); +exports.testEquals = makeTest('equals', [1, 1], [1, 2]); +exports.testSame = makeTest('same', + [{test: 'test'}, {test: 'test'}], + [{test: 'test'}, {monkey: 'penguin'}] +); + +// from the assert module: +exports.testEqual = makeTest('equal', [1, 1], [1, 2]); +exports.testNotEqual = makeTest('notEqual', [1, 2], [1, 1]); +exports.testDeepEqual = makeTest('deepEqual', + [{one: 1}, {one: 1}], [{one: 1}, {two: 2}] +); +exports.testNotDeepEqual = makeTest('notDeepEqual', + [{one: 1}, {two: 2}], [{one: 1}, {one: 1}] +); +exports.testStrictEqual = makeTest('strictEqual', [1, 1], [1, true]); +exports.testNotStrictEqual = makeTest('notStrictEqual', [true, 1], [1, 1]); +exports.testThrows = makeTest('throws', + [function () { + throw new Error('test'); + }], + [function () { + return; + }] +); +exports.testDoesNotThrows = makeTest('doesNotThrow', + [function () { + return; + }], + [function () { + throw new Error('test'); + }] +); +exports.testIfError = makeTest('ifError', [false], [new Error('test')]); + + +exports.testExpect = function (test) { + var test1_called = false, + test2_called = false, + test3_called = false; + + // correct number of tests run + nodeunit.runTest( + 'testname', + function (test) { + test.expect(2); + test.ok(true); + test.ok(true); + test.done(); + }, + {testDone: function (name, assertions) { + test.equals(assertions.length, 2); + test.equals(assertions.failures(), 0); + }}, + function () { + test1_called = true; + } + ); + + // no tests run + nodeunit.runTest( + 'testname', + function (test) { + test.expect(2); + test.done(); + }, + {testDone: function (name, assertions) { + test.equals(assertions.length, 1); + test.equals(assertions.failures(), 1); + }}, + function () { + test2_called = true; + } + ); + + // incorrect number of tests run + nodeunit.runTest( + 'testname', + function (test) { + test.expect(2); + test.ok(true); + test.ok(true); + test.ok(true); + test.done(); + }, + {testDone: function (name, assertions) { + test.equals(assertions.length, 4); + test.equals(assertions.failures(), 1); + }}, + function () { + test3_called = true; + } + ); + + // ensure callbacks fired + waitFor(function () { + assert.ok(test1_called); + assert.ok(test2_called); + assert.ok(test3_called); + tests_called.expect = true; + }, 500, test.done); +}; + + +// tests are async, so wait for them to be called +waitFor(function () { + assert.ok(tests_called.testCalled); + assert.ok(tests_called.ok); + assert.ok(tests_called.equals); + assert.ok(tests_called.same); + assert.ok(tests_called.expect); +}, 10000); diff --git a/node_modules/nodeunit/test/test-failing-callbacks.js b/node_modules/nodeunit/test/test-failing-callbacks.js new file mode 100644 index 000000000..08f7eb585 --- /dev/null +++ b/node_modules/nodeunit/test/test-failing-callbacks.js @@ -0,0 +1,114 @@ +var nodeunit = require('../lib/nodeunit'); + + +exports.testFailingLog = function (test) { + test.expect(3); + + // this is meant to bubble to the top, and will be ignored for the purposes + // of testing: + var ignored_error = new Error('ignore this callback error'); + var err_handler = function (err) { + if (err && err.message !== ignored_error.message) { + throw err; + } + }; + process.addListener('uncaughtException', err_handler); + + // A failing callback should not affect the test outcome + var testfn = function (test) { + test.ok(true, 'test.ok'); + test.done(); + }; + nodeunit.runTest('testname', testfn, { + log: function (assertion) { + test.ok(true, 'log called'); + throw ignored_error; + }, + testDone: function (name, assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 1, 'total'); + process.removeListener('uncaughtException', err_handler); + } + }, test.done); +}; + +exports.testFailingTestDone = function (test) { + test.expect(2); + + var ignored_error = new Error('ignore this callback error'); + var err_handler = function (err) { + if (err && err.message !== ignored_error.message) { + throw err; + } + }; + process.addListener('uncaughtException', err_handler); + + // A failing callback should not affect the test outcome + var testfn = function (test) { + test.done(); + }; + nodeunit.runTest('testname', testfn, { + log: function (assertion) { + test.ok(false, 'log should not be called'); + }, + testDone: function (name, assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 0, 'total'); + process.nextTick(function () { + process.removeListener('uncaughtException', err_handler); + test.done(); + }); + throw ignored_error; + } + }, function () {}); +}; + +exports.testAssertionObj = function (test) { + test.expect(4); + var testfn = function (test) { + test.ok(true, 'ok true'); + test.done(); + }; + nodeunit.runTest('testname', testfn, { + log: function (assertion) { + test.ok(assertion.passed() === true, 'assertion.passed'); + test.ok(assertion.failed() === false, 'assertion.failed'); + }, + testDone: function (name, assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 1, 'total'); + } + }, test.done); +}; + +exports.testLogOptional = function (test) { + test.expect(2); + var testfn = function (test) { + test.ok(true, 'ok true'); + test.done(); + }; + nodeunit.runTest('testname', testfn, { + testDone: function (name, assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 1, 'total'); + } + }, test.done); +}; + +exports.testExpectWithFailure = function (test) { + test.expect(3); + var testfn = function (test) { + test.expect(1); + test.ok(false, 'test.ok'); + test.done(); + }; + nodeunit.runTest('testname', testfn, { + log: function (assertion) { + test.equals(assertion.method, 'ok', 'assertion.method'); + }, + testDone: function (name, assertions) { + test.equals(assertions.failures(), 1, 'failures'); + test.equals(assertions.length, 1, 'total'); + } + }, test.done); +}; diff --git a/node_modules/nodeunit/test/test-httputil.js b/node_modules/nodeunit/test/test-httputil.js new file mode 100644 index 000000000..e5ee25c64 --- /dev/null +++ b/node_modules/nodeunit/test/test-httputil.js @@ -0,0 +1,55 @@ +var nodeunit = require('../lib/nodeunit'); +var httputil = require('../lib/utils').httputil; + +exports.testHttpUtilBasics = function (test) { + + test.expect(6); + + httputil(function (req, resp) { + test.equal(req.method, 'PUT'); + test.equal(req.url, '/newpair'); + test.equal(req.headers.foo, 'bar'); + + resp.writeHead(500, {'content-type': 'text/plain'}); + resp.end('failed'); + }, function (server, client) { + client.fetch('PUT', '/newpair', {'foo': 'bar'}, function (resp) { + test.equal(resp.statusCode, 500); + test.equal(resp.headers['content-type'], 'text/plain'); + test.equal(resp.body, 'failed'); + + server.close(); + test.done(); + }); + }); +}; + +exports.testHttpUtilJsonHandling = function (test) { + + test.expect(9); + + httputil(function (req, resp) { + test.equal(req.method, 'GET'); + test.equal(req.url, '/'); + test.equal(req.headers.foo, 'bar'); + + var testdata = {foo1: 'bar', foo2: 'baz'}; + + resp.writeHead(200, {'content-type': 'application/json'}); + resp.end(JSON.stringify(testdata)); + + }, function (server, client) { + client.fetch('GET', '/', {'foo': 'bar'}, function (resp) { + test.equal(resp.statusCode, 200); + test.equal(resp.headers['content-type'], 'application/json'); + + test.ok(resp.bodyAsObject); + test.equal(typeof resp.bodyAsObject, 'object'); + test.equal(resp.bodyAsObject.foo1, 'bar'); + test.equal(resp.bodyAsObject.foo2, 'baz'); + + server.close(); + test.done(); + }); + }); +}; diff --git a/node_modules/nodeunit/test/test-runfiles.js b/node_modules/nodeunit/test/test-runfiles.js new file mode 100644 index 000000000..b9ef754f2 --- /dev/null +++ b/node_modules/nodeunit/test/test-runfiles.js @@ -0,0 +1,214 @@ +var assert = require('assert'), + sys = require('sys'), + fs = require('fs'), + path = require('path'), + nodeunit = require('../lib/nodeunit'); + + +var setup = function (fn) { + return function (test) { + process.chdir(__dirname); + require.paths.push(__dirname); + var env = { + mock_module1: require('./fixtures/mock_module1'), + mock_module2: require('./fixtures/mock_module2'), + mock_module3: require('./fixtures/dir/mock_module3'), + mock_module4: require('./fixtures/dir/mock_module4') + }; + fn.call(env, test); + }; +}; + + +exports.testRunFiles = setup(function (test) { + test.expect(24); + var runModule_copy = nodeunit.runModule; + + var runModule_calls = []; + var modules = []; + + var opts = { + moduleStart: function () { + return 'moduleStart'; + }, + testDone: function () { + return 'testDone'; + }, + testStart: function () { + return 'testStart'; + }, + log: function () { + return 'log'; + }, + done: function (assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 4, 'length'); + test.ok(typeof assertions.duration === "number"); + + var called_with = function (name) { + return runModule_calls.some(function (m) { + return m.name === name; + }); + }; + test.ok(called_with('mock_module1'), 'mock_module1 ran'); + test.ok(called_with('mock_module2'), 'mock_module2 ran'); + test.ok(called_with('mock_module3'), 'mock_module3 ran'); + test.ok(called_with('mock_module4'), 'mock_module4 ran'); + test.equals(runModule_calls.length, 4); + + nodeunit.runModule = runModule_copy; + test.done(); + } + }; + + nodeunit.runModule = function (name, mod, options, callback) { + test.equals(options.testDone, opts.testDone); + test.equals(options.testStart, opts.testStart); + test.equals(options.log, opts.log); + test.ok(typeof name === "string"); + runModule_calls.push(mod); + var m = [{failed: function () { + return false; + }}]; + modules.push(m); + callback(null, m); + }; + + nodeunit.runFiles( + ['fixtures/mock_module1.js', 'fixtures/mock_module2.js', 'fixtures/dir'], + opts + ); +}); + +exports.testRunFilesEmpty = function (test) { + test.expect(3); + nodeunit.runFiles([], { + moduleStart: function () { + test.ok(false, 'should not be called'); + }, + testDone: function () { + test.ok(false, 'should not be called'); + }, + testStart: function () { + test.ok(false, 'should not be called'); + }, + log: function () { + test.ok(false, 'should not be called'); + }, + done: function (assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 0, 'length'); + test.ok(typeof assertions.duration === "number"); + test.done(); + } + }); +}; + + +exports.testEmptyDir = function (test) { + var dir2 = __dirname + '/fixtures/dir2'; + + // git doesn't like empty directories, so we have to create one + path.exists(dir2, function (exists) { + if (!exists) { + fs.mkdirSync(dir2, 0777); + } + + // runFiles on empty directory: + nodeunit.runFiles([dir2], { + moduleStart: function () { + test.ok(false, 'should not be called'); + }, + testDone: function () { + test.ok(false, 'should not be called'); + }, + testStart: function () { + test.ok(false, 'should not be called'); + }, + log: function () { + test.ok(false, 'should not be called'); + }, + done: function (assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 0, 'length'); + test.ok(typeof assertions.duration === "number"); + test.done(); + } + }); + }); +}; + + +var CoffeeScript; +try { + CoffeeScript = require('coffee-script'); +} catch (e) { +} + +if (CoffeeScript) { + exports.testCoffeeScript = function (test) { + process.chdir(__dirname); + require.paths.push(__dirname); + var env = { + mock_coffee_module: require('./fixtures/coffee/mock_coffee_module') + }; + + test.expect(9); + var runModule_copy = nodeunit.runModule; + + var runModule_calls = []; + var modules = []; + + var opts = { + moduleStart: function () { + return 'moduleStart'; + }, + testDone: function () { + return 'testDone'; + }, + testStart: function () { + return 'testStart'; + }, + log: function () { + return 'log'; + }, + done: function (assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 1, 'length'); + test.ok(typeof assertions.duration === "number"); + + var called_with = function (name) { + return runModule_calls.some(function (m) { + return m.name === name; + }); + }; + test.ok( + called_with('mock_coffee_15'), + 'mock_coffee_module ran' + ); + test.equals(runModule_calls.length, 1); + + nodeunit.runModule = runModule_copy; + test.done(); + } + }; + + nodeunit.runModule = function (name, mod, options, callback) { + test.equals(options.testDone, opts.testDone); + test.equals(options.testStart, opts.testStart); + test.equals(options.log, opts.log); + test.ok(typeof name === "string"); + runModule_calls.push(mod); + var m = [{failed: function () { + return false; + }}]; + modules.push(m); + callback(null, m); + }; + + nodeunit.runFiles( + ['fixtures/coffee/mock_coffee_module.coffee'], + opts + ); + }; +} diff --git a/node_modules/nodeunit/test/test-runmodule.js b/node_modules/nodeunit/test/test-runmodule.js new file mode 100644 index 000000000..218e8dbec --- /dev/null +++ b/node_modules/nodeunit/test/test-runmodule.js @@ -0,0 +1,125 @@ +/* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +var nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER + + +exports.testRunModule = function (test) { + test.expect(11); + var call_order = []; + var testmodule = { + test1: function (test) { + call_order.push('test1'); + test.ok(true, 'ok true'); + test.done(); + }, + test2: function (test) { + call_order.push('test2'); + test.ok(false, 'ok false'); + test.ok(false, 'ok false'); + test.done(); + }, + test3: function (test) { + call_order.push('test3'); + test.done(); + } + }; + nodeunit.runModule('testmodule', testmodule, { + log: function (assertion) { + call_order.push('log'); + }, + testStart: function (name) { + call_order.push('testStart'); + test.ok( + name.toString() === 'test1' || + name.toString() === 'test2' || + name.toString() === 'test3', + 'testStart called with test name ' + ); + }, + testDone: function (name, assertions) { + call_order.push('testDone'); + test.ok( + name.toString() === 'test1' || + name.toString() === 'test2' || + name.toString() === 'test3', + 'testDone called with test name' + ); + }, + moduleDone: function (name, assertions) { + call_order.push('moduleDone'); + test.equals(assertions.length, 3); + test.equals(assertions.failures(), 2); + test.equals(name, 'testmodule'); + test.ok(typeof assertions.duration === "number"); + test.same(call_order, [ + 'testStart', 'test1', 'log', 'testDone', + 'testStart', 'test2', 'log', 'log', 'testDone', + 'testStart', 'test3', 'testDone', + 'moduleDone' + ]); + } + }, test.done); +}; + +exports.testRunModuleEmpty = function (test) { + nodeunit.runModule('module with no exports', {}, { + log: function (assertion) { + test.ok(false, 'log should not be called'); + }, + testStart: function (name) { + test.ok(false, 'testStart should not be called'); + }, + testDone: function (name, assertions) { + test.ok(false, 'testDone should not be called'); + }, + moduleDone: function (name, assertions) { + test.equals(assertions.length, 0); + test.equals(assertions.failures(), 0); + test.equals(name, 'module with no exports'); + test.ok(typeof assertions.duration === "number"); + } + }, test.done); +}; + +exports.testNestedTests = function (test) { + var call_order = []; + var m = { + test1: function (test) { + test.done(); + }, + suite: { + t1: function (test) { + test.done(); + }, + t2: function (test) { + test.done(); + }, + another_suite: { + t3: function (test) { + test.done(); + } + } + } + }; + nodeunit.runModule('modulename', m, { + testStart: function (name) { + call_order.push(['testStart'].concat(name)); + }, + testDone: function (name, assertions) { + call_order.push(['testDone'].concat(name)); + } + }, function () { + test.same(call_order, [ + ['testStart', 'test1'], ['testDone', 'test1'], + ['testStart', 'suite', 't1'], ['testDone', 'suite', 't1'], + ['testStart', 'suite', 't2'], ['testDone', 'suite', 't2'], + ['testStart', 'suite', 'another_suite', 't3'], + ['testDone', 'suite', 'another_suite', 't3'] + ]); + test.done(); + }); +}; diff --git a/node_modules/nodeunit/test/test-runtest.js b/node_modules/nodeunit/test/test-runtest.js new file mode 100644 index 000000000..8fc3d5209 --- /dev/null +++ b/node_modules/nodeunit/test/test-runtest.js @@ -0,0 +1,46 @@ +/* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +var nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER + + +exports.testArgs = function (test) { + test.ok(test.expect instanceof Function, 'test.expect'); + test.ok(test.done instanceof Function, 'test.done'); + test.ok(test.ok instanceof Function, 'test.ok'); + test.ok(test.same instanceof Function, 'test.same'); + test.ok(test.equals instanceof Function, 'test.equals'); + test.done(); +}; + +exports.testDoneCallback = function (test) { + test.expect(4); + nodeunit.runTest('testname', exports.testArgs, { + testDone: function (name, assertions) { + test.equals(assertions.failures(), 0, 'failures'); + test.equals(assertions.length, 5, 'length'); + test.ok(typeof assertions.duration === "number"); + test.equals(name, 'testname'); + } + }, test.done); +}; + +exports.testThrowError = function (test) { + test.expect(3); + var err = new Error('test'); + var testfn = function (test) { + throw err; + }; + nodeunit.runTest('testname', testfn, { + log: function (assertion) { + test.same(assertion.error, err, 'assertion.error'); + }, + testDone: function (name, assertions) { + test.equals(assertions.failures(), 1); + test.equals(assertions.length, 1); + } + }, test.done); +}; diff --git a/node_modules/nodeunit/test/test-sandbox.js b/node_modules/nodeunit/test/test-sandbox.js new file mode 100644 index 000000000..1b249d7af --- /dev/null +++ b/node_modules/nodeunit/test/test-sandbox.js @@ -0,0 +1,31 @@ +var nodeunit = require('../lib/nodeunit'); +var sandbox = require('../lib/utils').sandbox; +var testCase = nodeunit.testCase; + +exports.testSimpleSandbox = function (test) { + var raw_jscode1 = sandbox(__dirname + '/fixtures/raw_jscode1.js'); + test.equal(raw_jscode1.hello_world('foo'), '_foo_', 'evaluation ok'); + test.done(); +}; + +exports.testSandboxContext = function (test) { + var a_variable = 42; // should not be visible in the sandbox + var raw_jscode2 = sandbox(__dirname + '/fixtures/raw_jscode2.js'); + a_variable = 42; // again for the win + test.equal( + raw_jscode2.get_a_variable(), + 'undefined', + 'the variable should not be defined' + ); + test.done(); +}; + +exports.testSandboxMultiple = function (test) { + var raw_jscode3 = sandbox([ + __dirname + '/fixtures/raw_jscode3.js', + __dirname + '/fixtures/raw_jscode3.js', + __dirname + '/fixtures/raw_jscode3.js' + ]); + test.equal(raw_jscode3.t, 3, 'two files loaded'); + test.done(); +}; diff --git a/node_modules/nodeunit/test/test-testcase.js b/node_modules/nodeunit/test/test-testcase.js new file mode 100644 index 000000000..a3ea331bd --- /dev/null +++ b/node_modules/nodeunit/test/test-testcase.js @@ -0,0 +1,234 @@ +/* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS! + * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build. + * Only code on that line will be removed, its mostly to avoid requiring code + * that is node specific + */ + +var nodeunit = require('../lib/nodeunit'); // @REMOVE_LINE_FOR_BROWSER +var testCase = nodeunit.testCase; + +exports.testTestCase = function (test) { + test.expect(7); + var call_order = []; + var s = testCase({ + setUp: function (callback) { + call_order.push('setUp'); + test.equals(this.one, undefined); + this.one = 1; + callback(); + }, + tearDown: function (callback) { + call_order.push('tearDown'); + test.ok(true, 'tearDown called'); + callback(); + }, + test1: function (t) { + call_order.push('test1'); + test.equals(this.one, 1); + this.one = 2; + t.done(); + }, + test2: function (t) { + call_order.push('test2'); + test.equals(this.one, 1); + t.done(); + } + }); + nodeunit.runSuite(null, s, {}, function () { + test.same(call_order, [ + 'setUp', 'test1', 'tearDown', + 'setUp', 'test2', 'tearDown' + ]); + test.done(); + }); +}; + +exports.tearDownAfterError = function (test) { + test.expect(1); + var s = testCase({ + tearDown: function (callback) { + test.ok(true, 'tearDown called'); + callback(); + }, + test: function (t) { + throw new Error('some error'); + } + }); + nodeunit.runSuite(null, s, {}, function () { + test.done(); + }); +}; + +exports.catchSetUpError = function (test) { + test.expect(2); + var test_error = new Error('test error'); + var s = testCase({ + setUp: function (callback) { + throw test_error; + }, + test: function (t) { + test.ok(false, 'test function should not be called'); + t.done(); + } + }); + nodeunit.runSuite(null, s, {}, function (err, assertions) { + test.equal(assertions.length, 1); + test.equal(assertions[0].error, test_error); + test.done(); + }); +}; + +exports.setUpErrorCallback = function (test) { + test.expect(2); + var test_error = new Error('test error'); + var s = testCase({ + setUp: function (callback) { + callback(test_error); + }, + test: function (t) { + test.ok(false, 'test function should not be called'); + t.done(); + } + }); + nodeunit.runSuite(null, s, {}, function (err, assertions) { + test.equal(assertions.length, 1); + test.equal(assertions[0].error, test_error); + test.done(); + }); +}; + +exports.catchTearDownError = function (test) { + test.expect(2); + var test_error = new Error('test error'); + var s = testCase({ + tearDown: function (callback) { + throw test_error; + }, + test: function (t) { + t.done(); + } + }); + nodeunit.runSuite(null, s, {}, function (err, assertions) { + test.equal(assertions.length, 1); + test.equal(assertions[0].error, test_error); + test.done(); + }); +}; + +exports.tearDownErrorCallback = function (test) { + test.expect(2); + var test_error = new Error('test error'); + var s = testCase({ + tearDown: function (callback) { + callback(test_error); + }, + test: function (t) { + t.done(); + } + }); + nodeunit.runSuite(null, s, {}, function (err, assertions) { + test.equal(assertions.length, 1); + test.equal(assertions[0].error, test_error); + test.done(); + }); +}; + +exports.testErrorAndtearDownError = function (test) { + test.expect(3); + var error1 = new Error('test error one'); + var error2 = new Error('test error two'); + var s = testCase({ + tearDown: function (callback) { + callback(error2); + }, + test: function (t) { + t.done(error1); + } + }); + nodeunit.runSuite(null, s, {}, function (err, assertions) { + test.equal(assertions.length, 2); + test.equal(assertions[0].error, error1); + test.equal(assertions[1].error, error2); + test.done(); + }); +}; + +exports.testCaseGroups = function (test) { + var call_order = []; + var s = testCase({ + setUp: function (callback) { + call_order.push('setUp'); + callback(); + }, + tearDown: function (callback) { + call_order.push('tearDown'); + callback(); + }, + test1: function (test) { + call_order.push('test1'); + test.done(); + }, + group1: { + test2: function (test) { + call_order.push('group1.test2'); + test.done(); + } + } + }); + nodeunit.runSuite(null, s, {}, function (err, assertions) { + test.same(call_order, [ + 'setUp', + 'test1', + 'tearDown', + 'setUp', + 'group1.test2', + 'tearDown' + ]); + test.done(); + }); +}; + +exports.nestedTestCases = function (test) { + var call_order = []; + var s = testCase({ + setUp: function (callback) { + call_order.push('setUp'); + callback(); + }, + tearDown: function (callback) { + call_order.push('tearDown'); + callback(); + }, + test1: function (test) { + call_order.push('test1'); + test.done(); + }, + group1: testCase({ + setUp: function (callback) { + call_order.push('group1.setUp'); + callback(); + }, + tearDown: function (callback) { + call_order.push('group1.tearDown'); + callback(); + }, + test2: function (test) { + call_order.push('group1.test2'); + test.done(); + } + }) + }); + nodeunit.runSuite(null, s, {}, function (err, assertions) { + test.same(call_order, [ + 'setUp', + 'test1', + 'tearDown', + 'setUp', + 'group1.setUp', + 'group1.test2', + 'group1.tearDown', + 'tearDown' + ]); + test.done(); + }); +}; diff --git a/node_modules/nodeunit/test/test.html b/node_modules/nodeunit/test/test.html new file mode 100644 index 000000000..31bda3053 --- /dev/null +++ b/node_modules/nodeunit/test/test.html @@ -0,0 +1,26 @@ + + + Nodeunit Test Suite + + + + + + + + + +

    Nodeunit Test Suite

    + + + diff --git a/node_modules/signals/.gitignore b/node_modules/signals/.gitignore new file mode 100644 index 000000000..c91a73ad2 --- /dev/null +++ b/node_modules/signals/.gitignore @@ -0,0 +1,8 @@ +.tmp* +.project +.settings/ + +.DS_Store? +ehthumbs.db +Icon? +Thumbs.db \ No newline at end of file diff --git a/node_modules/signals/.npmignore b/node_modules/signals/.npmignore new file mode 100644 index 000000000..826dec5d2 --- /dev/null +++ b/node_modules/signals/.npmignore @@ -0,0 +1,5 @@ +dev/ +dist/js-signals.amd.js +dist/js-signals.js +dist/js-signals.min.js +build.xml \ No newline at end of file diff --git a/node_modules/signals/CHANGELOG.markdown b/node_modules/signals/CHANGELOG.markdown new file mode 100644 index 000000000..e754bd4e2 --- /dev/null +++ b/node_modules/signals/CHANGELOG.markdown @@ -0,0 +1,184 @@ +# JS-Signals Changelog # + +## v0.6.1 (2011/05/03) ## + + - added NPM package.json and CommmonJS wrapper for NPM distribution. (thanks @tomyan) + + +## v0.6 (2011/04/09) ## + +### API changes ### + + - Added: + - `Signal.active` + - `SignalBinding.active` + + - Removed: + - `Signal.protytpe.enable()` + - `Signal.protytpe.disable()` + - `Signal.protytpe.isEnabled()` + - `SignalBinding.protytpe.enable()` + - `SignalBinding.protytpe.disable()` + - `SignalBinding.protytpe.isEnabled()` + +### Other ### + + - created AMD wrapped version. + - switched from "module pattern" to a closure with a global export. + + + +## v0.5.3 (2011/02/21) ## + +### API changes ### + + - added priority parameter to `add` and `addOnce`. + +### Other ### + + - improved code structure. + + + +## v0.5.2 (2011/02/18) ## + +### Other ### + + - changed to a module pattern. + - added YUI test coverage. + - improved build and src files structure. + - simplified `remove`, `removeAll`, `add`. + - improved error messages. + + + +## v0.5.1 (2011/01/30) ## + +### API changes ### + + - made `SignalBinding` constructor private. (issue #15) + - changed params order on `SignalBinding` constructor. + - removed `signals.isDef()`. (issue #14) + +### Other ### + + - added JSLint to the build process. (issue #12) + - validated source code using JSLint. (issue #13) + - improved docs. + + + +## v0.5 (2010/12/03) ## + +### API changes ### + + - Added: + - `SignalBinding.prototype.getListener()` (issue #3) + - `Signal.prototype.dispose()` (issue #6) + - `signals.VERSION` + - `signals.isDef()` + + - Removed: + - `SignalBinding.listener` (issue #3) + + - Renamed: + - `SignalBinding.listenerScope` -> `SignalBinding.context` (issue #4) + +### Fixes ### + + - Removed unnecessary function names (issue #5) + - Improved `remove()`, `removeAll()` to dispose binding (issue #10) + +### Test Changes ### + + - Added different HTML files to test dev/dist/min files. + - Updated test cases to match new API. + +### Other ### + + - Improved source code comments and documentation. + - Small refactoring for better organization and DRY. + - Added error messages for required params. + - Removed unnecessary info from `SignalBinding.toString()`. + + + +## v0.4 (2010/11/27) ## + +### API changes ### + + - Added: + - `SignalBinding.prototype.detach()` + - `SignalBinding.prototype.dispose()` + +### Test Changes ### + + - Added test cases for `detach` and `dispose`. + +### Other ### + + - Improved docs for a few methods. + - Added internal method `Signal.prototype._addBinding()`. + + + +## v0.3 (2010/11/27) ## + +### API changes ### + + - Renamed: + - `Signal.prototype.stopPropagation()` -> `Signal.prototype.halt()` + - `Signal.prototype.pause()` -> `Signal.prototype.disable()` + - `Signal.prototype.resume()` -> `Signal.prototype.enable()` + - `Signal.prototype.isPaused()` -> `Signal.prototype.isEnabled()` + - `SignalBinding.prototype.pause()` -> `SignalBinding.prototype.disable()` + - `SignalBinding.prototype.resume()` -> `SignalBinding.prototype.enable()` + - `SignalBinding.prototype.isPaused()` -> `SignalBinding.prototype.isEnabled()` + +### Fixes ### + + - Calling `halt()` before/after `dispatch()` doesn't affect listeners execution anymore, `halt()` only works during propagation. + +### Test Changes ### + + - updated API calls to reflect new method names. + - added tests that match `halt()` before/after `dispatch()`. + +### Other ### + +Added inline documentation to source code and included an HTML version of the documentation together with distribution files. + + + +## v0.2 (2010/11/26) ## + +### API changes ### + + - Added: + - `Signal.prototype.pause()` + - `Signal.prototype.resume()` + - `Signal.prototype.isPaused()` + - `Signal.prototype.stopPropagation()` + +### Fixes ### + + - `SignalBinding.prototype.isPaused()` + +### Test Changes ### + + - Increased test coverage a lot. + - Tests added: + - pause/resume (for individual bindings and signal) + - stopPropagation (using `return false` and `Signal.prototype.stopPropagation()`) + - `SignalBindings.prototype.isOnce()` + - if same listener added twice returns same binding + +### Other ### + +Small refactoring and code cleaning. + + + +## v0.1 (2010/11/26) ## + + - initial release, support of basic features. \ No newline at end of file diff --git a/node_modules/signals/README.markdown b/node_modules/signals/README.markdown new file mode 100644 index 000000000..6ab0d8399 --- /dev/null +++ b/node_modules/signals/README.markdown @@ -0,0 +1,64 @@ + +# JS-Signals # + +Custom event/messaging system for JavaScript inspired by [AS3-Signals](https://github.com/robertpenner/as3-signals). + +For a more in-depth introduction read the [JS-Signals Project Page](http://millermedeiros.github.com/js-signals/) and visit the links below. + + +## Links ## + + * [Project Page](http://millermedeiros.github.com/js-signals/) + * [Wiki](http://github.com/millermedeiros/js-signals/wiki/) + * [Documentation](http://millermedeiros.github.com/js-signals/docs) + * [Changelog](http://github.com/millermedeiros/js-signals/blob/master/CHANGELOG.markdown) + + +## License ## + + * [MIT License](http://www.opensource.org/licenses/mit-license.php) + + +## Distribution Files ## + +Files inside `dist` folder. + + * docs/index.html : Documentation. + * js-signals.js : Uncompressed source code with comments. + * js-signals.amd.js : Uncompressed source code wrapped as an [asynchronous module](http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition) to be used together with [RequireJS](http://requirejs.org/). + * js-signals.cjs.js : Uncompressed source code wrapped as an [CommonJS module](http://wiki.commonjs.org/wiki/Modules/1.1) to be used on [nodejs](http://nodejs.org/) or any other environment that supports CommonJS modules. + * js-signals.min.js : Compressed code. + +You can install JS-Signals on Node.js using [NPM](http://npmjs.org/) + + npm install signals + + +## Repository Structure ## + +### Folder Structure ### + + dev -> development files + |- build -> files used on the build process + |- src -> source files + |- tests -> unit tests + dist -> distribution files + |- docs -> documentation + +### Branches ### + + master -> always contain code from the latest stable version + release-** -> code canditate for the next stable version (alpha/beta) + develop -> main development branch (nightly) + **other** -> features/hotfixes/experimental, probably non-stable code + + +## Building your own ## + +This project uses [Apache Ant](http://ant.apache.org/) for the build process. If for some reason you need to build a custom version of JS-Signals install Ant and run: + + ant build + +This will delete all JS files inside the `dist` folder, merge/update/compress source files, validate generated code using [JSLint](http://www.jslint.com/) and copy the output to the `dist` folder. + +**IMPORTANT:** `dist` folder always contain the latest version, regular users should **not** need to run build task. \ No newline at end of file diff --git a/node_modules/signals/dist/docs/files.html b/node_modules/signals/dist/docs/files.html new file mode 100644 index 000000000..94551df7d --- /dev/null +++ b/node_modules/signals/dist/docs/files.html @@ -0,0 +1,68 @@ + + + + + + JsDoc Reference - File Index + + + + + + + + + +
    +
    + +
    + +

    Index

    + +

    Classes

    + + +
    + + +
    + + Documentation generated by JsDoc Toolkit 2.4.0 on Tue May 03 2011 01:07:20 GMT-0300 (BRT) + | template based on Steffen Siering jsdoc-simple. +
    + + diff --git a/node_modules/signals/dist/docs/index.html b/node_modules/signals/dist/docs/index.html new file mode 100644 index 000000000..21dedd857 --- /dev/null +++ b/node_modules/signals/dist/docs/index.html @@ -0,0 +1,92 @@ + + + + + + JsDoc Reference - Index + + + + + + + + +
    +
    + +
    + +

    Index

    + +

    Classes

    + + +
    + +
    +

    Class Index

    + + +
    + +
    + Built-In Namespace +
    +

    _global_

    + +
    + +
    + +
    + Namespace +
    +

    signals

    + Signals Namespace - Custom event/messaging system based on AS3 Signals +
    + +
    + +
    + Class +
    +

    signals.Signal

    + +
    + +
    + +
    + Class +
    +

    signals.SignalBinding

    + +
    + +
    + +
    +
    + + Documentation generated by JsDoc Toolkit 2.4.0 on Tue May 03 2011 01:07:20 GMT-0300 (BRT) + | template based on Steffen Siering jsdoc-simple. +
    + + diff --git a/node_modules/signals/dist/docs/static/default.css b/node_modules/signals/dist/docs/static/default.css new file mode 100644 index 000000000..0401aef2f --- /dev/null +++ b/node_modules/signals/dist/docs/static/default.css @@ -0,0 +1,248 @@ +/* + * based on urso jsdoc-simple template: http://github.com/urso/jsdoc-simple + * adapted by Miller Medeiros (http://millermedeiros.com/) + */ + +/* default.css */ +html{ + overflow-y:scroll; +} + +body +{ + font: 12px "Lucida Grande", Tahoma, Arial, Helvetica, sans-serif; + min-width: 1000px; + max-width: 1400px; + margin:0 auto; +} + +.header +{ + clear: both; + background-color: #ccc; +} + +a +{ + text-decoration: none; + color: #c00; + outline:none; +} + +a:active, a:focus, a:hover{ + color: #333; +} + +h1 +{ + font-size: 1.5em; + font-weight: bold; + padding: 0; + margin: 1em 0 0 .3em; +} + +hr +{ + border: none 0; + border-top: 1px solid #7F8FB1; + height: 1px; +} + +pre.code +{ + display: block; + padding: 8px; + border: 1px dashed #ccc; +} + +#header{ + /*height: 110px;*/ +} + +#index +{ + float: left; + width: 200px; + padding: 20px; +} + +#symbolList +{ + margin: 20px; + width: 200px; + float:right; +} + +#symbolList ul +{ + padding: 0; + margin: 0; + padding-left: 8px; + list-style: none; + font-size: 0.85em; +} + +#symbolList h3 +{ + margin-top:1.2em; + margin-bottom: 0.5em; +} + +#symbolList ul li +{ + padding: 0; + margin: 0; +} + +#content +{ + text-align: left; + padding:0 260px; + margin:0; +} + +.classList +{ + list-style-type: none; + padding: 0; + margin: 0 0 0 8px; + font-family: arial, sans-serif; + font-size: 1em; + overflow: auto; +} + +.classList li +{ + padding: 0; + margin: 0 0 8px 0; +} + +.summaryTable { width: 100%; } + +h1.classTitle +{ + font-size:1.7em; + line-height:1.3em; +} + +h2 { font-size: 1.1em; } +caption, div.sectionTitle +{ + background-color: #ddd; + color: #333; + font-size:1.3em; + text-align: left; + padding: 2px 6px 2px 6px; + margin-top: 1.5em; + border: 1px #ddd solid; +} + +div.sectionTitle { margin-bottom: 8px; } +.summaryTable thead { display: none; } + +.summaryTable td +{ + vertical-align: top; + padding: 4px; + border-bottom: 1px #7F8FB1 solid; + border-right: 1px #7F8FB1 solid; +} + +/*col#summaryAttributes {}*/ +.summaryTable td.attributes +{ + border-left: 1px #7F8FB1 solid; + width: 140px; + text-align: right; +} + +.fixedFont b +{ + color: #c00; +} + +td.attributes, .fixedFont +{ + line-height: 1.1em; + /* color: #002EBE; */ + font-family: "Courier New",Courier,monospace; + font-size: 1.3em; +} + +.modifiers { + float: right; + /* padding: 0 2em 0 2em; */ + padding: 0; + font-size: 0.85em; +} + +.member .description +{ + margin: 0.75em 0 0 0; + padding: 0 0.5em 0 0.5em; +} + +.summaryTable td.nameDescription +{ + text-align: left; + font-size: 1.1em; + line-height: 1.2em; +} + +.summaryTable td.nameDescription, .description +{ + line-height: 15px; + padding: 4px; + padding-left: 4px; +} + +.summaryTable { margin-bottom: 8px; } + +ul.inheritsList +{ + list-style: square; + margin-left: 20px; + padding-left: 0; +} + +.detailList { + margin-left: 20px; + line-height: 15px; +} +.detailList dt { margin-left: 20px; } + +.detailList .heading +{ + font-weight: bold; + padding-bottom: 6px; + margin-left: 0; +} + +.member +{ + border: 1px solid #ccc; + background: #f8f8ff; + margin: 1em 0 1em 0; + padding: 0.75em; +} + +.light, td.attributes, .light a:link, .light a:visited +{ + color: #777; + font-style: italic; +} + +code { + /*display: block; + margin: 1em;*/ + border: 1px solid #ccc; + padding: 2px 5px; + background: #f8f8ff; +} + +.fineprint +{ + text-align: right; + font-size: 10px; + padding:10px 0 20px; +} diff --git a/node_modules/signals/dist/docs/symbolindex.html b/node_modules/signals/dist/docs/symbolindex.html new file mode 100644 index 000000000..a76598b66 --- /dev/null +++ b/node_modules/signals/dist/docs/symbolindex.html @@ -0,0 +1,279 @@ + + + + + + JsDoc Reference - Index + + + + + + + + + + + + +
    +
    + +
    + +

    Index

    + +

    Classes

    + + +
    + +
    +

    Symbol Index

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    active + + signals.Signal#active + + signals.SignalBinding#active + +
    add + + signals.Signal#add + +
    addOnce + + signals.Signal#addOnce + +
    context + + signals.SignalBinding#context + +
    detach + + signals.SignalBinding#detach + +
    dispatch + + signals.Signal#dispatch + +
    dispose + + signals.Signal#dispose + + signals.SignalBinding#dispose + +
    execute + + signals.SignalBinding#execute + +
    getListener + + signals.SignalBinding#getListener + +
    getNumListeners + + signals.Signal#getNumListeners + +
    halt + + signals.Signal#halt + +
    isOnce + + signals.SignalBinding#isOnce + +
    remove + + signals.Signal#remove + +
    removeAll + + signals.Signal#removeAll + +
    Signal + + signals.Signal + +
    SignalBinding + + signals.SignalBinding + +
    signals + + signals + +
    toString + + signals.Signal#toString + + signals.SignalBinding#toString + +
    VERSION + + signals.VERSION + +
    +
    +
    +
    +
    + + Documentation generated by JsDoc Toolkit 2.4.0 on Tue May 03 2011 01:07:20 GMT-0300 (BRT) + | template based on Steffen Siering jsdoc-simple. +
    + + + diff --git a/node_modules/signals/dist/docs/symbols/_global_.html b/node_modules/signals/dist/docs/symbols/_global_.html new file mode 100644 index 000000000..120d62858 --- /dev/null +++ b/node_modules/signals/dist/docs/symbols/_global_.html @@ -0,0 +1,108 @@ + + + + + + + JsDoc Reference - _global_ + + + + + + + + + + + + + +
    +
    + +
    + +

    Index

    + +

    Classes

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

    + Built-In Namespace _global_ +

    + + +

    + + + + + + +

    + + + + + + + + + + + + +
    +
    + + + +
    + + Documentation generated by JsDoc Toolkit 2.4.0 on Tue May 03 2011 01:07:19 GMT-0300 (BRT) + | template based on Steffen Siering jsdoc-simple. +
    + + diff --git a/node_modules/signals/dist/docs/symbols/signals.Signal.html b/node_modules/signals/dist/docs/symbols/signals.Signal.html new file mode 100644 index 000000000..e15bee918 --- /dev/null +++ b/node_modules/signals/dist/docs/symbols/signals.Signal.html @@ -0,0 +1,594 @@ + + + + + + + JsDoc Reference - signals.Signal + + + + + + + + + + + + + +
    +
    + +
    + +

    Index

    + +

    Classes

    + + +
    + +
    + + +

    + Class +

    + + + + + + + +

    Fields

    + + + + + + + +

    Methods

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

    + Class signals.Signal +

    + + +

    + + + + + + +
    Defined in: js-signals.js. + +

    + + + +
    +
    + Class Detail +
    + +
    + signals.Signal() +
    + +
    +

    Custom event broadcaster +
    - inspired by Robert Penner's AS3 Signals.

    +
    Author: Miller Medeiros. +
    + + + + + + + + + + + + +
    + + + + +
    +
    + Field Detail +
    + +
    + +
    +
    +
    + + {boolean} + active + +
    +
    +

    If Signal is active and should broadcast events.

    + +

    IMPORTANT: Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.

    + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + Method Detail +
    + +
    + +
    +
    +
    + {SignalBinding} + add(listener, scope, priority) + +
    +
    +

    Add a listener to the signal.

    + + +
    + + + + +
    +
    Parameters:
    + +
    + {Function} listener + +
    +
    Signal handler function.
    + +
    + {Object} scope + Optional +
    +
    Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    + +
    + {Number} priority + Optional +
    +
    The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
    + +
    + + + + + +
    +
    Returns:
    + +
    {SignalBinding} An Object representing the binding between the Signal and listener.
    + +
    + + + + +
    + +
    + +
    +
    +
    + {SignalBinding} + addOnce(listener, scope, priority) + +
    +
    +

    Add listener to the signal that should be removed after first execution (will be executed only once).

    + + +
    + + + + +
    +
    Parameters:
    + +
    + {Function} listener + +
    +
    Signal handler function.
    + +
    + {Object} scope + Optional +
    +
    Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    + +
    + {Number} priority + Optional +
    +
    The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
    + +
    + + + + + +
    +
    Returns:
    + +
    {SignalBinding} An Object representing the binding between the Signal and listener.
    + +
    + + + + +
    + +
    + +
    +
    +
    + + dispatch(params) + +
    +
    +

    Dispatch/Broadcast Signal to all listeners added to the queue.

    + + +
    + + + + +
    +
    Parameters:
    + +
    + {...*} params + Optional +
    +
    Parameters that should be passed to each handler.
    + +
    + + + + + + + + +
    + +
    + +
    +
    +
    + + dispose() + +
    +
    +

    Remove all bindings from signal and destroy any reference to external objects (destroy Signal object).

    + +

    IMPORTANT: calling any method on the signal instance after calling dispose will throw errors.

    + + +
    + + + + + + + + + + + +
    + +
    + +
    +
    +
    + {number} + getNumListeners() + +
    +
    + + + +
    + + + + + + + + +
    +
    Returns:
    + +
    {number} Number of listeners attached to the Signal.
    + +
    + + + + +
    + +
    + +
    +
    +
    + + halt() + +
    +
    +

    Stop propagation of the event, blocking the dispatch to next listeners on the queue.

    + +

    IMPORTANT: should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.

    + + +
    + + + + + + + + + + +
    +
    See:
    + +
    signals.Signal.prototype.disable
    + +
    + + +
    + +
    + +
    +
    +
    + {Function} + remove(listener) + +
    +
    +

    Remove a single listener from the dispatch queue.

    + + +
    + + + + +
    +
    Parameters:
    + +
    + {Function} listener + +
    +
    Handler function that should be removed.
    + +
    + + + + + +
    +
    Returns:
    + +
    {Function} Listener handler function.
    + +
    + + + + +
    + +
    + +
    +
    +
    + + removeAll() + +
    +
    +

    Remove all listeners from the Signal.

    + + +
    + + + + + + + + + + + +
    + +
    + +
    +
    +
    + {string} + toString() + +
    +
    + + + +
    + + + + + + + + +
    +
    Returns:
    + +
    {string} String representation of the object.
    + +
    + + + + +
    + +
    + + + + +
    +
    + + + +
    + + Documentation generated by JsDoc Toolkit 2.4.0 on Tue May 03 2011 01:07:20 GMT-0300 (BRT) + | template based on Steffen Siering jsdoc-simple. +
    + + diff --git a/node_modules/signals/dist/docs/symbols/signals.SignalBinding.html b/node_modules/signals/dist/docs/symbols/signals.SignalBinding.html new file mode 100644 index 000000000..b578d9d68 --- /dev/null +++ b/node_modules/signals/dist/docs/symbols/signals.SignalBinding.html @@ -0,0 +1,500 @@ + + + + + + + JsDoc Reference - signals.SignalBinding + + + + + + + + + + + + + +
    +
    + +
    + +

    Index

    + +

    Classes

    + + +
    + +
    + + +

    + Class +

    + + + + + + + +

    Fields

    + + + + + + + +

    Methods

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

    + Class signals.SignalBinding +

    + + +

    + + + + + + +
    Defined in: js-signals.js. + +

    + + + +
    +
    + Class Detail +
    + +
    + signals.SignalBinding(signal, listener, isOnce, listenerContext, priority) +
    + +
    +

    Object that represents a binding between a Signal and a listener function. +
    - This is an internal constructor and shouldn't be called by regular users. +
    - inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes.

    +
    Author: Miller Medeiros. +
    + + + + + +
    +
    Parameters:
    + +
    + {signals.Signal} signal + +
    +
    Reference to Signal object that listener is currently bound to.
    + +
    + {Function} listener + +
    +
    Handler function bound to the signal.
    + +
    + {boolean} isOnce + +
    +
    If binding should be executed just once.
    + +
    + {Object} listenerContext + Optional +
    +
    Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    + +
    + {Number} priority + Optional +
    +
    The priority level of the event listener. (default = 0).
    + +
    + + + + + + + + +
    + + + + +
    +
    + Field Detail +
    + +
    + +
    +
    +
    + + {boolean} + active + +
    +
    +

    If binding is active and should be executed.

    + + +
    + + + + + + + +
    + +
    + +
    +
    +
    + + {Object|undefined|null} + context + +
    +
    +

    Context on which listener will be executed (object that should represent the this variable inside listener function).

    + + +
    + + + + + + + +
    + +
    + + + + +
    +
    + Method Detail +
    + +
    + +
    +
    +
    + {Function} + detach() + +
    +
    +

    Detach binding from signal. +- alias to: mySignal.remove(myBinding.getListener());

    + + +
    + + + + + + + + +
    +
    Returns:
    + +
    {Function} Handler function bound to the signal.
    + +
    + + + + +
    + +
    + +
    +
    +
    + + dispose() + +
    +
    +

    Remove binding from signal and destroy any reference to external Objects (destroy SignalBinding object).

    + +

    IMPORTANT: calling methods on the binding instance after calling dispose will throw errors.

    + + +
    + + + + + + + + + + + +
    + +
    + +
    +
    +
    + {*} + execute(paramsArr) + +
    +
    +

    Call listener passing arbitrary parameters.

    + +

    If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.

    + + +
    + + + + +
    +
    Parameters:
    + +
    + {Array} paramsArr + Optional +
    +
    Array of parameters that should be passed to the listener
    + +
    + + + + + +
    +
    Returns:
    + +
    {*} Value returned by the listener.
    + +
    + + + + +
    + +
    + +
    +
    +
    + {Function} + getListener() + +
    +
    + + + +
    + + + + + + + + +
    +
    Returns:
    + +
    {Function} Handler function bound to the signal.
    + +
    + + + + +
    + +
    + +
    +
    +
    + {boolean} + isOnce() + +
    +
    + + + +
    + + + + + + + + +
    +
    Returns:
    + +
    {boolean} If SignalBinding will only be executed once.
    + +
    + + + + +
    + +
    + +
    +
    +
    + {string} + toString() + +
    +
    + + + +
    + + + + + + + + +
    +
    Returns:
    + +
    {string} String representation of the object.
    + +
    + + + + +
    + +
    + + + + +
    +
    + + + +
    + + Documentation generated by JsDoc Toolkit 2.4.0 on Tue May 03 2011 01:07:20 GMT-0300 (BRT) + | template based on Steffen Siering jsdoc-simple. +
    + + diff --git a/node_modules/signals/dist/docs/symbols/signals.html b/node_modules/signals/dist/docs/symbols/signals.html new file mode 100644 index 000000000..263c648c2 --- /dev/null +++ b/node_modules/signals/dist/docs/symbols/signals.html @@ -0,0 +1,186 @@ + + + + + + + JsDoc Reference - signals + + + + + + + + + + + + + +
    +
    + +
    + +

    Index

    + +

    Classes

    + + +
    + +
    + + +

    + Namespace +

    + + + + + + + +

    Variables

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

    + Namespace signals +

    + + +

    + + + + Signals Namespace - Custom event/messaging system based on AS3 Signals + + +
    Defined in: js-signals.js. + +

    + + + +
    +
    + Namespace Detail +
    + +
    + signals +
    + +
    + + +
    + + + + + + + + + + + + +
    + + + + +
    +
    + Field Detail +
    + +
    + +
    +
    <static> +
    + + {String} + signals.VERSION + +
    +
    +

    Signals Version Number

    + + +
    + + + + + + + +
    + +
    + + + + + + + +
    +
    + + + +
    + + Documentation generated by JsDoc Toolkit 2.4.0 on Tue May 03 2011 01:07:19 GMT-0300 (BRT) + | template based on Steffen Siering jsdoc-simple. +
    + + diff --git a/node_modules/signals/dist/docs/symbols/src/_Users_millermedeiros_Projects__open_source_js-signals_dist_js-signals.js.html b/node_modules/signals/dist/docs/symbols/src/_Users_millermedeiros_Projects__open_source_js-signals_dist_js-signals.js.html new file mode 100644 index 000000000..f7c14d510 --- /dev/null +++ b/node_modules/signals/dist/docs/symbols/src/_Users_millermedeiros_Projects__open_source_js-signals_dist_js-signals.js.html @@ -0,0 +1,363 @@ +
      1 /*jslint onevar:true, undef:true, newcap:true, regexp:true, bitwise:true, maxerr:50, indent:4, white:false, nomen:false, plusplus:false */
    +  2 /*global window:false, global:false*/
    +  3 
    +  4 /*!!
    +  5  * JS Signals <http://millermedeiros.github.com/js-signals/>
    +  6  * Released under the MIT license <http://www.opensource.org/licenses/mit-license.php>
    +  7  * @author Miller Medeiros <http://millermedeiros.com/>
    +  8  * @version 0.6.1
    +  9  * @build 178 (05/03/2011 01:07 AM)
    + 10  */
    + 11 (function(global){
    + 12 	
    + 13 	/**
    + 14 	 * @namespace Signals Namespace - Custom event/messaging system based on AS3 Signals
    + 15 	 * @name signals
    + 16 	 */
    + 17 	var signals = /** @lends signals */{
    + 18 		/**
    + 19 		 * Signals Version Number
    + 20 		 * @type String
    + 21 		 * @const
    + 22 		 */
    + 23 		VERSION : '0.6.1'
    + 24 	};
    + 25 
    + 26 	// SignalBinding -------------------------------------------------
    + 27 	//================================================================
    + 28 	
    + 29 	/**
    + 30 	 * Object that represents a binding between a Signal and a listener function.
    + 31 	 * <br />- <strong>This is an internal constructor and shouldn't be called by regular users.</strong>
    + 32 	 * <br />- inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes.
    + 33 	 * @author Miller Medeiros
    + 34 	 * @constructor
    + 35 	 * @internal
    + 36 	 * @name signals.SignalBinding
    + 37 	 * @param {signals.Signal} signal	Reference to Signal object that listener is currently bound to.
    + 38 	 * @param {Function} listener	Handler function bound to the signal.
    + 39 	 * @param {boolean} isOnce	If binding should be executed just once.
    + 40 	 * @param {Object} [listenerContext]	Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    + 41 	 * @param {Number} [priority]	The priority level of the event listener. (default = 0).
    + 42 	 */
    + 43 	 function SignalBinding(signal, listener, isOnce, listenerContext, priority){
    + 44 		
    + 45 		/**
    + 46 		 * Handler function bound to the signal.
    + 47 		 * @type Function
    + 48 		 * @private
    + 49 		 */
    + 50 		this._listener = listener;
    + 51 		
    + 52 		/**
    + 53 		 * If binding should be executed just once.
    + 54 		 * @type boolean
    + 55 		 * @private
    + 56 		 */
    + 57 		this._isOnce = isOnce;
    + 58 		
    + 59 		/**
    + 60 		 * Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    + 61 		 * @memberOf signals.SignalBinding.prototype
    + 62 		 * @name context
    + 63 		 * @type Object|undefined|null
    + 64 		 */
    + 65 		this.context = listenerContext;
    + 66 		
    + 67 		/**
    + 68 		 * Reference to Signal object that listener is currently bound to.
    + 69 		 * @type signals.Signal
    + 70 		 * @private
    + 71 		 */
    + 72 		this._signal = signal;
    + 73 		
    + 74 		/**
    + 75 		 * Listener priority
    + 76 		 * @type Number
    + 77 		 * @private
    + 78 		 */
    + 79 		this._priority = priority || 0;
    + 80 	}
    + 81 	
    + 82 	SignalBinding.prototype = /** @lends signals.SignalBinding.prototype */ {
    + 83 		
    + 84 		/**
    + 85 		 * If binding is active and should be executed.
    + 86 		 * @type boolean
    + 87 		 */
    + 88 		active : true,
    + 89 		
    + 90 		/**
    + 91 		 * Call listener passing arbitrary parameters.
    + 92 		 * <p>If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.</p> 
    + 93 		 * @param {Array} [paramsArr]	Array of parameters that should be passed to the listener
    + 94 		 * @return {*} Value returned by the listener.
    + 95 		 */
    + 96 		execute : function(paramsArr){
    + 97 			var r;
    + 98 			if(this.active){
    + 99 				r = this._listener.apply(this.context, paramsArr);
    +100 				if(this._isOnce){
    +101 					this.detach();
    +102 				}
    +103 			}
    +104 			return r;
    +105 		},
    +106 		
    +107 		/**
    +108 		 * Detach binding from signal.
    +109 		 * - alias to: mySignal.remove(myBinding.getListener());
    +110 		 * @return {Function} Handler function bound to the signal.
    +111 		 */
    +112 		detach : function(){
    +113 			return this._signal.remove(this._listener);
    +114 		},
    +115 		
    +116 		/**
    +117 		 * @return {Function} Handler function bound to the signal.
    +118 		 */
    +119 		getListener : function(){
    +120 			return this._listener;
    +121 		},
    +122 		
    +123 		/**
    +124 		 * Remove binding from signal and destroy any reference to external Objects (destroy SignalBinding object).
    +125 		 * <p><strong>IMPORTANT:</strong> calling methods on the binding instance after calling dispose will throw errors.</p>
    +126 		 */
    +127 		dispose : function(){
    +128 			this.detach();
    +129 			this._destroy();
    +130 		},
    +131 		
    +132 		/**
    +133 		 * Delete all instance properties
    +134 		 * @private
    +135 		 */
    +136 		_destroy : function(){
    +137 			delete this._signal;
    +138 			delete this._isOnce;
    +139 			delete this._listener;
    +140 			delete this.context;
    +141 		},
    +142 		
    +143 		/**
    +144 		 * @return {boolean} If SignalBinding will only be executed once.
    +145 		 */
    +146 		isOnce : function(){
    +147 			return this._isOnce;
    +148 		},
    +149 		
    +150 		/**
    +151 		 * @return {string} String representation of the object.
    +152 		 */
    +153 		toString : function(){
    +154 			return '[SignalBinding isOnce: '+ this._isOnce +', active: '+ this.active +']';
    +155 		}
    +156 		
    +157 	};
    +158 
    +159 /*global signals:true, SignalBinding:true*/
    +160 	
    +161 	// Signal --------------------------------------------------------
    +162 	//================================================================
    +163 	
    +164 	/**
    +165 	 * Custom event broadcaster
    +166 	 * <br />- inspired by Robert Penner's AS3 Signals.
    +167 	 * @author Miller Medeiros
    +168 	 * @constructor
    +169 	 */
    +170 	signals.Signal = function(){
    +171 		/**
    +172 		 * @type Array.<SignalBinding>
    +173 		 * @private
    +174 		 */
    +175 		this._bindings = [];
    +176 	};
    +177 	
    +178 	signals.Signal.prototype = {
    +179 		
    +180 		/**
    +181 		 * @type boolean
    +182 		 * @private
    +183 		 */
    +184 		_shouldPropagate : true,
    +185 		
    +186 		/**
    +187 		 * If Signal is active and should broadcast events.
    +188 		 * <p><strong>IMPORTANT:</strong> Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.</p>
    +189 		 * @type boolean
    +190 		 */
    +191 		active : true,
    +192 		
    +193 		/**
    +194 		 * @param {Function} listener
    +195 		 * @param {boolean} isOnce
    +196 		 * @param {Object} [scope]
    +197 		 * @param {Number} [priority]
    +198 		 * @return {SignalBinding}
    +199 		 * @private
    +200 		 */
    +201 		_registerListener : function(listener, isOnce, scope, priority){
    +202 			
    +203 			if(typeof listener !== 'function'){
    +204 				throw new Error('listener is a required param of add() and addOnce() and should be a Function.');
    +205 			}
    +206 			
    +207 			var prevIndex = this._indexOfListener(listener),
    +208 				binding;
    +209 			
    +210 			if(prevIndex !== -1){ //avoid creating a new Binding for same listener if already added to list
    +211 				binding = this._bindings[prevIndex];
    +212 				if(binding.isOnce() !== isOnce){
    +213 					throw new Error('You cannot add'+ (isOnce? '' : 'Once') +'() then add'+ (!isOnce? '' : 'Once') +'() the same listener without removing the relationship first.');
    +214 				}
    +215 			} else {
    +216 				binding = new SignalBinding(this, listener, isOnce, scope, priority);
    +217 				this._addBinding(binding);
    +218 			}
    +219 			
    +220 			return binding;
    +221 		},
    +222 		
    +223 		/**
    +224 		 * @param {Function} binding
    +225 		 * @private
    +226 		 */
    +227 		_addBinding : function(binding){
    +228 			//simplified insertion sort
    +229 			var n = this._bindings.length;
    +230 			do { --n; } while (this._bindings[n] && binding._priority <= this._bindings[n]._priority);
    +231 			this._bindings.splice(n+1, 0, binding);
    +232 		},
    +233 		
    +234 		/**
    +235 		 * @param {Function} listener
    +236 		 * @return {number}
    +237 		 * @private
    +238 		 */
    +239 		_indexOfListener : function(listener){
    +240 			var n = this._bindings.length;
    +241 			while(n--){
    +242 				if(this._bindings[n]._listener === listener){
    +243 					return n;
    +244 				}
    +245 			}
    +246 			return -1;
    +247 		},
    +248 		
    +249 		/**
    +250 		 * Add a listener to the signal.
    +251 		 * @param {Function} listener	Signal handler function.
    +252 		 * @param {Object} [scope]	Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    +253 		 * @param {Number} [priority]	The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
    +254 		 * @return {SignalBinding} An Object representing the binding between the Signal and listener.
    +255 		 */
    +256 		add : function(listener, scope, priority){
    +257 			return this._registerListener(listener, false, scope, priority);
    +258 		},
    +259 		
    +260 		/**
    +261 		 * Add listener to the signal that should be removed after first execution (will be executed only once).
    +262 		 * @param {Function} listener	Signal handler function.
    +263 		 * @param {Object} [scope]	Context on which listener will be executed (object that should represent the `this` variable inside listener function).
    +264 		 * @param {Number} [priority]	The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0)
    +265 		 * @return {SignalBinding} An Object representing the binding between the Signal and listener.
    +266 		 */
    +267 		addOnce : function(listener, scope, priority){
    +268 			return this._registerListener(listener, true, scope, priority);
    +269 		},
    +270 		
    +271 		/**
    +272 		 * Remove a single listener from the dispatch queue.
    +273 		 * @param {Function} listener	Handler function that should be removed.
    +274 		 * @return {Function} Listener handler function.
    +275 		 */
    +276 		remove : function(listener){
    +277 			if(typeof listener !== 'function'){
    +278 				throw new Error('listener is a required param of remove() and should be a Function.');
    +279 			}
    +280 			
    +281 			var i = this._indexOfListener(listener);
    +282 			if(i !== -1){
    +283 				this._bindings[i]._destroy(); //no reason to a SignalBinding exist if it isn't attached to a signal
    +284 				this._bindings.splice(i, 1);
    +285 			}
    +286 			return listener;
    +287 		},
    +288 		
    +289 		/**
    +290 		 * Remove all listeners from the Signal.
    +291 		 */
    +292 		removeAll : function(){
    +293 			var n = this._bindings.length;
    +294 			while(n--){
    +295 				this._bindings[n]._destroy();
    +296 			}
    +297 			this._bindings.length = 0;
    +298 		},
    +299 		
    +300 		/**
    +301 		 * @return {number} Number of listeners attached to the Signal.
    +302 		 */
    +303 		getNumListeners : function(){
    +304 			return this._bindings.length;
    +305 		},
    +306 		
    +307 		/**
    +308 		 * Stop propagation of the event, blocking the dispatch to next listeners on the queue.
    +309 		 * <p><strong>IMPORTANT:</strong> should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.</p>
    +310 		 * @see signals.Signal.prototype.disable 
    +311 		 */
    +312 		halt : function(){
    +313 			this._shouldPropagate = false;
    +314 		},
    +315 		
    +316 		/**
    +317 		 * Dispatch/Broadcast Signal to all listeners added to the queue. 
    +318 		 * @param {...*} [params]	Parameters that should be passed to each handler.
    +319 		 */
    +320 		dispatch : function(params){
    +321 			if(! this.active){
    +322 				return;
    +323 			}
    +324 			
    +325 			var paramsArr = Array.prototype.slice.call(arguments),
    +326 				bindings = this._bindings.slice(), //clone array in case add/remove items during dispatch
    +327 				n = this._bindings.length;
    +328 			
    +329 			this._shouldPropagate = true; //in case `halt` was called before dispatch or during the previous dispatch.
    +330 			
    +331 			//execute all callbacks until end of the list or until a callback returns `false` or stops propagation
    +332 			//reverse loop since listeners with higher priority will be added at the end of the list
    +333 			do { n--; } while (bindings[n] && this._shouldPropagate && bindings[n].execute(paramsArr) !== false);
    +334 		},
    +335 		
    +336 		/**
    +337 		 * Remove all bindings from signal and destroy any reference to external objects (destroy Signal object).
    +338 		 * <p><strong>IMPORTANT:</strong> calling any method on the signal instance after calling dispose will throw errors.</p>
    +339 		 */
    +340 		dispose : function(){
    +341 			this.removeAll();
    +342 			delete this._bindings;
    +343 		},
    +344 		
    +345 		/**
    +346 		 * @return {string} String representation of the object.
    +347 		 */
    +348 		toString : function(){
    +349 			return '[Signal active: '+ this.active +' numListeners: '+ this.getNumListeners() +']';
    +350 		}
    +351 		
    +352 	};
    +353 
    +354 	global.signals = signals;
    +355 	
    +356 }(window || global || this));
    \ No newline at end of file diff --git a/node_modules/signals/dist/js-signals.cjs.js b/node_modules/signals/dist/js-signals.cjs.js new file mode 100644 index 000000000..3f61d391f --- /dev/null +++ b/node_modules/signals/dist/js-signals.cjs.js @@ -0,0 +1,354 @@ +/*jslint onevar:true, undef:true, newcap:true, regexp:true, bitwise:true, maxerr:50, indent:4, white:false, nomen:false, plusplus:false */ +/*global module:false*/ + +/*!! + * JS Signals + * Released under the MIT license + * @author Miller Medeiros + * @version 0.6.1 + * @build 179 (05/03/2011 01:20 AM) + */ + + /** + * @namespace Signals Namespace - Custom event/messaging system based on AS3 Signals + * @name signals + */ + var signals = /** @lends signals */{ + /** + * Signals Version Number + * @type String + * @const + */ + VERSION : '0.6.1' + }; + + // SignalBinding ------------------------------------------------- + //================================================================ + + /** + * Object that represents a binding between a Signal and a listener function. + *
    - This is an internal constructor and shouldn't be called by regular users. + *
    - inspired by Joa Ebert AS3 SignalBinding and Robert Penner's Slot classes. + * @author Miller Medeiros + * @constructor + * @internal + * @name signals.SignalBinding + * @param {signals.Signal} signal Reference to Signal object that listener is currently bound to. + * @param {Function} listener Handler function bound to the signal. + * @param {boolean} isOnce If binding should be executed just once. + * @param {Object} [listenerContext] Context on which listener will be executed (object that should represent the `this` variable inside listener function). + * @param {Number} [priority] The priority level of the event listener. (default = 0). + */ + function SignalBinding(signal, listener, isOnce, listenerContext, priority){ + + /** + * Handler function bound to the signal. + * @type Function + * @private + */ + this._listener = listener; + + /** + * If binding should be executed just once. + * @type boolean + * @private + */ + this._isOnce = isOnce; + + /** + * Context on which listener will be executed (object that should represent the `this` variable inside listener function). + * @memberOf signals.SignalBinding.prototype + * @name context + * @type Object|undefined|null + */ + this.context = listenerContext; + + /** + * Reference to Signal object that listener is currently bound to. + * @type signals.Signal + * @private + */ + this._signal = signal; + + /** + * Listener priority + * @type Number + * @private + */ + this._priority = priority || 0; + } + + SignalBinding.prototype = /** @lends signals.SignalBinding.prototype */ { + + /** + * If binding is active and should be executed. + * @type boolean + */ + active : true, + + /** + * Call listener passing arbitrary parameters. + *

    If binding was added using `Signal.addOnce()` it will be automatically removed from signal dispatch queue, this method is used internally for the signal dispatch.

    + * @param {Array} [paramsArr] Array of parameters that should be passed to the listener + * @return {*} Value returned by the listener. + */ + execute : function(paramsArr){ + var r; + if(this.active){ + r = this._listener.apply(this.context, paramsArr); + if(this._isOnce){ + this.detach(); + } + } + return r; + }, + + /** + * Detach binding from signal. + * - alias to: mySignal.remove(myBinding.getListener()); + * @return {Function} Handler function bound to the signal. + */ + detach : function(){ + return this._signal.remove(this._listener); + }, + + /** + * @return {Function} Handler function bound to the signal. + */ + getListener : function(){ + return this._listener; + }, + + /** + * Remove binding from signal and destroy any reference to external Objects (destroy SignalBinding object). + *

    IMPORTANT: calling methods on the binding instance after calling dispose will throw errors.

    + */ + dispose : function(){ + this.detach(); + this._destroy(); + }, + + /** + * Delete all instance properties + * @private + */ + _destroy : function(){ + delete this._signal; + delete this._isOnce; + delete this._listener; + delete this.context; + }, + + /** + * @return {boolean} If SignalBinding will only be executed once. + */ + isOnce : function(){ + return this._isOnce; + }, + + /** + * @return {string} String representation of the object. + */ + toString : function(){ + return '[SignalBinding isOnce: '+ this._isOnce +', active: '+ this.active +']'; + } + + }; + +/*global signals:true, SignalBinding:true*/ + + // Signal -------------------------------------------------------- + //================================================================ + + /** + * Custom event broadcaster + *
    - inspired by Robert Penner's AS3 Signals. + * @author Miller Medeiros + * @constructor + */ + signals.Signal = function(){ + /** + * @type Array. + * @private + */ + this._bindings = []; + }; + + signals.Signal.prototype = { + + /** + * @type boolean + * @private + */ + _shouldPropagate : true, + + /** + * If Signal is active and should broadcast events. + *

    IMPORTANT: Setting this property during a dispatch will only affect the next dispatch, if you want to stop the propagation of a signal use `halt()` instead.

    + * @type boolean + */ + active : true, + + /** + * @param {Function} listener + * @param {boolean} isOnce + * @param {Object} [scope] + * @param {Number} [priority] + * @return {SignalBinding} + * @private + */ + _registerListener : function(listener, isOnce, scope, priority){ + + if(typeof listener !== 'function'){ + throw new Error('listener is a required param of add() and addOnce() and should be a Function.'); + } + + var prevIndex = this._indexOfListener(listener), + binding; + + if(prevIndex !== -1){ //avoid creating a new Binding for same listener if already added to list + binding = this._bindings[prevIndex]; + if(binding.isOnce() !== isOnce){ + throw new Error('You cannot add'+ (isOnce? '' : 'Once') +'() then add'+ (!isOnce? '' : 'Once') +'() the same listener without removing the relationship first.'); + } + } else { + binding = new SignalBinding(this, listener, isOnce, scope, priority); + this._addBinding(binding); + } + + return binding; + }, + + /** + * @param {Function} binding + * @private + */ + _addBinding : function(binding){ + //simplified insertion sort + var n = this._bindings.length; + do { --n; } while (this._bindings[n] && binding._priority <= this._bindings[n]._priority); + this._bindings.splice(n+1, 0, binding); + }, + + /** + * @param {Function} listener + * @return {number} + * @private + */ + _indexOfListener : function(listener){ + var n = this._bindings.length; + while(n--){ + if(this._bindings[n]._listener === listener){ + return n; + } + } + return -1; + }, + + /** + * Add a listener to the signal. + * @param {Function} listener Signal handler function. + * @param {Object} [scope] Context on which listener will be executed (object that should represent the `this` variable inside listener function). + * @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0) + * @return {SignalBinding} An Object representing the binding between the Signal and listener. + */ + add : function(listener, scope, priority){ + return this._registerListener(listener, false, scope, priority); + }, + + /** + * Add listener to the signal that should be removed after first execution (will be executed only once). + * @param {Function} listener Signal handler function. + * @param {Object} [scope] Context on which listener will be executed (object that should represent the `this` variable inside listener function). + * @param {Number} [priority] The priority level of the event listener. Listeners with higher priority will be executed before listeners with lower priority. Listeners with same priority level will be executed at the same order as they were added. (default = 0) + * @return {SignalBinding} An Object representing the binding between the Signal and listener. + */ + addOnce : function(listener, scope, priority){ + return this._registerListener(listener, true, scope, priority); + }, + + /** + * Remove a single listener from the dispatch queue. + * @param {Function} listener Handler function that should be removed. + * @return {Function} Listener handler function. + */ + remove : function(listener){ + if(typeof listener !== 'function'){ + throw new Error('listener is a required param of remove() and should be a Function.'); + } + + var i = this._indexOfListener(listener); + if(i !== -1){ + this._bindings[i]._destroy(); //no reason to a SignalBinding exist if it isn't attached to a signal + this._bindings.splice(i, 1); + } + return listener; + }, + + /** + * Remove all listeners from the Signal. + */ + removeAll : function(){ + var n = this._bindings.length; + while(n--){ + this._bindings[n]._destroy(); + } + this._bindings.length = 0; + }, + + /** + * @return {number} Number of listeners attached to the Signal. + */ + getNumListeners : function(){ + return this._bindings.length; + }, + + /** + * Stop propagation of the event, blocking the dispatch to next listeners on the queue. + *

    IMPORTANT: should be called only during signal dispatch, calling it before/after dispatch won't affect signal broadcast.

    + * @see signals.Signal.prototype.disable + */ + halt : function(){ + this._shouldPropagate = false; + }, + + /** + * Dispatch/Broadcast Signal to all listeners added to the queue. + * @param {...*} [params] Parameters that should be passed to each handler. + */ + dispatch : function(params){ + if(! this.active){ + return; + } + + var paramsArr = Array.prototype.slice.call(arguments), + bindings = this._bindings.slice(), //clone array in case add/remove items during dispatch + n = this._bindings.length; + + this._shouldPropagate = true; //in case `halt` was called before dispatch or during the previous dispatch. + + //execute all callbacks until end of the list or until a callback returns `false` or stops propagation + //reverse loop since listeners with higher priority will be added at the end of the list + do { n--; } while (bindings[n] && this._shouldPropagate && bindings[n].execute(paramsArr) !== false); + }, + + /** + * Remove all bindings from signal and destroy any reference to external objects (destroy Signal object). + *

    IMPORTANT: calling any method on the signal instance after calling dispose will throw errors.

    + */ + dispose : function(){ + this.removeAll(); + delete this._bindings; + }, + + /** + * @return {string} String representation of the object. + */ + toString : function(){ + return '[Signal active: '+ this.active +' numListeners: '+ this.getNumListeners() +']'; + } + + }; + +module.exports = signals; + diff --git a/node_modules/signals/package.json b/node_modules/signals/package.json new file mode 100644 index 000000000..287fd47c1 --- /dev/null +++ b/node_modules/signals/package.json @@ -0,0 +1,25 @@ +{ + "name" : "signals", + "description" : "Custom Event/Messaging System", + "keywords" : ["js-signals", "signals", "pub/sub", "event", "publish", "subscribe", "observer"], + "homepage" : "http://millermedeiros.github.com/js-signals/", + "version" : "0.6.1", + "author" : "Miller Medeiros", + "repository" : { + "type" : "git", + "url" : "https://github.com/millermedeiros/js-signals.git" + }, + "main" : "dist/js-signals.cjs.js", + "bugs" : { + "web" : "https://github.com/millermedeiros/js-signals/issues" + }, + "directories" : { + "doc" : "./dist/docs" + }, + "licenses" : [ + { + "type" : "MIT", + "url" : "http://www.opensource.org/licenses/mit-license.php" + } + ] +} \ No newline at end of file diff --git a/node_modules/underscore.string/.gitignore b/node_modules/underscore.string/.gitignore new file mode 100644 index 000000000..496ee2ca6 --- /dev/null +++ b/node_modules/underscore.string/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/node_modules/underscore.string/.hgignore b/node_modules/underscore.string/.hgignore new file mode 100644 index 000000000..9b4e43b94 --- /dev/null +++ b/node_modules/underscore.string/.hgignore @@ -0,0 +1,5 @@ +glob:.git +glob:.project +glob:index.html +glob:*.swp +glob:.DS_Store \ No newline at end of file diff --git a/node_modules/underscore.string/README.markdown b/node_modules/underscore.string/README.markdown new file mode 100644 index 000000000..44fe372b1 --- /dev/null +++ b/node_modules/underscore.string/README.markdown @@ -0,0 +1,339 @@ +# Underscore.string # + +Idea: Esa-Matti Suuronen (esa-matti aet suuronen dot org) + +Authors: Esa-Matti Suuronen, Edward Tsech + +Javascript lacks complete string manipulation operations. +This an attempt to fill that cap. List of buildin methods can be found +for example from [Dive Into JavaScript][d]. + +[d]: http://www.diveintojavascript.com/core-javascript-reference/the-string-object + + +As name states this an extension for [Underscore.js][u], but it can be used +independently from **_s**-global variable. But with Underscore.js you can +use Object-Oriented style and chaining: + +[u]: http://documentcloud.github.com/underscore/ + + _(" epeli ").chain().trim().capitalize().value() + => "Epeli" + +## Node.js installation ## + +**npm package** + + npm install underscore.string + +**Standalone usage**: + + var _s = require('undescore.string'); + +**Integrate with Underscore.js**: + + var _ = require('underscore'); + _.mixin(require('underscore.string')); + +## String Functions ## + +**capitalize** _.capitalize(string) + +Converts first letter of the string to uppercase. + + _.capitalize("epeli") + => "Epeli" + +**chop** _.chop(string, step) + + _.chop('whitespace', 3) + => ['whi','tes','pac','e'] + +**clean** _.clean(str) + +Compress some whitespaces to one. + + _.clean(" foo bar ") + => 'foo bar' + +**chars** _.chars(str) + + _.chars('Hello') + => ['H','e','l','l','o'] + +**includes** _.includes(string, substring) + +Tests if string contains a substring. + + _.includes("foobar", "ob") + => true + +**count** _.count(string, substring) + + _('Hello world').count('l') + => 3 + +**escapeHTML** _.escapeHTML(string) + +Converts HTML special characters to their entity equivalents. + + _('
    Blah blah blah
    ').escapeHTML(); + => '<div>Blah blah blah</div>' + +**unescapeHTML** _.unescapeHTML(string) + +Converts entity characters to HTML equivalents. + + _('<div>Blah blah blah</div>').unescapeHTML(); + => '
    Blah blah blah
    ' + +**insert** _.insert(string, index, substing) + + _('Hello ').insert(6, 'world') + => 'Hello world' + +**join** _.join(separator, *strings) + +Joins strings together with given separator + + _.join(" ", "foo", "bar") + => "foo bar" + +**lines** _.lines(str) + + _.lines("Hello\nWorld") + => ["Hello", "World"] + +**reverse** + +This functions has been removed, because this function override underscore.js 'reverse'. +But now you can do that: + + _("foobar").chars().reverse().join('') + +**splice** _.splice(string, index, howmany, substring) + +Like a array splice. + + _('https://edtsech@bitbucket.org/edtsech/underscore.strings').splice(30, 7, 'epeli') + => 'https://edtsech@bitbucket.org/epeli/underscore.strings' + +**startsWith** _.startsWith(string, starts) + +This method checks whether string starts with starts. + + _("image.gif").startsWith("image") + => true + +**endsWith** _.endsWith(string, ends) + +This method checks whether string ends with ends. + + _("image.gif").endsWith("gif") + => true + +**succ** _.succ(str) + +Returns the successor to str. + + _('a').succ() + => 'b' + + _('A').succ() + => 'B' + +**supplant** + +Supplant function was removed, use Underscore.js [template function][p]. + +[p]: http://documentcloud.github.com/underscore/#template + +**strip** alias for *trim* + +**lstrip** alias for *ltrim* + +**rstrip** alias for *rtrim* + +**titleize** _.titleize(string) + + _('my name is epeli').titleize() + => 'My Name Is Epeli' + +**camelize** _.camelize(string) + +Converts underscored or dasherized string to a camelized one + + _('-moz-transform').camelize() + => 'MozTransform' + +**underscored** _.underscored(string) + +Converts a camelized or dasherized string into an underscored one + + _(MozTransform).underscored() + => 'moz_transform' + +**dasherize** _.dasherize(string) + +Converts a underscored or camelized string into an dasherized one + + _('MozTransform').dasherize() + => '-moz-transform' + +**trim** _.trim(string, [characters]) + +trims defined characters from begining and ending of the string. +Defaults to whitespace characters. + + _.trim(" foobar ") + => "foobar" + + _.trim("_-foobar-_", "_-") + => "foobar" + + +**ltrim** _.ltrim(string, [characters]) + +Left trim. Similar to trim, but only for left side. + + +**rtrim** _.rtrim(string, [characters]) + +Left trim. Similar to trim, but only for right side. + +**truncate** _.truncate(string, length, truncateString) + + _('Hello world').truncate(5) + => 'Hello...' + +**words** _.words(str, delimiter=" ") + +Split string by delimiter (String or RegExp), ' ' by default. + + _.words("I love you") + => ["I","love","you"] + + _.words("I_love_you", "_") + => ["I","love","you"] + + _.words("I-love-you", /-/) + => ["I","love","you"] + +**sprintf** _.sprintf(string format, *arguments) + +C like string formatting. +Credits goes to [Alexandru Marasteanu][o]. +For more detailed documentation, see the [original page][o]. + +[o]: http://www.diveintojavascript.com/projects/sprintf-for-javascript + + _.sprintf("%.1f", 1.17) + "1.2" + +**pad** _.pad(str, length, [padStr, type]) + +pads the `str` with characters until the total string length is equal to the passed `length` parameter. By default, pads on the **left** with the space char (`" "`). `padStr` is truncated to a single character if necessary. + + _.pad("1", 8) + -> " 1"; + + _.pad("1", 8, '0') + -> "00000001"; + + _.pad("1", 8, '0', 'right') + -> "10000000"; + + _.pad("1", 8, '0', 'both') + -> "00001000"; + + _.pad("1", 8, 'bleepblorp', 'both') + -> "bbbb1bbb"; + +**lpad** _.lpad(str, length, [padStr]) + +left-pad a string. Alias for `pad(str, length, padStr, 'left')` + + _.lpad("1", 8, '0') + -> "00000001"; + +**rpad** _.rpad(str, length, [padStr]) + +right-pad a string. Alias for `pad(str, length, padStr, 'right')` + + _.rpad("1", 8, '0') + -> "10000000"; + +**lrpad** _.lrpad(str, length, [padStr]) + +left/right-pad a string. Alias for `pad(str, length, padStr, 'both')` + + _.lrpad("1", 8, '0') + -> "00001000"; + +**center** alias for **lrpad** + +**ljust** alias for *lpad* + +**rjust** alias for *rpad* + +## Roadmap ## + +* Resolve problem with function names crossing between libraries (include, contains and etc). + +Any suggestions or bug reports are welcome. Just email me or more preferably open an issue. + +## Changelog ## + +### 1.1.4 ### + +* Added pad, lpad, rpad, lrpad methods and aliases center, ljust, rjust +* Integration with Underscore 1.1.6 + +### 1.1.3 ### + +* Added methods: underscored, camelize, dasherize +* Support newer version of npm + +### 1.1.2 ### + +* Created functions: lines, chars, words functions + +### 1.0.2 ### + +* Created integration test suite with underscore.js 1.1.4 (now it's absolutely compatible) +* Removed 'reverse' function, because this function override underscore.js 'reverse' + +## Contributors list ## + +* Esa-Matti Suuronen (), +* Edward Tsech , +* Sasha Koss (), +* Vladimir Dronnikov , +* Pete Kruckenberg (), +* Paul Chavard (), +* Ed Finkler () + +## Licence ## + +The MIT License + +Copyright (c) 2011 Eduard Tsech edtsech@gmail.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/node_modules/underscore.string/Rakefile b/node_modules/underscore.string/Rakefile new file mode 100644 index 000000000..ae369a080 --- /dev/null +++ b/node_modules/underscore.string/Rakefile @@ -0,0 +1,8 @@ +require 'rubygems' +require 'closure-compiler' + +desc 'Use the Closure Compiler to compress Underscore.string' +task :build do + File.open('dist/underscore.string.min.js', 'w').write \ + Closure::Compiler.new.compile(File.open('lib/underscore.string.js', 'r')) +end diff --git a/node_modules/underscore.string/dist/underscore.string.min.js b/node_modules/underscore.string/dist/underscore.string.min.js new file mode 100644 index 000000000..307498bba --- /dev/null +++ b/node_modules/underscore.string/dist/underscore.string.min.js @@ -0,0 +1,9 @@ +(function(){function g(b,a){for(var c=[];a>0;c[--a]=b);return c.join("")}function f(b){if(b)return d.escapeRegExp(b);return"\\s"}var i=String.prototype.trim,d={isBlank:function(b){return!!b.match(/^\s*$/)},capitalize:function(b){return b.charAt(0).toUpperCase()+b.substring(1).toLowerCase()},chop:function(b,a){a=a||b.length;for(var c=[],d=0;d=0&&c++,e=e+(d>=0?d:0)+a.length;return c},chars:function(b){return b.split("")},escapeHTML:function(b){return String(b||"").replace(/&/g,"&").replace(//g,">")},unescapeHTML:function(b){return String(b||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")},escapeRegExp:function(b){return String(b||"").replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1")},insert:function(b,a,c){b=b.split("");b.splice(a,0,c);return b.join("")},includes:function(b,a){return b.indexOf(a)!== +-1},join:function(b){b=String(b);for(var a="",c=1;c=a.length&&b.substring(0,a.length)===a},endsWith:function(b,a){return b.length>=a.length&&b.substring(b.length-a.length)===a},succ:function(b){var a=b.split("");a.splice(b.length-1,1,String.fromCharCode(b.charCodeAt(b.length- +1)+1));return a.join("")},titleize:function(b){b=b.split(" ");for(var a,c=0;c1&&(c=c[0]):c=" ";switch(d){case "right":e= +a-b.length;e=g(c,e);b+=e;break;case "both":e=a-b.length;e={left:g(c,Math.ceil(e/2)),right:g(c,Math.floor(e/2))};b=e.left+b+e.right;break;default:e=a-b.length,e=g(c,e),b=e+b}return b},lpad:function(b,a,c){return d.pad(b,a,c)},rpad:function(b,a,c){return d.pad(b,a,c,"right")},lrpad:function(b,a,c){return d.pad(b,a,c,"both")},sprintf:function(){for(var b=0,a,c=arguments[b++],d=[],e,h,f;c;){if(e=/^[^\x25]+/.exec(c))d.push(e[0]);else if(e=/^\x25{2}/.exec(c))d.push("%");else if(e=/^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(c)){if((a= +arguments[e[1]||b++])==null||a==void 0)throw"Too few arguments.";if(/[^s]/.test(e[7])&&typeof a!="number")throw"Expecting number but found "+typeof a;switch(e[7]){case "b":a=a.toString(2);break;case "c":a=String.fromCharCode(a);break;case "d":a=parseInt(a);break;case "e":a=e[6]?a.toExponential(e[6]):a.toExponential();break;case "f":a=e[6]?parseFloat(a).toFixed(e[6]):parseFloat(a);break;case "o":a=a.toString(8);break;case "s":a=(a=String(a))&&e[6]?a.substring(0,e[6]):a;break;case "u":a=Math.abs(a); +break;case "x":a=a.toString(16);break;case "X":a=a.toString(16).toUpperCase()}a=/[def]/.test(e[7])&&e[2]&&a>=0?"+"+a:a;h=e[3]?e[3]=="0"?"0":e[3].charAt(1):" ";f=e[5]-String(a).length-0;h=e[5]?g(h,f):"";d.push(""+(e[4]?a+h:h+a))}else throw"Huh ?!";c=c.substring(e[0].length)}return d.join("")}};d.strip=d.trim;d.lstrip=d.ltrim;d.rstrip=d.rtrim;d.center=d.lrpad;d.ljust=d.lpad;d.rjust=d.rpad;typeof window==="undefined"&&typeof module!=="undefined"?module.exports=d:typeof this._!=="undefined"?this._.mixin(d): +this._=d})(); diff --git a/node_modules/underscore.string/lib/underscore.string.js b/node_modules/underscore.string/lib/underscore.string.js new file mode 100644 index 000000000..d6722777a --- /dev/null +++ b/node_modules/underscore.string/lib/underscore.string.js @@ -0,0 +1,302 @@ +// Underscore.string +// (c) 2010 Esa-Matti Suuronen +// Underscore.strings is freely distributable under the terms of the MIT license. +// Documentation: https://github.com/edtsech/underscore.string +// Some code is borrowed from MooTools and Alexandru Marasteanu. + +// Version 1.1.4 + +(function(){ + // ------------------------- Baseline setup --------------------------------- + + // Establish the root object, "window" in the browser, or "global" on the server. + var root = this; + + var nativeTrim = String.prototype.trim; + + function str_repeat(i, m) { + for (var o = []; m > 0; o[--m] = i); + return o.join(''); + } + + function defaultToWhiteSpace(characters){ + if (characters) { + return _s.escapeRegExp(characters); + } + return '\\s'; + } + + var _s = { + + isBlank: function(str){ + return !!str.match(/^\s*$/); + }, + + capitalize : function(str) { + return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase(); + }, + + chop: function(str, step){ + step = step || str.length; + var arr = []; + for (var i = 0; i < str.length;) { + arr.push(str.slice(i,i + step)); + i = i + step; + } + return arr; + }, + + clean: function(str){ + return _s.strip(str.replace(/\s+/g, ' ')); + }, + + count: function(str, substr){ + var count = 0, index; + for (var i=0; i < str.length;) { + index = str.indexOf(substr, i); + index >= 0 && count++; + i = i + (index >= 0 ? index : 0) + substr.length; + } + return count; + }, + + chars: function(str) { + return str.split(''); + }, + + escapeHTML: function(str) { + return String(str||'').replace(/&/g,'&').replace(//g,'>'); + }, + + unescapeHTML: function(str) { + return String(str||'').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); + }, + + escapeRegExp: function(str){ + // From MooTools core 1.2.4 + return String(str||'').replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); + }, + + insert: function(str, i, substr){ + var arr = str.split(''); + arr.splice(i, 0, substr); + return arr.join(''); + }, + + includes: function(str, needle){ + return str.indexOf(needle) !== -1; + }, + + join: function(sep) { + // TODO: Could this be faster by converting + // arguments to Array and using array.join(sep)? + sep = String(sep); + var str = ""; + for (var i=1; i < arguments.length; i += 1) { + str += String(arguments[i]); + if ( i !== arguments.length-1 ) { + str += sep; + } + } + return str; + }, + + lines: function(str) { + return str.split("\n"); + }, + +// reverse: function(str){ +// return Array.prototype.reverse.apply(str.split('')).join(''); +// }, + + splice: function(str, i, howmany, substr){ + var arr = str.split(''); + arr.splice(i, howmany, substr); + return arr.join(''); + }, + + startsWith: function(str, starts){ + return str.length >= starts.length && str.substring(0, starts.length) === starts; + }, + + endsWith: function(str, ends){ + return str.length >= ends.length && str.substring(str.length - ends.length) === ends; + }, + + succ: function(str){ + var arr = str.split(''); + arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1)); + return arr.join(''); + }, + + titleize: function(str){ + var arr = str.split(' '), + word; + for (var i=0; i < arr.length; i++) { + word = arr[i].split(''); + if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase(); + i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' '; + } + return arr.join(''); + }, + + camelize: function(str){ + return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) { + return chr ? chr.toUpperCase() : ''; + }); + }, + + underscored: function(str){ + return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase(); + }, + + dasherize: function(str){ + return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase(); + }, + + trim: function(str, characters){ + if (!characters && nativeTrim) { + return nativeTrim.call(str); + } + characters = defaultToWhiteSpace(characters); + return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), ''); + }, + + ltrim: function(str, characters){ + characters = defaultToWhiteSpace(characters); + return str.replace(new RegExp('\^[' + characters + ']+', 'g'), ''); + }, + + rtrim: function(str, characters){ + characters = defaultToWhiteSpace(characters); + return str.replace(new RegExp('[' + characters + ']+$', 'g'), ''); + }, + + truncate: function(str, length, truncateStr){ + truncateStr = truncateStr || '...'; + return str.slice(0,length) + truncateStr; + }, + + words: function(str, delimiter) { + delimiter = delimiter || " "; + return str.split(delimiter); + }, + + + pad: function(str, length, padStr, type) { + + var padding = ''; + var padlen = 0; + + if (!padStr) { padStr = ' '; } + else if (padStr.length > 1) { padStr = padStr[0]; } + switch(type) { + case "right": + padlen = (length - str.length); + padding = str_repeat(padStr, padlen); + str = str+padding; + break; + case "both": + padlen = (length - str.length); + padding = { + 'left' : str_repeat(padStr, Math.ceil(padlen/2)), + 'right': str_repeat(padStr, Math.floor(padlen/2)) + }; + str = padding.left+str+padding.right; + break; + default: // "left" + padlen = (length - str.length); + padding = str_repeat(padStr, padlen);; + str = padding+str; + } + return str; + }, + + lpad: function(str, length, padStr) { + return _s.pad(str, length, padStr); + }, + + rpad: function(str, length, padStr) { + return _s.pad(str, length, padStr, 'right'); + }, + + lrpad: function(str, length, padStr) { + return _s.pad(str, length, padStr, 'both'); + }, + + + /** + * Credits for this function goes to + * http://www.diveintojavascript.com/projects/sprintf-for-javascript + * + * Copyright (c) Alexandru Marasteanu + * All rights reserved. + * */ + sprintf: function(){ + + var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = ''; + while (f) { + if (m = /^[^\x25]+/.exec(f)) { + o.push(m[0]); + } + else if (m = /^\x25{2}/.exec(f)) { + o.push('%'); + } + else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) { + if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) { + throw('Too few arguments.'); + } + if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) { + throw('Expecting number but found ' + typeof(a)); + } + switch (m[7]) { + case 'b': a = a.toString(2); break; + case 'c': a = String.fromCharCode(a); break; + case 'd': a = parseInt(a); break; + case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break; + case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break; + case 'o': a = a.toString(8); break; + case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break; + case 'u': a = Math.abs(a); break; + case 'x': a = a.toString(16); break; + case 'X': a = a.toString(16).toUpperCase(); break; + } + a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a); + c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' '; + x = m[5] - String(a).length - s.length; + p = m[5] ? str_repeat(c, x) : ''; + o.push(s + (m[4] ? a + p : p + a)); + } + else { + throw('Huh ?!'); + } + f = f.substring(m[0].length); + } + return o.join(''); + } + } + + // Aliases + + _s.strip = _s.trim; + _s.lstrip = _s.ltrim; + _s.rstrip = _s.rtrim; + _s.center = _s.lrpad + _s.ljust = _s.lpad + _s.rjust = _s.rpad + + // CommonJS module is defined + if (typeof window === 'undefined' && typeof module !== 'undefined') { + // Export module + module.exports = _s; + + // Integrate with Underscore.js + } else if (typeof root._ !== 'undefined') { + root._.mixin(_s); + + // Or define it + } else { + root._ = _s; + } + +}()); diff --git a/node_modules/underscore.string/package.json b/node_modules/underscore.string/package.json new file mode 100644 index 000000000..67f429d66 --- /dev/null +++ b/node_modules/underscore.string/package.json @@ -0,0 +1,39 @@ +{ + "name": "underscore.string", + "version": "1.1.4", + "description": "String manipulation extensions for Underscore.js javascript library.", + "homepage": "https://github.com/edtsech/underscore.string", + "contributors": [ + "Esa-Matti Suuronen (http://esa-matti.suuronen.org/)", + "Edward Tsech ", + "Sasha Koss (http://koss.nocorp.me/)", + "Vladimir Dronnikov ", + "Pete Kruckenberg ()", + "Paul Chavard ()", + "Ed Finkler ()" + ], + "keywords": [ + "underscore", + "string" + ], + "main": "./lib/underscore.string", + "directories": { + "lib": "./lib" + }, + "engines": { + "node": "*" + }, + "dependencies": { + "underscore": "1.1.6" + }, + "repository": { + "type": "git", + "url": "https://github.com/edtsech/underscore.string.git" + }, + "bugs": { + "url": "https://github.com/edtsech/underscore.string/issues" + }, + "licenses" : [ + { "type" : "MIT" } + ] +} diff --git a/node_modules/underscore.string/test/speed.js b/node_modules/underscore.string/test/speed.js new file mode 100644 index 000000000..264984471 --- /dev/null +++ b/node_modules/underscore.string/test/speed.js @@ -0,0 +1,106 @@ +(function() { + + JSLitmus.test('trimNoNative', function() { + return _.trim(" foobar ", " "); + }); + + JSLitmus.test('trim', function() { + return _.trim(" foobar "); + }); + + JSLitmus.test('trim object-oriented', function() { + return _(" foobar ").trim(); + }); + + JSLitmus.test('trim jQuery', function() { + return jQuery.trim(" foobar "); + }); + + JSLitmus.test('ltrimp', function() { + return _.ltrim(" foobar ", " "); + }); + + JSLitmus.test('rtrimp', function() { + return _.rtrim(" foobar ", " "); + }); + + JSLitmus.test('startsWith', function() { + return _.startsWith("foobar", "foo"); + }); + + JSLitmus.test('endsWith', function() { + return _.endsWith("foobar", "xx"); + }); + + JSLitmus.test('chop', function(){ + return _('whitespace').chop(2); + }); + + JSLitmus.test('count', function(){ + return _('Hello worls').count('l'); + }); + + JSLitmus.test('insert', function() { + return _('Hello ').insert(6, 'world'); + }); + + JSLitmus.test('splice', function() { + return _('https://edtsech@bitbucket.org/edtsech/underscore.strings').splice(30, 7, 'epeli'); + }); + + JSLitmus.test('succ', function(){ + var let = 'a', alphabet = []; + + for (var i=0; i < 26; i++) { + alphabet.push(let); + let = _(let).succ(); + } + + return alphabet; + }); + + JSLitmus.test('titleize', function(){ + return _('the titleize string method').titleize() + }); + + JSLitmus.test('truncate', function(){ + return _('Hello world').truncate(5); + }); + + JSLitmus.test('isBlank', function(){ + return _('').isBlank(); + }); + + JSLitmus.test('escapeHTML', function(){ + _('
    Blah blah blah
    ').escapeHTML() + }); + + JSLitmus.test('unescapeHTML', function(){ + _('<div>Blah blah blah</div>').unescapeHTML() + }); + + JSLitmus.test('reverse', function(){ + _('Hello World').reverse(); + }); + + JSLitmus.test('pad default', function(){ + _('foo').pad(12); + }); + + JSLitmus.test('pad hash left', function(){ + _('foo').pad(12, '#'); + }); + + JSLitmus.test('pad hash right', function(){ + _('foo').pad(12, '#', 'right'); + }); + + JSLitmus.test('pad hash both', function(){ + _('foo').pad(12, '#', 'both'); + }); + + JSLitmus.test('pad hash both longPad', function(){ + _('foo').pad(12, 'f00f00f00', 'both'); + }); + +})(); diff --git a/node_modules/underscore.string/test/strings.js b/node_modules/underscore.string/test/strings.js new file mode 100644 index 000000000..b6c874dda --- /dev/null +++ b/node_modules/underscore.string/test/strings.js @@ -0,0 +1,236 @@ +$(document).ready(function() { + + module("String extensions"); + + test("Strings: basic", function() { + equals(_.trim(" epeli "), "epeli", "Basic"); + equals(_.strip(" epeli "), "epeli", "Aliases"); + equals(_(" epeli ").trim(), "epeli", "Object-Oriented style"); + equals(_(" epeli ").chain().trim().capitalize().value(), "Epeli", "Can chain"); + }); + + test("Strings: capitalize", function() { + equals(_("fabio").capitalize(), "Fabio", 'First letter is upper case'); + equals(_.capitalize("fabio"), "Fabio", 'First letter is upper case'); + }); + + test("Strings: join", function() { + equals(_.join("", "foo", "bar"), "foobar", 'basic join'); + equals(_.join("", 1, "foo", 2), "1foo2", 'join numbers and strings'); + equals(_.join(" ","foo", "bar"), "foo bar", 'join with spaces'); + equals(_.join("1", "2", "2"), "212", 'join number strings'); + equals(_.join(1, 2, 2), "212", 'join numbers'); + equals(_(" ").join("foo", "bar"), "foo bar", 'join object oriented'); + }); + +// test("Strings: reverse", function() { +// equals(_.reverse("foo"), "oof" ); +// equals(_.reverse("foobar"), "raboof" ); +// equals(_.reverse("foo bar"), "rab oof" ); +// equals(_.reverse("saippuakauppias"), "saippuakauppias" ); +// }); + + test("Strings: trim", function() { + equals(_(" foo").trim(), "foo"); + equals(_("foo ").trim(), "foo"); + equals(_(" foo ").trim(), "foo"); + equals(_(" foo ").trim(), "foo"); + equals(_(" foo ", " ").trim(), "foo", "Manually set whitespace"); + + equals(_("ffoo").trim("f"), "oo"); + equals(_("ooff").trim("f"), "oo"); + equals(_("ffooff").trim("f"), "oo"); + + + equals(_("_-foobar-_").trim("_-"), "foobar"); + + equals(_("http://foo/").trim("/"), "http://foo"); + equals(_("c:\\").trim('\\'), "c:"); + }); + + test("Strings: ltrim", function() { + equals(_(" foo").ltrim(), "foo"); + equals(_(" foo").ltrim(), "foo"); + equals(_("foo ").ltrim(), "foo "); + equals(_(" foo ").ltrim(), "foo "); + + + equals(_("ffoo").ltrim("f"), "oo"); + equals(_("ooff").ltrim("f"), "ooff"); + equals(_("ffooff").ltrim("f"), "ooff"); + + equals(_("_-foobar-_").ltrim("_-"), "foobar-_"); + }); + + test("Strings: rtrim", function() { + equals(_("http://foo/").rtrim("/"), "http://foo", 'clean trailing slash'); + equals(_(" foo").rtrim(), " foo"); + equals(_("foo ").rtrim(), "foo"); + equals(_("foo ").rtrim(), "foo"); + equals(_("foo bar ").rtrim(), "foo bar"); + equals(_(" foo ").rtrim(), " foo"); + + equals(_("ffoo").rtrim("f"), "ffoo"); + equals(_("ooff").rtrim("f"), "oo"); + equals(_("ffooff").rtrim("f"), "ffoo"); + + equals(_("_-foobar-_").rtrim("_-"), "_-foobar"); + }); + + test("Strings: clean", function() { + equals(_(" foo bar ").clean(), "foo bar"); + }); + + test("Strings: sprintf", function() { + // Should be very tested function already. Thanks to + // http://www.diveintojavascript.com/projects/sprintf-for-javascript + equals(_.sprintf("Hello %s", "me"), "Hello me", 'basic'); + equals(_("Hello %s").sprintf("me"), "Hello me", 'object'); + equals(_("hello %s").chain().sprintf("me").capitalize().value(), "Hello me", 'Chaining works'); + equals(_.sprintf("%.1f", 1.22222), "1.2", 'round'); + equals(_.sprintf("%.1f", 1.17), "1.2", 'round 2'); + }); + + test("Strings: startsWith", function() { + ok(_("foobar").startsWith("foo"), 'foobar starts with foo'); + ok(!_("oobar").startsWith("foo"), 'oobar does not start with foo'); + }); + + test("Strings: endsWith", function() { + ok(_("foobar").endsWith("bar"), 'foobar ends with bar'); + ok(_.endsWith("foobar", "bar"), 'foobar ends with bar'); + ok(_.endsWith("00018-0000062.Plone.sdh264.1a7264e6912a91aa4a81b64dc5517df7b8875994.mp4", "mp4"), 'endsWith .mp4'); + ok(!_("fooba").endsWith("bar"), 'fooba does not end with bar'); + }); + + test("Strings: includes", function() { + ok(_("foobar").includes("bar"), 'foobar includes bar'); + ok(!_("foobar").includes("buzz"), 'foobar does not includes buzz'); + }); + + test('String: chop', function(){ + ok(_('whitespace').chop(2).length === 5, "output ['wh','it','es','pa','ce']"); + ok(_('whitespace').chop(3).length === 4, "output ['whi','tes','pac','e']"); + ok(_('whitespace').chop()[0].length === 10, "output ['whitespace']"); + }); + + test('String: count', function(){ + equals(_('Hello world').count('l'), 3); + equals(_('Hello world').count('Hello'), 1); + equals(_('Hello world').count('foo'), 0); + }); + + test('String: insert', function(){ + equals(_('Hello ').insert(6, 'Jessy'), 'Hello Jessy'); + }); + + test('String: splice', function(){ + equals(_('https://edtsech@bitbucket.org/edtsech/underscore.strings').splice(30, 7, 'epeli'), + 'https://edtsech@bitbucket.org/epeli/underscore.strings'); + }); + + test('String: succ', function(){ + equals(_('a').succ(), 'b'); + equals(_('A').succ(), 'B'); + equals(_('+').succ(), ','); + }); + + test('String: titleize', function(){ + equals(_('the titleize string method').titleize(), 'The Titleize String Method'); + equals(_('the titleize string method').titleize(), 'The Titleize String Method'); + }); + + test('String: camelize', function(){ + equals(_('the_camelize_string_method').camelize(), 'theCamelizeStringMethod'); + equals(_('-the-camelize-string-method').camelize(), 'TheCamelizeStringMethod'); + equals(_('the camelize string method').camelize(), 'theCamelizeStringMethod'); + equals(_(' the camelize string method').camelize(), 'theCamelizeStringMethod'); + equals(_('the camelize string method').camelize(), 'theCamelizeStringMethod'); + }); + + test('String: underscored', function(){ + equals(_('the-underscored-string-method').underscored(), 'the_underscored_string_method'); + equals(_('theUnderscoredStringMethod').underscored(), 'the_underscored_string_method'); + equals(_('TheUnderscoredStringMethod').underscored(), 'the_underscored_string_method'); + equals(_(' the underscored string method').underscored(), 'the_underscored_string_method'); + }); + + test('String: dasherize', function(){ + equals(_('the_dasherize_string_method').dasherize(), 'the-dasherize-string-method'); + equals(_('TheDasherizeStringMethod').dasherize(), '-the-dasherize-string-method'); + equals(_('the dasherize string method').dasherize(), 'the-dasherize-string-method'); + equals(_('the dasherize string method ').dasherize(), 'the-dasherize-string-method'); + }); + + test('String: truncate', function(){ + equals(_('Hello world').truncate(6, 'read more'), 'Hello read more'); + equals(_('Hello world').truncate(5), 'Hello...'); + }); + + test('String: isBlank', function(){ + ok(_('').isBlank()); + ok(_(' ').isBlank()); + ok(_('\n').isBlank()); + ok(!_('a').isBlank()); + }); + + test('String: escapeHTML', function(){ + equals(_('
    Blah blah blah
    ').escapeHTML(), '<div>Blah blah blah</div>'); + equals(_(5).escapeHTML(), '5'); + equals(_(undefined).escapeHTML(), ''); + }); + + test('String: unescapeHTML', function(){ + equals(_('<div>Blah blah blah</div>').unescapeHTML(), '
    Blah blah blah
    '); + equals(_(5).unescapeHTML(), '5'); + equals(_(undefined).unescapeHTML(), ''); + }); + + test('String: words', function() { + equals(_("I love you!").words().length, 3); + equals(_("I_love_you!").words('_').length, 3); + equals(_("I-love-you!").words(/-/).length, 3); + }); + + test('String: chars', function() { + equals(_("Hello").chars().length, 5); + }); + + test('String: lines', function() { + equals(_("Hello\nWorld").lines().length, 2); + equals(_("Hello World").lines().length, 1); + }); + + test('String: pad', function() { + equals(_("1").pad(8), ' 1'); + equals(_("1").pad(8, '0'), '00000001'); + equals(_("1").pad(8, '0', 'left'), '00000001'); + equals(_("1").pad(8, '0', 'right'), '10000000'); + equals(_("1").pad(8, '0', 'both'), '00001000'); + equals(_("foo").pad(8, '0', 'both'), '000foo00'); + equals(_("foo").pad(7, '0', 'both'), '00foo00'); + equals(_("foo").pad(7, '!@$%dofjrofj', 'both'), '!!foo!!'); + }); + + test('String: lpad', function() { + equals(_("1").lpad(8), ' 1'); + equals(_("1").lpad(8, '0'), '00000001'); + equals(_("1").lpad(8, '0', 'left'), '00000001'); + }); + + test('String: rpad', function() { + equals(_("1").rpad(8), '1 '); + equals(_("1").rpad(8, '0'), '10000000'); + equals(_("foo").rpad(8, '0'), 'foo00000'); + equals(_("foo").rpad(7, '0'), 'foo0000'); + }); + + test('String: lrpad', function() { + equals(_("1").lrpad(8), ' 1 '); + equals(_("1").lrpad(8, '0'), '00001000'); + equals(_("foo").lrpad(8, '0'), '000foo00'); + equals(_("foo").lrpad(7, '0'), '00foo00'); + equals(_("foo").lrpad(7, '!@$%dofjrofj'), '!!foo!!'); + }); + +}); diff --git a/node_modules/underscore.string/test/test.html b/node_modules/underscore.string/test/test.html new file mode 100644 index 000000000..c928b4541 --- /dev/null +++ b/node_modules/underscore.string/test/test.html @@ -0,0 +1,31 @@ + + + + Underscore.strings Test Suite + + + + + + + + + + + +

    Underscore.string Test Suite

    +

    +

    +
      +
      +

      Underscore.string Speed Suite

      + +
      + + diff --git a/node_modules/underscore.string/test/test_underscore/arrays.js b/node_modules/underscore.string/test/test_underscore/arrays.js new file mode 100644 index 000000000..e031afe9b --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/arrays.js @@ -0,0 +1,124 @@ +$(document).ready(function() { + + module("Array-only functions (last, compact, uniq, and so on...)"); + + test("arrays: first", function() { + equals(_.first([1,2,3]), 1, 'can pull out the first element of an array'); + equals(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); + equals(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); + equals(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); + var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); + equals(result, 4, 'works on an arguments object.'); + result = _.map([[1,2,3],[1,2,3]], _.first); + equals(result.join(','), '1,1', 'works well with _.map'); + }); + + test("arrays: rest", function() { + var numbers = [1, 2, 3, 4]; + equals(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); + equals(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); + equals(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); + var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); + equals(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); + result = _.map([[1,2,3],[1,2,3]], _.rest); + equals(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); + }); + + test("arrays: last", function() { + equals(_.last([1,2,3]), 3, 'can pull out the last element of an array'); + var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4); + equals(result, 4, 'works on an arguments object'); + }); + + test("arrays: compact", function() { + equals(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); + var result = (function(){ return _(arguments).compact().length; })(0, 1, false, 2, false, 3); + equals(result, 3, 'works on an arguments object'); + }); + + test("arrays: flatten", function() { + var list = [1, [2], [3, [[[4]]]]]; + equals(_.flatten(list).join(', '), '1, 2, 3, 4', 'can flatten nested arrays'); + var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); + equals(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); + }); + + test("arrays: without", function() { + var list = [1, 2, 1, 0, 3, 1, 4]; + equals(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); + var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); + equals(result.join(', '), '2, 3, 4', 'works on an arguments object'); + + var list = [{one : 1}, {two : 2}]; + ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.'); + ok(_.without(list, list[0]).length == 1, 'ditto.'); + }); + + test("arrays: uniq", function() { + var list = [1, 2, 1, 3, 1, 4]; + equals(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); + + var list = [1, 1, 1, 2, 2, 3]; + equals(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); + + var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4); + equals(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); + }); + + test("arrays: intersect", function() { + var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; + equals(_.intersect(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); + equals(_(stooges).intersect(leaders).join(''), 'moe', 'can perform an OO-style intersection'); + var result = (function(){ return _.intersect(arguments, leaders); })('moe', 'curly', 'larry'); + equals(result.join(''), 'moe', 'works on an arguments object'); + }); + + test('arrays: zip', function() { + var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; + var stooges = _.zip(names, ages, leaders); + equals(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); + }); + + test("arrays: indexOf", function() { + var numbers = [1, 2, 3]; + numbers.indexOf = null; + equals(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); + var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); + equals(result, 1, 'works on an arguments object'); + equals(_.indexOf(null, 2), -1, 'handles nulls properly'); + + var numbers = [10, 20, 30, 40, 50], num = 35; + var index = _.indexOf(numbers, num, true); + equals(index, -1, '35 is not in the list'); + + numbers = [10, 20, 30, 40, 50]; num = 40; + index = _.indexOf(numbers, num, true); + equals(index, 3, '40 is in the list'); + + numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; + index = _.indexOf(numbers, num, true); + equals(index, 1, '40 is in the list'); + }); + + test("arrays: lastIndexOf", function() { + var numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; + numbers.lastIndexOf = null; + equals(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); + equals(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); + var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); + equals(result, 5, 'works on an arguments object'); + equals(_.indexOf(null, 2), -1, 'handles nulls properly'); + }); + + test("arrays: range", function() { + equals(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); + equals(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); + equals(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); + equals(_.range(8, 5).join(''), '', 'range with two arguments a & b, b<a generates an empty array'); + equals(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); + equals(_.range(3, 10, 15).join(''), '3', 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); + equals(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); + equals(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); + }); + +}); diff --git a/node_modules/underscore.string/test/test_underscore/chaining.js b/node_modules/underscore.string/test/test_underscore/chaining.js new file mode 100644 index 000000000..e633ba5ad --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/chaining.js @@ -0,0 +1,47 @@ +$(document).ready(function() { + + module("Underscore chaining."); + + test("chaining: map/flatten/reduce", function() { + var lyrics = [ + "I'm a lumberjack and I'm okay", + "I sleep all night and I work all day", + "He's a lumberjack and he's okay", + "He sleeps all night and he works all day" + ]; + var counts = _(lyrics).chain() + .map(function(line) { return line.split(''); }) + .flatten() + .reduce(function(hash, l) { + hash[l] = hash[l] || 0; + hash[l]++; + return hash; + }, {}).value(); + ok(counts['a'] == 16 && counts['e'] == 10, 'counted all the letters in the song'); + }); + + test("chaining: select/reject/sortBy", function() { + var numbers = [1,2,3,4,5,6,7,8,9,10]; + numbers = _(numbers).chain().select(function(n) { + return n % 2 == 0; + }).reject(function(n) { + return n % 4 == 0; + }).sortBy(function(n) { + return -n; + }).value(); + equals(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + }); + + test("chaining: reverse/concat/unshift/pop/map", function() { + var numbers = [1,2,3,4,5]; + numbers = _(numbers).chain() + .reverse() + .concat([5, 5, 5]) + .unshift(17) + .pop() + .map(function(n){ return n * 2; }) + .value(); + equals(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); + }); + +}); diff --git a/node_modules/underscore.string/test/test_underscore/collections.js b/node_modules/underscore.string/test/test_underscore/collections.js new file mode 100644 index 000000000..ff365f8ea --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/collections.js @@ -0,0 +1,207 @@ +$(document).ready(function() { + + module("Collection functions (each, any, select, and so on...)"); + + test("collections: each", function() { + _.each([1, 2, 3], function(num, i) { + equals(num, i + 1, 'each iterators provide value and iteration count'); + }); + + var answers = []; + _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5}); + equals(answers.join(', '), '5, 10, 15', 'context object property accessed'); + + answers = []; + _.forEach([1, 2, 3], function(num){ answers.push(num); }); + equals(answers.join(', '), '1, 2, 3', 'aliased as "forEach"'); + + answers = []; + var obj = {one : 1, two : 2, three : 3}; + obj.constructor.prototype.four = 4; + _.each(obj, function(value, key){ answers.push(key); }); + equals(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); + delete obj.constructor.prototype.four; + + answer = null; + _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); + ok(answer, 'can reference the original collection from inside the iterator'); + + answers = []; + _.each({range : 1, speed : 2, length : 3}, function(v){ answers.push(v); }); + ok(answers.join(', '), '1, 2, 3', 'can iterate over objects with numeric length properties'); + + answers = 0; + _.each(null, function(){ ++answers; }); + equals(answers, 0, 'handles a null properly'); + }); + + test('collections: map', function() { + var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); + equals(doubled.join(', '), '2, 4, 6', 'doubled numbers'); + + var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); + equals(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); + + var doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); + equals(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); + + var ids = _.map(document.body.childNodes, function(n){ return n.id; }); + ok(_.include(ids, 'qunit-header'), 'can use collection methods on NodeLists'); + + var ids = _.map(document.images, function(n){ return n.id; }); + ok(ids[0] == 'chart_image', 'can use collection methods on HTMLCollections'); + + var ifnull = _.map(null, function(){}); + ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly'); + }); + + test('collections: reduce', function() { + var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); + equals(sum, 6, 'can sum up an array'); + + var context = {multiplier : 3}; + sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context); + equals(sum, 18, 'can reduce with a context object'); + + sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); + equals(sum, 6, 'aliased as "inject"'); + + sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); + equals(sum, 6, 'OO-style reduce'); + + var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }); + equals(sum, 6, 'default initial value'); + + var ifnull; + try { + _.reduce(null, function(){}); + } catch (ex) { + ifnull = ex; + } + ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); + + ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); + }); + + test('collections: reduceRight', function() { + var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); + equals(list, 'bazbarfoo', 'can perform right folds'); + + var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); + equals(list, 'bazbarfoo', 'aliased as "foldr"'); + + var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }); + equals(list, 'bazbarfoo', 'default initial value'); + + var ifnull; + try { + _.reduceRight(null, function(){}); + } catch (ex) { + ifnull = ex; + } + ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); + + ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); + }); + + test('collections: detect', function() { + var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; }); + equals(result, 2, 'found the first "2" and broke the loop'); + }); + + test('collections: select', function() { + var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equals(evens.join(', '), '2, 4, 6', 'selected each even number'); + + evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equals(evens.join(', '), '2, 4, 6', 'aliased as "filter"'); + }); + + test('collections: reject', function() { + var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); + equals(odds.join(', '), '1, 3, 5', 'rejected each even number'); + }); + + test('collections: all', function() { + ok(_.all([], _.identity), 'the empty set'); + ok(_.all([true, true, true], _.identity), 'all true values'); + ok(!_.all([true, false, true], _.identity), 'one false value'); + ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers'); + ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number'); + ok(_.every([true, true, true], _.identity), 'aliased as "every"'); + }); + + test('collections: any', function() { + ok(!_.any([]), 'the empty set'); + ok(!_.any([false, false, false]), 'all false values'); + ok(_.any([false, false, true]), 'one true value'); + ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers'); + ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number'); + ok(_.some([false, false, true]), 'aliased as "some"'); + }); + + test('collections: include', function() { + ok(_.include([1,2,3], 2), 'two is in the array'); + ok(!_.include([1,3,9], 2), 'two is not in the array'); + ok(_.contains({moe:1, larry:3, curly:9}, 3) === true, '_.include on objects checks their values'); + ok(_([1,2,3]).include(2), 'OO-style include'); + }); + + test('collections: invoke', function() { + var list = [[5, 1, 7], [3, 2, 1]]; + var result = _.invoke(list, 'sort'); + equals(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equals(result[1].join(', '), '1, 2, 3', 'second array sorted'); + }); + + test('collections: invoke w/ function reference', function() { + var list = [[5, 1, 7], [3, 2, 1]]; + var result = _.invoke(list, Array.prototype.sort); + equals(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equals(result[1].join(', '), '1, 2, 3', 'second array sorted'); + }); + + test('collections: pluck', function() { + var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}]; + equals(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects'); + }); + + test('collections: max', function() { + equals(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); + + var neg = _.max([1, 2, 3], function(num){ return -num; }); + equals(neg, 1, 'can perform a computation-based max'); + }); + + test('collections: min', function() { + equals(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); + + var neg = _.min([1, 2, 3], function(num){ return -num; }); + equals(neg, 3, 'can perform a computation-based min'); + }); + + test('collections: sortBy', function() { + var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; + people = _.sortBy(people, function(person){ return person.age; }); + equals(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); + }); + + test('collections: sortedIndex', function() { + var numbers = [10, 20, 30, 40, 50], num = 35; + var index = _.sortedIndex(numbers, num); + equals(index, 3, '35 should be inserted at index 3'); + }); + + test('collections: toArray', function() { + ok(!_.isArray(arguments), 'arguments object is not an array'); + ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); + + var numbers = _.toArray({one : 1, two : 2, three : 3}); + equals(numbers.join(', '), '1, 2, 3', 'object flattened into array'); + }); + + test('collections: size', function() { + equals(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object'); + }); + +}); diff --git a/node_modules/underscore.string/test/test_underscore/functions.js b/node_modules/underscore.string/test/test_underscore/functions.js new file mode 100644 index 000000000..95267e73a --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/functions.js @@ -0,0 +1,154 @@ +$(document).ready(function() { + + module("Function functions (bind, bindAll, and so on...)"); + + test("functions: bind", function() { + var context = {name : 'moe'}; + var func = function(arg) { return "name: " + (this.name || arg); }; + var bound = _.bind(func, context); + equals(bound(), 'name: moe', 'can bind a function to a context'); + + bound = _(func).bind(context); + equals(bound(), 'name: moe', 'can do OO-style binding'); + + bound = _.bind(func, null, 'curly'); + equals(bound(), 'name: curly', 'can bind without specifying a context'); + + func = function(salutation, name) { return salutation + ': ' + name; }; + func = _.bind(func, this, 'hello'); + equals(func('moe'), 'hello: moe', 'the function was partially applied in advance'); + + var func = _.bind(func, this, 'curly'); + equals(func(), 'hello: curly', 'the function was completely applied in advance'); + + var func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; + func = _.bind(func, this, 'hello', 'moe', 'curly'); + equals(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); + + func = function(context, message) { equals(this, context, message); }; + _.bind(func, 0, 0, 'can bind a function to `0`')(); + _.bind(func, '', '', 'can bind a function to an empty string')(); + _.bind(func, false, false, 'can bind a function to `false`')(); + }); + + test("functions: bindAll", function() { + var curly = {name : 'curly'}, moe = { + name : 'moe', + getName : function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; } + }; + curly.getName = moe.getName; + _.bindAll(moe, 'getName', 'sayHi'); + curly.sayHi = moe.sayHi; + equals(curly.getName(), 'name: curly', 'unbound function is bound to current object'); + equals(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); + + curly = {name : 'curly'}; + moe = { + name : 'moe', + getName : function() { return 'name: ' + this.name; }, + sayHi : function() { return 'hi: ' + this.name; } + }; + _.bindAll(moe); + curly.sayHi = moe.sayHi; + equals(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object'); + }); + + test("functions: memoize", function() { + var fib = function(n) { + return n < 2 ? n : fib(n - 1) + fib(n - 2); + }; + var fastFib = _.memoize(fib); + equals(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + equals(fastFib(10), 55, 'a memoized version of fibonacci produces identical results'); + + var o = function(str) { + return str; + }; + var fastO = _.memoize(o); + equals(o('toString'), 'toString', 'checks hasOwnProperty'); + equals(fastO('toString'), 'toString', 'checks hasOwnProperty'); + }); + + asyncTest("functions: delay", 2, function() { + var delayed = false; + _.delay(function(){ delayed = true; }, 100); + _.delay(function(){ ok(!delayed, "didn't delay the function quite yet"); }, 50); + _.delay(function(){ ok(delayed, 'delayed the function'); start(); }, 150); + }); + + asyncTest("functions: defer", 1, function() { + var deferred = false; + _.defer(function(bool){ deferred = bool; }, true); + _.delay(function(){ ok(deferred, "deferred the function"); start(); }, 50); + }); + + asyncTest("functions: throttle", 1, function() { + var counter = 0; + var incr = function(){ counter++; }; + var throttledIncr = _.throttle(incr, 100); + throttledIncr(); throttledIncr(); throttledIncr(); + setTimeout(throttledIncr, 120); + setTimeout(throttledIncr, 140); + setTimeout(throttledIncr, 220); + setTimeout(throttledIncr, 240); + _.delay(function(){ ok(counter == 3, "incr was throttled"); start(); }, 400); + }); + + asyncTest("functions: debounce", 1, function() { + var counter = 0; + var incr = function(){ counter++; }; + var debouncedIncr = _.debounce(incr, 50); + debouncedIncr(); debouncedIncr(); debouncedIncr(); + setTimeout(debouncedIncr, 30); + setTimeout(debouncedIncr, 60); + setTimeout(debouncedIncr, 90); + setTimeout(debouncedIncr, 120); + setTimeout(debouncedIncr, 150); + _.delay(function(){ ok(counter == 1, "incr was debounced"); start(); }, 220); + }); + + test("functions: once", function() { + var num = 0; + var increment = _.once(function(){ num++; }); + increment(); + increment(); + equals(num, 1); + }); + + test("functions: wrap", function() { + var greet = function(name){ return "hi: " + name; }; + var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); + equals(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function'); + + var inner = function(){ return "Hello "; }; + var obj = {name : "Moe"}; + obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); + equals(obj.hi(), "Hello Moe"); + }); + + test("functions: compose", function() { + var greet = function(name){ return "hi: " + name; }; + var exclaim = function(sentence){ return sentence + '!'; }; + var composed = _.compose(exclaim, greet); + equals(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); + + composed = _.compose(greet, exclaim); + equals(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); + }); + + test("functions: after", function() { + var testAfter = function(afterAmount, timesCalled) { + var afterCalled = 0; + var after = _.after(afterAmount, function() { + afterCalled++; + }); + while (timesCalled--) after(); + return afterCalled; + }; + + equals(testAfter(5, 5), 1, "after(N) should fire after being called N times"); + equals(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); + }); + +}); diff --git a/node_modules/underscore.string/test/test_underscore/objects.js b/node_modules/underscore.string/test/test_underscore/objects.js new file mode 100644 index 000000000..93c441176 --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/objects.js @@ -0,0 +1,246 @@ +$(document).ready(function() { + + module("Object functions (values, extend, isEqual, and so on...)"); + + test("objects: keys", function() { + var exception = /object/; + equals(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object'); + // the test above is not safe because it relies on for-in enumeration order + var a = []; a[1] = 0; + equals(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); + raises(function() { _.keys(null); }, exception, 'throws an error for `null` values'); + raises(function() { _.keys(void 0); }, exception, 'throws an error for `undefined` values'); + raises(function() { _.keys(1); }, exception, 'throws an error for number primitives'); + raises(function() { _.keys('a'); }, exception, 'throws an error for string primitives'); + raises(function() { _.keys(true); }, exception, 'throws an error for boolean primitives'); + }); + + test("objects: values", function() { + equals(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object'); + }); + + test("objects: functions", function() { + var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce}; + ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object'); + }); + + test("objects: extend", function() { + var result; + equals(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); + equals(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); + equals(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden'); + result = _.extend({x:'x'}, {a:'a'}, {b:'b'}); + ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects'); + result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'}); + ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps'); + result = _.extend({}, {a: void 0, b: null}); + equals(_.keys(result).join(''), 'b', 'extend does not copy undefined values'); + }); + + test("objects: defaults", function() { + var result; + var options = {zero: 0, one: 1, empty: "", nan: NaN, string: "string"}; + + _.defaults(options, {zero: 1, one: 10, twenty: 20}); + equals(options.zero, 0, 'value exists'); + equals(options.one, 1, 'value exists'); + equals(options.twenty, 20, 'default applied'); + + _.defaults(options, {empty: "full"}, {nan: "nan"}, {word: "word"}, {word: "dog"}); + equals(options.empty, "", 'value exists'); + ok(_.isNaN(options.nan), "NaN isn't overridden"); + equals(options.word, "word", 'new value is added, first one wins'); + }); + + test("objects: clone", function() { + var moe = {name : 'moe', lucky : [13, 27, 34]}; + var clone = _.clone(moe); + equals(clone.name, 'moe', 'the clone as the attributes of the original'); + + clone.name = 'curly'; + ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original'); + + clone.lucky.push(101); + equals(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); + }); + + test("objects: isEqual", function() { + var moe = {name : 'moe', lucky : [13, 27, 34]}; + var clone = {name : 'moe', lucky : [13, 27, 34]}; + ok(moe != clone, 'basic equality between objects is false'); + ok(_.isEqual(moe, clone), 'deep equality is true'); + ok(_(moe).isEqual(clone), 'OO-style deep equality works'); + ok(!_.isEqual(5, NaN), '5 is not equal to NaN'); + ok(NaN != NaN, 'NaN is not equal to NaN (native equality)'); + ok(NaN !== NaN, 'NaN is not equal to NaN (native identity)'); + ok(!_.isEqual(NaN, NaN), 'NaN is not equal to NaN'); + ok(_.isEqual(new Date(100), new Date(100)), 'identical dates are equal'); + ok(_.isEqual((/hello/ig), (/hello/ig)), 'identical regexes are equal'); + ok(!_.isEqual(null, [1]), 'a falsy is never equal to a truthy'); + ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), 'objects with the same number of undefined keys are not equal'); + ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'wrapped objects are not equal'); + equals(_({x: 1, y: 2}).chain().isEqual(_({x: 1, y: 2}).chain()).value(), true, 'wrapped objects are equal'); + }); + + test("objects: isEmpty", function() { + ok(!_([1]).isEmpty(), '[1] is not empty'); + ok(_.isEmpty([]), '[] is empty'); + ok(!_.isEmpty({one : 1}), '{one : 1} is not empty'); + ok(_.isEmpty({}), '{} is empty'); + ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); + ok(_.isEmpty(null), 'null is empty'); + ok(_.isEmpty(), 'undefined is empty'); + ok(_.isEmpty(''), 'the empty string is empty'); + ok(!_.isEmpty('moe'), 'but other strings are not'); + + var obj = {one : 1}; + delete obj.one; + ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); + }); + + // Setup remote variables for iFrame tests. + var iframe = document.createElement('iframe'); + jQuery(iframe).appendTo(document.body); + var iDoc = iframe.contentDocument || iframe.contentWindow.document; + iDoc.write( + "" + ); + iDoc.close(); + + test("objects: isElement", function() { + ok(!_.isElement('div'), 'strings are not dom elements'); + ok(_.isElement($('html')[0]), 'the html tag is a DOM element'); + ok(_.isElement(iElement), 'even from another frame'); + }); + + test("objects: isArguments", function() { + var args = (function(){ return arguments; })(1, 2, 3); + ok(!_.isArguments('string'), 'a string is not an arguments object'); + ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); + ok(_.isArguments(args), 'but the arguments object is an arguments object'); + ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); + ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.'); + ok(_.isArguments(iArguments), 'even from another frame'); + }); + + test("objects: isArray", function() { + ok(!_.isArray(arguments), 'the arguments object is not an array'); + ok(_.isArray([1, 2, 3]), 'but arrays are'); + ok(_.isArray(iArray), 'even from another frame'); + }); + + test("objects: isString", function() { + ok(!_.isString(document.body), 'the document body is not a string'); + ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); + ok(_.isString(iString), 'even from another frame'); + }); + + test("objects: isNumber", function() { + ok(!_.isNumber('string'), 'a string is not a number'); + ok(!_.isNumber(arguments), 'the arguments object is not a number'); + ok(!_.isNumber(undefined), 'undefined is not a number'); + ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); + ok(!_.isNumber(NaN), 'NaN is not a number'); + ok(_.isNumber(Infinity), 'Infinity is a number'); + ok(_.isNumber(iNumber), 'even from another frame'); + }); + + test("objects: isBoolean", function() { + ok(!_.isBoolean(2), 'a number is not a boolean'); + ok(!_.isBoolean("string"), 'a string is not a boolean'); + ok(!_.isBoolean("false"), 'the string "false" is not a boolean'); + ok(!_.isBoolean("true"), 'the string "true" is not a boolean'); + ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); + ok(!_.isBoolean(undefined), 'undefined is not a boolean'); + ok(!_.isBoolean(NaN), 'NaN is not a boolean'); + ok(!_.isBoolean(null), 'null is not a boolean'); + ok(_.isBoolean(true), 'but true is'); + ok(_.isBoolean(false), 'and so is false'); + ok(_.isBoolean(iBoolean), 'even from another frame'); + }); + + test("objects: isFunction", function() { + ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); + ok(!_.isFunction('moe'), 'strings are not functions'); + ok(_.isFunction(_.isFunction), 'but functions are'); + ok(_.isFunction(iFunction), 'even from another frame'); + }); + + test("objects: isDate", function() { + ok(!_.isDate(100), 'numbers are not dates'); + ok(!_.isDate({}), 'objects are not dates'); + ok(_.isDate(new Date()), 'but dates are'); + ok(_.isDate(iDate), 'even from another frame'); + }); + + test("objects: isRegExp", function() { + ok(!_.isRegExp(_.identity), 'functions are not RegExps'); + ok(_.isRegExp(/identity/), 'but RegExps are'); + ok(_.isRegExp(iRegExp), 'even from another frame'); + }); + + test("objects: isNaN", function() { + ok(!_.isNaN(undefined), 'undefined is not NaN'); + ok(!_.isNaN(null), 'null is not NaN'); + ok(!_.isNaN(0), '0 is not NaN'); + ok(_.isNaN(NaN), 'but NaN is'); + ok(_.isNaN(iNaN), 'even from another frame'); + }); + + test("objects: isNull", function() { + ok(!_.isNull(undefined), 'undefined is not null'); + ok(!_.isNull(NaN), 'NaN is not null'); + ok(_.isNull(null), 'but null is'); + ok(_.isNull(iNull), 'even from another frame'); + }); + + test("objects: isUndefined", function() { + ok(!_.isUndefined(1), 'numbers are defined'); + ok(!_.isUndefined(null), 'null is defined'); + ok(!_.isUndefined(false), 'false is defined'); + ok(!_.isUndefined(NaN), 'NaN is defined'); + ok(_.isUndefined(), 'nothing is undefined'); + ok(_.isUndefined(undefined), 'undefined is undefined'); + ok(_.isUndefined(iUndefined), 'even from another frame'); + }); + + if (window.ActiveXObject) { + test("objects: IE host objects", function() { + var xml = new ActiveXObject("Msxml2.DOMDocument.3.0"); + ok(!_.isNumber(xml)); + ok(!_.isBoolean(xml)); + ok(!_.isNaN(xml)); + ok(!_.isFunction(xml)); + ok(!_.isNull(xml)); + ok(!_.isUndefined(xml)); + }); + } + + test("objects: tap", function() { + var intercepted = null; + var interceptor = function(obj) { intercepted = obj; }; + var returned = _.tap(1, interceptor); + equals(intercepted, 1, "passes tapped object to interceptor"); + equals(returned, 1, "returns tapped object"); + + returned = _([1,2,3]).chain(). + map(function(n){ return n * 2; }). + max(). + tap(interceptor). + value(); + ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain'); + }); +}); diff --git a/node_modules/underscore.string/test/test_underscore/speed.js b/node_modules/underscore.string/test/test_underscore/speed.js new file mode 100644 index 000000000..86663a237 --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/speed.js @@ -0,0 +1,70 @@ +(function() { + + var numbers = []; + for (var i=0; i<1000; i++) numbers.push(i); + var objects = _.map(numbers, function(n){ return {num : n}; }); + var randomized = _.sortBy(numbers, function(){ return Math.random(); }); + + JSLitmus.test('_.each()', function() { + var timesTwo = []; + _.each(numbers, function(num){ timesTwo.push(num * 2); }); + return timesTwo; + }); + + JSLitmus.test('_(list).each()', function() { + var timesTwo = []; + _(numbers).each(function(num){ timesTwo.push(num * 2); }); + return timesTwo; + }); + + JSLitmus.test('jQuery.each()', function() { + var timesTwo = []; + jQuery.each(numbers, function(){ timesTwo.push(this * 2); }); + return timesTwo; + }); + + JSLitmus.test('_.map()', function() { + return _.map(objects, function(obj){ return obj.num; }); + }); + + JSLitmus.test('jQuery.map()', function() { + return jQuery.map(objects, function(obj){ return obj.num; }); + }); + + JSLitmus.test('_.pluck()', function() { + return _.pluck(objects, 'num'); + }); + + JSLitmus.test('_.uniq()', function() { + return _.uniq(randomized); + }); + + JSLitmus.test('_.uniq() (sorted)', function() { + return _.uniq(numbers, true); + }); + + JSLitmus.test('_.sortBy()', function() { + return _.sortBy(numbers, function(num){ return -num; }); + }); + + JSLitmus.test('_.isEqual()', function() { + return _.isEqual(numbers, randomized); + }); + + JSLitmus.test('_.keys()', function() { + return _.keys(objects); + }); + + JSLitmus.test('_.values()', function() { + return _.values(objects); + }); + + JSLitmus.test('_.intersect()', function() { + return _.intersect(numbers, randomized); + }); + + JSLitmus.test('_.range()', function() { + return _.range(1000); + }); + +})(); \ No newline at end of file diff --git a/node_modules/underscore.string/test/test_underscore/temp.js b/node_modules/underscore.string/test/test_underscore/temp.js new file mode 100644 index 000000000..68c39dc57 --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/temp.js @@ -0,0 +1,27 @@ +(function() { + + var func = function(){}; + var date = new Date(); + var str = "a string"; + var numbers = []; + for (var i=0; i<1000; i++) numbers.push(i); + var objects = _.map(numbers, function(n){ return {num : n}; }); + var randomized = _.sortBy(numbers, function(){ return Math.random(); }); + + JSLitmus.test('_.isNumber', function() { + return _.isNumber(1000) + }); + + JSLitmus.test('_.newIsNumber', function() { + return _.newIsNumber(1000) + }); + + JSLitmus.test('_.isNumber(NaN)', function() { + return _.isNumber(NaN) + }); + + JSLitmus.test('_.newIsNumber(NaN)', function() { + return _.newIsNumber(NaN) + }); + +})(); \ No newline at end of file diff --git a/node_modules/underscore.string/test/test_underscore/temp_tests.html b/node_modules/underscore.string/test/test_underscore/temp_tests.html new file mode 100644 index 000000000..bd34f9dd7 --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/temp_tests.html @@ -0,0 +1,19 @@ + + + + Underscore Temporary Tests + + + + + + + +

      Underscore Temporary Tests

      +

      + A page for temporary speed tests, used for developing faster implementations + of existing Underscore methods. +

      +
      + + diff --git a/node_modules/underscore.string/test/test_underscore/test.html b/node_modules/underscore.string/test/test_underscore/test.html new file mode 100644 index 000000000..65ba7c3a2 --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/test.html @@ -0,0 +1,42 @@ + + + + Underscore Test Suite + + + + + + + + + + + + + + + +

      Underscore Test Suite

      +

      +

      +
        +
        +

        Underscore Speed Suite

        +

        + A representative sample of the functions are benchmarked here, to provide + a sense of how fast they might run in different browsers. + Each iteration runs on an array of 1000 elements.

        + For example, the 'intersect' test measures the number of times you can + find the intersection of two thousand-element arrays in one second. +

        +
        + + + + + diff --git a/node_modules/underscore.string/test/test_underscore/utility.js b/node_modules/underscore.string/test/test_underscore/utility.js new file mode 100644 index 000000000..94252a654 --- /dev/null +++ b/node_modules/underscore.string/test/test_underscore/utility.js @@ -0,0 +1,127 @@ +$(document).ready(function() { + + module("Utility functions (uniqueId, template)"); + + test("utility: noConflict", function() { + var underscore = _.noConflict(); + ok(underscore.isUndefined(_), "The '_' variable has been returned to its previous state."); + var intersection = underscore.intersect([-1, 0, 1, 2], [1, 2, 3, 4]); + equals(intersection.join(', '), '1, 2', 'but the intersection function still works'); + window._ = underscore; + }); + + test("utility: identity", function() { + var moe = {name : 'moe'}; + equals(_.identity(moe), moe, 'moe is the same as his identity'); + }); + + test("utility: uniqueId", function() { + var ids = [], i = 0; + while(i++ < 100) ids.push(_.uniqueId()); + equals(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); + }); + + test("utility: times", function() { + var vals = []; + _.times(3, function (i) { vals.push(i); }); + ok(_.isEqual(vals, [0,1,2]), "is 0 indexed"); + // + vals = []; + _(3).times(function (i) { vals.push(i); }); + ok(_.isEqual(vals, [0,1,2]), "works as a wrapper"); + }); + + test("utility: mixin", function() { + _.mixin({ + myReverse: function(string) { + return string.split('').reverse().join(''); + } + }); + equals(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); + equals(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); + }); + + test("utility: template", function() { + var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); + var result = basicTemplate({thing : 'This'}); + equals(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); + + var backslashTemplate = _.template("<%= thing %> is \\ridanculous"); + equals(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); + + var fancyTemplate = _.template("
          <% \ + for (key in people) { \ + %>
        • <%= people[key] %>
        • <% } %>
        "); + result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); + equals(result, "
        • Moe
        • Larry
        • Curly
        ", 'can run arbitrary javascript in templates'); + + var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
        \">
        <% }); %>"); + result = namespaceCollisionTemplate({ + pageCount: 3, + thumbnails: { + 1: "p1-thumbnail.gif", + 2: "p2-thumbnail.gif", + 3: "p3-thumbnail.gif" + } + }); + equals(result, "3 p3-thumbnail.gif
        "); + + var noInterpolateTemplate = _.template("

        Just some text. Hey, I know this is silly but it aids consistency.

        "); + result = noInterpolateTemplate(); + equals(result, "

        Just some text. Hey, I know this is silly but it aids consistency.

        "); + + var quoteTemplate = _.template("It's its, not it's"); + equals(quoteTemplate({}), "It's its, not it's"); + + var quoteInStatementAndBody = _.template("<%\ + if(foo == 'bar'){ \ + %>Statement quotes and 'quotes'.<% } %>"); + equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + + var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); + equals(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + + if (!$.browser.msie) { + var fromHTML = _.template($('#template').html()); + equals(fromHTML({data : 12345}).replace(/\s/g, ''), '
      1. 24690
      2. '); + } + + _.templateSettings = { + evaluate : /\{\{([\s\S]+?)\}\}/g, + interpolate : /\{\{=([\s\S]+?)\}\}/g + }; + + var custom = _.template("
          {{ for (key in people) { }}
        • {{= people[key] }}
        • {{ } }}
        "); + result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); + equals(result, "
        • Moe
        • Larry
        • Curly
        ", 'can run arbitrary javascript in templates'); + + var customQuote = _.template("It's its, not it's"); + equals(customQuote({}), "It's its, not it's"); + + var quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}"); + equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + + _.templateSettings = { + evaluate : /<\?([\s\S]+?)\?>/g, + interpolate : /<\?=([\s\S]+?)\?>/g + }; + + var customWithSpecialChars = _.template("
        "); + result = customWithSpecialChars({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); + equals(result, "
        • Moe
        • Larry
        • Curly
        ", 'can run arbitrary javascript in templates'); + + var customWithSpecialCharsQuote = _.template("It's its, not it's"); + equals(customWithSpecialCharsQuote({}), "It's its, not it's"); + + var quoteInStatementAndBody = _.template("Statement quotes and 'quotes'."); + equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + + _.templateSettings = { + interpolate : /\{\{(.+?)\}\}/g + }; + + var mustache = _.template("Hello {{planet}}!"); + equals(mustache({planet : "World"}), "Hello World!", "can mimic mustache.js"); + }); + +}); diff --git a/node_modules/underscore.string/test/underscore.js b/node_modules/underscore.string/test/underscore.js new file mode 100644 index 000000000..eaba008c4 --- /dev/null +++ b/node_modules/underscore.string/test/underscore.js @@ -0,0 +1,807 @@ +// Underscore.js 1.1.6 +// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore is freely distributable under the MIT license. +// Portions of Underscore are inspired or borrowed from Prototype, +// Oliver Steele's Functional, and John Resig's Micro-Templating. +// For all details and documentation: +// http://documentcloud.github.com/underscore + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var slice = ArrayProto.slice, + unshift = ArrayProto.unshift, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { return new wrapper(obj); }; + + // Export the Underscore object for **CommonJS**, with backwards-compatibility + // for the old `require()` API. If we're not in CommonJS, add `_` to the + // global object. + if (typeof module !== 'undefined' && module.exports) { + module.exports = _; + _._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.1.6'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects implementing `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (_.isNumber(obj.length)) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (hasOwnProperty.call(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = memo !== void 0; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial && index === 0) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError("Reduce of empty array with no initial value"); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse(); + return _.reduce(reversed, iterator, memo, context); + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + each(obj, function(value, index, list) { + if (!iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result = iterator.call(context, value, index, list)) return breaker; + }); + return result; + }; + + // Determine if a given value is included in the array or object using `===`. + // Aliased as `contains`. + _.include = _.contains = function(obj, target) { + var found = false; + if (obj == null) return found; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + any(obj, function(value) { + if (found = value === target) return true; + }); + return found; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + return _.map(obj, function(value) { + return (method.call ? method || value : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Return the maximum element or (element-based computation). + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); + var result = {computed : -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); + var result = {computed : Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, iterator, context) { + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }), 'value'); + }; + + // Use a comparator function to figure out at what index an object should + // be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator) { + iterator || (iterator = _.identity); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >> 1; + iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) return iterable.toArray(); + if (_.isArray(iterable)) return iterable; + if (_.isArguments(iterable)) return slice.call(iterable); + return _.values(iterable); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + return _.toArray(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head`. The **guard** check allows it to work + // with `_.map`. + _.first = _.head = function(array, n, guard) { + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the first entry of the array. Aliased as `tail`. + // Especially useful on the arguments object. Passing an **index** will return + // the rest of the values in the array from that index onward. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = function(array, index, guard) { + return slice.call(array, (index == null) || guard ? 1 : index); + }; + + // Get the last element of an array. + _.last = function(array) { + return array[array.length - 1]; + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, function(value){ return !!value; }); + }; + + // Return a completely flattened version of an array. + _.flatten = function(array) { + return _.reduce(array, function(memo, value) { + if (_.isArray(value)) return memo.concat(_.flatten(value)); + memo[memo.length] = value; + return memo; + }, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + var values = slice.call(arguments, 1); + return _.filter(array, function(value){ return !_.include(values, value); }); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted) { + return _.reduce(array, function(memo, el, i) { + if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el; + return memo; + }, []); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersect = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); + return results; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i, l; + if (isSorted) { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); + for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item) { + if (array == null) return -1; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); + var i = array.length; + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Binding with arguments is also known as `curry`. + // Delegates to **ECMAScript 5**'s native `Function.bind` if available. + // We check for `func.bind` first, to fail fast when `func` is undefined. + _.bind = function(func, obj) { + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + var args = slice.call(arguments, 2); + return function() { + return func.apply(obj, args.concat(slice.call(arguments))); + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length == 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(func, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Internal function used to implement `_.throttle` and `_.debounce`. + var limit = function(func, wait, debounce) { + var timeout; + return function() { + var context = this, args = arguments; + var throttler = function() { + timeout = null; + func.apply(context, args); + }; + if (debounce) clearTimeout(timeout); + if (debounce || !timeout) timeout = setTimeout(throttler, wait); + }; + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + return limit(func, wait, false); + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. + _.debounce = function(func, wait) { + return limit(func, wait, true); + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + return memo = func.apply(this, arguments); + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func].concat(slice.call(arguments)); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = slice.call(arguments); + return function() { + var args = slice.call(arguments); + for (var i=funcs.length-1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + return function() { + if (--times < 1) { return func.apply(this, arguments); } + }; + }; + + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + return _.map(obj, _.identity); + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (source[prop] !== void 0) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + // Check object identity. + if (a === b) return true; + // Different types? + var atype = typeof(a), btype = typeof(b); + if (atype != btype) return false; + // Basic equality test (watch out for coercions). + if (a == b) return true; + // One is falsy and the other truthy. + if ((!a && b) || (a && !b)) return false; + // Unwrap any wrapped objects. + if (a._chain) a = a._wrapped; + if (b._chain) b = b._wrapped; + // One of them implements an isEqual()? + if (a.isEqual) return a.isEqual(b); + // Check dates' integer values. + if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); + // Both are NaN? + if (_.isNaN(a) && _.isNaN(b)) return false; + // Compare regular expressions. + if (_.isRegExp(a) && _.isRegExp(b)) + return a.source === b.source && + a.global === b.global && + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline; + // If a is not an object by this point, we can't handle it. + if (atype !== 'object') return false; + // Check for different array lengths before comparing contents. + if (a.length && (a.length !== b.length)) return false; + // Nothing else worked, deep compare the contents. + var aKeys = _.keys(a), bKeys = _.keys(b); + // Different object sizes? + if (aKeys.length != bKeys.length) return false; + // Recursive comparison of contents. + for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false; + return true; + }; + + // Is a given array or object empty? + _.isEmpty = function(obj) { + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (hasOwnProperty.call(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType == 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) === '[object Array]'; + }; + + // Is a given variable an arguments object? + _.isArguments = function(obj) { + return !!(obj && hasOwnProperty.call(obj, 'callee')); + }; + + // Is a given value a function? + _.isFunction = function(obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); + }; + + // Is a given value a string? + _.isString = function(obj) { + return !!(obj === '' || (obj && obj.charCodeAt && obj.substr)); + }; + + // Is a given value a number? + _.isNumber = function(obj) { + return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed)); + }; + + // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript + // that does not equal itself. + _.isNaN = function(obj) { + return obj !== obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false; + }; + + // Is a given value a date? + _.isDate = function(obj) { + return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear); + }; + + // Is the given value a regular expression? + _.isRegExp = function(obj) { + return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false)); + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function (n, iterator, context) { + for (var i = 0; i < n; i++) iterator.call(context, i); + }; + + // Add your own custom functions to the Underscore object, ensuring that + // they're correctly added to the OOP wrapper as well. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + addToWrapper(name, _[name] = obj[name]); + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g + }; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(str, data) { + var c = _.templateSettings; + var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + + 'with(obj||{}){__p.push(\'' + + str.replace(/\\/g, '\\\\') + .replace(/'/g, "\\'") + .replace(c.interpolate, function(match, code) { + return "'," + code.replace(/\\'/g, "'") + ",'"; + }) + .replace(c.evaluate || null, function(match, code) { + return "');" + code.replace(/\\'/g, "'") + .replace(/[\r\n\t]/g, ' ') + "__p.push('"; + }) + .replace(/\r/g, '\\r') + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t') + + "');}return __p.join('');"; + var func = new Function('obj', tmpl); + return data ? func(data) : func; + }; + + // The OOP Wrapper + // --------------- + + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + var wrapper = function(obj) { this._wrapped = obj; }; + + // Expose `wrapper.prototype` as `_.prototype` + _.prototype = wrapper.prototype; + + // Helper function to continue chaining intermediate results. + var result = function(obj, chain) { + return chain ? _(obj).chain() : obj; + }; + + // A method to easily add functions to the OOP wrapper. + var addToWrapper = function(name, func) { + wrapper.prototype[name] = function() { + var args = slice.call(arguments); + unshift.call(args, this._wrapped); + return result(func.apply(_, args), this._chain); + }; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + wrapper.prototype[name] = function() { + method.apply(this._wrapped, arguments); + return result(this._wrapped, this._chain); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + wrapper.prototype[name] = function() { + return result(method.apply(this._wrapped, arguments), this._chain); + }; + }); + + // Start chaining a wrapped Underscore object. + wrapper.prototype.chain = function() { + this._chain = true; + return this; + }; + + // Extracts the result from a wrapped and chained object. + wrapper.prototype.value = function() { + return this._wrapped; + }; + +})(); diff --git a/node_modules/underscore/.npmignore b/node_modules/underscore/.npmignore new file mode 100644 index 000000000..f5717584f --- /dev/null +++ b/node_modules/underscore/.npmignore @@ -0,0 +1,4 @@ +test/ +underscore-min.js +Rakefile +docs/ \ No newline at end of file diff --git a/node_modules/underscore/LICENSE b/node_modules/underscore/LICENSE new file mode 100644 index 000000000..58c73b9e9 --- /dev/null +++ b/node_modules/underscore/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2011 Jeremy Ashkenas, DocumentCloud + +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/node_modules/underscore/README b/node_modules/underscore/README new file mode 100644 index 000000000..408145aab --- /dev/null +++ b/node_modules/underscore/README @@ -0,0 +1,19 @@ + __ + /\ \ __ + __ __ ___ \_\ \ __ _ __ ____ ___ ___ _ __ __ /\_\ ____ +/\ \/\ \ /' _ `\ /'_ \ /'__`\/\ __\/ ,__\ / ___\ / __`\/\ __\/'__`\ \/\ \ /',__\ +\ \ \_\ \/\ \/\ \/\ \ \ \/\ __/\ \ \//\__, `\/\ \__//\ \ \ \ \ \//\ __/ __ \ \ \/\__, `\ + \ \____/\ \_\ \_\ \___,_\ \____\\ \_\\/\____/\ \____\ \____/\ \_\\ \____\/\_\ _\ \ \/\____/ + \/___/ \/_/\/_/\/__,_ /\/____/ \/_/ \/___/ \/____/\/___/ \/_/ \/____/\/_//\ \_\ \/___/ + \ \____/ + \/___/ + +Underscore is a utility-belt library for JavaScript that provides +support for the usual functional suspects (each, map, reduce, filter...) +without extending any core JavaScript objects. + +For Docs, License, Tests, and pre-packed downloads, see: +http://documentcloud.github.com/underscore/ + +Many thanks to our contributors: +https://github.com/documentcloud/underscore/contributors diff --git a/node_modules/underscore/index.html b/node_modules/underscore/index.html new file mode 100644 index 000000000..04c878ad0 --- /dev/null +++ b/node_modules/underscore/index.html @@ -0,0 +1,1560 @@ + + + + + + Underscore.js + + + + +
        + +

        Underscore.js

        + +

        + Underscore is a + utility-belt library for JavaScript that provides a lot of the + functional programming support that you would expect in + Prototype.js + (or Ruby), + but without extending any of the built-in JavaScript objects. It's the + tie to go along with jQuery's tux. +

        + +

        + Underscore provides 60-odd functions that support both the usual + functional suspects: map, select, invoke — + as well as more specialized helpers: function binding, javascript + templating, deep equality testing, and so on. It delegates to built-in + functions, if present, so modern browsers will use the + native implementations of forEach, map, reduce, + filter, every, some and indexOf. +

        + +

        + A complete Test & Benchmark Suite + is included for your perusal. +

        + +

        + You may also read through the annotated source code. +

        + +

        + The project is + hosted on GitHub. + You can report bugs and discuss features on the + issues page, + on Freenode in the #documentcloud channel, + or send tweets to @documentcloud. +

        + +

        + Underscore is an open-source component of DocumentCloud. +

        + +

        Downloads (Right-click, and use "Save As")

        + + + + + + + + + + +
        Development Version (1.1.6)27kb, Uncompressed with Comments
        Production Version (1.1.6)3kb, Minified and Gzipped
        + +

        Table of Contents

        + + Object-Oriented and Functional Styles + +

        + Collections +
        + each, map, + reduce, reduceRight, + detect, select, + reject, all, + any, include, + invoke, pluck, + max, min, + sortBy, sortedIndex, + toArray, size +

        + +

        + Arrays +
        + first, rest, last, + compact, flatten, without, uniq, + intersect, zip, indexOf, + lastIndexOf, range +

        + +

        + Functions +
        + bind, bindAll, + memoize, delay, defer, + throttle, debounce, + once, after, wrap, compose +

        + +

        + Objects +
        + keys, values, + functions, extend, defaults, clone, tap, + isEqual, isEmpty, isElement, + isArray, isArguments, isFunction, isString, + isNumber, isBoolean, isDate, isRegExp + isNaN, isNull, + isUndefined + +

        + +

        + Utility +
        + noConflict, + identity, times, + mixin, uniqueId, + template +

        + +

        + Chaining +
        + chain, value +

        + +
        + +

        Object-Oriented and Functional Styles

        + +

        + You can use Underscore in either an object-oriented or a functional style, + depending on your preference. The following two lines of code are + identical ways to double a list of numbers. +

        + +
        +_.map([1, 2, 3], function(n){ return n * 2; });
        +_([1, 2, 3]).map(function(n){ return n * 2; });
        + +

        + Using the object-oriented style allows you to chain together methods. Calling + chain on a wrapped object will cause all future method calls to + return wrapped objects as well. When you've finished the computation, + use value to retrieve the final value. Here's an example of chaining + together a map/flatten/reduce, in order to get the word count of + every word in a song. +

        + +
        +var lyrics = [
        +  {line : 1, words : "I'm a lumberjack and I'm okay"},
        +  {line : 2, words : "I sleep all night and I work all day"},
        +  {line : 3, words : "He's a lumberjack and he's okay"},
        +  {line : 4, words : "He sleeps all night and he works all day"}
        +];
        +
        +_(lyrics).chain()
        +  .map(function(line) { return line.words.split(' '); })
        +  .flatten()
        +  .reduce(function(counts, word) {
        +    counts[word] = (counts[word] || 0) + 1;
        +    return counts;
        +}, {}).value();
        +
        +=> {lumberjack : 2, all : 4, night : 2 ... }
        + +

        + In addition, the + Array prototype's methods + are proxied through the chained Underscore object, so you can slip a + reverse or a push into your chain, and continue to + modify the array. +

        + +

        Collection Functions (Arrays or Objects)

        + +

        + each_.each(list, iterator, [context]) + Alias: forEach +
        + Iterates over a list of elements, yielding each in turn to an iterator + function. The iterator is bound to the context object, if one is + passed. Each invocation of iterator is called with three arguments: + (element, index, list). If list is a JavaScript object, iterator's + arguments will be (value, key, list). Delegates to the native + forEach function if it exists. +

        +
        +_.each([1, 2, 3], function(num){ alert(num); });
        +=> alerts each number in turn...
        +_.each({one : 1, two : 2, three : 3}, function(num, key){ alert(num); });
        +=> alerts each number in turn...
        + +

        + map_.map(list, iterator, [context]) +
        + Produces a new array of values by mapping each value in list + through a transformation function (iterator). If the native map method + exists, it will be used instead. If list is a JavaScript object, + iterator's arguments will be (value, key, list). +

        +
        +_.map([1, 2, 3], function(num){ return num * 3; });
        +=> [3, 6, 9]
        +_.map({one : 1, two : 2, three : 3}, function(num, key){ return num * 3; });
        +=> [3, 6, 9]
        + +

        + reduce_.reduce(list, iterator, memo, [context]) + Aliases: inject, foldl +
        + Also known as inject and foldl, reduce boils down a + list of values into a single value. Memo is the initial state + of the reduction, and each successive step of it should be returned by + iterator. +

        +
        +var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
        +=> 6
        +
        + +

        + reduceRight_.reduceRight(list, iterator, memo, [context]) + Alias: foldr +
        + The right-associative version of reduce. Delegates to the + JavaScript 1.8 version of reduceRight, if it exists. Foldr + is not as useful in JavaScript as it would be in a language with lazy + evaluation. +

        +
        +var list = [[0, 1], [2, 3], [4, 5]];
        +var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
        +=> [4, 5, 2, 3, 0, 1]
        +
        + +

        + detect_.detect(list, iterator, [context]) +
        + Looks through each value in the list, returning the first one that + passes a truth test (iterator). The function returns as + soon as it finds an acceptable element, and doesn't traverse the + entire list. +

        +
        +var even = _.detect([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
        +=> 2
        +
        + +

        + select_.select(list, iterator, [context]) + Alias: filter +
        + Looks through each value in the list, returning an array of all + the values that pass a truth test (iterator). Delegates to the + native filter method, if it exists. +

        +
        +var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
        +=> [2, 4, 6]
        +
        + +

        + reject_.reject(list, iterator, [context]) +
        + Returns the values in list without the elements that the truth + test (iterator) passes. The opposite of select. +

        +
        +var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
        +=> [1, 3, 5]
        +
        + +

        + all_.all(list, [iterator], [context]) + Alias: every +
        + Returns true if all of the values in the list pass the iterator + truth test. If an iterator is not provided, the truthy value of + the element will be used instead. Delegates to the native method every, if + present. +

        +
        +_.all([true, 1, null, 'yes']);
        +=> false
        +
        + +

        + any_.any(list, [iterator], [context]) + Alias: some +
        + Returns true if any of the values in the list pass the + iterator truth test. Short-circuits and stops traversing the list + if a true element is found. Delegates to the native method some, + if present. +

        +
        +_.any([null, 0, 'yes', false]);
        +=> true
        +
        + +

        + include_.include(list, value) + Alias: contains +
        + Returns true if the value is present in the list, using + === to test equality. Uses indexOf internally, if list + is an Array. +

        +
        +_.include([1, 2, 3], 3);
        +=> true
        +
        + +

        + invoke_.invoke(list, methodName, [*arguments]) +
        + Calls the method named by methodName on each value in the list. + Any extra arguments passed to invoke will be forwarded on to the + method invocation. +

        +
        +_.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
        +=> [[1, 5, 7], [1, 2, 3]]
        +
        + +

        + pluck_.pluck(list, propertyName) +
        + An convenient version of what is perhaps the most common use-case for + map: extracting a list of property values. +

        +
        +var stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}];
        +_.pluck(stooges, 'name');
        +=> ["moe", "larry", "curly"]
        +
        + +

        + max_.max(list, [iterator], [context]) +
        + Returns the maximum value in list. If iterator is passed, + it will be used on each value to generate the criterion by which the + value is ranked. +

        +
        +var stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}];
        +_.max(stooges, function(stooge){ return stooge.age; });
        +=> {name : 'curly', age : 60};
        +
        + +

        + min_.min(list, [iterator], [context]) +
        + Returns the minimum value in list. If iterator is passed, + it will be used on each value to generate the criterion by which the + value is ranked. +

        +
        +var numbers = [10, 5, 100, 2, 1000];
        +_.min(numbers);
        +=> 2
        +
        + +

        + sortBy_.sortBy(list, iterator, [context]) +
        + Returns a sorted list, ranked by the results of running each + value through iterator. +

        +
        +_.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
        +=> [5, 4, 6, 3, 1, 2]
        +
        + +

        + sortedIndex_.sortedIndex(list, value, [iterator]) +
        + Uses a binary search to determine the index at which the value + should be inserted into the list in order to maintain the list's + sorted order. If an iterator is passed, it will be used to compute + the sort ranking of each value. +

        +
        +_.sortedIndex([10, 20, 30, 40, 50], 35);
        +=> 3
        +
        + +

        + toArray_.toArray(list) +
        + Converts the list (anything that can be iterated over), into a + real Array. Useful for transmuting the arguments object. +

        +
        +(function(){ return _.toArray(arguments).slice(0); })(1, 2, 3);
        +=> [1, 2, 3]
        +
        + +

        + size_.size(list) +
        + Return the number of values in the list. +

        +
        +_.size({one : 1, two : 2, three : 3});
        +=> 3
        +
        + +

        Array Functions

        + +

        + Note: All array functions will also work on the arguments object. +

        + +

        + first_.first(array, [n]) + Alias: head +
        + Returns the first element of an array. Passing n will + return the first n elements of the array. +

        +
        +_.first([5, 4, 3, 2, 1]);
        +=> 5
        +
        + +

        + rest_.rest(array, [index]) + Alias: tail +
        + Returns the rest of the elements in an array. Pass an index + to return the values of the array from that index onward. +

        +
        +_.rest([5, 4, 3, 2, 1]);
        +=> [4, 3, 2, 1]
        +
        + +

        + last_.last(array) +
        + Returns the last element of an array. +

        +
        +_.last([5, 4, 3, 2, 1]);
        +=> 1
        +
        + +

        + compact_.compact(array) +
        + Returns a copy of the array with all falsy values removed. + In JavaScript, false, null, 0, "", + undefined and NaN are all falsy. +

        +
        +_.compact([0, 1, false, 2, '', 3]);
        +=> [1, 2, 3]
        +
        + +

        + flatten_.flatten(array) +
        + Flattens a nested array (the nesting can be to any depth). +

        +
        +_.flatten([1, [2], [3, [[[4]]]]]);
        +=> [1, 2, 3, 4];
        +
        + +

        + without_.without(array, [*values]) +
        + Returns a copy of the array with all instances of the values + removed. === is used for the equality test. +

        +
        +_.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
        +=> [2, 3, 4]
        +
        + +

        + uniq_.uniq(array, [isSorted]) + Alias: unique +
        + Produces a duplicate-free version of the array, using === to test + object equality. If you know in advance that the array is sorted, + passing true for isSorted will run a much faster algorithm. +

        +
        +_.uniq([1, 2, 1, 3, 1, 4]);
        +=> [1, 2, 3, 4]
        +
        + +

        + intersect_.intersect(*arrays) +
        + Computes the list of values that are the intersection of all the arrays. + Each value in the result is present in each of the arrays. +

        +
        +_.intersect([1, 2, 3], [101, 2, 1, 10], [2, 1]);
        +=> [1, 2]
        +
        + +

        + zip_.zip(*arrays) +
        + Merges together the values of each of the arrays with the + values at the corresponding position. Useful when you have separate + data sources that are coordinated through matching array indexes. + If you're working with a matrix of nested arrays, zip.apply + can transpose the matrix in a similar fashion. +

        +
        +_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
        +=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
        +
        + +

        + indexOf_.indexOf(array, value, [isSorted]) +
        + Returns the index at which value can be found in the array, + or -1 if value is not present in the array. Uses the native + indexOf function unless it's missing. If you're working with a + large array, and you know that the array is already sorted, pass true + for isSorted to use a faster binary search. +

        +
        +_.indexOf([1, 2, 3], 2);
        +=> 1
        +
        + +

        + lastIndexOf_.lastIndexOf(array, value) +
        + Returns the index of the last occurrence of value in the array, + or -1 if value is not present. Uses the native lastIndexOf + function if possible. +

        +
        +_.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
        +=> 4
        +
        + +

        + range_.range([start], stop, [step]) +
        + A function to create flexibly-numbered lists of integers, handy for + each and map loops. start, if omitted, defaults + to 0; step defaults to 1. Returns a list of integers + from start to stop, incremented (or decremented) by step, + exclusive. +

        +
        +_.range(10);
        +=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        +_.range(1, 11);
        +=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        +_.range(0, 30, 5);
        +=> [0, 5, 10, 15, 20, 25]
        +_.range(0, -10, -1);
        +=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
        +_.range(0);
        +=> []
        +
        + +

        Function (uh, ahem) Functions

        + +

        + bind_.bind(function, object, [*arguments]) +
        + Bind a function to an object, meaning that whenever + the function is called, the value of this will be the object. + Optionally, bind arguments to the function to pre-fill them, + also known as currying. +

        +
        +var func = function(greeting){ return greeting + ': ' + this.name };
        +func = _.bind(func, {name : 'moe'}, 'hi');
        +func();
        +=> 'hi: moe'
        +
        + +

        + bindAll_.bindAll(object, [*methodNames]) +
        + Binds a number of methods on the object, specified by + methodNames, to be run in the context of that object whenever they + are invoked. Very handy for binding functions that are going to be used + as event handlers, which would otherwise be invoked with a fairly useless + this. If no methodNames are provided, all of the object's + function properties will be bound to it. +

        +
        +var buttonView = {
        +  label   : 'underscore',
        +  onClick : function(){ alert('clicked: ' + this.label); },
        +  onHover : function(){ console.log('hovering: ' + this.label); }
        +};
        +_.bindAll(buttonView);
        +jQuery('#underscore_button').bind('click', buttonView.onClick);
        +=> When the button is clicked, this.label will have the correct value...
        +
        + +

        + memoize_.memoize(function, [hashFunction]) +
        + Memoizes a given function by caching the computed result. Useful + for speeding up slow-running computations. If passed an optional + hashFunction, it will be used to compute the hash key for storing + the result, based on the arguments to the original function. The default + hashFunction just uses the first argument to the memoized function + as the key. +

        +
        +var fibonacci = function(n) {
        +  return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
        +};
        +var fastFibonacci = _.memoize(fibonacci);
        +
        + +

        + delay_.delay(function, wait, [*arguments]) +
        + Much like setTimeout, invokes function after wait + milliseconds. If you pass the optional arguments, they will be + forwarded on to the function when it is invoked. +

        +
        +var log = _.bind(console.log, console);
        +_.delay(log, 1000, 'logged later');
        +=> 'logged later' // Appears after one second.
        +
        + +

        + defer_.defer(function) +
        + Defers invoking the function until the current call stack has cleared, + similar to using setTimeout with a delay of 0. Useful for performing + expensive computations or HTML rendering in chunks without blocking the UI thread + from updating. +

        +
        +_.defer(function(){ alert('deferred'); });
        +// Returns from the function before the alert runs.
        +
        + +

        + throttle_.throttle(function, wait) +
        + Returns a throttled version of the function, that, when invoked repeatedly, + will only actually call the wrapped function at most once per every wait + milliseconds. Useful for rate-limiting events that occur faster than you + can keep up with. +

        +
        +var throttled = _.throttle(updatePosition, 100);
        +$(window).scroll(throttled);
        +
        + +

        + debounce_.debounce(function, wait) +
        + Repeated calls to a debounced function will postpone it's execution + until after wait milliseconds have elapsed. Useful for implementing + behavior that should only happen after the input has stopped arriving. + For example: rendering a preview of a Markdown comment, recalculating a + layout after the window has stopped being resized... +

        +
        +var lazyLayout = _.debounce(calculateLayout, 300);
        +$(window).resize(lazyLayout);
        +
        + +

        + once_.once(function) +
        + Creates a version of the function that can only be called one time. + Repeated calls to the modified function will have no effect, returning + the value from the original call. Useful for initialization functions, + instead of having to set a boolean flag and then check it later. +

        +
        +var initialize = _.once(createApplication);
        +initialize();
        +initialize();
        +// Application is only created once.
        +
        + +

        + after_.after(count, function) +
        + Creates a version of the function that will only be run after first + being called count times. Useful for grouping asynchronous responses, + where you want to be sure that all the async calls have finished, before + proceeding. +

        +
        +var renderNotes = _.after(notes.length, render);
        +_.each(notes, function(note) {
        +  note.asyncSave({success: renderNotes}); 
        +});
        +// renderNotes is run once, after all notes have saved.
        +
        + +

        + wrap_.wrap(function, wrapper) +
        + Wraps the first function inside of the wrapper function, + passing it as the first argument. This allows the wrapper to + execute code before and after the function runs, adjust the arguments, + and execute it conditionally. +

        +
        +var hello = function(name) { return "hello: " + name; };
        +hello = _.wrap(hello, function(func) {
        +  return "before, " + func("moe") + ", after";
        +});
        +hello();
        +=> 'before, hello: moe, after'
        +
        + +

        + compose_.compose(*functions) +
        + Returns the composition of a list of functions, where each function + consumes the return value of the function that follows. In math terms, + composing the functions f(), g(), and h() produces + f(g(h())). +

        +
        +var greet    = function(name){ return "hi: " + name; };
        +var exclaim  = function(statement){ return statement + "!"; };
        +var welcome = _.compose(exclaim, greet);
        +welcome('moe');
        +=> 'hi: moe!'
        +
        + +

        Object Functions

        + +

        + keys_.keys(object) +
        + Retrieve all the names of the object's properties. +

        +
        +_.keys({one : 1, two : 2, three : 3});
        +=> ["one", "two", "three"]
        +
        + +

        + values_.values(object) +
        + Return all of the values of the object's properties. +

        +
        +_.values({one : 1, two : 2, three : 3});
        +=> [1, 2, 3]
        +
        + +

        + functions_.functions(object) + Alias: methods +
        + Returns a sorted list of the names of every method in an object — + that is to say, the name of every function property of the object. +

        +
        +_.functions(_);
        +=> ["all", "any", "bind", "bindAll", "clone", "compact", "compose" ...
        +
        + +

        + extend_.extend(destination, *sources) +
        + Copy all of the properties in the source objects over to the + destination object. It's in-order, to the last source will override + properties of the same name in previous arguments. +

        +
        +_.extend({name : 'moe'}, {age : 50});
        +=> {name : 'moe', age : 50}
        +
        + +

        + defaults_.defaults(object, *defaults) +
        + Fill in missing properties in object with default values from the + defaults objects. As soon as the property is filled, further defaults + will have no effect. +

        +
        +var iceCream = {flavor : "chocolate"};
        +_.defaults(iceCream, {flavor : "vanilla", sprinkles : "lots"});
        +=> {flavor : "chocolate", sprinkles : "lots"}
        +
        + +

        + clone_.clone(object) +
        + Create a shallow-copied clone of the object. Any nested objects + or arrays will be copied by reference, not duplicated. +

        +
        +_.clone({name : 'moe'});
        +=> {name : 'moe'};
        +
        + +

        + tap_.tap(object, interceptor) +
        + Invokes interceptor with the object, and then returns object. + The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain. +

        +
        +_([1,2,3,200]).chain().
        +  select(function(num) { return num % 2 == 0; }).
        +  tap(console.log).
        +  map(function(num) { return num * num }).
        +  value();
        +=> [2, 200]
        +=> [4, 40000]
        +
        + +

        + isEqual_.isEqual(object, other) +
        + Performs an optimized deep comparison between the two objects, to determine + if they should be considered equal. +

        +
        +var moe   = {name : 'moe', luckyNumbers : [13, 27, 34]};
        +var clone = {name : 'moe', luckyNumbers : [13, 27, 34]};
        +moe == clone;
        +=> false
        +_.isEqual(moe, clone);
        +=> true
        +
        + +

        + isEmpty_.isEmpty(object) +
        + Returns true if object contains no values. +

        +
        +_.isEmpty([1, 2, 3]);
        +=> false
        +_.isEmpty({});
        +=> true
        +
        + +

        + isElement_.isElement(object) +
        + Returns true if object is a DOM element. +

        +
        +_.isElement(jQuery('body')[0]);
        +=> true
        +
        + +

        + isArray_.isArray(object) +
        + Returns true if object is an Array. +

        +
        +(function(){ return _.isArray(arguments); })();
        +=> false
        +_.isArray([1,2,3]);
        +=> true
        +
        + +

        + isArguments_.isArguments(object) +
        + Returns true if object is an Arguments object. +

        +
        +(function(){ return _.isArguments(arguments); })(1, 2, 3);
        +=> true
        +_.isArguments([1,2,3]);
        +=> false
        +
        + +

        + isFunction_.isFunction(object) +
        + Returns true if object is a Function. +

        +
        +_.isFunction(alert);
        +=> true
        +
        + +

        + isString_.isString(object) +
        + Returns true if object is a String. +

        +
        +_.isString("moe");
        +=> true
        +
        + +

        + isNumber_.isNumber(object) +
        + Returns true if object is a Number. +

        +
        +_.isNumber(8.4 * 5);
        +=> true
        +
        + +

        + isBoolean_.isBoolean(object) +
        + Returns true if object is either true or false. +

        +
        +_.isBoolean(null);
        +=> false
        +
        + +

        + isDate_.isDate(object) +
        + Returns true if object is a Date. +

        +
        +_.isDate(new Date());
        +=> true
        +
        + +

        + isRegExp_.isRegExp(object) +
        + Returns true if object is a RegExp. +

        +
        +_.isRegExp(/moe/);
        +=> true
        +
        + +

        + isNaN_.isNaN(object) +
        + Returns true if object is NaN.
        Note: this is not + the same as the native isNaN function, which will also return + true if the variable is undefined. +

        +
        +_.isNaN(NaN);
        +=> true
        +isNaN(undefined);
        +=> true
        +_.isNaN(undefined);
        +=> false
        +
        + +

        + isNull_.isNull(object) +
        + Returns true if the value of object is null. +

        +
        +_.isNull(null);
        +=> true
        +_.isNull(undefined);
        +=> false
        +
        + +

        + isUndefined_.isUndefined(variable) +
        + Returns true if variable is undefined. +

        +
        +_.isUndefined(window.missingVariable);
        +=> true
        +
        + +

        Utility Functions

        + +

        + noConflict_.noConflict() +
        + Give control of the "_" variable back to its previous owner. Returns + a reference to the Underscore object. +

        +
        +var underscore = _.noConflict();
        + +

        + identity_.identity(value) +
        + Returns the same value that is used as the argument. In math: + f(x) = x
        + This function looks useless, but is used throughout Underscore as + a default iterator. +

        +
        +var moe = {name : 'moe'};
        +moe === _.identity(moe);
        +=> true
        + +

        + times_.times(n, iterator) +
        + Invokes the given iterator function n times. +

        +
        +_(3).times(function(){ genie.grantWish(); });
        + +

        + mixin_.mixin(object) +
        + Allows you to extend Underscore with your own utility functions. Pass + a hash of {name: function} definitions to have your functions + added to the Underscore object, as well as the OOP wrapper. +

        +
        +_.mixin({
        +  capitalize : function(string) {
        +    return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
        +  }
        +});
        +_("fabio").capitalize();
        +=> "Fabio"
        +
        + +

        + uniqueId_.uniqueId([prefix]) +
        + Generate a globally-unique id for client-side models or DOM elements + that need one. If prefix is passed, the id will be appended to it. +

        +
        +_.uniqueId('contact_');
        +=> 'contact_104'
        + +

        + template_.template(templateString, [context]) +
        + Compiles JavaScript templates into functions that can be evaluated + for rendering. Useful for rendering complicated bits of HTML from JSON + data sources. Template functions can both interpolate variables, using
        + <%= … %>, as well as execute arbitrary JavaScript code, with + <% … %>. When you evaluate a template function, pass in a + context object that has properties corresponding to the template's free + variables. If you're writing a one-off, you can pass the context + object as the second parameter to template in order to render + immediately instead of returning a template function. +

        +
        +var compiled = _.template("hello: <%= name %>");
        +compiled({name : 'moe'});
        +=> "hello: moe"
        +
        +var list = "<% _.each(people, function(name) { %> <li><%= name %></li> <% }); %>";
        +_.template(list, {people : ['moe', 'curly', 'larry']});
        +=> "<li>moe</li><li>curly</li><li>larry</li>"
        + +

        + You can also use print from within JavaScript code. This is + sometimes more convenient than using <%= ... %>. +

        + +
        +var compiled = _.template("<% print('Hello ' + epithet); %>");
        +compiled({epithet: "stooge"});
        +=> "Hello stooge."
        + +

        + If ERB-style delimiters aren't your cup of tea, you can change Underscore's + template settings to use different symbols to set off interpolated code. + Define an interpolate regex, and an (optional) evaluate regex + to match expressions that should be inserted and evaluated, respectively. + If no evaluate regex is provided, your templates will only be + capable of interpolating values. + For example, to perform + Mustache.js + style templating: +

        + +
        +_.templateSettings = {
        +  interpolate : /\{\{(.+?)\}\}/g
        +};
        +
        +var template = _.template("Hello {{ name }}!");
        +template({name : "Mustache"});
        +=> "Hello Mustache!"
        + +

        Chaining

        + +

        + chain_(obj).chain() +
        + Returns a wrapped object. Calling methods on this object will continue + to return wrapped objects until value is used. ( + A more realistic example.) +

        +
        +var stooges = [{name : 'curly', age : 25}, {name : 'moe', age : 21}, {name : 'larry', age : 23}];
        +var youngest = _(stooges).chain()
        +  .sortBy(function(stooge){ return stooge.age; })
        +  .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
        +  .first()
        +  .value();
        +=> "moe is 21"
        +
        + +

        + value_(obj).value() +
        + Extracts the value of a wrapped object. +

        +
        +_([1, 2, 3]).value();
        +=> [1, 2, 3]
        +
        + +

        Duck Typing

        + +

        + The isType (isArray, isFunction, isString ...) family of type-checking + functions use property detection to do their work, which, although + orders of magnitude faster than the alternative, isn't entirely safe when dealing + with objects that are used as hashes, where arbitrary strings are being + set for the keys. It's entirely possible for an object to masquerade as + another type, if you're setting properties with names like "concat" and + "charCodeAt". So be aware. +

        + + +

        Links & Suggested Reading

        + +

        + Underscore.lua, + a Lua port of the functions that are applicable in both languages. + Includes OOP-wrapping and chaining. + The source is + available on GitHub. +

        + +

        + Underscore.string, + an Underscore extension that adds functions for string-manipulation: + trim, startsWith, contains, capitalize, + reverse, sprintf, and more. +

        + +

        + Ruby's Enumerable module. +

        + +

        + Prototype.js, which provides + JavaScript with collection functions in the manner closest to Ruby's Enumerable. +

        + +

        + Oliver Steele's + Functional JavaScript, + which includes comprehensive higher-order function support as well as string lambdas. +

        + +

        + Python's itertools. +

        + +

        Change Log

        + +

        + 1.1.6April 18, 2011
        + Added _.after, which will return a function that only runs after + first being called a specified number of times. + _.invoke can now take a direct function reference. + _.every now requires an iterator function to be passed, which + mirrors the ECMA5 API. + _.extend no longer copies keys when the value is undefined. + _.bind now errors when trying to bind an undefined value. +

        + +

        + 1.1.5Mar 20, 2011
        + Added an _.defaults function, for use merging together JS objects + representing default options. + Added an _.once function, for manufacturing functions that should + only ever execute a single time. + _.bind now delegates to the native ECMAScript 5 version, + where available. + _.keys now throws an error when used on non-Object values, as in + ECMAScript 5. + Fixed a bug with _.keys when used over sparse arrays. +

        + +

        + 1.1.4Jan 9, 2011
        + Improved compliance with ES5's Array methods when passing null + as a value. _.wrap now correctly sets this for the + wrapped function. _.indexOf now takes an optional flag for + finding the insertion index in an array that is guaranteed to already + be sorted. Avoiding the use of .callee, to allow _.isArray + to work properly in ES5's strict mode. +

        + +

        + 1.1.3Dec 1, 2010
        + In CommonJS, Underscore may now be required with just:
        + var _ = require("underscore"). + Added _.throttle and _.debounce functions. + Removed _.breakLoop, in favor of an ECMA5-style un-break-able + each implementation — this removes the try/catch, and you'll now have + better stack traces for exceptions that are thrown within an Underscore iterator. + Improved the isType family of functions for better interoperability + with Internet Explorer host objects. + _.template now correctly escapes backslashes in templates. + Improved _.reduce compatibility with the ECMA5 version: + if you don't pass an initial value, the first item in the collection is used. + _.each no longer returns the iterated collection, for improved + consistency with ES5's forEach. +

        + +

        + 1.1.2
        + Fixed _.contains, which was mistakenly pointing at + _.intersect instead of _.include, like it should + have been. Added _.unique as an alias for _.uniq. +

        + +

        + 1.1.1
        + Improved the speed of _.template, and its handling of multiline + interpolations. Ryan Tenney contributed optimizations to many Underscore + functions. An annotated version of the source code is now available. +

        + +

        + 1.1.0
        + The method signature of _.reduce has been changed to match + the ECMAScript 5 signature, instead of the Ruby/Prototype.js version. + This is a backwards-incompatible change. _.template may now be + called with no arguments, and preserves whitespace. _.contains + is a new alias for _.include. +

        + +

        + 1.0.4
        + Andri Möll contributed the _.memoize + function, which can be used to speed up expensive repeated computations + by caching the results. +

        + +

        + 1.0.3
        + Patch that makes _.isEqual return false if any property + of the compared object has a NaN value. Technically the correct + thing to do, but of questionable semantics. Watch out for NaN comparisons. +

        + +

        + 1.0.2
        + Fixes _.isArguments in recent versions of Opera, which have + arguments objects as real Arrays. +

        + +

        + 1.0.1
        + Bugfix for _.isEqual, when comparing two objects with the same + number of undefined keys, but with different names. +

        + +

        + 1.0.0
        + Things have been stable for many months now, so Underscore is now + considered to be out of beta, at 1.0. Improvements since 0.6 + include _.isBoolean, and the ability to have _.extend + take multiple source objects. +

        + +

        + 0.6.0
        + Major release. Incorporates a number of + Mile Frawley's refactors for + safer duck-typing on collection functions, and cleaner internals. A new + _.mixin method that allows you to extend Underscore with utility + functions of your own. Added _.times, which works the same as in + Ruby or Prototype.js. Native support for ECMAScript 5's Array.isArray, + and Object.keys. +

        + +

        + 0.5.8
        + Fixed Underscore's collection functions to work on + NodeLists and + HTMLCollections + once more, thanks to + Justin Tulloss. +

        + +

        + 0.5.7
        + A safer implementation of _.isArguments, and a + faster _.isNumber,
        thanks to + Jed Schmidt. +

        + +

        + 0.5.6
        + Customizable delimiters for _.template, contributed by + Noah Sloan. +

        + +

        + 0.5.5
        + Fix for a bug in MobileSafari's OOP-wrapper, with the arguments object. +

        + +

        + 0.5.4
        + Fix for multiple single quotes within a template string for + _.template. See: + Rick Strahl's blog post. +

        + +

        + 0.5.2
        + New implementations of isArray, isDate, isFunction, + isNumber, isRegExp, and isString, thanks to + a suggestion from + Robert Kieffer. + Instead of doing Object#toString + comparisons, they now check for expected properties, which is less safe, + but more than an order of magnitude faster. Most other Underscore + functions saw minor speed improvements as a result. + Evgeniy Dolzhenko + contributed _.tap, + similar to Ruby 1.9's, + which is handy for injecting side effects (like logging) into chained calls. +

        + +

        + 0.5.1
        + Added an _.isArguments function. Lots of little safety checks + and optimizations contributed by + Noah Sloan and + Andri Möll. +

        + +

        + 0.5.0
        + [API Changes] _.bindAll now takes the context object as + its first parameter. If no method names are passed, all of the context + object's methods are bound to it, enabling chaining and easier binding. + _.functions now takes a single argument and returns the names + of its Function properties. Calling _.functions(_) will get you + the previous behavior. + Added _.isRegExp so that isEqual can now test for RegExp equality. + All of the "is" functions have been shrunk down into a single definition. + Karl Guertin contributed patches. +

        + +

        + 0.4.7
        + Added isDate, isNaN, and isNull, for completeness. + Optimizations for isEqual when checking equality between Arrays + or Dates. _.keys is now 25%–2X faster (depending on your + browser) which speeds up the functions that rely on it, such as _.each. +

        + +

        + 0.4.6
        + Added the range function, a port of the + Python + function of the same name, for generating flexibly-numbered lists + of integers. Original patch contributed by + Kirill Ishanov. +

        + +

        + 0.4.5
        + Added rest for Arrays and arguments objects, and aliased + first as head, and rest as tail, + thanks to Luke Sutton's patches. + Added tests ensuring that all Underscore Array functions also work on + arguments objects. +

        + +

        + 0.4.4
        + Added isString, and isNumber, for consistency. Fixed + _.isEqual(NaN, NaN) to return true (which is debatable). +

        + +

        + 0.4.3
        + Started using the native StopIteration object in browsers that support it. + Fixed Underscore setup for CommonJS environments. +

        + +

        + 0.4.2
        + Renamed the unwrapping function to value, for clarity. +

        + +

        + 0.4.1
        + Chained Underscore objects now support the Array prototype methods, so + that you can perform the full range of operations on a wrapped array + without having to break your chain. Added a breakLoop method + to break in the middle of any Underscore iteration. Added an + isEmpty function that works on arrays and objects. +

        + +

        + 0.4.0
        + All Underscore functions can now be called in an object-oriented style, + like so: _([1, 2, 3]).map(...);. Original patch provided by + Marc-André Cournoyer. + Wrapped objects can be chained through multiple + method invocations. A functions method + was added, providing a sorted list of all the functions in Underscore. +

        + +

        + 0.3.3
        + Added the JavaScript 1.8 function reduceRight. Aliased it + as foldr, and aliased reduce as foldl. +

        + +

        + 0.3.2
        + Now runs on stock Rhino + interpreters with: load("underscore.js"). + Added identity as a utility function. +

        + +

        + 0.3.1
        + All iterators are now passed in the original collection as their third + argument, the same as JavaScript 1.6's forEach. Iterating over + objects is now called with (value, key, collection), for details + see _.each. +

        + +

        + 0.3.0
        + Added Dmitry Baranovskiy's + comprehensive optimizations, merged in + Kris Kowal's patches to make Underscore + CommonJS and + Narwhal compliant. +

        + +

        + 0.2.0
        + Added compose and lastIndexOf, renamed inject to + reduce, added aliases for inject, filter, + every, some, and forEach. +

        + +

        + 0.1.1
        + Added noConflict, so that the "Underscore" object can be assigned to + other variables. +

        + +

        + 0.1.0
        + Initial release of Underscore.js. +

        + +

        + + A DocumentCloud Project + +

        + +
        + +
        + + + + + + diff --git a/node_modules/underscore/index.js b/node_modules/underscore/index.js new file mode 100644 index 000000000..2cf0ca5b0 --- /dev/null +++ b/node_modules/underscore/index.js @@ -0,0 +1 @@ +module.exports = require('./underscore'); diff --git a/node_modules/underscore/package.json b/node_modules/underscore/package.json new file mode 100644 index 000000000..86c918592 --- /dev/null +++ b/node_modules/underscore/package.json @@ -0,0 +1,12 @@ +{ + "name" : "underscore", + "description" : "JavaScript's functional programming helper library.", + "homepage" : "http://documentcloud.github.com/underscore/", + "keywords" : ["util", "functional", "server", "client", "browser"], + "author" : "Jeremy Ashkenas ", + "contributors" : [], + "dependencies" : [], + "repository" : {"type": "git", "url": "git://github.com/documentcloud/underscore.git"}, + "main" : "underscore.js", + "version" : "1.1.6" +} diff --git a/node_modules/underscore/underscore.js b/node_modules/underscore/underscore.js new file mode 100644 index 000000000..eaba008c4 --- /dev/null +++ b/node_modules/underscore/underscore.js @@ -0,0 +1,807 @@ +// Underscore.js 1.1.6 +// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore is freely distributable under the MIT license. +// Portions of Underscore are inspired or borrowed from Prototype, +// Oliver Steele's Functional, and John Resig's Micro-Templating. +// For all details and documentation: +// http://documentcloud.github.com/underscore + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var slice = ArrayProto.slice, + unshift = ArrayProto.unshift, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { return new wrapper(obj); }; + + // Export the Underscore object for **CommonJS**, with backwards-compatibility + // for the old `require()` API. If we're not in CommonJS, add `_` to the + // global object. + if (typeof module !== 'undefined' && module.exports) { + module.exports = _; + _._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.1.6'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects implementing `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (_.isNumber(obj.length)) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (hasOwnProperty.call(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = memo !== void 0; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial && index === 0) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError("Reduce of empty array with no initial value"); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse(); + return _.reduce(reversed, iterator, memo, context); + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + each(obj, function(value, index, list) { + if (!iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result = iterator.call(context, value, index, list)) return breaker; + }); + return result; + }; + + // Determine if a given value is included in the array or object using `===`. + // Aliased as `contains`. + _.include = _.contains = function(obj, target) { + var found = false; + if (obj == null) return found; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + any(obj, function(value) { + if (found = value === target) return true; + }); + return found; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + return _.map(obj, function(value) { + return (method.call ? method || value : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Return the maximum element or (element-based computation). + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); + var result = {computed : -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); + var result = {computed : Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, iterator, context) { + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }), 'value'); + }; + + // Use a comparator function to figure out at what index an object should + // be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator) { + iterator || (iterator = _.identity); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >> 1; + iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) return iterable.toArray(); + if (_.isArray(iterable)) return iterable; + if (_.isArguments(iterable)) return slice.call(iterable); + return _.values(iterable); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + return _.toArray(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head`. The **guard** check allows it to work + // with `_.map`. + _.first = _.head = function(array, n, guard) { + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the first entry of the array. Aliased as `tail`. + // Especially useful on the arguments object. Passing an **index** will return + // the rest of the values in the array from that index onward. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = function(array, index, guard) { + return slice.call(array, (index == null) || guard ? 1 : index); + }; + + // Get the last element of an array. + _.last = function(array) { + return array[array.length - 1]; + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, function(value){ return !!value; }); + }; + + // Return a completely flattened version of an array. + _.flatten = function(array) { + return _.reduce(array, function(memo, value) { + if (_.isArray(value)) return memo.concat(_.flatten(value)); + memo[memo.length] = value; + return memo; + }, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + var values = slice.call(arguments, 1); + return _.filter(array, function(value){ return !_.include(values, value); }); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted) { + return _.reduce(array, function(memo, el, i) { + if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el; + return memo; + }, []); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersect = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); + return results; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i, l; + if (isSorted) { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); + for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item) { + if (array == null) return -1; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); + var i = array.length; + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Binding with arguments is also known as `curry`. + // Delegates to **ECMAScript 5**'s native `Function.bind` if available. + // We check for `func.bind` first, to fail fast when `func` is undefined. + _.bind = function(func, obj) { + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + var args = slice.call(arguments, 2); + return function() { + return func.apply(obj, args.concat(slice.call(arguments))); + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length == 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(func, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Internal function used to implement `_.throttle` and `_.debounce`. + var limit = function(func, wait, debounce) { + var timeout; + return function() { + var context = this, args = arguments; + var throttler = function() { + timeout = null; + func.apply(context, args); + }; + if (debounce) clearTimeout(timeout); + if (debounce || !timeout) timeout = setTimeout(throttler, wait); + }; + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + return limit(func, wait, false); + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. + _.debounce = function(func, wait) { + return limit(func, wait, true); + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + return memo = func.apply(this, arguments); + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func].concat(slice.call(arguments)); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = slice.call(arguments); + return function() { + var args = slice.call(arguments); + for (var i=funcs.length-1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + return function() { + if (--times < 1) { return func.apply(this, arguments); } + }; + }; + + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + return _.map(obj, _.identity); + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (source[prop] !== void 0) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + // Check object identity. + if (a === b) return true; + // Different types? + var atype = typeof(a), btype = typeof(b); + if (atype != btype) return false; + // Basic equality test (watch out for coercions). + if (a == b) return true; + // One is falsy and the other truthy. + if ((!a && b) || (a && !b)) return false; + // Unwrap any wrapped objects. + if (a._chain) a = a._wrapped; + if (b._chain) b = b._wrapped; + // One of them implements an isEqual()? + if (a.isEqual) return a.isEqual(b); + // Check dates' integer values. + if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); + // Both are NaN? + if (_.isNaN(a) && _.isNaN(b)) return false; + // Compare regular expressions. + if (_.isRegExp(a) && _.isRegExp(b)) + return a.source === b.source && + a.global === b.global && + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline; + // If a is not an object by this point, we can't handle it. + if (atype !== 'object') return false; + // Check for different array lengths before comparing contents. + if (a.length && (a.length !== b.length)) return false; + // Nothing else worked, deep compare the contents. + var aKeys = _.keys(a), bKeys = _.keys(b); + // Different object sizes? + if (aKeys.length != bKeys.length) return false; + // Recursive comparison of contents. + for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false; + return true; + }; + + // Is a given array or object empty? + _.isEmpty = function(obj) { + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (hasOwnProperty.call(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType == 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) === '[object Array]'; + }; + + // Is a given variable an arguments object? + _.isArguments = function(obj) { + return !!(obj && hasOwnProperty.call(obj, 'callee')); + }; + + // Is a given value a function? + _.isFunction = function(obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); + }; + + // Is a given value a string? + _.isString = function(obj) { + return !!(obj === '' || (obj && obj.charCodeAt && obj.substr)); + }; + + // Is a given value a number? + _.isNumber = function(obj) { + return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed)); + }; + + // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript + // that does not equal itself. + _.isNaN = function(obj) { + return obj !== obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false; + }; + + // Is a given value a date? + _.isDate = function(obj) { + return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear); + }; + + // Is the given value a regular expression? + _.isRegExp = function(obj) { + return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false)); + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function (n, iterator, context) { + for (var i = 0; i < n; i++) iterator.call(context, i); + }; + + // Add your own custom functions to the Underscore object, ensuring that + // they're correctly added to the OOP wrapper as well. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + addToWrapper(name, _[name] = obj[name]); + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g + }; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(str, data) { + var c = _.templateSettings; + var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + + 'with(obj||{}){__p.push(\'' + + str.replace(/\\/g, '\\\\') + .replace(/'/g, "\\'") + .replace(c.interpolate, function(match, code) { + return "'," + code.replace(/\\'/g, "'") + ",'"; + }) + .replace(c.evaluate || null, function(match, code) { + return "');" + code.replace(/\\'/g, "'") + .replace(/[\r\n\t]/g, ' ') + "__p.push('"; + }) + .replace(/\r/g, '\\r') + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t') + + "');}return __p.join('');"; + var func = new Function('obj', tmpl); + return data ? func(data) : func; + }; + + // The OOP Wrapper + // --------------- + + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + var wrapper = function(obj) { this._wrapped = obj; }; + + // Expose `wrapper.prototype` as `_.prototype` + _.prototype = wrapper.prototype; + + // Helper function to continue chaining intermediate results. + var result = function(obj, chain) { + return chain ? _(obj).chain() : obj; + }; + + // A method to easily add functions to the OOP wrapper. + var addToWrapper = function(name, func) { + wrapper.prototype[name] = function() { + var args = slice.call(arguments); + unshift.call(args, this._wrapped); + return result(func.apply(_, args), this._chain); + }; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + wrapper.prototype[name] = function() { + method.apply(this._wrapped, arguments); + return result(this._wrapped, this._chain); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + wrapper.prototype[name] = function() { + return result(method.apply(this._wrapped, arguments), this._chain); + }; + }); + + // Start chaining a wrapped Underscore object. + wrapper.prototype.chain = function() { + this._chain = true; + return this; + }; + + // Extracts the result from a wrapped and chained object. + wrapper.prototype.value = function() { + return this._wrapped; + }; + +})(); diff --git a/package.json b/package.json index 344152b77..73369ba07 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,31 @@ { - "name": "jsdoc", - "version": "3.0.0beta1", - "revision": "2011-04-13-0000", - "description": "An automatic documentation generator for javascript.", - "keywords": [ "documentation", "javascript" ], - "licenses": [ - { - "type": "Apache 2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0" - } - ], - "repositories": [ - { - "type": "git", - "url": "git://github.com/micmath/jsdoc.git" - }, - { - "type": "svn", - "url": "https://jsdoc.googlecode.com/svn/trunk" - } - ], - "bugs": "http://code.google.com/p/jsdoc/issues/list", - "contributors" : [ + "name" : "jsdoc", + "description" : "Automated documentation generator for JavaScript", + "keywords" : ["jsdoc", "documentation"], + "homepage" : "http://micmath.github.com/jsdoc/", + "version" : "3.0.0a", + "author" : "Michael Mathews", + "repository" : { + "type" : "git", + "url" : "https://github.com/micmath/jsdoc.git" + }, + "maintainers" : [ { - "name": "Michael Mathews", - "email": "micmath@gmail.com" - } + "name" : "Michael Mathews", + "email" : "micmath@gmail.com" + } ], - "maintainers": [ + "main" : "./jsdoc.js", + "bugs" : { + "web" : "https://github.com/micmath/jsdoc/issues" + }, + "directories" : { + "doc" : "./docs" + }, + "licenses" : [ { - "name": "Michael Mathews", - "email": "micmath@gmail.com" + "type" : "MIT", + "url" : "http://www.opensource.org/licenses/mit-license.php" } - ] -} + ] +} \ No newline at end of file diff --git a/plugin/md.js b/plugin/md.js new file mode 100644 index 000000000..64f13f60b --- /dev/null +++ b/plugin/md.js @@ -0,0 +1,12 @@ +/** + @overview Converts the text of a doclet's description, from MarkDown to HTML. + */ + +var plugin = require('jsdoc/plugin'), + markdown = require('markdown/lib/markdown'); // TODO: allow rhino to use commonjs module id here + +plugin.manager.on('doclet', function(doclet) { + if (typeof doclet.description === 'string') { + doclet.description = markdown.toHTML(doclet.description); + } +}); \ No newline at end of file diff --git a/plugins/markdown.js b/plugins/markdown.js deleted file mode 100644 index efacb173a..000000000 --- a/plugins/markdown.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - @overview Translate doclet descriptions from MarkDown into HTML. - */ - -(function() { - - var markdown = require('evilstreak/markdown'); - - app.jsdoc.parser.on('newDoclet', function(e) { - if (e.doclet.description) { - e.doclet.description = markdown.toHTML(e.doclet.description) - .replace( /&/g, "&" ) // because markdown escapes these - .replace( /</g, "<" ) - .replace( />/g, ">" ); - } - }); - -})(); \ No newline at end of file diff --git a/rhino_modules/fs/index.js b/rhino_modules/fs/index.js new file mode 100644 index 000000000..b01e60c29 --- /dev/null +++ b/rhino_modules/fs/index.js @@ -0,0 +1,67 @@ + +module.exports = { + readFileSync: function(filename, encoding, callback) { + if (typeof arguments[1] === 'function') { + encoding = null; + callback = arguments[1]; + } + + encoding = encoding || java.lang.System.getProperty('file.encoding'); + + try { + var content = new java.util.Scanner( + new java.io.File(filename), + encoding + ).useDelimiter("\\Z"); + + return String( content.next() ); + } + catch (e) { + throw('Cannot read module file '+filename); + } + + }, + readdirSync: function(path) { + var dir = new java.io.File(path); + if (!dir.directory) { return [String(dir)]; } + var files = dir.list(); + return files; + }, + + ls: function(dir, recurse, _allFiles, _path) { + var files, + file; + + if (typeof _path === 'undefined') { // initially + _allFiles = []; + _path = [dir]; + } + + if (_path.length === 0) { return _allFiles; } + if (typeof recurse === 'undefined') { recurse = 1; } + + files = this.readdirSync(dir); + + for (var f = 0, lenf = files.length; f < lenf; f++) { + file = String(files[f]); + + if (file.match(/^\.[^\.\/\\]/)) { continue; } // skip dot files + + if ((new java.io.File(_path.join('/') + '/' + file)).list()) { // it's a directory + _path.push(file); + + if (_path.length - 1 < recurse) { + exports.ls(_path.join('/'), recurse, _allFiles, _path); + } + _path.pop(); + } + else { // it's a file + _allFiles.push( + (_path.join('/') + '/' + file).replace(/[\/\\]+/g, '/') + ); + } + } + + return _allFiles; + } +}; \ No newline at end of file diff --git a/rhino_modules/path/index.js b/rhino_modules/path/index.js new file mode 100644 index 000000000..ebefaebf9 --- /dev/null +++ b/rhino_modules/path/index.js @@ -0,0 +1,9 @@ + +module.exports = { + 'basename' : function(path) { + var parts = path.split('/'); + parts.pop(); + path = parts.join('/'); + return path; + } +}; \ No newline at end of file diff --git a/rhino_modules/sys/index.js b/rhino_modules/sys/index.js new file mode 100644 index 000000000..f7d764640 --- /dev/null +++ b/rhino_modules/sys/index.js @@ -0,0 +1,6 @@ + +module.exports = { + 'puts' : function(str) { + print(String(str)); + } +}; \ No newline at end of file diff --git a/templates/default/index.js b/templates/default/index.js new file mode 100644 index 000000000..3b2566ce6 --- /dev/null +++ b/templates/default/index.js @@ -0,0 +1,21 @@ +exports.publish = function(docDb) { + var ejs = require('ejs'); + + function find(spec) { + return docDb.get( docDb.find(spec) ); + } + + var functions = find({'kind': 'function'}); + //console.log(functions); + + var data = { + title: 'All Functions', + functions: functions + }; + + var template = '

        <%= title %>

        \n
          \n<%for (var f in functions){%>
        • <%=functions[f].longname%>
        • \n<%}%>
        '; + + var output = ejs.render(template, {locals: data}); + + return output; +} \ No newline at end of file diff --git a/templates/default/publish.js b/templates/default/publish.js deleted file mode 100644 index 327a7233c..000000000 --- a/templates/default/publish.js +++ /dev/null @@ -1,290 +0,0 @@ -(function() { - - var template = require('underscore/template'), - fs = require('fs'); - - template.settings.evaluate = /<\?js([\s\S]+?)\?>/g; - template.settings.interpolate = /<\?js=([\s\S]+?)\?>/g; - - /** - @global - @param {TAFFY} data See . - @param {object} opts - */ - publish = function(data, opts) { - var out = '', - containerTemplate = template.render(fs.read(BASEDIR + 'templates/default/tmpl/container.tmpl')); - - function render(tmpl, partialData) { - var renderFunction = arguments.callee.cache[tmpl]; - if (!renderFunction) { - renderFunction = arguments.callee.cache[tmpl] = template.render(fs.read(BASEDIR + 'templates/default/tmpl/'+tmpl)); - } - partialData.render = arguments.callee; - partialData.find = find; - partialData.linkto = linkto; - partialData.htmlsafe = htmlsafe; - - return renderFunction.call(partialData, partialData); - } - render.cache = {}; - - function find(spec) { - return data.get( data.find(spec) ); - } - - function htmlsafe(str) { - return str.replace(/'+htmlsafe(returnTypes.length? ' ⇒ '+returnTypes.join('|') : '')+'
        '; - } - - function addSignatureType(f) { - var types = []; - - if (f.type && f.type.names) { - types = f.type.names; - } - - f.signature = (f.signature || '') + ''+htmlsafe(types.length? ' :'+types.join('|') : '')+''; - } - - function addAttribs(f) { - var attribs = []; - - if (f.access && f.access !== 'public') { - attribs.push(f.access); - } - - if (f.scope && f.scope !== 'instance') { - if (f.kind == 'function' || f.kind == 'property') attribs.push(f.scope); - } - - if (f.readonly === true) { - if (f.kind == 'property') attribs.push('readonly'); - } - - f.attribs = ''+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+''; - } - - data.remove({undocumented: true}); - - var packageInfo = (data.get( data.find({kind: 'package'}) ) || []) [0]; - - function renderLinks(text) { - text = text.replace(/\{@link (\S+)\}/g, function(match, longname) { - var link = linkto(longname); - return link; - }); - - return text; - } - - data.forEach(function(doclet) { - doclet.signature = ''; - doclet.attribs = ''; - - if (doclet.kind === 'function' || doclet.kind === 'class') { - addSignatureParams(doclet); - addSignatureReturns(doclet); - addAttribs(doclet); - } - - if (doclet.kind === 'property') { - addSignatureType(doclet); - addAttribs(doclet) - } - - if (doclet.examples) { - doclet.examples = doclet.examples.map(function(example) { - var caption, code; - - if (example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { - caption = RegExp.$1; - code = RegExp.$3; - } - - return { - caption: caption || '', - code: code || example - }; - }); - } - }); - - data.orderBy(['longname', 'version', 'since']); - - // kinds of containers - var globals = data.get( data.find({kind: ['property', 'function'], memberof: {isUndefined: true}}) ), - modules = data.get( data.find({kind: 'module'}) ), - namespaces = data.get( data.find({kind: 'namespace'}) ); - - var outdir = opts.destination; - if (packageInfo) { - outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; - } - fs.mkPath(outdir); - - // copy static files to outdir - var fromDir = BASEDIR + 'templates/default/static', - staticFiles = fs.ls(fromDir, 3); - - staticFiles.forEach(function(fileName) { - var toDir = fs.toDir(fileName.replace(fromDir, outdir)); - fs.mkPath(toDir); - fs.copyFile(fileName, toDir); - }); - - function linkto(longname, linktext) { - var url = longnameToUrl[longname]; - return url? ''+(linktext || longname)+'' : (linktext || longname); - } - - var containers= ['class', 'module', 'namespace'], - urlToLongname = {}, - longnameToUrl = {}; - - data.forEach(function(doclet) { - if (containers.indexOf(doclet.kind) < 0) { - var longname = doclet.longname, - urlSafe = ('global' || doclet.memberof).replace(/[^$a-z0-9._-]/gi, '_'), // TODO handle name collisions - url = urlSafe + '.html#'+doclet.name; - } - else { - var longname = doclet.longname, - urlSafe = longname.replace(/[^$a-z0-9._-]/gi, '_'), // TODO handle name collisions - url = urlSafe + '.html'; - } - - // bidirectional lookups: url <=> longname - urlToLongname[urlSafe] = longname; - longnameToUrl[longname] = url; - }); - - // do this after the urls have all been generated - data.forEach(function(doclet) { - if (doclet.classdesc) doclet.classdesc = renderLinks(doclet.classdesc); - if (doclet.description) doclet.description = renderLinks(doclet.description); - - doclet.ancestors = generateAncestry(doclet); - }); - - var nav = '', - seen = {}; - - var moduleNames = data.get( data.find({kind: 'module'}) ); - if (moduleNames.length) { - nav = nav + '

        Modules

          '; - moduleNames.forEach(function(m) { - if ( !seen.hasOwnProperty(m.longname) ) nav += '
        • '+linkto(m.longname, m.name)+'
        • '; - seen[m.longname] = true; - }); - - nav = nav + '
        '; - } - var namespaceNames = data.get( data.find({kind: 'namespace'}) ); - if (namespaceNames.length) { - nav = nav + '

        Namespaces

          '; - namespaceNames.forEach(function(n) { - if ( !seen.hasOwnProperty(n.longname) ) nav += '
        • '+linkto(n.longname, n.name)+'
        • '; - seen[n.longname] = true; - }); - - nav = nav + '
        '; - } - var classNames = data.get( data.find({kind: 'class'}) ); - if (classNames.length) { - nav = nav + '

        Classes

          '; - classNames.forEach(function(c) { - if ( !seen.hasOwnProperty(c.longname) ) nav += '
        • '+linkto(c.longname, c.name)+'
        • '; - seen[c.longname] = true; - }); - - nav = nav + '
        '; - } - - var globalNames = data.get( data.find({kind: ['property', 'function'], 'memberof': {'isUndefined': true}}) ); - - if (globalNames.length) { - nav = nav + '

        Global

          '; - globalNames.forEach(function(g) { - if ( !seen.hasOwnProperty(g.longname) ) nav += '
        • '+linkto(g.longname, g.name)+'
        • '; - seen[g.longname] = true; - }); - - nav = nav + '
        '; - } - - for (var longname in longnameToUrl) { - var classes = data.get( data.find({kind: 'class', longname: longname}) ); - if (classes.length) generate('Class: '+classes[0].name, classes, longnameToUrl[longname]); - - var modules = data.get( data.find({kind: 'module', longname: longname}) ); - if (modules.length) generate('Module: '+modules[0].name, modules, longnameToUrl[longname]); - - var namespaces = data.get( data.find({kind: 'namespace', longname: longname}) ); - if (namespaces.length) generate('Namespace: '+namespaces[0].name, namespaces, longnameToUrl[longname]); - } - - if (globals.length) generate('Global', [{kind: 'globalobj'}], 'global.html'); - - - function generate(title, docs, filename) { - var data = { - title: title, - docs: docs, - nav: nav, - - // helpers - render: render, - find: find, - linkto: linkto, - htmlsafe: htmlsafe - }; - - var path = outdir + '/' + filename, - html = containerTemplate.call(data, data); - - fs.write(path, html) - } - } - -})(); \ No newline at end of file diff --git a/templates/default/static/styles/jsdoc-default.css b/templates/default/static/styles/jsdoc-default.css deleted file mode 100644 index 23a79857c..000000000 --- a/templates/default/static/styles/jsdoc-default.css +++ /dev/null @@ -1,231 +0,0 @@ -body -{ - font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Verdana, Tahoma, sans-serif; - font-size: 13px; - color: #000; -} - -a { - color: #444; -} - -a:visited { - color: #444; -} - -a:active { - color: #444; -} - -header -{ - display: block; - /*border-bottom: 1px solid #ddd;*/ - padding: 6px 4px; -} - -.class-description { - font-style: italic; - font-family: Palatino, 'Palatino Linotype', serif; - font-size: 18px; -} - -#main { - float: left; - width: 100%; -} - -section -{ - display: block; - - background-color: #fff; - padding: 12px 24px; - border-bottom: 1px solid #ccc; - margin-right: 240px; -} - -sup { - color: #aaa; -} - -nav -{ - display: block; - - - float: left; - margin-left: -230px; - margin-top: 28px; - width: 220px; - border-left: 1px solid #ccc; - padding-left: 9px; -} - -nav ul { - font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; - font-size: 13px; - line-height: 17px; - padding:0; - margin:0; - list-style-type:none; -} - -nav h3 { - margin-top: 12px; -} - -nav li { - margin-top: 6px; -} - -nav a { - color: #5C5954; -} - -nav a:visited { - color: #5C5954; -} - -nav a:active { - color: #5C5954; -} - -footer { - display: block; - padding: 6px; - margin-top: 12px; - font-style: italic; - font-size: 11px; -} - -h1 -{ - font-size: 32px; - font-weight: bold; - letter-spacing: -0.03em; - margin: 6px 0 9px 0; -} - -h2 -{ - font-size: 22px; - font-weight: bold; - letter-spacing: -0.03em; - margin: 6px 0 3px 0; -} - -h3 -{ - font-size: 20px; - font-weight: bold; - letter-spacing: -0.03em; - margin-top: 16px; - margin: 6px 0 3px 0; -} - -h4 -{ - font-size: 18px; - font-weight: bold; - letter-spacing: -0.03em; - margin-top: 16px; - margin: 18px 0 3px 0; - color: #A35A00; -} - -h5 -{ - font-size: 16px; - font-weight: bold; - letter-spacing: -0.03em; - margin: 8px 0 3px -16px; -} - -h6 -{ - font-size: 13px; - letter-spacing: -0.03em; - margin: 6px 0 3px 0; - font-style: italic; -} - -.important -{ - font-weight: bold; - color: #950B02; -} - -.yes-def { - text-indent: -1000px; -} - -.type-signature { - color: #aaa; -} - -.details dt { width:100px; float:left;} -.details dd { margin-left: 100px; } - -.description { margin-left: -16px; } - -.code-caption -{ - font-style: italic; - font-family: Palatino, 'Palatino Linotype', serif; - font-size: 14px; - margin: 0; -} - -.sh_sourceCode -{ - border: 1px solid #ddd; - width: 80%; -} - -.sh_sourceCode code -{ - font-family: Consolas, 'Lucida Console', Monaco, monospace; - font-size: 12px; - line-height: 18px; - display: block; - padding: 4px 12px; - margin: 0; - background-color: #fff; - color: #000; - border-left: 3px #ddd solid; -} - -.params -{ - border-spacing: 0; - border: 0; - border-collapse: collapse; -} - -.params .name { color: #1C02A3; } - -.params td, .params th -{ - border: 1px solid #ddd; - margin: 0px; - text-align: left; - vertical-align: top; - padding: 4px 6px; - display: table-cell; -} - -.params thead tr -{ - background-color: #ddd; - font-weight: bold; -} - -.params .params thead tr -{ - background-color: #fff; - font-weight: bold; -} - -.params th { border-right: 1px solid #aaa; } -.params thead .last { border-right: 1px solid #ddd; } \ No newline at end of file diff --git a/templates/default/static/styles/node-dark.css b/templates/default/static/styles/node-dark.css deleted file mode 100644 index 5849e2341..000000000 --- a/templates/default/static/styles/node-dark.css +++ /dev/null @@ -1,150 +0,0 @@ - .sh_sourceCode { - background-color: #ffffff; - color: #000000; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_keyword { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_type { - color: #a52a2a; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_string { - color: #006400; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_regexp { - color: #006400; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_specialchar { - color: #2e8b57; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_comment { - color: #000000; - font-weight: normal; - font-style: italic; -} - - .sh_sourceCode .sh_number { - color: #006400; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_preproc { - color: #27408b; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_symbol { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_function { - color: #000000; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_cbracket { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_url { - color: #006400; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_date { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_time { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_file { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_ip { - color: #006400; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_name { - color: #006400; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_variable { - color: #dda0dd; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_oldfile { - color: #2e8b57; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_newfile { - color: #006400; - font-weight: normal; - font-style: normal; -} - - .sh_sourceCode .sh_difflines { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_selector { - color: #dda0dd; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_property { - color: #000000; - font-weight: bold; - font-style: normal; -} - - .sh_sourceCode .sh_value { - color: #006400; - font-weight: normal; - font-style: normal; -} diff --git a/templates/default/tmpl/container.tmpl b/templates/default/tmpl/container.tmpl deleted file mode 100644 index ebe5ab238..000000000 --- a/templates/default/tmpl/container.tmpl +++ /dev/null @@ -1,202 +0,0 @@ - - - - - JSDoc: <?js= title ?> - - - - - - - - - - - -
        - -

        - - - -
        - -
        - '); - - if (doc.ancestors && doc.ancestors.length) { - print(''+doc.ancestors.join(' » ')+''); - print(' » '+doc.name); - } - else { - print(doc.name) - } - - if (doc.variation) { - print(''+doc.variation+''); - } - - print('

        '); - - if (doc.classdesc) { - print('

        '+doc.classdesc+'

        '); - } - ?> - - -
        - ' + doc.description + '

        '); - } - - print(render('details.tmpl', doc)); - - if (doc.examples && doc.examples.length) { - print('

        Example' + (doc.examples.length > 1? 's':'') + '

        '); - print( render('examples.tmpl', doc.examples) ); - } - } - ?> - - -

        Extends

        - -
          -
        • -
        - - - -

        Requires

        - -
          -
        • -
        - - - - -

        Classes

        - -
        -
        -
        -
        - - - - - -

        Namespaces

        - -
        -
        -
        -
        - - - - - -

        Properties

        - -
        - - - - - -

        Methods

        - -
        - - - - - -

        Events

        - -
        - -
        - - - -
        - -
        -
        - Documentation generated by JSDoc 3 on -
        - - - - \ No newline at end of file diff --git a/templates/default/tmpl/details.tmpl b/templates/default/tmpl/details.tmpl deleted file mode 100644 index 17d15e303..000000000 --- a/templates/default/tmpl/details.tmpl +++ /dev/null @@ -1,55 +0,0 @@ -
        - -
        Version:
        -
        - - - -
        Since:
        -
        - - - -
        Deprecated
        Yes
        '); - else print(':
        '+this.deprecated+'
        '); - ?> - - - - -
        Author:
        -
        - - - -
        Copyright:
        -
        - - - -
        License:
        -
        - - - -
        Default Value:
        -
        - - - -
        Source:
        -
        , line
        - - - -
        See:
        -
        -
          '+linkto(s)+''); - }); - ?>
        -
        - -
        diff --git a/templates/default/tmpl/example.tmpl b/templates/default/tmpl/example.tmpl deleted file mode 100644 index 47a10889a..000000000 --- a/templates/default/tmpl/example.tmpl +++ /dev/null @@ -1,2 +0,0 @@ - -
        diff --git a/templates/default/tmpl/examples.tmpl b/templates/default/tmpl/examples.tmpl deleted file mode 100644 index fdf78762e..000000000 --- a/templates/default/tmpl/examples.tmpl +++ /dev/null @@ -1,10 +0,0 @@ - -

        - -
        - \ No newline at end of file diff --git a/templates/default/tmpl/exceptions.tmpl b/templates/default/tmpl/exceptions.tmpl deleted file mode 100644 index e44498672..000000000 --- a/templates/default/tmpl/exceptions.tmpl +++ /dev/null @@ -1,28 +0,0 @@ -
      3. - -
        - -
        - - - -
        -
        - Type -
        -
        - - - -
        -
        - -
      4. \ No newline at end of file diff --git a/templates/default/tmpl/fires.tmpl b/templates/default/tmpl/fires.tmpl deleted file mode 100644 index 1eef451f6..000000000 --- a/templates/default/tmpl/fires.tmpl +++ /dev/null @@ -1,3 +0,0 @@ -
      5. - -
      6. \ No newline at end of file diff --git a/templates/default/tmpl/method.tmpl b/templates/default/tmpl/method.tmpl deleted file mode 100644 index 4f6cf04e0..000000000 --- a/templates/default/tmpl/method.tmpl +++ /dev/null @@ -1,73 +0,0 @@ - -
        -

        - - -

        - -
        -
        - - -

        - -

        - - - - - This:'); - print( '
        • '+linkto(this['this'], this['this'])+'
        ' ); - } - ?> - - Parameters:'); - print( render('params.tmpl', params) ); - } - ?> - - -
        Fires:
        -
        - - - -
        Throws:
        -
        - - - Returns:'); - print('

        '+rdesc.join('

        ')+'

        '); - } - } - ?> - - Example' + (examples.length > 1? 's':'') + ''); - print( render('examples.tmpl', examples) ); - } - ?> - -
        diff --git a/templates/default/tmpl/params.tmpl b/templates/default/tmpl/params.tmpl deleted file mode 100644 index 41e84ab50..000000000 --- a/templates/default/tmpl/params.tmpl +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        NameTypeArgumentDefaultDescription
        - - -
        ' ); - } - - if (param.nullable) { - print( '<nullable>
        ' ); - } - ?> -
        - - Properties' + render('params.tmpl', param.subparams) ); - }?>
        \ No newline at end of file diff --git a/templates/default/tmpl/properties.tmpl b/templates/default/tmpl/properties.tmpl deleted file mode 100644 index 91687252c..000000000 --- a/templates/default/tmpl/properties.tmpl +++ /dev/null @@ -1,24 +0,0 @@ - -
        -

        - - -

        - -
        -
        - -

        - -

        - - - - - Example' + (examples.length > 1? 's':'') + ''); - print( render('examples.tmpl', examples) ); - } - ?> -
        diff --git a/templates/default/tmpl/returns.tmpl b/templates/default/tmpl/returns.tmpl deleted file mode 100644 index 464d67f05..000000000 --- a/templates/default/tmpl/returns.tmpl +++ /dev/null @@ -1,31 +0,0 @@ -
      7. - -
        - -
        - - - - -
        -
        - Type -
        -
        - - - -
        -
        - -
      8. \ No newline at end of file diff --git a/templates/haruki/publish.js b/templates/haruki/publish.js deleted file mode 100644 index 782f3de65..000000000 --- a/templates/haruki/publish.js +++ /dev/null @@ -1,189 +0,0 @@ -/** - @overview Builds a tree-like JSON string from the doclet data. - @version 0.0.1 - */ - -(function() { - - /** - @global - @param {TAFFY} data - @param {object} opts - */ - publish = function(data, opts) { - - var root = {}, - docs; - - data.remove({undocumented: true}); - docs = data.get(); // <-- an array of Doclet objects - - graft(root, docs); - - - - if (opts.destination === 'console') { - if (opts.query && opts.query.format === 'xml') { - var xml = require('goessner/json2xml'); - print( '\n' + xml.convert(root) + '\n' ); - } - else { - dump(root); - } - } - else { - print('The only -d destination option currently supported is "console"!'); - } - - } - - function graft(parentNode, childNodes, parentLongname, parentName) { - childNodes - .filter(function (element) { - return (element.memberof === parentLongname); - }) - .forEach(function (element, i) { - //print((i+1)+': '+element.kind+' '+element.longname+' ('+element.name+')'); - - if (element.kind === 'namespace') { - if (! parentNode.namespaces) { - parentNode.namespaces = { }; - } - - var thisNamespace = parentNode.namespaces[element.name] = { - 'name': element.name, - 'description': element.description || '', - 'access': element.access || '' - }; - - graft(thisNamespace, childNodes, element.longname, element.name); - } - else if (element.kind === 'mixin') { - if (! parentNode.mixins) { - parentNode.mixins = { }; - } - - var thisMixin = parentNode.mixins[element.name] = { - 'name': element.name, - 'description': element.description || '', - 'access': element.access || '' - }; - - graft(thisMixin, childNodes, element.longname, element.name); - } - else if (element.kind === 'function') { - if (! parentNode.functions) { - parentNode.functions = { }; - } - - var thisFunction = parentNode.functions[element.name] = { - 'name': element.name, - 'access': element.access || '', - 'description': element.description || '', - 'parameters': [ ] - }; - - if (element.returns) { - parentNode.functions[element.name].returns = { - 'type': element.returns.type? (element.returns.type.names.length === 1? element.returns.type.names[0] : element.returns.type.names) : '', - 'description': element.returns.description || '' - }; - } - - if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { - thisFunction.parameters.push({ - 'name': element.params[i].name, - 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', - 'description': element.params[i].description || '', - 'default': element.params[i].defaultvalue || '', - 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', - 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' - }); - } - } - } - else if (element.kind === 'property') { - if (! parentNode.properties) { - parentNode.properties = { }; - } - parentNode.properties[element.name] = { - 'name': element.name, - 'access': element.access || '', - 'description': element.description || '', - 'type': element.type? (element.type.length === 1? element.type[0] : element.type) : '' - }; - } - - else if (element.kind === 'event') { - if (! parentNode.events) { - parentNode.events = { }; - } - - var thisEvent = parentNode.events[element.name] = { - 'name': element.name, - 'access': element.access || '', - 'description': element.description || '', - 'parameters': [ - ] - }; - - if (element.returns) { - parentNode.events[element.name].returns = { - 'type': element.returns.type? (element.returns.type.names.length === 1? element.returns.type.names[0] : element.returns.type.names) : '', - 'description': element.returns.description || '' - }; - } - - if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { - thisEvent.parameters.push({ - 'name': element.params[i].name, - 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', - 'description': element.params[i].description || '', - 'default': element.params[i].defaultvalue || '', - 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', - 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' - }); - } - } - } - else if (element.kind === 'class') { - if (! parentNode.classes) { - parentNode.classes = { }; - } - - var thisClass = parentNode.classes[element.name] = { - 'name': element.name, - 'description': element.classdesc || '', - 'extends': element.augments || [], - 'access': element.access || '', - 'fires': element.fires || '', - 'constructor': { - 'name': element.name, - 'description': element.description || '', - 'parameters': [ - ] - } - }; - - if (element.params) { - for (var i = 0, len = element.params.length; i < len; i++) { - thisClass.constructor.parameters.push({ - 'name': element.params[i].name, - 'type': element.params[i].type? (element.params[i].type.names.length === 1? element.params[i].type.names[0] : element.params[i].type.names) : '', - 'description': element.params[i].description || '', - 'default': element.params[i].defaultvalue || '', - 'optional': typeof element.params[i].optional === 'boolean'? element.params[i].optional : '', - 'nullable': typeof element.params[i].nullable === 'boolean'? element.params[i].nullable : '' - }); - } - } - - graft(thisClass, childNodes, element.longname, element.name); - } - }); - } - -})(); - diff --git a/modules/typicaljoe/taffy.js b/templates/lib/taffy.js similarity index 100% rename from modules/typicaljoe/taffy.js rename to templates/lib/taffy.js diff --git a/test-rhino.js b/test-rhino.js new file mode 100755 index 000000000..9c948f9f6 --- /dev/null +++ b/test-rhino.js @@ -0,0 +1,15 @@ +load('lib/rhino-shim.js'); +load('lib/nodeunit.js'); + +var fs = require('fs'), + testFiles = fs.ls('./test/'), + testFile; + +while ( testFile = testFiles.shift() ) { + var testName = testFile.replace(/\.js$/, ''); + var test = {}; + + test[testName] = require(testName); + + nodeunit.run(test); +} \ No newline at end of file diff --git a/test.js b/test.js new file mode 100644 index 000000000..df7a4ca76 --- /dev/null +++ b/test.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +if (typeof load !== 'undefined') { + load('lib/rhino-shim.js'); +} + +var reporter = require('nodeunit').reporters['default']; +reporter.run(['./test']); \ No newline at end of file diff --git a/test/cases/accesstag.js b/test/cases/accesstag.js deleted file mode 100644 index 497c20dd1..000000000 --- a/test/cases/accesstag.js +++ /dev/null @@ -1,29 +0,0 @@ -/** @constructor */ -function Thingy() { - - /** @access private */ - var foo = 0; - - /** @access protected */ - this._bar = 1; - - /** @access public */ - this.pez = 2; - -} - -// same as... - -/** @constructor */ -function OtherThingy() { - - /** @private */ - var foo = 0; - - /** @protected */ - this._bar = 1; - - /** @public */ - this.pez = 2; - -} \ No newline at end of file diff --git a/test/cases/alias.js b/test/cases/alias.js deleted file mode 100644 index bbb7305cb..000000000 --- a/test/cases/alias.js +++ /dev/null @@ -1,13 +0,0 @@ -var myObject = (function() { - - /** Give x another name. - @alias myObject - @namespace - */ - var x = { - /** document me */ - myProperty: 'foo' - } - - return x; -})(); \ No newline at end of file diff --git a/test/cases/alias2.js b/test/cases/alias2.js deleted file mode 100644 index e1d8e5a52..000000000 --- a/test/cases/alias2.js +++ /dev/null @@ -1,10 +0,0 @@ -(function() { - - /** @alias ns.Myclass# */ - var x = { - /** document me */ - myProperty: 'foo' - } - - return x; -})(); \ No newline at end of file diff --git a/test/cases/alias3.js b/test/cases/alias3.js deleted file mode 100644 index 3cea9abab..000000000 --- a/test/cases/alias3.js +++ /dev/null @@ -1,12 +0,0 @@ -Klass('trackr.CookieManager', - - /** @class - @alias trackr.CookieManager - @param {object} kv - */ - function(kv) { - /** document me */ - this.value = kv; - } - -); \ No newline at end of file diff --git a/test/cases/also.js b/test/cases/also.js deleted file mode 100644 index 602bc041a..000000000 --- a/test/cases/also.js +++ /dev/null @@ -1,20 +0,0 @@ -/** @class */ -function Asset() { - this._name = ''; -} - -/** - * - * Set the value of the name property. - * @param {string} newName - * - *//** - * - * Get the value of the name property. - * @returns {string} - * - */ -Asset.prototype.name = function(newName) { - if (newName) { this._name = newName; } - else { return this._name; } -} \ No newline at end of file diff --git a/test/cases/augmentstag.js b/test/cases/augmentstag.js deleted file mode 100644 index 7c2bd4c59..000000000 --- a/test/cases/augmentstag.js +++ /dev/null @@ -1,14 +0,0 @@ -/** -* @constructor -*/ -function Foo() { -} - - -/** -* @extends Foo -*/ -function Bar() { -} - - diff --git a/test/cases/authortag.js b/test/cases/authortag.js deleted file mode 100644 index 155bb5703..000000000 --- a/test/cases/authortag.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @constructor - @author Michael Mathews -*/ -function Thingy() { - -} \ No newline at end of file diff --git a/test/cases/borrowstag.js b/test/cases/borrowstag.js deleted file mode 100644 index b6d065ddc..000000000 --- a/test/cases/borrowstag.js +++ /dev/null @@ -1,14 +0,0 @@ -/** @namespace - @borrows trstr as trim -*/ -var util = { - "trim": trstr -}; - -/** - Remove whitespace from around a string. - @param {string} str - */ -function trstr(str) { -} - diff --git a/test/cases/borrowstag2.js b/test/cases/borrowstag2.js deleted file mode 100644 index 9e80dd0d5..000000000 --- a/test/cases/borrowstag2.js +++ /dev/null @@ -1,21 +0,0 @@ -/** @namespace - @borrows rtrim -*/ -var str = { - rtrim: util.rtrim -}; - -/** @namespace - @borrows rtrim -*/ -var util = { - rtrim: rtrim -}; - -/** - Remove whitespace from the right side of a string. - @param {string} str - */ -function rtrim(str) { -} - diff --git a/test/cases/classtag.js b/test/cases/classtag.js deleted file mode 100644 index ea4584b11..000000000 --- a/test/cases/classtag.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - Describe the Ticker class here. - @class - */ -var Ticker = function() { - -}; - -/** - Describe the NewsSource class here. - @class NewsSource - */ \ No newline at end of file diff --git a/test/cases/constructortag.js b/test/cases/constructortag.js deleted file mode 100644 index 275ef3c62..000000000 --- a/test/cases/constructortag.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - Describe your constructor function here. - @class Describe your class here. - @constructor - @param {string} url - @throws MalformedURL - */ -function Feed(url) { -} - -/** - Document your method here. -*/ -Feed.prototype.refresh = function() { -} diff --git a/test/cases/constructstag.js b/test/cases/constructstag.js deleted file mode 100644 index a31a1cd02..000000000 --- a/test/cases/constructstag.js +++ /dev/null @@ -1,19 +0,0 @@ -Classify('TextBlock', { - - /** - Document your constructor function here. - @constructs TextBlock - @classdesc Describe your class here - @param {object} opts - @throws MissingNode - */ - construct: function(node, opts) { - }, - - /** - Document your method here. - @memberof TextBlock# - */ - align: function() { - } -}); \ No newline at end of file diff --git a/test/cases/constructstag2.js b/test/cases/constructstag2.js deleted file mode 100644 index 604d4bade..000000000 --- a/test/cases/constructstag2.js +++ /dev/null @@ -1,16 +0,0 @@ -Classify('Menu', - /** - @constructs Menu - @param items - */ - function (items) { - - }, - { - /** - @memberof Menu# - */ - show: function(){ - } - } -); diff --git a/test/cases/constructstag3.js b/test/cases/constructstag3.js deleted file mode 100644 index 3b4e410fd..000000000 --- a/test/cases/constructstag3.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - A class that represents a person. - @class - */ -var Person = Class.create({ - - /** - @constructs Person - @param {string} name - */ - initialize: function(name) { - - /** The name of the person. */ - this.name = name; - }, - - /** - @memberof Person# - @param {string} message - */ - say: function(message) { - - /** The person's message. */ - this.message = message; - } -}); diff --git a/test/cases/copyrighttag.js b/test/cases/copyrighttag.js deleted file mode 100644 index 68a09f34e..000000000 --- a/test/cases/copyrighttag.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @constructor - @copyright (c) 2011 Michael Mathews -*/ -function Thingy() { - -} \ No newline at end of file diff --git a/test/cases/defaulttag.js b/test/cases/defaulttag.js deleted file mode 100644 index bd8461aa2..000000000 --- a/test/cases/defaulttag.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - @default - */ -var request = null; - -/** - @default - */ -var response = 'ok'; - -/** - @default - */ -var rcode = 200; - -/** - @default - */ -var rvalid = true; - -/** - @default - */ -var rerrored = false; - -/** - @default the parent window - */ -var win = getParentWindow(); - -/** - @default - */ -var header = getHeaders(request); \ No newline at end of file diff --git a/test/cases/deprecatedtag.js b/test/cases/deprecatedtag.js deleted file mode 100644 index 6b6269cba..000000000 --- a/test/cases/deprecatedtag.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @deprecated -*/ -function foo() { - -} - -/** @deprecated since version 2.0 -*/ -function bar() { - -} \ No newline at end of file diff --git a/test/cases/exceptiontag.js b/test/cases/exceptiontag.js deleted file mode 100644 index 14d7d9a74..000000000 --- a/test/cases/exceptiontag.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - @throws {InvalidArgumentException} -*/ -function foo(x) { - -} - -/** - @exception Will throw an error if argument is null. -*/ -function bar(x) { - -} - -/** - @exception {DivideByZero} Argument x must be non-zero. -*/ -function pez(x) { - -} \ No newline at end of file diff --git a/test/cases/exports.js b/test/cases/exports.js deleted file mode 100644 index 9c1324781..000000000 --- a/test/cases/exports.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * An example of a server-side JavaScript module. - * @module hello/world - * @example - * var g = require('hello/world').sayHello('Gracie'); - */ - -/** - * Generate a greeting. - * @param {string} [subject="world"] To whom we greet. - * @returns {string} - */ -exports.sayHello = function(subject) { - return 'Hello ' + (subject || 'World'); -}; diff --git a/test/cases/exportstag.js b/test/cases/exportstag.js deleted file mode 100644 index 26f787eac..000000000 --- a/test/cases/exportstag.js +++ /dev/null @@ -1,20 +0,0 @@ -define(function () { - /** - A module representing a shirt. - @exports my/shirt - @version 1.0 - */ - var shirt = { - - /** A property of the module. */ - color: "black", - - /** @constructor */ - Turtleneck: function(size) { - /** A property of the class. */ - this.size = size; - } - }; - - return shirt; -}); \ No newline at end of file diff --git a/test/cases/exportstag2.js b/test/cases/exportstag2.js deleted file mode 100644 index 5e00268a3..000000000 --- a/test/cases/exportstag2.js +++ /dev/null @@ -1,18 +0,0 @@ -define( - ["my/buttons"], - function () { - /** - A module representing a coat. - @exports my/coat - @requires my/buttons - @version 1.0 - */ - var myModule = function(wool) { - /** document me */ - this.wool = wool; - } - - return myModule; - - } -); \ No newline at end of file diff --git a/test/cases/exportstag3.js b/test/cases/exportstag3.js deleted file mode 100644 index d48e94e29..000000000 --- a/test/cases/exportstag3.js +++ /dev/null @@ -1,22 +0,0 @@ -define( - /** - Utility functions to ease working with DOM elements. - @exports html/utils - */ - function () { - - var exports = { - /** Get the value of a property on an element. */ - getStyleProperty: function(element, propertyName) { - // ... - } - }; - - /** Determine if an element is in the document head. */ - exports.isInHead = function(element) { - // ... - } - - return exports; - } -); \ No newline at end of file diff --git a/test/cases/file.js b/test/cases/file.js deleted file mode 100644 index 15fb0fc92..000000000 --- a/test/cases/file.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @overview This is a file doclet. - */ - -function ignoreMe() { -} \ No newline at end of file diff --git a/test/cases/globaltag.js b/test/cases/globaltag.js deleted file mode 100644 index 44f1d186a..000000000 --- a/test/cases/globaltag.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - @global - @constructor - */ -window.Bar = new Function('', a, b, c); - -(function() { - - /** @global */ - var foo; - - foo = 'hello foo'; - - this.foo = foo; - -}).apply(window); \ No newline at end of file diff --git a/test/cases/ignoretag.js b/test/cases/ignoretag.js deleted file mode 100644 index 4e8c21215..000000000 --- a/test/cases/ignoretag.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - @ignore -*/ -function foo(x) { - -} diff --git a/test/cases/inner.js b/test/cases/inner.js deleted file mode 100644 index 4c07b97ab..000000000 --- a/test/cases/inner.js +++ /dev/null @@ -1,7 +0,0 @@ -function sendMessage(text) { - /** document me */ - var encoding = 'utf8'; - - /** document me */ - function encrypt(){} -} \ No newline at end of file diff --git a/test/cases/innerscope.js b/test/cases/innerscope.js deleted file mode 100644 index 5d2d5f5c4..000000000 --- a/test/cases/innerscope.js +++ /dev/null @@ -1,18 +0,0 @@ -/** @constructor */ -function Message(to) { - - var headers = {}, - response; - - /** document me */ - headers.to = to; - - (function() { - /** document me */ - response.code = '200'; - - /** document me */ - headers.from = ''; - })() -} - diff --git a/test/cases/innerscope2.js b/test/cases/innerscope2.js deleted file mode 100644 index 68f30cba6..000000000 --- a/test/cases/innerscope2.js +++ /dev/null @@ -1,19 +0,0 @@ -/** @constructor */ -function Message(to) { - - var headers = {}; - - /** document me */ - headers.to = to; - - (function() { - var headers = { - /** document me */ - cache: {} - }; - - /** document me */ - headers.from = ''; - })() -} - diff --git a/test/cases/jslangnames.js b/test/cases/jslangnames.js deleted file mode 100644 index 65f8404a9..000000000 --- a/test/cases/jslangnames.js +++ /dev/null @@ -1,24 +0,0 @@ - -/** @namespace */ -var constructor = { - /** document me */ - toString: function(){} -}; - -/** @namespace */ -var prototye = { - /** document me */ - valueOf: function(){} -} - -/** - This is Object - @namespace Object -*/ - -/** - This is Object.hasOwnProperty - @method Object.hasOwnProperty - */ - -// NOTE: you can't document a prototype of an object in JSDoc -- seriously, you just can't \ No newline at end of file diff --git a/test/cases/lends.js b/test/cases/lends.js deleted file mode 100644 index 6d86ae6df..000000000 --- a/test/cases/lends.js +++ /dev/null @@ -1,16 +0,0 @@ -/** @class */ -var Person = makeClass( - /** @lends Person# */ - { - /** Set up initial values. */ - initialize: function(name) { - /** The name of the person. */ - this.name = name; - }, - - /** Speak a message. */ - say: function(message) { - return this.name + " says: " + message; - } - } -); \ No newline at end of file diff --git a/test/cases/lends2.js b/test/cases/lends2.js deleted file mode 100644 index 57c4fa385..000000000 --- a/test/cases/lends2.js +++ /dev/null @@ -1,18 +0,0 @@ - -var Person = makeClass( - /** @lends Person# */ - { - /** Construct a Person. - @constructs Person - */ - initialize: function(name) { - /** The name of the person. */ - this.name = name; - }, - - /** Speak a message. */ - say: function(message) { - return this.name + " says: " + message; - } - } -); \ No newline at end of file diff --git a/test/cases/lendsglobal.js b/test/cases/lendsglobal.js deleted file mode 100644 index 13803be19..000000000 --- a/test/cases/lendsglobal.js +++ /dev/null @@ -1,14 +0,0 @@ -declare({ - globals: /** @lends */ { - - /** document me */ - 'test': function() { }, - - /** @namespace */ - 'test1': { - - /** document me */ - 'test2': function() { } - } - } -}); \ No newline at end of file diff --git a/test/cases/memberoftag.js b/test/cases/memberoftag.js deleted file mode 100644 index 2337c49f5..000000000 --- a/test/cases/memberoftag.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @constructor - @member mathlib - */ -function Data() { - - /** @property */ - this.point = {}; -} - -/** @namespace */ -mathlib = {Data: Data}; \ No newline at end of file diff --git a/test/cases/memberoftag2.js b/test/cases/memberoftag2.js deleted file mode 100644 index b926dc6ae..000000000 --- a/test/cases/memberoftag2.js +++ /dev/null @@ -1,10 +0,0 @@ -create( - 'Observable', - { - /** @memberof Observable */ - cache: [], - - /** @memberof Observable.prototype */ - publish: function(msg) {} - } -); \ No newline at end of file diff --git a/test/cases/modules/data/mod-1.js b/test/cases/modules/data/mod-1.js deleted file mode 100644 index a5de108fe..000000000 --- a/test/cases/modules/data/mod-1.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @module */ -define({ - property: "foo", - method: function() {} -}); \ No newline at end of file diff --git a/test/cases/modules/data/mod-2.js b/test/cases/modules/data/mod-2.js deleted file mode 100644 index 1027fd9d3..000000000 --- a/test/cases/modules/data/mod-2.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @module my/module/name */ -define({ - property: "foo", - method: function() {} -}); \ No newline at end of file diff --git a/test/cases/modules/data/mod-3.js b/test/cases/modules/data/mod-3.js deleted file mode 100644 index 91a3251a0..000000000 --- a/test/cases/modules/data/mod-3.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - My test module. - @module my/module - */ -define(function() { - - /** - @undocumented - @alias module:my/module - */ - var mod = { - - /** Document a property. */ - myProperty: "foo", - - /** Document a method. */ - myMethod: function() {} - }; - - return mod; -}); \ No newline at end of file diff --git a/test/cases/moduletag.js b/test/cases/moduletag.js deleted file mode 100644 index fb4c4665c..000000000 --- a/test/cases/moduletag.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @module bookshelf - */ - -/** - * @class - */ -this.Book = function(title) { - /** document me */ - this.title = title; -} \ No newline at end of file diff --git a/test/cases/moduletag2.js b/test/cases/moduletag2.js deleted file mode 100644 index 0dd0bc3ec..000000000 --- a/test/cases/moduletag2.js +++ /dev/null @@ -1,9 +0,0 @@ -/** @module color/mixer */ - -module.exports = { - /** Blend two colors together. */ - blend: function(color1, color2) { } -} - -/** Darken a color by the given shade. */ -exports.darken = function(color, shade) { } \ No newline at end of file diff --git a/test/cases/objectlit.js b/test/cases/objectlit.js deleted file mode 100644 index f976cfb44..000000000 --- a/test/cases/objectlit.js +++ /dev/null @@ -1,8 +0,0 @@ -/** document me */ -var tools = { - /** document me */ - serialiser: { - /** document me */ - value: '' - } -}; diff --git a/test/cases/objectlit2.js b/test/cases/objectlit2.js deleted file mode 100644 index 072d4eda5..000000000 --- a/test/cases/objectlit2.js +++ /dev/null @@ -1,8 +0,0 @@ -/** document me */ -var position = { - axis: { - /** document me */ - x: 0, - y: 0 - } -}; \ No newline at end of file diff --git a/test/cases/paramtag.js b/test/cases/paramtag.js deleted file mode 100644 index db431bfcc..000000000 --- a/test/cases/paramtag.js +++ /dev/null @@ -1,41 +0,0 @@ -/** -* @param { String | Array} targetName The name (or names) of what to find. -*/ -function find(targetName) { -} - -/** -* @param {function} callback -*/ -function bind(callback) { -} - -/** -* @param {function} -*/ -function unbind(callback) { -} - -/** -* @param id The id of the element. -*/ -function getElement(id) { -} - -/** -* @param ... Two or more elements. -*/ -function combine() { -} - -/** -* @param delimiter - What to split on. -*/ -function split(delimiter) { -} - -/** -* @param - If true make the commit atomic. -*/ -function commit(atomic) { -} diff --git a/test/cases/privatetag.js b/test/cases/privatetag.js deleted file mode 100644 index 71e0d9710..000000000 --- a/test/cases/privatetag.js +++ /dev/null @@ -1,11 +0,0 @@ -/** -* @constructor -* @private -*/ -function Foo() { - - /** document me */ - this.bar = 1; -} - - diff --git a/test/cases/projecttag.js b/test/cases/projecttag.js deleted file mode 100644 index b00c8e2c6..000000000 --- a/test/cases/projecttag.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - An automated documentation generator for JavaScript. - @project JSDoc - @version 3.0.0 - @copyright 2011 (c) Michael Mathews - @license Apache Version 2 - */ -function blah(url) { -} \ No newline at end of file diff --git a/test/cases/quotename.js b/test/cases/quotename.js deleted file mode 100644 index e484db5ed..000000000 --- a/test/cases/quotename.js +++ /dev/null @@ -1,19 +0,0 @@ -/** @namespace */ -var chat = {}; - -/** - @namespace - */ -chat["#channel"] = {}; - - -/** - @property - @type {boolean} - @defaultvalue - */ -chat["#channel"].open = true; - -/** - @event chat."#channel"."op:announce-motd" - */ \ No newline at end of file diff --git a/test/cases/quotename2.js b/test/cases/quotename2.js deleted file mode 100644 index b05c81367..000000000 --- a/test/cases/quotename2.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @namespace */ -var contacts = { - - /** @namespace */ - 'say-"hello"@example.com': { - - /** document me */ - "username": 'Sue Smart' - } -} \ No newline at end of file diff --git a/test/cases/readonlytag.js b/test/cases/readonlytag.js deleted file mode 100644 index 30e826ce2..000000000 --- a/test/cases/readonlytag.js +++ /dev/null @@ -1,10 +0,0 @@ -/** -* @constructor -*/ -function Collection() { - - /** @readonly */ - this.length = 0; -} - - diff --git a/test/cases/requirestag.js b/test/cases/requirestag.js deleted file mode 100644 index 3fe272293..000000000 --- a/test/cases/requirestag.js +++ /dev/null @@ -1,12 +0,0 @@ -/** -* @requires module:foo/helper -*/ -function foo() { -} - -/** -* @requires foo -* @requires Pez#blat this text is ignored -*/ -function bar() { -} diff --git a/test/cases/returnstag.js b/test/cases/returnstag.js deleted file mode 100644 index d3cf4de4e..000000000 --- a/test/cases/returnstag.js +++ /dev/null @@ -1,11 +0,0 @@ -/** -* @returns { String | Array} The names of the found item(s). -*/ -function find(targetName) { -} - -/** -* @return The binding id. -*/ -function bind(callback) { -} diff --git a/test/cases/seetag.js b/test/cases/seetag.js deleted file mode 100644 index 9b9ad7235..000000000 --- a/test/cases/seetag.js +++ /dev/null @@ -1,11 +0,0 @@ -/** -* @see #search -*/ -function foo() { -} - -/** -* @see http://example.com/someref -*/ -function bar() { -} diff --git a/test/cases/sincetag.js b/test/cases/sincetag.js deleted file mode 100644 index 527b2a398..000000000 --- a/test/cases/sincetag.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - @since 1.2.3 -*/ -function foo(x) { - -} diff --git a/test/cases/src/ignored.txt b/test/cases/src/ignored.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/cases/src/one.js b/test/cases/src/one.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/cases/src/two.js b/test/cases/src/two.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/cases/this-and-objectlit.js b/test/cases/this-and-objectlit.js deleted file mode 100644 index c8d5ca0fa..000000000 --- a/test/cases/this-and-objectlit.js +++ /dev/null @@ -1,12 +0,0 @@ -/** @constructor */ -function Page(title) { - this.parts = { - title: title, - body: { - /** document me */ - heading: '', - main: '' - } - } -} - diff --git a/test/cases/this.js b/test/cases/this.js deleted file mode 100644 index 7c167606d..000000000 --- a/test/cases/this.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - @constructor - */ -function Singer() { - - this.tralala = function() { // method of constructor Singer - /** document me */ - this.isSinging = true; // setting a member of constructor Singer - }; -} \ No newline at end of file diff --git a/test/cases/this2.js b/test/cases/this2.js deleted file mode 100644 index 88fd9fa61..000000000 --- a/test/cases/this2.js +++ /dev/null @@ -1,15 +0,0 @@ -/** @constructor */ -function TemplateBuilder(templateType) { - //** document me */ - //this.templateType = templateType; - - /** @constructor */ - this.Template = function() { // nested constructor of constructor TemplateFactory - /** document me */ - this.render = function(data) { - /** document me */ - this.rendered = true; - } - }; - -} \ No newline at end of file diff --git a/test/cases/this3.js b/test/cases/this3.js deleted file mode 100644 index 7e4e0a77e..000000000 --- a/test/cases/this3.js +++ /dev/null @@ -1,5 +0,0 @@ -function setPosition(newP) { - /** document me */ - this.position = newP; // sets global property -} - diff --git a/test/cases/thistag.js b/test/cases/thistag.js deleted file mode 100644 index 1ea79253f..000000000 --- a/test/cases/thistag.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @constructor */ -function Foo(name) { - setName.apply(this, name); -} - -/** @this Foo */ -function setName(name) { - /** document me */ - this.name = name; -} \ No newline at end of file diff --git a/test/cases/typedeftag.js b/test/cases/typedeftag.js deleted file mode 100644 index 9d82cfdca..000000000 --- a/test/cases/typedeftag.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @typedef {(string|number)} calc.NumberLike */ - -/** @param {calc.NumberLike} x A number or a string. */ -calc.readNumber = function(x) { -} \ No newline at end of file diff --git a/test/cases/typekind.js b/test/cases/typekind.js deleted file mode 100644 index cc4c54501..000000000 --- a/test/cases/typekind.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - @module {ConnectServer} blog/server -*/ - -module.exports = require('connect').createServer( - Connect.logger(), - Connect.conditionalGet(), - Connect.favicon(), - Connect.cache(), - Connect.gzip(), - require('wheat')(__dirname) -); - -/** - @property {number} module:blog/server.port - @default 8080 -*/ \ No newline at end of file diff --git a/test/cases/typetag.js b/test/cases/typetag.js deleted file mode 100644 index acaa7cac8..000000000 --- a/test/cases/typetag.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - @type {string|Array} -*/ -var foo; - - -/** - @type integer -*/ -var bar = +(new Date()).getTime(); \ No newline at end of file diff --git a/test/cases/var.js b/test/cases/var.js deleted file mode 100644 index 0daea9f53..000000000 --- a/test/cases/var.js +++ /dev/null @@ -1,10 +0,0 @@ -/** document me */ -const GREEN = 1, - RED = 0; - -/** document me */ -var validate = function(){}; - -var i, - /** document me */ - results; \ No newline at end of file diff --git a/test/cases/variations.js b/test/cases/variations.js deleted file mode 100644 index e292cae77..000000000 --- a/test/cases/variations.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @namespace anim - */ - -/** - * @method anim.fadein(1) - * @desc Show the nodelist elements by fading them to opaque. - * @since 1.0 - * - * @param {number} [duration=400] How long the animation will run. - * @param {function} [callback] Called once the animation is complete. - * - * @returns {this} - */ - -/** - * @method anim.fadein(2) - * @desc Show the nodelist elements by fading them to opaque. - * @since 1.4.3 - * - * @param {number} [duration=400] How long the animation will run. - * @param {string} [easing=swing] The easing function for the transition. - * @param {function} [callback] Called once the animation is complete. - * - * @returns {this} - */ \ No newline at end of file diff --git a/test/cases/versiontag.js b/test/cases/versiontag.js deleted file mode 100644 index a482075ad..000000000 --- a/test/cases/versiontag.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - @version 1.2.3 -*/ -function foo(x) { - -} diff --git a/test/cases/virtual.js b/test/cases/virtual.js deleted file mode 100644 index 4eb25c1d2..000000000 --- a/test/cases/virtual.js +++ /dev/null @@ -1,3 +0,0 @@ -/** @name dimensions */ - -var width = 12 \ No newline at end of file diff --git a/test/examples/only_comments.js b/test/examples/only_comments.js new file mode 100644 index 000000000..97b325ce6 --- /dev/null +++ b/test/examples/only_comments.js @@ -0,0 +1,2 @@ +/**@overview nothing but comments 1*/ +/**@overview nothing but comments 2*/ \ No newline at end of file diff --git a/test/jsdoc_parser.js b/test/jsdoc_parser.js new file mode 100644 index 000000000..416b1a30f --- /dev/null +++ b/test/jsdoc_parser.js @@ -0,0 +1,13 @@ +var parser = require('jsdoc/parser'); + +exports['The jsdoc parser should exist.'] = function(t) { + t.expect(1); + t.equal( typeof parser, 'object' ); + t.done(); +}; + +exports['The parser should have a parse function.'] = function(t) { + t.expect(1); + t.equal( typeof parser.parse, 'function' ); + t.done(); +}; diff --git a/test/jsdoc_parser_comments.js b/test/jsdoc_parser_comments.js new file mode 100644 index 000000000..8f64fa2ce --- /dev/null +++ b/test/jsdoc_parser_comments.js @@ -0,0 +1,49 @@ +var parser = require('jsdoc/parser'); +var dumper = require('jsdoc/util/dumper'); + +exports['Parse a source containing only a jsdoc comment.'] = function(t) { + t.expect(1); + var docs = parser.parse('/**@doc*/'); + t.equal( docs.length, 1, 'should result in docs that contain the comment' ); + t.done(); +}; + +exports['Parse a source ending with a jsdoc comment.'] = function(t) { + t.expect(1); + var docs = parser.parse(';/**@doc*/'); + t.equal( docs.length, 1, 'should result in docs that contain the comment' ); + t.done(); +}; + +exports['Parse a source with a jsdoc comment preceding a jsdoc comment.'] = function(t) { + t.expect(1); + var docs = parser.parse('/**@doc1*/ /**@doc2*/ var x;'); + t.equal( docs.length, 2, 'should result in docs containing both the comments' ); + t.done(); +}; + +exports['Parse a source with only single line comments.'] = function(t) { + t.expect(1); + var docs = parser.parse('// foo'); + t.equal( docs.length, 0, 'should result in docs that are empty' ); + + t.done(); +}; + +exports['Parse a source with only single non-jsdoc multi-line comments.'] = function(t) { + t.expect(1); + var docs = parser.parse('/*foo*/'); + t.equal( docs.length, 0, 'should result in docs that are empty' ); + t.done(); +}; + +exports['Parse second source, should be unaffected by the first pasre.'] = function(t) { + t.expect(2); + var docs = parser.parse('/**@doc1*/ /**@doc2*/ var x;'); + t.equal( docs.length, 2 ); + + docs = parser.parse('function y(){}'); + t.equal( docs.length, 1 ); + + t.done(); +}; diff --git a/test/jsdoc_parser_function.js b/test/jsdoc_parser_function.js new file mode 100644 index 000000000..1bbc2986a --- /dev/null +++ b/test/jsdoc_parser_function.js @@ -0,0 +1,64 @@ +var parser = require('jsdoc/parser'); + +exports['An undocumented named function.'] = function(t) { + t.expect(3); + var docs = parser.parse('function foo() {}'); + t.equal( docs.length, 1, 'should result in 1 doc' ); + t.equal( typeof docs[0].longname, 'string', 'should have a longname set' ); + t.equal( docs[0].longname, 'foo', 'should have a longname equal to the function name' ); + t.done(); +}; + +exports['A documented named function.'] = function(t) { + t.expect(4); + var docs = parser.parse('/**@desc a function*/ function foo() {}'); + t.equal( docs.length, 1, 'should result in 1 doc' ); + t.equal( docs[0].longname, 'foo', 'should have a longname equal to the function name' ); + t.equal( typeof docs[0], 'object', 'should have a jsdoc set' ); + t.equal( docs[0].jsdoc, '@desc a function', 'should have a jsdoc equal to the preceding doc comment' ); + t.done(); +}; + +exports['A nested documented named function.'] = function(t) { + t.expect(2); + var docs = parser.parse('function foo() { /**@desc a function*/ function bar() {} }'); + t.equal( docs.length, 2, 'should result in 2 docs' ); + t.equal( docs[1].longname, 'foo~bar', 'the inner function should have a longname equal to ~' ); + t.done(); +}; + +exports['A nested documented named function.'] = function(t) { + t.expect(2); + var docs = parser.parse('function foo() { /**@desc a function*/ function bar() {} }'); + t.equal( docs.length, 2, 'should result in 2 docs' ); + t.equal( docs[1].longname, 'foo~bar', 'the inner function should have a longname equal to ~' ); + t.done(); +}; + +exports['An undocumented anonymous function.'] = function(t) { + t.expect(4); + var docs = parser.parse('var foo = function() {}'); + t.equal( docs.length, 1, 'should result in 1 doc' ); + t.equal( docs[0].longname, 'foo', 'should have a longname set' ); + t.equal( docs[0].kind, 'function', 'should have a kind set to function' ); + t.equal( docs[0].longname, 'foo', 'should have a longname equal to the function name' ); + t.done(); +}; + +exports['An undocumented anonymous function nested inside an undocumented anonymous function.'] = function(t) { + t.expect(3); + var docs = parser.parse('var foo = function() { var bar = function() {} }'); + t.equal( docs.length, 2, 'should result in 2 docs' ); + t.equal( typeof docs[1].longname, 'string', 'the inner function doc should have a longname set' ); + t.equal( docs[1].longname, 'foo~bar', 'should have a longname equal to ~' ); + t.done(); +}; + +exports['An anonymous function as a child of a var.'] = function(t) { + t.expect(3); + var docs = parser.parse('var x, foo = function() {}'); + t.equal( docs.length, 2, 'should result in 3 docs' ); + t.equal( docs[1].longname, 'foo', 'the inner function doc should have a longname set' ); + t.equal( docs[1].kind, 'function', 'should have a kind set to function' ); + t.done(); +}; \ No newline at end of file diff --git a/test/narcissus.js b/test/narcissus.js new file mode 100644 index 000000000..aa062d9d6 --- /dev/null +++ b/test/narcissus.js @@ -0,0 +1,24 @@ +var narcissus = require('narcissus'); + +exports['Narcissus should exist.'] = function(t) { + t.expect(2); + t.equal( typeof narcissus, 'object' ); + t.equal( typeof narcissus.Narcissus, 'object' ); + t.done(); +}; + +exports['Narcissus parse should be a function.'] = function(t) { + t.expect(2); + t.equal( typeof narcissus.Narcissus.parser, 'object' ); + t.equal( typeof narcissus.Narcissus.parser.parse, 'function' ); + t.done(); +}; + +exports['Narcissus parse should generate an AST from source.'] = function(t) { + t.expect(1); + var src = 'var foo = 1;', + ast = narcissus.Narcissus.parser.parse(src, 'filename', 1); + + t.equal( typeof ast, 'object' ); + t.done(); +}; \ No newline at end of file diff --git a/test/runner.js b/test/runner.js deleted file mode 100644 index 3bdd56d96..000000000 --- a/test/runner.js +++ /dev/null @@ -1,149 +0,0 @@ -var assert = require('common/assert'); - -var passCount = 0, - failCount = 0, - errorLog = [], - currentTestFile = ''; - -function test(description, f) { - try { - f(); - passCount++; - } - catch(e) { - errorLog.push(description + (currentTestFile? ' ['+currentTestFile+']':'') + '\n' + (e.message||'') + '\n - Expected: ' + e.expected + '\n - Actual: ' + e.actual); - failCount++; - } -} - -function testFile(filepath) { - currentTestFile = filepath; - include(filepath); - currentTestFile = ''; -} - -function report() { - print('\033[032mPASSED: ' + passCount + ' test' + (passCount == 1? '' : 's') + '.\033[0m'); - if (failCount) { - print('\033[031mFAILED: '+ failCount + ' test' + (passCount == 1? '' : 's') + '.\033[0m'); - for (var i = 0, leni = errorLog.length; i < leni; i++) { - print(' ' + (i+1) + '. ' + (i+1 < 10? ' ' : '') + (errorLog[i]||'') + '\n'); - } - } -} - -// helpers -var testhelpers = { - getDocSetFromFile: function(filename) { - var sourceCode = readFile(BASEDIR + filename), - testParser, - doclets; - - testParser = new (require('jsdoc/src/parser')).Parser(); - require('jsdoc/src/handlers').attachTo(testParser); - - doclets = testParser.parse('javascript:' + sourceCode); - testhelpers.indexAll(doclets); - - return { - doclets: doclets, - getByLongname: function(longname) { - return doclets.filter(function(doclet) { - return (doclet.longname || doclet.name) === longname; - }); - } - }; - }, - indexAll: function(docs) { - var index = {}; - docs.forEach(function(doc) { - if (!index[doc.longname]) index[doc.longname] = []; - index[doc.longname].push(doc); - }); - docs.index = index; - } -}; - -testFile('test/t/common/util.js'); -testFile('test/t/common/dumper.js'); -testFile('test/t/common/events.js'); -testFile('test/t/common/query.js'); -testFile('test/t/common/scanner.js'); - -testFile('test/t/jsdoc/opts/parser.js'); -testFile('test/t/jsdoc/src/parser.js'); -testFile('test/t/jsdoc/src/handlers.js'); -testFile('test/t/jsdoc/name.js'); - -testFile('test/t/cases/file.js'); - -testFile('test/t/cases/virtual.js'); - -testFile('test/t/cases/objectlit.js'); -testFile('test/t/cases/objectlit2.js'); - -testFile('test/t/cases/this.js'); -testFile('test/t/cases/this2.js'); -testFile('test/t/cases/this3.js'); - -testFile('test/t/cases/this-and-objectlit.js'); - -testFile('test/t/cases/var.js'); - -testFile('test/t/cases/inner.js'); -testFile('test/t/cases/innerscope.js'); -testFile('test/t/cases/innerscope2.js'); - -testFile('test/t/cases/modules/data/mod-1.js'); -testFile('test/t/cases/modules/data/mod-2.js'); - -testFile('test/t/cases/accesstag.js'); -testFile('test/t/cases/alias.js'); -testFile('test/t/cases/alias2.js'); -testFile('test/t/cases/alias3.js'); -testFile('test/t/cases/also.js'); -testFile('test/t/cases/augmentstag.js'); -testFile('test/t/cases/authortag.js'); -testFile('test/t/cases/borrowstag.js'); -testFile('test/t/cases/borrowstag2.js'); -testFile('test/t/cases/classtag.js'); -testFile('test/t/cases/constructstag.js'); -testFile('test/t/cases/constructstag2.js'); -testFile('test/t/cases/constructstag3.js'); -testFile('test/t/cases/constructortag.js'); -testFile('test/t/cases/copyrighttag.js'); -testFile('test/t/cases/defaulttag.js'); -testFile('test/t/cases/deprecatedtag.js'); -testFile('test/t/cases/exports.js'); -testFile('test/t/cases/exportstag.js'); -testFile('test/t/cases/exportstag2.js'); -testFile('test/t/cases/exportstag3.js'); -testFile('test/t/cases/exceptiontag.js'); -testFile('test/t/cases/globaltag.js'); -testFile('test/t/cases/ignoretag.js'); -testFile('test/t/cases/lends.js'); -testFile('test/t/cases/lends2.js'); -testFile('test/t/cases/lendsglobal.js'); -testFile('test/t/cases/memberoftag.js'); -testFile('test/t/cases/memberoftag2.js'); -testFile('test/t/cases/moduletag.js'); -testFile('test/t/cases/moduletag2.js'); -testFile('test/t/cases/paramtag.js'); -testFile('test/t/cases/privatetag.js'); -testFile('test/t/cases/quotename.js'); -testFile('test/t/cases/quotename2.js'); -testFile('test/t/cases/readonlytag.js'); -testFile('test/t/cases/requirestag.js'); -testFile('test/t/cases/returnstag.js'); -testFile('test/t/cases/seetag.js'); -testFile('test/t/cases/sincetag.js'); -testFile('test/t/cases/thistag.js'); -testFile('test/t/cases/typekind.js'); -testFile('test/t/cases/typetag.js'); -testFile('test/t/cases/typedeftag.js'); -testFile('test/t/cases/variations.js'); -testFile('test/t/cases/versiontag.js'); - - -report(); - diff --git a/test/t/cases/accesstag.js b/test/t/cases/accesstag.js deleted file mode 100644 index 9165cf959..000000000 --- a/test/t/cases/accesstag.js +++ /dev/null @@ -1,39 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/accesstag.js'), - foo = docSet.getByLongname('Thingy~foo')[0], - _bar = docSet.getByLongname('Thingy#_bar')[0], - pez = docSet.getByLongname('Thingy#pez')[0]; - - //dump(docSet.doclets); - - test('When a symbol has a @access private tag, the doclet has a access="private" property.', function() { - assert.equal(foo.access, 'private'); - }); - - test('When a symbol has a @access protected tag, the doclet has a access="protected" property.', function() { - assert.equal(_bar.access, 'protected'); - }); - - test('When a symbol has a @access public tag, the doclet has no access property.', function() { - assert.equal(typeof pez.access, 'undefined'); - }); - - // same as... - - foo = docSet.getByLongname('OtherThingy~foo')[0]; - _bar = docSet.getByLongname('OtherThingy#_bar')[0]; - pez = docSet.getByLongname('OtherThingy#pez')[0]; - - test('When a symbol has a @private tag, the doclet has a access="private" property.', function() { - assert.equal(foo.access, 'private'); - }); - - test('When a symbol has a @protected tag, the doclet has a access="protected" property.', function() { - assert.equal(_bar.access, 'protected'); - }); - - test('When a symbol has a @public tag, the doclet has no access property.', function() { - assert.equal(typeof pez.access, 'undefined'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/alias.js b/test/t/cases/alias.js deleted file mode 100644 index 2ffba19c8..000000000 --- a/test/t/cases/alias.js +++ /dev/null @@ -1,20 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/alias.js'), - found = docSet.getByLongname('myObject').filter(function($) { - return ! $.undocumented; - }), - - foundMember = docSet.getByLongname('myObject.myProperty') - - //dump(found); - - test('When a symbol is given an alias it is documented as if the name is the alias value.', function() { - assert.equal(found[0].longname, 'myObject'); - }); - - test('When a symbol is a member of an alias it is documented as if the memberof is the alias value.', function() { - assert.equal(foundMember[0].longname, 'myObject.myProperty'); - assert.equal(foundMember[0].memberof, 'myObject'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/alias2.js b/test/t/cases/alias2.js deleted file mode 100644 index e14f31b29..000000000 --- a/test/t/cases/alias2.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/alias2.js'), - found = docSet.getByLongname('myObject').filter(function($) { - return ! $.undocumented; - }), - - foundMember = docSet.getByLongname('ns.Myclass#myProperty') - - //dump(found); - - test('When a symbol is a member of an alias of a nested name it is documented as if the memberof is the nested alias value.', function() { - assert.equal(foundMember[0].longname, 'ns.Myclass#myProperty'); - assert.equal(foundMember[0].name, 'myProperty'); - assert.equal(foundMember[0].memberof, 'ns.Myclass'); - assert.equal(foundMember[0].scope, 'instance'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/alias3.js b/test/t/cases/alias3.js deleted file mode 100644 index 1ff0e0ac1..000000000 --- a/test/t/cases/alias3.js +++ /dev/null @@ -1,12 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/alias3.js'), - tcm = docSet.getByLongname('trackr.CookieManager')[0], - tcmValue = docSet.getByLongname('trackr.CookieManager#value')[0]; - - //dump(found); - - test('When a symbol is a member of an aliased class, a this-variables is documented as if it were a member that class.', function() { - assert.equal(tcmValue.memberof, 'trackr.CookieManager'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/also.js b/test/t/cases/also.js deleted file mode 100644 index a48b23819..000000000 --- a/test/t/cases/also.js +++ /dev/null @@ -1,13 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/also.js'), - name = docSet.getByLongname('Asset#name').filter(function($) { - return ! $.undocumented; - }); - - //dump(name); - - test('When a symbol has two doclets adjacent to each other both doclets apply to the symbol.', function() { - assert.equal(name.length, 2, 'myObject'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/augmentstag.js b/test/t/cases/augmentstag.js deleted file mode 100644 index dbaf4b0c7..000000000 --- a/test/t/cases/augmentstag.js +++ /dev/null @@ -1,12 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/augmentstag.js'), - foo = docSet.getByLongname('Foo')[0], - bar = docSet.getByLongname('Bar')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @augments tag, the doclet has a augments property that includes that value.', function() { - assert.equal(typeof bar.augments, 'object'); - assert.equal(bar.augments[0], 'Foo'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/authortag.js b/test/t/cases/authortag.js deleted file mode 100644 index 7b7fabe81..000000000 --- a/test/t/cases/authortag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/authortag.js'), - Thingy = docSet.getByLongname('Thingy')[0]; - - //dump(docSet.doclets); - - test('When a symbol has a @author tag, the doclet has a author property with that value.', function() { - assert.equal(Thingy.author, 'Michael Mathews '); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/borrowstag.js b/test/t/cases/borrowstag.js deleted file mode 100644 index 4bef5814e..000000000 --- a/test/t/cases/borrowstag.js +++ /dev/null @@ -1,14 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/borrowstag.js'), - util = docSet.getByLongname('util').filter(function($) { - return ! $.undocumented; - })[0]; - - //dump(found); - - test('When a symbol has a @borrows-as tag, that is added to the symbol\'s "borrowed" property.', function() { - assert.equal(util.borrowed.length, 1); - assert.equal(util.borrowed[0].from, 'trstr'); - assert.equal(util.borrowed[0].as, 'trim'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/borrowstag2.js b/test/t/cases/borrowstag2.js deleted file mode 100644 index 002e7cf3d..000000000 --- a/test/t/cases/borrowstag2.js +++ /dev/null @@ -1,17 +0,0 @@ -var borrow = require('jsdoc/borrow'); - -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/borrowstag2.js'); - - borrow.resolveBorrows(docSet.doclets); - - var str_rtrim = docSet.getByLongname('str.rtrim').filter(function($) { - return ! $.undocumented; - })[0]; - - //dump(docSet); exit(); - - test('When a symbol has a @borrows tag, the borrowed symbol is added to the symbol.', function() { - assert.equal(typeof str_rtrim, 'object'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/classtag.js b/test/t/cases/classtag.js deleted file mode 100644 index d3293564e..000000000 --- a/test/t/cases/classtag.js +++ /dev/null @@ -1,16 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/classtag.js'), - ticker = docSet.getByLongname('Ticker')[0], - news = docSet.getByLongname('NewsSource')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has a @class tag, the doclet has a kind property set to "class".', function() { - assert.equal(ticker.kind, 'class'); - }); - - test('When a symbol has a @class tag with a value, the doclet has a name property set to that value.', function() { - assert.equal(news.kind, 'class'); - assert.equal(news.longname, 'NewsSource'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/constructortag.js b/test/t/cases/constructortag.js deleted file mode 100644 index af23362b5..000000000 --- a/test/t/cases/constructortag.js +++ /dev/null @@ -1,15 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/constructortag.js'), - feed = docSet.getByLongname('Feed')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @constructor tag, it is documented as a class.', function() { - assert.equal(feed.kind, 'class'); - }); - - test('When a symbol has an @constructor tag and a @class tag, the value of the @class tag becomes the classdesc property.', function() { - assert.equal(feed.classdesc, 'Describe your class here.'); - assert.equal(feed.description, 'Describe your constructor function here.'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/constructstag.js b/test/t/cases/constructstag.js deleted file mode 100644 index ec8c9d61a..000000000 --- a/test/t/cases/constructstag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/constructstag.js'), - textblock = docSet.getByLongname('TextBlock')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @constructs tag, it is documented as a class with that name.', function() { - assert.equal(textblock.kind, 'class'); - assert.equal(textblock.longname, 'TextBlock'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/constructstag2.js b/test/t/cases/constructstag2.js deleted file mode 100644 index 2cd8e5fc7..000000000 --- a/test/t/cases/constructstag2.js +++ /dev/null @@ -1,12 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/constructstag2.js'), - menu = docSet.getByLongname('Menu')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @constructs tag, it is documented as a class.', function() { - assert.equal(menu.name, 'Menu'); - assert.equal(menu.kind, 'class'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/constructstag3.js b/test/t/cases/constructstag3.js deleted file mode 100644 index 5e7a96496..000000000 --- a/test/t/cases/constructstag3.js +++ /dev/null @@ -1,12 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/constructstag3.js'), - personName = docSet.getByLongname('Person#name')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a function symbol has an @constructs tag, any this-variables are ducumented as instance members of the class.', function() { - assert.equal(personName.memberof, 'Person'); - assert.equal(personName.scope, 'instance'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/copyrighttag.js b/test/t/cases/copyrighttag.js deleted file mode 100644 index 76a144c40..000000000 --- a/test/t/cases/copyrighttag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/copyrighttag.js'), - Thingy = docSet.getByLongname('Thingy')[0]; - - //dump(docSet.doclets); - - test('When a symbol has a @copyright tag, the doclet has a copyright property with that value.', function() { - assert.equal(Thingy.copyright, '(c) 2011 Michael Mathews'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/defaulttag.js b/test/t/cases/defaulttag.js deleted file mode 100644 index e4d70c93a..000000000 --- a/test/t/cases/defaulttag.js +++ /dev/null @@ -1,41 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/defaulttag.js'), - request = (docSet.getByLongname('request') || [])[0], - response = (docSet.getByLongname('response') || [])[0], - rcode = (docSet.getByLongname('rcode') || [])[0], - rvalid = (docSet.getByLongname('rvalid') || [])[0], - rerrored = (docSet.getByLongname('rerrored') || [])[0], - win = (docSet.getByLongname('win') || [])[0]; - header = (docSet.getByLongname('header') || [])[0]; - - //dump(response); - - test('When symbol set to null has a @default tag with no text.', function() { - assert.equal(request.defaultvalue, 'null', 'The doclet\'s defaultValue property should be: undefined.'); - }); - - test('When symbol set to a string has a @default tag with no text.', function() { - assert.equal(response.defaultvalue, '"ok"', 'The doclet\'s defaultValue property should be that quoted string.'); - }); - - test('When symbol set to a number has a @default tag with no text.', function() { - assert.equal(rcode.defaultvalue, '200', 'The doclet\'s defaultValue property should be that number.'); - }); - - test('When symbol has a @default tag with text.', function() { - assert.equal(win.defaultvalue, 'the parent window', 'The doclet\'s defaultValue property should be that text.'); - }); - - test('When symbol has a @default tag with true.', function() { - assert.equal(rvalid.defaultvalue, 'true'); - }); - - test('When symbol has a @default tag with false.', function() { - assert.equal(rerrored.defaultvalue, 'false'); - }); - - test('When symbol has a @default tag with a function call.', function() { - assert.equal(typeof header.defaultvalue, 'undefined'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/deprecatedtag.js b/test/t/cases/deprecatedtag.js deleted file mode 100644 index 5a93f131c..000000000 --- a/test/t/cases/deprecatedtag.js +++ /dev/null @@ -1,16 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/deprecatedtag.js'), - foo = docSet.getByLongname('foo')[0], - bar = docSet.getByLongname('bar')[0]; - - //dump(docSet.doclets); - - test('When a symbol has a @deprecated tag with no value, the doclet has a deprecated property set to true.', function() { - assert.equal(foo.deprecated, true); - }); - - test('When a symbol has a @deprec tag with a value, the doclet has a deprecated property set to that value.', function() { - assert.equal(bar.deprecated, 'since version 2.0'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/exceptiontag.js b/test/t/cases/exceptiontag.js deleted file mode 100644 index de0ce730b..000000000 --- a/test/t/cases/exceptiontag.js +++ /dev/null @@ -1,20 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/exceptiontag.js'), - foo = docSet.getByLongname('foo')[0], - bar = docSet.getByLongname('bar')[0], - pez = docSet.getByLongname('pez')[0]; - - //dump(docSet.doclets); - - test('When a symbol has an @exception tag, the doclet has a exception property set to that value.', function() { - assert.equal(typeof foo.exceptions, 'object'); - assert.equal(foo.exceptions.length, 1); - - assert.equal(typeof bar.exceptions, 'object'); - assert.equal(bar.exceptions.length, 1); - - assert.equal(typeof pez.exceptions, 'object'); - assert.equal(pez.exceptions.length, 1); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/exports.js b/test/t/cases/exports.js deleted file mode 100644 index fc80c8fcd..000000000 --- a/test/t/cases/exports.js +++ /dev/null @@ -1,13 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/exports.js'), - helloworld = docSet.getByLongname('module:hello/world')[0], - sayhello = docSet.getByLongname('module:hello/world.sayHello')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol starts with the special name "exports" and is in a file with a @module tag, the symbol is documented as a member of that module.', function() { - assert.equal(typeof sayhello, 'object'); - assert.equal(sayhello.kind, 'function'); - assert.equal(sayhello.memberof, 'module:hello/world'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/exportstag.js b/test/t/cases/exportstag.js deleted file mode 100644 index c5395615c..000000000 --- a/test/t/cases/exportstag.js +++ /dev/null @@ -1,31 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/exportstag.js'), - shirt = docSet.getByLongname('module:my/shirt')[0], - color = docSet.getByLongname('module:my/shirt.color')[0], - tneck = docSet.getByLongname('module:my/shirt.Turtleneck')[0], - size = docSet.getByLongname('module:my/shirt.Turtleneck#size')[0]; - - //dump(docSet.doclets); exit(0); - - test('When an objlit symbol has an @exports tag, the doclet is aliased to "module:" + the tag value.', function() { - assert.equal(typeof shirt, 'object'); - assert.equal(shirt.alias, 'my/shirt'); - }); - - test('When an objlit symbol has an @exports tag, the doclet\'s longname includes the "module:" namespace.', function() { - assert.equal(shirt.longname, 'module:my/shirt'); - }); - - test('When an objlit symbol has an @exports tag, the doclet kind is set to module.', function() { - assert.equal(shirt.kind, 'module'); - }); - - test('When an objlit symbol has an @exports tag, the objlit members are documented as members of the module.', function() { - assert.equal(typeof color, 'object'); - assert.equal(color.memberof, 'module:my/shirt'); - - assert.equal(typeof tneck, 'object'); - assert.equal(typeof size, 'object'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/exportstag2.js b/test/t/cases/exportstag2.js deleted file mode 100644 index c638b9b33..000000000 --- a/test/t/cases/exportstag2.js +++ /dev/null @@ -1,26 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/exportstag2.js'), - coat = docSet.getByLongname('module:my/coat')[0], - wool = docSet.getByLongname('module:my/coat#wool')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a function symbol has an @exports tag, the doclet is aliased to "module:" + the tag value.', function() { - assert.equal(typeof coat, 'object'); - assert.equal(coat.alias, 'my/coat'); - }); - - test('When a function symbol has an @exports tag, the doclet\'s longname includes the "module:" namespace.', function() { - assert.equal(coat.longname, 'module:my/coat'); - }); - - test('When a function symbol has an @exports tag, the doclet kind is set to module.', function() { - assert.equal(coat.kind, 'module'); - }); - - test('When a function symbol has an @exports tag, the this members are documented as instance members of the module.', function() { - assert.equal(typeof wool, 'object'); - assert.equal(wool.memberof, 'module:my/coat'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/exportstag3.js b/test/t/cases/exportstag3.js deleted file mode 100644 index a75c9b372..000000000 --- a/test/t/cases/exportstag3.js +++ /dev/null @@ -1,19 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/exportstag3.js'), - html = docSet.getByLongname('module:html/utils')[0], - getstyle = docSet.getByLongname('module:html/utils.getStyleProperty')[0], - inhead = docSet.getByLongname('module:html/utils.isInHead')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a function symbol has an @exports tag and there is an objlit named "exports" the members are documented as members of the module.', function() { - assert.equal(typeof getstyle, 'object'); - assert.equal(getstyle.memberof, 'module:html/utils'); - }); - - test('When a function symbol has an @exports tag and there are members assinged to an "exports" name, the members are documented as members of the module.', function() { - assert.equal(typeof inhead, 'object'); - assert.equal(inhead.memberof, 'module:html/utils'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/file.js b/test/t/cases/file.js deleted file mode 100644 index 94fd7e66d..000000000 --- a/test/t/cases/file.js +++ /dev/null @@ -1,19 +0,0 @@ -(function() { - var srcParser = require('jsdoc/src/parser'), - doclets; - - app.jsdoc.parser = new srcParser.Parser(); - - require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); - - doclets = app.jsdoc.parser.parse(BASEDIR + 'test/cases/file.js') - - //dump(doclets); - - test('When a file overview tag appears in a doclet.', function() { - var m = /^.*([\/\\]cases[\/\\]file\.js)$/.exec(doclets[0].name); - assert.equal(m.length, 2, 'The name of the doclet should start with file: and should end with the path to the file.'); - assert.equal(doclets[0].name, doclets[0].longname, 'The name and longname should be equal.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/globaltag.js b/test/t/cases/globaltag.js deleted file mode 100644 index efea48076..000000000 --- a/test/t/cases/globaltag.js +++ /dev/null @@ -1,28 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/globaltag.js'), - found = docSet.getByLongname('foo').filter(function($) { - return ! $.undocumented; - }); - - //dump(docSet.doclets); - - test('When an inner symbol has a @global tag it is documented as if it were global.', function() { - assert.equal(found[0].name, 'foo'); - assert.equal(found[0].longname, 'foo'); - assert.equal(found[0].memberof, undefined); - assert.equal(found[0].scope, 'global'); - - }); - - found = docSet.getByLongname('Bar').filter(function($) { - return ! $.undocumented; - }); - - test('When an nested symbol has a @global tag it is documented as if it were global.', function() { - assert.equal(found[0].name, 'Bar'); - assert.equal(found[0].longname, 'Bar'); - assert.equal(found[0].memberof, undefined); - assert.equal(found[0].scope, 'global'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/ignoretag.js b/test/t/cases/ignoretag.js deleted file mode 100644 index c18eabcaa..000000000 --- a/test/t/cases/ignoretag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/ignoretag.js'), - foo = docSet.getByLongname('foo')[0]; - - //dump(docSet.doclets); - - test('When a symbol has an @ignore tag, the doclet has a ignore property set to true.', function() { - assert.equal(foo.ignore, true); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/inner.js b/test/t/cases/inner.js deleted file mode 100644 index 63a48f761..000000000 --- a/test/t/cases/inner.js +++ /dev/null @@ -1,21 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/inner.js'), - found1 = docSet.getByLongname('sendMessage~encoding'), - found2 = docSet.getByLongname('sendMessage~encrypt'); - - //dump(docSet); - - test('When a documented var member is inside a named function.', function() { - assert.equal(found1.length, 1, 'A doclet with the correct longname should be found.'); - assert.equal(found1[0].name, 'encoding', 'The short name should be correct.'); - assert.equal(found1[0].memberof, 'sendMessage', 'The memberof should be correct.'); - assert.equal(found1[0].scope, 'inner', 'The scope should default to "static".'); - }); - - test('When a documented function is inside a named function.', function() { - assert.equal(found2.length, 1, 'A doclet with the correct longname should be found.'); - assert.equal(found2[0].name, 'encrypt', 'The short name should be correct.'); - assert.equal(found2[0].memberof, 'sendMessage', 'The memberof should be correct.'); - assert.equal(found2[0].scope, 'inner', 'The scope should default to "static".'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/innerscope.js b/test/t/cases/innerscope.js deleted file mode 100644 index 8e6bee7d9..000000000 --- a/test/t/cases/innerscope.js +++ /dev/null @@ -1,20 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/innerscope.js'), - to = docSet.getByLongname('Message~headers.to'), - from = docSet.getByLongname('Message~headers.from'), - response = docSet.getByLongname('Message~response.code'); - - //dump(docSet); exit(); - - test('When a member of a var member is documented.', function() { - assert.equal(to.length, 1, 'It is like Outer~inner.member.'); - }); - - test('When a second member of a var member is documented.', function() { - assert.equal(response.length, 1, 'It is like Outer~inner.member.'); - }); - - test('When a deeply nested member of a var member is documented.', function() { - assert.equal(from.length, 1, 'It is still like Outer~inner.member.'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/innerscope2.js b/test/t/cases/innerscope2.js deleted file mode 100644 index c29516ac2..000000000 --- a/test/t/cases/innerscope2.js +++ /dev/null @@ -1,21 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/innerscope2.js'), - to = docSet.getByLongname('Message~headers.to'), - from = docSet.getByLongname('~headers.from'), - cache = docSet.getByLongname('~headers.cache'); - - //dump(docSet); - - test('When a var is declared in a function.', function() { - assert.equal(cache.length, 1, 'It is like Inner~member.'); - }); - - test('When a var is masked by an inner var and a member of the inner is documented.', function() { - assert.equal(from.length, 1, 'It is like Inner~inner.member.'); - }); - - test('When a documented member is assigned to a var that masks an outer var.', function() { - assert.equal(from[0].name, 'from'); - assert.equal(from[0].memberof, '~headers'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/lends.js b/test/t/cases/lends.js deleted file mode 100644 index 324590515..000000000 --- a/test/t/cases/lends.js +++ /dev/null @@ -1,14 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/lends.js'), - init = docSet.getByLongname('Person#initialize'), - say = docSet.getByLongname('Person#say'), - name = docSet.getByLongname('Person#name'); - - //dump(docSet); - - test('When a documented member is inside an objlit associated with a @lends tag.', function() { - assert.equal(init.length, 1, 'The member should be documented as a member of the lendee.'); - assert.equal(name.length, 1, 'The this member should be documented as a member of the lendee.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/lends2.js b/test/t/cases/lends2.js deleted file mode 100644 index d3355fc2b..000000000 --- a/test/t/cases/lends2.js +++ /dev/null @@ -1,17 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/lends2.js'), - person = docSet.getByLongname('Person').filter(function($) { - return ! $.undocumented; - })[0], - say = docSet.getByLongname('Person#say'), - name = docSet.getByLongname('Person#name') - - //dump(person); - - test('When a documented member is inside an objlit associated with a @lends tag.', function() { - assert.equal(person.description, 'Construct a Person.', 'A tag with a @constructs tag is documented as a constructor.'); - assert.equal(say.length, 1, 'The member should be documented as a member of the lendee.'); - assert.equal(name.length, 1, 'The this member should be documented as a member of the lendee.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/lendsglobal.js b/test/t/cases/lendsglobal.js deleted file mode 100644 index 451e9a0a9..000000000 --- a/test/t/cases/lendsglobal.js +++ /dev/null @@ -1,17 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/lendsglobal.js'), - testf = docSet.getByLongname('test')[0], - test1 = docSet.getByLongname('test1')[0], - test12 = docSet.getByLongname('test1.test2')[0]; - - //dump(testf, test1, test12); exit(); - - test('When a documented member is inside an objlit associated with a @lends tag that has no value.', function() { - assert.equal(typeof testf.memberof, 'undefined', 'The members of the objlit are not members of any symbol.'); - assert.equal(testf.longname, 'test', 'The members of the objlit are documented as global.'); - - - assert.equal(test12.memberof, 'test1', 'The nested members of the objlit are members of a global symbol.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/memberoftag.js b/test/t/cases/memberoftag.js deleted file mode 100644 index 895ce5c01..000000000 --- a/test/t/cases/memberoftag.js +++ /dev/null @@ -1,15 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/memberoftag.js'), - Data = docSet.getByLongname('mathlib.Data')[0], - point = docSet.getByLongname('mathlib.Data#point')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @member tag, the doclet has a long name that includes the parent.', function() { - assert.equal(typeof Data, 'object'); - assert.equal(typeof point, 'object'); - - assert.equal(Data.memberof, 'mathlib'); - assert.equal(Data.name, 'Data'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/memberoftag2.js b/test/t/cases/memberoftag2.js deleted file mode 100644 index dc40e5773..000000000 --- a/test/t/cases/memberoftag2.js +++ /dev/null @@ -1,23 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/memberoftag2.js'), - publish = docSet.getByLongname('Observable#publish')[0], - cache = docSet.getByLongname('Observable.cache')[0]; - - //dump(docSet.doclets); exit(0); - - test('A symbol is documented as a static @memberof a class.', function() { - assert.equal(typeof cache, 'object', 'it should appear as a static member of that class.'); - assert.equal(cache.memberof, 'Observable'); - assert.equal(cache.scope, 'static'); - assert.equal(cache.name, 'cache'); - assert.equal(cache.longname, 'Observable.cache'); - }); - - test('A symbol is documented as a static @memberof a class prototype.', function() { - assert.equal(typeof publish, 'object', 'it should appear as an instance member of that class.'); - assert.equal(publish.memberof, 'Observable'); - assert.equal(publish.scope, 'instance'); - assert.equal(publish.name, 'publish'); - assert.equal(publish.longname, 'Observable#publish'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/modules/data/mod-1.js b/test/t/cases/modules/data/mod-1.js deleted file mode 100644 index 6f26452f6..000000000 --- a/test/t/cases/modules/data/mod-1.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var srcParser = require('jsdoc/src/parser'), - doclets; - - env.opts._ = [BASEDIR + 'test/cases/modules/']; - - app.jsdoc.parser = new srcParser.Parser(); - - require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); - - doclets = app.jsdoc.parser.parse(BASEDIR + 'test/cases/modules/data/mod-1.js') - - test('When a module has no name documented, the name comes from the file path.', function() { - assert.ok(doclets.length > 1); - assert.equal(doclets[0].longname, 'module:data/mod-1'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/modules/data/mod-2.js b/test/t/cases/modules/data/mod-2.js deleted file mode 100644 index 5aff85abd..000000000 --- a/test/t/cases/modules/data/mod-2.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var srcParser = require('jsdoc/src/parser'), - doclets; - - env.opts._ = [BASEDIR + 'test/cases/modules/']; - - app.jsdoc.parser = new srcParser.Parser(); - - require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); - - doclets = app.jsdoc.parser.parse(BASEDIR + 'test/cases/modules/data/mod-2.js') - - test('When a module has a name documented, that name is used.', function() { - assert.ok(doclets.length > 1); - assert.equal(doclets[0].longname, 'module:my/module/name'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/modules/data/mod-3.js b/test/t/cases/modules/data/mod-3.js deleted file mode 100644 index d4dcf9c2a..000000000 --- a/test/t/cases/modules/data/mod-3.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var srcParser = require('jsdoc/src/parser'), - doclets; - - env.opts._ = [BASEDIR + 'test/cases/modules/']; - - app.jsdoc.parser = new srcParser.Parser(); - - require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); - - doclets = app.jsdoc.parser.parse(BASEDIR + 'test/cases/modules/data/mod-2.js') - - test('When a module has a name documented, that name is used.', function() { - assert.ok(doclets.length > 1); - assert.equal(doclets[0].name, 'module:my/module/name'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/moduletag.js b/test/t/cases/moduletag.js deleted file mode 100644 index e7acd52cc..000000000 --- a/test/t/cases/moduletag.js +++ /dev/null @@ -1,17 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/moduletag.js'), - book = docSet.getByLongname('module:bookshelf.Book')[0], - title = docSet.getByLongname('module:bookshelf.Book#title')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a global symbol starts with "this" and is in a file with a @module tag, the symbol is documented as a member of that module.', function() { - assert.equal(typeof book, 'object'); - assert.equal(book.memberof, 'module:bookshelf'); - }); - - test('When an inner symbol starts with "this" and is in a file with a @module tag, the symbol is documented as a member of its enclosing constructor.', function() { - assert.equal(typeof title, 'object'); - assert.equal(title.memberof, 'module:bookshelf.Book'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/moduletag2.js b/test/t/cases/moduletag2.js deleted file mode 100644 index 7ddba8ddc..000000000 --- a/test/t/cases/moduletag2.js +++ /dev/null @@ -1,25 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/moduletag2.js'), - mixer = docSet.getByLongname('module:color/mixer').filter(function($) { - return ! $.undocumented; - })[0], - blend = docSet.getByLongname('module:color/mixer.blend')[0], - darken = docSet.getByLongname('module:color/mixer.darken')[0]; - - //dump(mixer); exit(0); - - test('When a @module tag defines a module module.', function() { - assert.equal(typeof mixer, 'object'); - assert.equal(mixer.kind, 'module', 'A symbol of kind "module" is documented.'); - }); - - test('When an object literal is lent to a module with a @lends tag.', function() { - assert.equal(typeof blend, 'object'); - assert.equal(blend.kind, 'function', 'A member of that object literal is documented as a member of the module.'); - }); - - test('When a documented symbol is a member of a namespace "exports".', function() { - assert.equal(typeof darken, 'object'); - assert.equal(darken.kind, 'function', 'It is documented as a member of the module.'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/objectlit.js b/test/t/cases/objectlit.js deleted file mode 100644 index b777dd7af..000000000 --- a/test/t/cases/objectlit.js +++ /dev/null @@ -1,13 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/objectlit.js'), - found = docSet.getByLongname('tools.serialiser.value'); - - //dump(docSet); - - test('When a child of an objlit has no @name or @memberof tags.', function() { - assert.equal(found.length, 1, 'A doclet with the correct longname should be found.'); - assert.equal(found[0].name, 'value', 'The short name should be correct.'); - assert.equal(found[0].memberof, 'tools.serialiser', 'The memberof should be correct.'); - assert.equal(found[0].scope, 'static', 'The scope should default to "static".'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/objectlit2.js b/test/t/cases/objectlit2.js deleted file mode 100644 index ec486aae9..000000000 --- a/test/t/cases/objectlit2.js +++ /dev/null @@ -1,13 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/objectlit2.js'), - found = docSet.getByLongname('position.axis.x'); - - //dump(docSet); - - test('When a parent of an objlit has no documentation.', function() { - assert.equal(found.length, 1, 'A doclet with the correct longname should be found.'); - assert.equal(found[0].name, 'x', 'The short name should be correct.'); - assert.equal(found[0].memberof, 'position.axis', 'The memberof should be correct.'); - assert.equal(found[0].scope, 'static', 'The scope should default to "static".'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/paramtag.js b/test/t/cases/paramtag.js deleted file mode 100644 index 9a62441a5..000000000 --- a/test/t/cases/paramtag.js +++ /dev/null @@ -1,69 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/paramtag.js'), - find = docSet.getByLongname('find')[0], - unbind = docSet.getByLongname('unbind')[0], - bind = docSet.getByLongname('bind')[0], - getElement = docSet.getByLongname('getElement')[0], - combine = docSet.getByLongname('combine')[0], - split = docSet.getByLongname('split')[0], - commit = docSet.getByLongname('commit')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @param tag with a type before the name, the doclet has a params property that includes that param.', function() { - assert.equal(typeof find.params, 'object'); - assert.equal(find.params.length, 1); - assert.equal(find.params[0].type.names.join(', '), 'String, Array'); - assert.equal(find.params[0].name, 'targetName'); - assert.equal(find.params[0].description, 'The name (or names) of what to find.'); - }); - - test('When a symbol has an @param tag with only a type and name, the doclet has a params property that includes that param.', function() { - assert.equal(typeof bind.params, 'object'); - assert.equal(bind.params.length, 1); - assert.equal(bind.params[0].type.names.join(', '), 'function'); - assert.equal(bind.params[0].name, 'callback'); - assert.equal(bind.params[0].description, undefined); - }); - - test('When a symbol has an @param tag with only a type, the doclet has a params property that includes that param.', function() { - assert.equal(typeof unbind.params, 'object'); - assert.equal(unbind.params.length, 1); - assert.equal(unbind.params[0].type.names.join(', '), 'function'); - assert.equal(unbind.params[0].name, undefined); - assert.equal(unbind.params[0].description, undefined); - }); - - test('When a symbol has an @param tag with no type, the doclet has a params property that includes that param.', function() { - assert.equal(typeof getElement.params, 'object'); - assert.equal(getElement.params.length, 1); - assert.equal(getElement.params[0].type, undefined); - assert.equal(getElement.params[0].name, 'id'); - assert.equal(getElement.params[0].description, 'The id of the element.'); - }); - - test('When a symbol has an @param tag with a non-alpha name like "...", the doclet has a params property that includes that param.', function() { - assert.equal(typeof combine.params, 'object'); - assert.equal(combine.params.length, 1); - assert.equal(combine.params[0].type, undefined); - assert.equal(combine.params[0].name, '...'); - assert.equal(combine.params[0].description, 'Two or more elements.'); - }); - - test('When a symbol has an @param tag with name followed by a dash, the doclet has a params property that includes that param.', function() { - assert.equal(typeof split.params, 'object'); - assert.equal(split.params.length, 1); - assert.equal(split.params[0].type, undefined); - assert.equal(split.params[0].name, 'delimiter'); - assert.equal(split.params[0].description, 'What to split on.'); - }); - - test('When a symbol has an @param tag with no name or type, the doclet has a params property that includes that param.', function() { - assert.equal(typeof commit.params, 'object'); - assert.equal(commit.params.length, 1); - assert.equal(commit.params[0].type, undefined); - assert.equal(commit.params[0].name, undefined); - assert.equal(commit.params[0].description, 'If true make the commit atomic.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/privatetag.js b/test/t/cases/privatetag.js deleted file mode 100644 index 9eae4304a..000000000 --- a/test/t/cases/privatetag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/privatetag.js'), - foo = docSet.getByLongname('Foo')[0], - bar = docSet.getByLongname('Foo#bar')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @private tag, the doclet has an access property that is "private".', function() { - assert.equal(foo.access, 'private'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/projecttag.js b/test/t/cases/projecttag.js deleted file mode 100644 index 5e7a96496..000000000 --- a/test/t/cases/projecttag.js +++ /dev/null @@ -1,12 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/constructstag3.js'), - personName = docSet.getByLongname('Person#name')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a function symbol has an @constructs tag, any this-variables are ducumented as instance members of the class.', function() { - assert.equal(personName.memberof, 'Person'); - assert.equal(personName.scope, 'instance'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/quotename.js b/test/t/cases/quotename.js deleted file mode 100644 index 489080dfb..000000000 --- a/test/t/cases/quotename.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/quotename.js'), - found1 = docSet.getByLongname('chat.\"#channel\".open')[0]; - - // dump(docSet); - - test('When a member is quoted in square brackets.', function() { - assert.equal(found1.name, 'open', 'The short name should be correct.'); - assert.equal(found1.memberof, 'chat.\"#channel\"', 'The memberof should be correct.'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/quotename2.js b/test/t/cases/quotename2.js deleted file mode 100644 index e33799a2c..000000000 --- a/test/t/cases/quotename2.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/quotename2.js'), - found1 = docSet.getByLongname("contacts.\"say-\\\"hello\\\"@example.com\".username")[0]; - - // dump(docSet); - - test('When a key name of a member of an objlit is quoted.', function() { - assert.equal(found1.name, 'username', 'The short name should be correct.'); - assert.equal(found1.memberof, "contacts.\"say-\\\"hello\\\"@example.com\"", 'The memberof should be correct.'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/readonlytag.js b/test/t/cases/readonlytag.js deleted file mode 100644 index f295ed35a..000000000 --- a/test/t/cases/readonlytag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/readonlytag.js'), - Collection = docSet.getByLongname('Collection')[0], - length = docSet.getByLongname('Collection#length')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @readonly tag, the doclet has an readonly property that is true.', function() { - assert.equal(length.readonly, true); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/requirestag.js b/test/t/cases/requirestag.js deleted file mode 100644 index de4537d58..000000000 --- a/test/t/cases/requirestag.js +++ /dev/null @@ -1,17 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/requirestag.js'), - foo = docSet.getByLongname('foo')[0], - bar = docSet.getByLongname('bar')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @requires tag, the doclet has a requires property that includes that value, with the "module:" namespace added.', function() { - assert.equal(typeof foo.requires, 'object'); - assert.equal(foo.requires[0], 'module:foo/helper'); - - assert.equal(typeof bar.requires, 'object'); - assert.equal(bar.requires[0], 'module:foo'); - assert.equal(bar.requires[1], 'module:Pez#blat'); - - }); -})(); \ No newline at end of file diff --git a/test/t/cases/returnstag.js b/test/t/cases/returnstag.js deleted file mode 100644 index ab04b34ee..000000000 --- a/test/t/cases/returnstag.js +++ /dev/null @@ -1,22 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/returnstag.js'), - find = docSet.getByLongname('find')[0], - bind = docSet.getByLongname('bind')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @returns tag with a type and description, the doclet has a returns array that includes that return.', function() { - assert.equal(typeof find.returns, 'object'); - assert.equal(find.returns.length, 1); - assert.equal(find.returns[0].type.names.join(', '), 'String, Array'); - assert.equal(find.returns[0].description, 'The names of the found item(s).'); - }); - - test('When a symbol has an @param tag with only a type and name, the doclet has a returns array property that includes that param.', function() { - assert.equal(typeof bind.returns, 'object'); - assert.equal(bind.returns.length, 1); - assert.equal(bind.returns[0].description, 'The binding id.'); - }); - - -})(); \ No newline at end of file diff --git a/test/t/cases/seetag.js b/test/t/cases/seetag.js deleted file mode 100644 index 646d892b7..000000000 --- a/test/t/cases/seetag.js +++ /dev/null @@ -1,15 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/seetag.js'), - foo = docSet.getByLongname('foo')[0], - bar = docSet.getByLongname('bar')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @see tag, the doclet has a see property that includes that value.', function() { - assert.equal(typeof foo.see, 'object'); - assert.equal(foo.see[0], '#search'); - - assert.equal(typeof bar.see, 'object'); - assert.equal(bar.see[0], 'http://example.com/someref'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/sincetag.js b/test/t/cases/sincetag.js deleted file mode 100644 index bf56bafda..000000000 --- a/test/t/cases/sincetag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/sincetag.js'), - foo = docSet.getByLongname('foo')[0]; - - //dump(docSet.doclets); - - test('When a symbol has an @since tag, the doclet has a since property set to true.', function() { - assert.equal(foo.since, '1.2.3'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/this-and-objectlit.js b/test/t/cases/this-and-objectlit.js deleted file mode 100644 index e6bd105d8..000000000 --- a/test/t/cases/this-and-objectlit.js +++ /dev/null @@ -1,26 +0,0 @@ -/** @constructor */ -function Page(title) { - this.parts = { - title: title, - body: { - /** document me */ - heading: '', - main: '' - } - } -} - -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/this-and-objectlit.js'), - found = docSet.getByLongname('Page#parts.body.heading'); - - //dump(docSet); - - test('When a member is nested inside an objectlit this property inside a constructor.', function() { - assert.equal(found.length, 1, 'A this member should be like Constructor#objlit.member.'); - assert.equal(found[0].name, 'heading', 'The short name should be correct.'); - assert.equal(found[0].memberof, 'Page#parts.body', 'The memberof should be correct.'); - assert.equal(found[0].scope, 'static', 'The scope should default to "static".'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/this.js b/test/t/cases/this.js deleted file mode 100644 index 212907da7..000000000 --- a/test/t/cases/this.js +++ /dev/null @@ -1,21 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/this.js'), - found1 = docSet.getByLongname('Singer#tralala'), - found2 = docSet.getByLongname('Singer#isSinging'); - - // dump(docSet); - - test('When a member is attached to this in a constructor.', function() { - assert.equal(found1.length, 1, 'The longname should be like Constructor#member.'); - assert.equal(found1[0].name, 'tralala', 'The short name should be correct.'); - assert.equal(found1[0].memberof, 'Singer', 'The memberof should be correct.'); - assert.equal(found1[0].scope, 'instance', 'The scope should default to "static".'); - }); - - test('When a member is attached to this in a method of a constructor.', function() { - assert.equal(found2.length, 1, 'The longname should be like Constructor#member.'); - assert.equal(found2[0].name, 'isSinging', 'The short name should be correct.'); - assert.equal(found2[0].memberof, 'Singer', 'The memberof should be correct.'); - assert.equal(found2[0].scope, 'instance', 'The scope should default to "static".'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/this2.js b/test/t/cases/this2.js deleted file mode 100644 index 86990e32c..000000000 --- a/test/t/cases/this2.js +++ /dev/null @@ -1,14 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/this2.js'), - found = docSet.getByLongname('TemplateBuilder#Template#rendered'); - - //dump(docSet); - - test('When a constructor is nested inside another a constructor.', function() { - assert.equal(found.length, 1, 'A this member should be like Constructor#Constructor#member.'); - assert.equal(found[0].name, 'rendered', 'The short name should be correct.'); - assert.equal(found[0].memberof, 'TemplateBuilder#Template', 'The memberof should be correct.'); - assert.equal(found[0].scope, 'instance', 'The scope should default to "static".'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/this3.js b/test/t/cases/this3.js deleted file mode 100644 index c7078cd80..000000000 --- a/test/t/cases/this3.js +++ /dev/null @@ -1,13 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/this3.js'), - found = docSet.getByLongname('position'); - - //dump(docSet); - - test('When a this is assigned to inside a non-constructor function.', function() { - assert.equal(found.length, 1, 'The member name should be global, like "member".'); - assert.equal(found[0].name, 'position', 'The short name should be correct.'); - assert.equal(found[0].memberof, undefined, 'The memberof should be correct.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/thistag.js b/test/t/cases/thistag.js deleted file mode 100644 index 8277ea458..000000000 --- a/test/t/cases/thistag.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/thistag.js'), - setName = docSet.getByLongname('setName')[0], - fooName = docSet.getByLongname('Foo#name')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has a @this tag, the doclet has a this property that is set to that value.', function() { - assert.equal(setName['this'], 'Foo'); - }); - - test('When a this symbol is documented inside a function with a @this tag, the symbol is documented as a member of that tags value.', function() { - assert.equal(typeof fooName, 'object'); - assert.equal(fooName.name, 'name'); - assert.equal(fooName.memberof, 'Foo'); - assert.equal(fooName.scope, 'instance'); - }); -})(); \ No newline at end of file diff --git a/test/t/cases/typedeftag.js b/test/t/cases/typedeftag.js deleted file mode 100644 index 1728c4cc2..000000000 --- a/test/t/cases/typedeftag.js +++ /dev/null @@ -1,24 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/typedeftag.js'), - numberlike = docSet.getByLongname('calc.NumberLike')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @typedef tag, the doclet has a kind property set to "typedef".', function() { - assert.equal(numberlike.kind, 'typedef'); - }); - - test('When a symbol has an @typedef tag with a type, the doclet has a type property set to that type.', function() { - assert.equal(typeof numberlike.type, 'object'); - assert.equal(typeof numberlike.type.names, 'object'); - assert.equal(numberlike.type.names.length, 2); - assert.equal(numberlike.type.names[0], 'string'); - assert.equal(numberlike.type.names[1], 'number'); - }); - - test('When a symbol has an @typedef tag with a name, the doclet has a name property set to that name.', function() { - assert.equal(numberlike.name, 'NumberLike'); - assert.equal(numberlike.longname, 'calc.NumberLike'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/typekind.js b/test/t/cases/typekind.js deleted file mode 100644 index 8e6b7975b..000000000 --- a/test/t/cases/typekind.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/typekind.js'), - blog = docSet.getByLongname('module:blog/server')[0], - port = docSet.getByLongname('module:blog/server.port')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a module symbol has an kind tag, that includes a {type} clause', function() { - assert.equal(typeof blog.type, 'object', 'the doclet has a type.'); - assert.equal(blog.type.names.join(', '), 'ConnectServer', 'the doclet has a type property set to that {type} clause.'); - }); - - test('When a property symbol has an kind tag, that includes a {type} clause', function() { - assert.equal(typeof port.type, 'object', 'the doclet has a type.'); - assert.equal(port.type.names.join(', '), 'number', 'the doclet has a type property set to that {type} clause.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/typetag.js b/test/t/cases/typetag.js deleted file mode 100644 index 4d0dc9bc9..000000000 --- a/test/t/cases/typetag.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/typetag.js'), - foo = docSet.getByLongname('foo')[0], - bar = docSet.getByLongname('bar')[0]; - - //dump(docSet.doclets); exit(0); - - test('When a symbol has an @type tag, the doclet has a type property set to that value\'s type.', function() { - assert.equal(typeof foo.type, 'object'); - assert.equal(typeof foo.type.names, 'object'); - assert.equal(foo.type.names.join(', '), 'string, Array'); - }); - - test('When a symbol has an @type tag set to a plain string, the doclet has a type property set to that string as if it were a type.', function() { - assert.equal(bar.type.names.join(', '), 'integer'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/var.js b/test/t/cases/var.js deleted file mode 100644 index 0524da08f..000000000 --- a/test/t/cases/var.js +++ /dev/null @@ -1,31 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/var.js'), - found = [ - docSet.getByLongname('GREEN'), - docSet.getByLongname('RED'), - docSet.getByLongname('validate'), - docSet.getByLongname('i'), - docSet.getByLongname('results') - ]; - - //dump(docSet); - - test('When a series of constants are documented.', function() { - assert.equal(found[0].length, 1, 'The first constant should be found'); - assert.equal(found[0][0].comment, '/** document me */', 'The first constant should get the docs.'); - assert.equal(found[0][0].name, 'GREEN', 'The short name should be correct.'); - assert.equal(found[0][0].memberof, undefined, 'The memberof should be undefined.'); - assert.equal(found[0][0].scope, undefined, 'The scope should be undefined.'); - - assert.equal(found[1].length, 1, 'The second constant should be found'); - assert.equal(found[1][0].undocumented, true, 'The second constant should not get the docs.'); - }); - - test('When member of a series of vars are documented.', function() { - assert.equal(found[4][0].comment, '/** document me */', 'The correct var should get the docs.'); - assert.equal(found[4][0].name, 'results', 'The short name should be correct.'); - assert.equal(found[4][0].memberof, undefined, 'The memberof should be undefined.'); - assert.equal(found[4][0].scope, undefined, 'The scope should be undefined.'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/variations.js b/test/t/cases/variations.js deleted file mode 100644 index e2a9046f4..000000000 --- a/test/t/cases/variations.js +++ /dev/null @@ -1,23 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/variations.js'), - fadein1 = docSet.getByLongname('anim.fadein(1)')[0], - fadein2 = docSet.getByLongname('anim.fadein(2)')[0]; - - //dump(docSet.doclets); - - test('When a symbol has a name with a variation, the doclet has a variation property.', function() { - assert.equal(fadein1.variation, '1'); - assert.equal(fadein2.variation, '2'); - }); - - test('When a symbol has a name with a variation in the name, the doclet name has no variation in it.', function() { - assert.equal(fadein1.name, 'fadein'); - assert.equal(fadein2.name, 'fadein'); - }); - - test('When a symbol has a name with a variation in the name, the doclet longname has the variation in it.', function() { - assert.equal(fadein1.longname, 'anim.fadein(1)'); - assert.equal(fadein2.longname, 'anim.fadein(2)'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/versiontag.js b/test/t/cases/versiontag.js deleted file mode 100644 index a1e24b794..000000000 --- a/test/t/cases/versiontag.js +++ /dev/null @@ -1,11 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/versiontag.js'), - foo = docSet.getByLongname('foo')[0]; - - //dump(docSet.doclets); - - test('When a symbol has an @version tag, the doclet has a version property set to that value.', function() { - assert.equal(foo.version, '1.2.3'); - }); - -})(); \ No newline at end of file diff --git a/test/t/cases/virtual.js b/test/t/cases/virtual.js deleted file mode 100644 index dbe400602..000000000 --- a/test/t/cases/virtual.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - var docSet = testhelpers.getDocSetFromFile('test/cases/virtual.js'), - found = [ - docSet.getByLongname('dimensions'), - docSet.getByLongname('width') - ]; - - //dump(docSet); - - test('When a virtual symbol is documented.', function() { - assert.equal(found[0].length, 1, 'The symbol should be documented'); - }); - - test('When an undocumented is after a comment for a virtual symbol is documented.', function() { - assert.equal(found[1].length, 1, 'The symbol should be documented'); - }); - -})(); \ No newline at end of file diff --git a/test/t/common/dumper.js b/test/t/common/dumper.js deleted file mode 100644 index 73d067061..000000000 --- a/test/t/common/dumper.js +++ /dev/null @@ -1,91 +0,0 @@ -var common = {dumper: require('common/dumper')}; - -test('There is a common/dumper module.', function() { - assert.equal(typeof common.dumper, 'object', 'The common/dumper module should be an object.'); -}); - -test('The common/dumper module exports a "dump" function.', function() { - assert.equal(typeof common.dumper.dump, 'function', 'The module:module:common/dumper.dump member should be a function.'); -}); - -test('The module:module:common/dumper.dump function dumps string values.', function() { - assert.equal(common.dumper.dump('hello'), '"hello"'); - assert.equal(common.dumper.dump('hello "world"'), '"hello \\"world\\""', 'Double quotes should be escaped.'); - assert.equal(common.dumper.dump('hello\nworld'), '"hello\\nworld"', 'Newlines should be escaped.'); -}); - -test('The module:module:common/dumper.dump function dumps number values.', function() { - assert.equal(common.dumper.dump(1), '1'); - assert.equal(common.dumper.dump(0.1), '0.1', 'Decimal numbers shuld be dumped.'); -}); - -test('The module:module:common/dumper.dump function dumps boolean values.', function() { - assert.equal(common.dumper.dump(true), 'true'); - assert.equal(common.dumper.dump(false), 'false'); -}); - -test('The module:module:common/dumper.dump function dumps null values.', function() { - assert.equal(common.dumper.dump(null), 'null'); -}); - -test('The module:module:common/dumper.dump function dumps undefined values.', function() { - assert.equal(common.dumper.dump(undefined), 'undefined'); -}); - -test('The module:module:common/dumper.dump function dumps regex values.', function() { - assert.equal(common.dumper.dump(/^[Ff]oo$/gi), ''); -}); - -test('The module:module:common/dumper.dump function dumps date values.', function() { - assert.equal(common.dumper.dump(new Date('January 1, 1901 GMT')), ''); -}); - -test('The module:module:common/dumper.dump function dumps function values.', function() { - assert.equal(common.dumper.dump(function myFunc(){}), ''); - assert.equal(common.dumper.dump(function(){}), ''); -}); - -test('The module:module:common/dumper.dump function dumps array values.', function() { - var actual = common.dumper.dump(["hello", "world"]), - expected = '[\n "hello",\n "world"\n]'; - - assert.equal(actual, expected); -}); - -test('The module:module:common/dumper.dump function dumps simple object values.', function() { - var actual = common.dumper.dump({hello: "world"}), - expected = '{\n "hello": "world"\n}'; - - assert.equal(actual, expected); -}); - -test('The module:common/dumper.dump function dumps constructed instance values.', function() { - function Foo(name){ this.name = name; } - Foo.prototype.sayHello = function(){} - - var actual = common.dumper.dump(new Foo('hello')), - expected = '{\n "name": "hello"\n}'; - - assert.equal(actual, expected, 'Members of the instance should appear, but not prototype members.'); -}); - -test('The module:common/dumper.dump function dumps complex mixed values.', function() { - function Foo(){} - - var actual = common.dumper.dump( - [undefined, null, new Foo(), 1, true, 'hello\n"world', new Error('oops'), /foo/gi, new Date('December 26, 2010 GMT'), {f: function myFunc(){}, o: {a:1}}] - ), - expected = '[\n undefined,\n null,\n {\n },\n 1,\n true,\n "hello\\n\\"world",\n {\n "message": "oops"\n },\n ,\n ,\n {\n "f": ,\n "o": {\n "a": 1\n }\n }\n]'; - - assert.equal(actual, expected); -}); - -test('The module:common/dumper.dump function doesn\'t crash on circular references.', function() { - var a = {}; - a.b = a; - - var actual = common.dumper.dump(a), - expected = '{\n "b": \n}'; - - assert.equal(actual, expected); -}); \ No newline at end of file diff --git a/test/t/common/events.js b/test/t/common/events.js deleted file mode 100644 index 22685a507..000000000 --- a/test/t/common/events.js +++ /dev/null @@ -1,34 +0,0 @@ -var common = {events: require('common/events')}; - -test('The common/events module is defined.', function() { - assert.notEqual(typeof common.events, 'undefined', 'The common/events module should be defined.'); - assert.equal(typeof common.events, 'object', 'The common/events module should be an object.'); -}); - -test('The common/events module exports a "on" function.', function() { - assert.notEqual(typeof common.events.on, 'undefined', 'The common/events.on member should be defined.'); - assert.equal(typeof common.events.on, 'function', 'The common/events.on member should be a function.'); -}); - -test('The common/events module exports a "fire" function.', function() { - assert.notEqual(typeof common.events.fire, 'undefined', 'The common/events.fire member should be defined.'); - assert.equal(typeof common.events.fire, 'function', 'The common/events.fire member should be a function.'); -}); - -test('The common/events module exports a "removeListener" function.', function() { - assert.notEqual(typeof common.events.removeListener, 'undefined', 'The common/events.removeListener member should be defined.'); - assert.equal(typeof common.events.removeListener, 'function', 'The common/events.removeListener member should be a function.'); -}); - -test('The common/events.on function attaches a handler to an object that can be fired.', function() { - var target = {}, - result = false; - - target.on = common.events.on; - target.fire = common.events.fire; - - target.on('test', function() { result = true; }); - target.fire('test'); - - assert.equal(result, true); -}); diff --git a/test/t/common/query.js b/test/t/common/query.js deleted file mode 100644 index f77e9947f..000000000 --- a/test/t/common/query.js +++ /dev/null @@ -1,16 +0,0 @@ -var common = {query: require('common/query')}; - -test('The common/query module is defined.', function() { - assert.notEqual(typeof common.query, 'undefined', 'The common/query module should be defined.'); - assert.equal(typeof common.query, 'object', 'The common/query module should be an object.'); -}); - -test('The common/query module exports a "toObject" function.', function() { - assert.notEqual(typeof common.query.toObject, 'undefined', 'The common/query.toObject member should be defined.'); - assert.equal(typeof common.query.toObject, 'function', 'The common/query.toObject member should be a function.'); -}); - -test('The common/query.toObject function dumps an object from a query string.', function() { - assert.deepEqual(common.query.toObject('name=Michael+Mathews'), {name: 'Michael Mathews'}); - assert.deepEqual(common.query.toObject('name=Michael+Mathews&city=London'), {name: 'Michael Mathews', city: 'London'}); -}); diff --git a/test/t/common/scanner.js b/test/t/common/scanner.js deleted file mode 100644 index a1983151f..000000000 --- a/test/t/common/scanner.js +++ /dev/null @@ -1,19 +0,0 @@ -var scanner = new (require('jsdoc/src/scanner').Scanner)(), - includeMatch = new RegExp(".+\\.js(doc)?$"), - excludeMatch = new RegExp("(^|\\/)_"), - sourceFiles = scanner.scan([BASEDIR+'/test/cases/src/'], 3, includeMatch, excludeMatch); - -sourceFiles = sourceFiles.map(function($) { - return $.replace(BASEDIR, ''); -}); - -//dump(sourceFiles); exit(); - -test('The scanner should return the correct source files', function() { - - assert.equal(sourceFiles.length, 3); - assert.equal(sourceFiles.indexOf("test/cases/src/one.js") > -1, true); - assert.equal(sourceFiles.indexOf("test/cases/src/two.js") > -1, true); - assert.equal(sourceFiles.indexOf("test/cases/src/dir1/three.js") > -1, true); - -}); diff --git a/test/t/common/util.js b/test/t/common/util.js deleted file mode 100644 index 656df1770..000000000 --- a/test/t/common/util.js +++ /dev/null @@ -1,51 +0,0 @@ -var common = {util: require('common/util')}; - -test('There is a common/util module.', function() { - assert.equal(typeof common.util, 'object', 'The common/util module should be an object.'); -}); - -test('The common/util module exports a "mixin" function.', function() { - assert.equal(typeof common.util.mixin, 'function', 'The module:common/util.mixin member should be a function.'); -}); - -test('The module:common/util.mixin function takes a target object and returns it.', function() { - var target = {a:1}, - returned; - - returned = common.util.mixin(target); // mixing nothing in - - assert.deepEqual(returned, target); -}); - -test('The module:common/util.mixin function can mix a source object into the target.', function() { - var target = {a: 1, b: 2}, - source = {c: 3}; - - common.util.mixin(target, source); // modify the target object - - assert.deepEqual( target, {a: 1, b: 2, c: 3} ); -}); - -test('The module:common/util.mixin function overwrites properties in the target if they exist in the source.', function() { - var target = {a: 1, b: 2}, - source = {b: 3, c: 4}; - - common.util.mixin(target, source); - - assert.equal(target.a, 1, 'Existing properties in the target with unique keys are left alone.'); - assert.equal(target.b, source.b, 'Existing properties in the target with same-named keys are overwritten.'); - assert.equal(target.c, source.c, 'Properties in the source with unique keys are added to the target.'); -}); - -test('The module:common/util.mixin function can mix several source objects into the target.', function() { - var target = {}, - source1 = {a: 1, b: 2}, - source2 = {b: 7, c: 4}, - source3 = {b: 3, d: 5}, - returned; - - returned = common.util.mixin({}, source1, source2, source3); // use a dummy target and the return value to avoid modifying the real target (source1) - - assert.deepEqual( source1, {a: 1, b: 2}, 'The source objects themselves are not modified by being mixed in.'); - assert.deepEqual( returned, {a: 1, b: 3, c: 4, d: 5}, 'The returned object has all the properties of the sources.'); -}); diff --git a/test/t/jsdoc/name.js b/test/t/jsdoc/name.js deleted file mode 100644 index 570d144a1..000000000 --- a/test/t/jsdoc/name.js +++ /dev/null @@ -1,119 +0,0 @@ -var jsdoc = {name: require('jsdoc/name') }; - -test('There is a jsdoc/name module.', function() { - assert.equal(typeof jsdoc.name, 'object'); -}); - -test('The jsdoc/name module exports an shorten function.', function() { - assert.equal(typeof jsdoc.name.shorten, 'function'); -}); - -test('The module:jsdoc/name.shorten function breaks a longname up into the correct memberof, name and scope parts.', function() { - var startName = 'lib.Panel#open', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, 'open', 'The name should be the bit after the last scoping character.'); - assert.equal(parts.memberof, 'lib.Panel', 'The memberof should be the bit before the last scoping character.'); - assert.equal(parts.scope, '#', 'The scope should be the scoping character itself.'); -}); - -test('The module:jsdoc/name.shorten function works on static names.', function() { - var startName = 'elements.selected.getVisible', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, 'getVisible'); - assert.equal(parts.memberof, 'elements.selected'); - assert.equal(parts.scope, '.'); -}); - -test('The module:jsdoc/name.shorten function works on protoyped names.', function() { - var startName = 'Validator.prototype.$element', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, '$element'); - assert.equal(parts.memberof, 'Validator'); - assert.equal(parts.scope, '#'); -}); - -test('The module:jsdoc/name.shorten function works on inner names.', function() { - var startName = 'Button~_onclick', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, '_onclick'); - assert.equal(parts.memberof, 'Button'); - assert.equal(parts.scope, '~'); -}); - -test('The module:jsdoc/name.shorten function works on global names.', function() { - var startName = 'close', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, 'close'); - assert.equal(parts.memberof, '', 'The memberof should be an empty string for global symbols.'); - assert.equal(parts.scope, '', 'The scope should be an empty string for global symbols.'); -}); - -test('The module:jsdoc/name.shorten function works on bracketed stringy names.', function() { - var startName = 'channels["#ops"]#open', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, 'open'); - assert.equal(parts.memberof, 'channels."#ops"', 'Bracketed stringy names should appear as quoted strings.'); - assert.equal(parts.scope, '#'); - - startName = 'channels["#bots"]["log.max"]', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, '"log.max"'); - assert.equal(parts.memberof, 'channels."#bots"'); - assert.equal(parts.scope, '.'); -}); - -test('The module:jsdoc/name.shorten function works on fully stringy names, like "foo.bar".', function() { - var startName = '"foo.bar"', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.name, '"foo.bar"', 'The name should be the full quoted string.'); - assert.equal(parts.longname, '"foo.bar"', 'The longname should be the full quoted string.'); - assert.equal(parts.memberof, '', 'There should be no memberof, as it is global.'); - assert.equal(parts.scope, '', 'The scope should be as global.'); -}); - -test('The jsdoc/name module exports an applyNamespace function.', function() { - assert.equal(typeof jsdoc.name.applyNamespace, 'function'); -}); - -test('The module:jsdoc/name.applyNamespace function inserts the namespace in the correct place.', function() { - var startName = 'lib.Panel#open', - endName = jsdoc.name.applyNamespace(startName, 'event'); - - assert.equal(endName, 'lib.Panel#event:open', 'The namespace should be inserted only before the name part of the longname.'); - - startName = 'maths/bigint'; - endName = jsdoc.name.applyNamespace(startName, 'module'); - - assert.equal(endName, 'module:maths/bigint', 'The namespace should be inserted before a global name.'); -}); - -test('The module:jsdoc/name.applyNamespace function treats quoted parts of the name as atomic.', function() { - var startName = 'foo."*dont\'t.look~in#here!"', - endName = jsdoc.name.applyNamespace(startName, 'event'); - - assert.equal(endName, 'foo.event:"*dont\'t.look~in#here!"', 'The namespace should be inserted before a quoted shortname.'); -}); - -test('The module:jsdoc/name.applyNamespace function will not add another namespace if one already exists.', function() { - var startName = 'lib.Panel#event:open', - endName = jsdoc.name.applyNamespace(startName, 'event'); - - assert.equal(endName, 'lib.Panel#event:open', 'The namespace should not be inserted twice.'); -}); - -test('The module:jsdoc/name.shorten function finds the variation.', function() { - var startName = 'anim.fadein(2)', - parts = jsdoc.name.shorten(startName); - - assert.equal(parts.variation, '2'); - assert.equal(parts.name, 'fadein'); - assert.equal(parts.longname, 'anim.fadein(2)'); -}); diff --git a/test/t/jsdoc/opts/parser.js b/test/t/jsdoc/opts/parser.js deleted file mode 100644 index fdfe67bbf..000000000 --- a/test/t/jsdoc/opts/parser.js +++ /dev/null @@ -1,37 +0,0 @@ -var opts = require('jsdoc/opts/parser'); - -test('The opts module is defined.', function() { - assert.notEqual(typeof opts, 'undefined', 'The opts module should not be undefined.'); - assert.equal(typeof opts, 'object', 'The opts module should be an object.'); -}); - -test('The opts module exports a "parse" function.', function() { - assert.notEqual(typeof opts.parse, 'undefined', 'The opts.parse method should not be undefined.'); - assert.equal(typeof opts.parse, 'function', 'The opts.parse method should be a function.'); -}); - -test('The opts module exports a "get" function.', function() { - assert.notEqual(typeof opts.get, 'undefined', 'The opts.get method should not be undefined.'); - assert.equal(typeof opts.get, 'function', 'The opts.get method should be a function.'); -}); - -test('The opts.parse function accepts a -t opt.', function() { - opts.parse(['-t', 'mytemplate']); - var r = opts.get(); - - assert.equal(r.template, 'mytemplate', 'The opts.get method should return an object with a set template property.'); -}); - -test('The opts.parse function accepts a -d opt.', function() { - opts.parse(['-d', 'mydestination']); - var r = opts.get(); - - assert.equal(r.destination, 'mydestination', 'The opts.get method should return an object with a set destination property.'); -}); - -test('The opts.parse function accepts a naked opt.', function() { - opts.parse(['myfile1', 'myfile2']); - var r = opts.get(); - - assert.deepEqual(r._, ['myfile1', 'myfile2'], 'The opts.get method should return an object with a set _ property.'); -}); \ No newline at end of file diff --git a/test/t/jsdoc/src/handlers.js b/test/t/jsdoc/src/handlers.js deleted file mode 100644 index 7121d6057..000000000 --- a/test/t/jsdoc/src/handlers.js +++ /dev/null @@ -1,16 +0,0 @@ -(function() { - - var jsdoc = {src: { parser: require('jsdoc/src/parser')}}, - testParser = new jsdoc.src.parser.Parser(); - - require('jsdoc/src/handlers').attachTo(testParser); - - test('A jsdoc comment with a @name and no code is turned into a doclet object.', function() { - var sourceCode = 'javascript:/** @name bar */', - result = testParser.parse(sourceCode); - - assert.equal(result.length, 1); - assert.equal(result[0].name, 'bar'); - }); - -})(); \ No newline at end of file diff --git a/test/t/jsdoc/src/parser.js b/test/t/jsdoc/src/parser.js deleted file mode 100644 index 7efa240ce..000000000 --- a/test/t/jsdoc/src/parser.js +++ /dev/null @@ -1,52 +0,0 @@ -(function() { - - var jsdoc = {src: { parser: require('jsdoc/src/parser')}}; - - test('There is a jsdoc/src/parser module.', function() { - assert.equal(typeof jsdoc.src.parser, 'object'); - }); - - test('The jsdoc/src/parser module has a Parser constructor.', function() { - assert.equal(typeof jsdoc.src.parser.Parser, 'function'); - }); - - test('The module:jsdoc/src/parser.Parser class has a parse function.', function() { - assert.equal(typeof jsdoc.src.parser.Parser.prototype.parse, 'function'); - }); - - test('The jsdoc/src/parser module has a results function.', function() { - assert.equal(typeof jsdoc.src.parser.Parser.prototype.results, 'function'); - }); - - test('The module:jsdoc/src/parser.Parser#parse function fires jsdocCommentFound events when parsing source code containing a jsdoc comment.', function() { - var sourceCode = 'javascript:/** @name bar */', - jsdocCounter = 0, - event = null, - parser = new jsdoc.src.parser.Parser(); - - parser - .on('jsdocCommentFound', function(e) { - jsdocCounter++; - event = e; - }) - .parse(sourceCode); - - assert.equal(jsdocCounter, 1); - - assert.equal(event.comment, '/** @name bar */', 'The full text of the comment is included in the event.'); - }); - - test('The module:jsdoc/src/parser.Parser#parse function fires symbolFound events when parsing source code containing a named symbol.', function() { - var sourceCode = 'javascript:var foo = 1;', - symbolCounter = 0, - parser = new jsdoc.src.parser.Parser(); - - parser - .on('symbolFound', function(e) { - symbolCounter++; - }) - .parse(sourceCode); - - assert.equal(symbolCounter, 1); - }); -})(); \ No newline at end of file