diff --git a/data/scripts/jsdoc/Jake/lib/mustache.js b/data/scripts/jsdoc/Jake/lib/mustache.js new file mode 100644 index 00000000..60687a38 --- /dev/null +++ b/data/scripts/jsdoc/Jake/lib/mustache.js @@ -0,0 +1,335 @@ +/* + mustache.js — Logic-less templates in JavaScript + + See http://mustache.github.com/ for more info. +*/ + +var Mustache = function() { + var Renderer = function() {}; + + Renderer.prototype = { + otag: "{{", + ctag: "}}", + pragmas: {}, + buffer: [], + pragmas_implemented: { + "IMPLICIT-ITERATOR": true, + "ARRAY-ORDINALS": true // define #first? and #last? when looping arrays + }, + context: {}, + + render: function(template, context, partials, in_recursion) { + // reset buffer & set context + if(!in_recursion) { + this.context = context; + this.buffer = []; // TODO: make this non-lazy + } + + // fail fast + if(!this.includes("", template)) { + if(in_recursion) { + return template; + } else { + this.send(template); + return; + } + } + + template = this.render_pragmas(template); + var html = this.render_section(template, context, partials); + if(in_recursion) { + return this.render_tags(html, context, partials, in_recursion); + } + + this.render_tags(html, context, partials, in_recursion); + }, + + /* + Sends parsed lines + */ + send: function(line) { + if(line != "") { + this.buffer.push(line); + } + }, + + /* + Looks for %PRAGMAS + */ + render_pragmas: function(template) { + // no pragmas + if(!this.includes("%", template)) { + return template; + } + + var that = this; + var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + + this.ctag); + return template.replace(regex, function(match, pragma, options) { + if(!that.pragmas_implemented[pragma]) { + throw({message: + "This implementation of mustache doesn't understand the '" + + pragma + "' pragma"}); + } + that.pragmas[pragma] = {}; + if(options) { + var opts = options.split("="); + that.pragmas[pragma][opts[0]] = opts[1]; + } + return ""; + // ignore unknown pragmas silently + }); + }, + + /* + Tries to find a partial in the curent scope and render it + */ + render_partial: function(name, context, partials) { + name = this.trim(name); + if(!partials || partials[name] === undefined) { + throw({message: "unknown_partial '" + name + "'"}); + } + if(typeof(context[name]) != "object") { + return this.render(partials[name], context, partials, true); + } + return this.render(partials[name], context[name], partials, true); + }, + + /* + Renders inverted (^) and normal (#) sections + */ + render_section: function(template, context, partials) { + if(!this.includes("#", template) && !this.includes("^", template)) { + return template; + } + + var that = this; + // CSW - Added "+?" so it finds the tighest bound, not the widest + var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag + + "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag + + "\\s*", "mg"); + + // for each {{#foo}}{{/foo}} section do... + return template.replace(regex, function(match, type, name, content) { + var value = that.find(name, context); + if(type == "^") { // inverted section + if(!value || that.is_array(value) && value.length === 0) { + // false or empty list, render it + return that.render(content, context, partials, true); + } else { + return ""; + } + } else if(type == "#") { // normal section + if(that.is_array(value)) { // Enumerable, Let's loop! + var len = value.length; + return value.map(function(row, i) { + return that.render(content, that.create_context(row, {first: i === 0, last: i === len-1}), + partials, true); + }).join(""); + } else if(that.is_object(value)) { // Object, Use it as subcontext! + return that.render(content, that.create_context(value), + partials, true); + } else if(typeof value === "function") { + // higher order section + return value.call(context, content, function(text) { + return that.render(text, context, partials, true); + }); + } else if(value) { // boolean section + return that.render(content, context, partials, true); + } else { + return ""; + } + } + }); + }, + + /* + Replace {{foo}} and friends with values from our view + */ + render_tags: function(template, context, partials, in_recursion) { + // tit for tat + var that = this; + + var new_regex = function() { + return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" + + that.ctag + "+", "g"); + }; + + var regex = new_regex(); + var tag_replace_callback = function(match, operator, name) { + switch(operator) { + case "!": // ignore comments + return ""; + case "=": // set new delimiters, rebuild the replace regexp + that.set_delimiters(name); + regex = new_regex(); + return ""; + case ">": // render partial + return that.render_partial(name, context, partials); + case "{": // the triple mustache is unescaped + return that.find(name, context); + default: // escape the value + return that.escape(that.find(name, context)); + } + }; + var lines = template.split("\n"); + for(var i = 0; i < lines.length; i++) { + lines[i] = lines[i].replace(regex, tag_replace_callback, this); + if(!in_recursion) { + this.send(lines[i]); + } + } + + if(in_recursion) { + return lines.join("\n"); + } + }, + + set_delimiters: function(delimiters) { + var dels = delimiters.split(" "); + this.otag = this.escape_regex(dels[0]); + this.ctag = this.escape_regex(dels[1]); + }, + + escape_regex: function(text) { + // thank you Simon Willison + if(!arguments.callee.sRE) { + var specials = [ + '/', '.', '*', '+', '?', '|', + '(', ')', '[', ']', '{', '}', '\\' + ]; + arguments.callee.sRE = new RegExp( + '(\\' + specials.join('|\\') + ')', 'g' + ); + } + return text.replace(arguments.callee.sRE, '\\$1'); + }, + + /* + find `name` in current `context`. That is find me a value + from the view object + */ + find: function(name, context) { + name = this.trim(name); + + // Checks whether a value is thruthy or false or 0 + function is_kinda_truthy(bool) { + return bool === false || bool === 0 || bool; + } + + var value; + if(is_kinda_truthy(context[name])) { + value = context[name]; + } else if(is_kinda_truthy(this.context[name])) { + value = this.context[name]; + } + + if(typeof value === "function") { + return value.apply(context); + } + if(value !== undefined) { + return value; + } + // silently ignore unkown variables + return ""; + }, + + // Utility methods + + /* includes tag */ + includes: function(needle, haystack) { + return haystack.indexOf(this.otag + needle) != -1; + }, + + /* + Does away with nasty characters + */ + escape: function(s) { + s = String(s === null ? "" : s); + return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) { + switch(s) { + case "&": return "&"; + case "\\": return "\\\\"; + case '"': return '"'; + case "'": return '''; + case "<": return "<"; + case ">": return ">"; + default: return s; + } + }); + }, + + // by @langalex, support for arrays of strings + create_context: function(_context, opts) { + if(this.is_object(_context)) { + if (this.pragmas["ARRAY-ORDINALS"] && opts) { + _context['first?'] = opts.first || false; + _context['last?'] = opts.last || false; + } + return _context; + } else { + var iterator = "."; + if(this.pragmas["IMPLICIT-ITERATOR"]) { + iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; + } + var ctx = {}; + ctx[iterator] = _context; + if (this.pragmas["ARRAY-ORDINALS"] && opts){ + ctx['first?'] = opts.first || false; + ctx['last?'] = opts.last || false; + } + return ctx; + } + }, + + is_object: function(a) { + return a && typeof a == "object"; + }, + + is_array: function(a) { + return Object.prototype.toString.call(a) === '[object Array]'; + }, + + /* + Gets rid of leading and trailing whitespace + */ + trim: function(s) { + return s.replace(/^\s*|\s*$/g, ""); + }, + + /* + Why, why, why? Because IE. Cry, cry cry. + */ + map: function(array, fn) { + if (typeof array.map == "function") { + return array.map(fn); + } else { + var r = []; + var l = array.length; + for(var i = 0; i < l; i++) { + r.push(fn(array[i])); + } + return r; + } + } + }; + + return({ + name: "mustache.js", + version: "0.3.1-dev", + + /* + Turns a template and view into HTML + */ + to_html: function(template, view, partials, send_fun) { + var renderer = new Renderer(); + if(send_fun) { + renderer.send = send_fun; + } + renderer.render(template, view, partials); + if(!send_fun) { + return renderer.buffer.join("\n"); + } + } + }); +}(); \ No newline at end of file diff --git a/data/scripts/jsdoc/Jake/templates/package.json.tmpl b/data/scripts/jsdoc/Jake/templates/package.json.tmpl new file mode 100644 index 00000000..d5159473 --- /dev/null +++ b/data/scripts/jsdoc/Jake/templates/package.json.tmpl @@ -0,0 +1,46 @@ +{ + "name": "{{appname}}", + "version": "{{appversion}}", + "revision": "{{timestamp}}", + "description": "An API 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": "https://github.com/jsdoc3/jsdoc" + } + ], + "dependencies": { + "async": "0.1.22", + "catharsis": "0.5.6", + "crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505", + "js2xmlparser": "0.1.0", + "jshint": "0.9.1", + "markdown": "git+https://github.com/jsdoc3/markdown-js.git", + "marked": "0.2.8", + "taffydb": "git+https://github.com/hegemonic/taffydb.git", + "underscore": "1.4.2", + "wrench": "1.3.9" + }, + "bin": "./nodejs/bin/jsdoc", + "bugs": "https://github.com/jsdoc3/jsdoc/issues", + "author": { + "name": "Michael Mathews", + "email": "micmath@gmail.com" + }, + "contributors": [ + { + "url": "https://github.com/jsdoc3/jsdoc/graphs/contributors" + } + ], + "maintainers": { + "name": "Jeff Williams", + "email": "jeffrey.l.williams@gmail.com" + } +} diff --git a/data/scripts/jsdoc/Jakefile.js b/data/scripts/jsdoc/Jakefile.js new file mode 100644 index 00000000..751dd54e --- /dev/null +++ b/data/scripts/jsdoc/Jakefile.js @@ -0,0 +1,82 @@ +/*global desc: true, fail: true, Mustache: true, task: true */ +// see: https://github.com/mde/jake + +desc('Updating package.json revision.'); +task('default', [], function(params) { + /*jshint evil: true */ + var fs = require('fs'); + + // import the Mustache template tool + eval(fs.readFileSync('Jake/lib/mustache.js', 'utf8')); + + var templates = { + packagejson : fs.readFileSync('Jake/templates/package.json.tmpl', 'utf8') + }; + + var metadata = { + appname : 'jsdoc', + appversion : '3.2.0-dev', + timestamp : '' + new Date().getTime() + }; + + var outdir = './'; + + var rendered = Mustache.to_html(templates.packagejson, metadata); + + fs.writeFileSync(outdir + 'package.json', rendered, 'utf8'); + + process.exit(0); + +}); + +desc('Installs a plugin/template.'); +task('install', [], function(loc) { + var fs = require('fs'), + util = require('util'), + path = require('path'), + wrench = require('wrench'); + + if(!loc) { + fail("You must specify the location of the plugin/template."); + } + + if(!fs.existsSync(loc)) { + fail("plugin/template location [" + loc + "] is not valid."); + } + + var pluginLoc = path.join(loc, "plugins"), + templateLoc = path.join(loc, "templates"), + jsdocLoc = process.cwd(), + name, + config; + + //First the plugin + if(fs.existsSync(pluginLoc)) { + //copy it over + wrench.copyDirSyncRecursive(pluginLoc, path.join(jsdocLoc, "plugins"), { + preserve : true + }); + //find out what it's called + name = fs.readdirSync(pluginLoc)[0].replace(".js", ""); + //And finally edit the conf.json + try { + config = JSON.parse(fs.readFileSync(path.join(jsdocLoc, 'conf.json'), 'utf8')); + if(config.plugins.indexOf('plugins/' + name) == -1) { + config.plugins.push('plugins/' + name); + fs.writeFileSync(path.join(jsdocLoc, 'conf.json'), JSON.stringify(config, null, " "), 'utf8'); + } + } catch (e) { + fail("Could not edit the conf.json file: " + e); + } + } + + //Then the template + if(fs.existsSync(templateLoc)) { + wrench.copyDirSyncRecursive(templateLoc, path.join(jsdocLoc, "templates"), { + preserve : true + }); + } + + process.exit(0); + +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/LICENSE.md b/data/scripts/jsdoc/LICENSE.md new file mode 100644 index 00000000..e06fb138 --- /dev/null +++ b/data/scripts/jsdoc/LICENSE.md @@ -0,0 +1,344 @@ +# License # + +JSDoc 3 is free software, licensed under the Apache License, Version 2.0 (the +"License"). Commercial and non-commercial use are permitted in compliance with +the License. + +Copyright (c) 2011-2012 Michael Mathews +All rights reserved. + +You may obtain a copy of the License at: +http://www.apache.org/licenses/LICENSE-2.0 + +In addition, a copy of the License is included with this distribution. + +As stated in Section 7, "Disclaimer of Warranty," of the License: + +> Licensor provides the Work (and each Contributor provides its Contributions) +> on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +> express or implied, including, without limitation, any warranties or +> conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +> PARTICULAR PURPOSE. You are solely responsible for determining the +> appropriateness of using or redistributing the Work and assume any risks +> associated with Your exercise of mpermissions under this License. + +The source code for JSDoc 3 is available at: +https://github.com/jsdoc3/jsdoc + + +# Third-Party Software # + +JSDoc 3 includes or depends upon the following third-party software, either in +whole or in part. Each third-party software package is provided under its own +license. + +## MIT License ## + +Several of the following software packages are distributed under the MIT +license, which is reproduced below: + +> 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. + + +## Async.js ## + +Async.js is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2010 Caolan McMahon. + +The source code for Async.js is available at: +https://github.com/caolan/async + + +## Catharsis ## + +Catharsis is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2012-2013 Jeff Williams. + +The source code for Catharsis is available at: +https://github.com/hegemonic/catharsis + + +## crypto-browserify ## + +License information for crypto-browserify is not available. It is assumed that +the package is distributed under the MIT license or a similar open source +license. + +The source code for crypto-browserify is available at: +https://github.com/dominictarr/crypto-browserify + + +## github-flavored-markdown ## + +github-flavored-markdown is distributed under the BSD 3-clause license: + +> Copyright (c) 2007, John Fraser All rights +> reserved. +> +> Original Markdown copyright (c) 2004, John Gruber +> All rights reserved. +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted provided that the following conditions are met: +> +> - Redistributions of source code must retain the above copyright notice, +> this list of conditions and the following disclaimer. +> +> - Redistributions in binary form must reproduce the above copyright notice, +> this list of conditions and the following disclaimer in the documentation +> and/or other materials provided with the distribution. + +> - Neither the name "Markdown" nor the names of its contributors may be used +> to endorse or promote products derived from this software without specific +> prior written permission. +> +> This software is provided by the copyright holders and contributors "as is" +> and any express or implied warranties, including, but not limited to, the +> implied warranties of merchantability and fitness for a particular purpose are +> disclaimed. In no event shall the copyright owner or contributors be liable +> for any direct, indirect, incidental, special, exemplary, or consequential +> damages (including, but not limited to, procurement of substitute goods or +> services; loss of use, data, or profits; or business interruption) however +> caused and on any theory of liability, whether in contract, strict liability, +> or tort (including negligence or otherwise) arising in any way out of the use +> of this software, even if advised of the possibility of such damage. + +The source code for github-flavored-markdown is available at: +https://github.com/hegemonic/github-flavored-markdown + + +## Google Code Prettify ## + +Google Code Prettify is distributed under the Apache License 2.0, which is +included with this package. + +Copyright (c) 2006 Google Inc. + +The source code for Google Code Prettify is available at: +https://code.google.com/p/google-code-prettify/ + + +## Jasmine ## + +Jasmine is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2008-2011 Pivotal Labs. + +The source code for Jasmine is available at: +https://github.com/pivotal/jasmine + + +## jasmine-node ## + +jasmine-node is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2010 Adam Abrons and Misko Hevery (http://getangular.com). + +The source code for jasmine-node is available at: +https://github.com/mhevery/jasmine-node + + +## js2xmlparser ## + +js2xmlparser is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2012 Michael Kourlas. + +The source code for js2xmlparser is available at: +https://github.com/michaelkourlas/node-js2xmlparser + + +## JSHint ## + +JSHint is distributed under the MIT license, which is reproduced above. + +Portions of JSHint are derived from JSLint, which is distributed under a +modified MIT license: + +> Copyright (c) 2002 Douglas Crockford (www.JSLint.com) +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> The Software shall be used for Good, not Evil. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +The source code for JSHint is available at: +https://github.com/jshint/jshint + + +## markdown-js ## + +markdown-js is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2009-2010 Dominic Baggott. Copyright (c) 2009-2010 Ash Berlin. +Copyright (c) 2011 Christoph Dorn +(http://www.christophdorn.com). + +The source code for markdown-js is available at: +https://github.com/evilstreak/markdown-js + + +## Node.js ## + +Portions of the Node.js source code are incorporated into the following files: + +- `rhino/fs.js` +- `rhino/path.js` +- `rhino/querystring.js` +- `rhino/util.js` + +Node.js is distributed under the MIT license, which is reproduced above. + +Copyright Joyent, Inc. and other Node contributors. All rights reserved. + +The source code for Node.js is available at: +https://github.com/joyent/node + + +## node-browserify ## + +Portions of the node-browserify source code are incorporated into the following +files: + +- `rhino/events.js` + +node-browserify is distributed under the MIT license, which is reproduced above. + +The source code for node-browserify is available at: +https://github.com/substack/node-browserify + + +## TaffyDB ## + +TaffyDB is distributed under a modified BSD license: + +> All rights reserved. +> +> 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. + +The source code for TaffyDB is available at: +https://github.com/hegemonic/taffydb + + +## Tomorrow Theme for Google Code Prettify ## + +License information for the Tomorrow Theme for Google Code Prettify is not +available. It is assumed that the package is distributed under an open source +license that is compatible with the Apache License 2.0. + +Copyright (c) Yoshihide Jimbo. + +The source code for the Tomorrow Theme is available at: +https://github.com/jmblog/color-themes-for-google-code-prettify + + +## Rhino ## + +Rhino is distributed under the following licenses: + +### MPL/GPL License ### +The majority of the source code for Rhino is available under a MPL 1.1/GPL 2.0 +license. JSDoc 3 uses the source code under the MPL 1.1 license, which is +included in this distribution. + +### License for portions of the Rhino debugger ### +Additionally, some files are available under the BSD 3-clause license: + +> Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved. +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted provided that the following conditions are met: +> +> - Redistributions of source code must retain the above copyright notice, +> this list of conditions and the following disclaimer. +> - Redistributions in binary form must reproduce the above copyright +> notice, this list of conditions and the following disclaimer in the +> documentation and/or other materials provided with the distribution. +> - Neither the name of Sun Microsystems nor the names of its contributors +> may be used to endorse or promote products derived from this software +> without specific prior written permission. +> +> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +> AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +### Source Code ### +The source code for Rhino is available at: +https://github.com/hegemonic/rhino + + +## Underscore.js ## + +Underscore.js is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2009-2012 Jeremy Ashkenas, DocumentCloud. + +The source code for Underscore.js is available at: +https://github.com/documentcloud/underscore + + +## wrench-js ## + +wrench-js is distributed under the MIT license, which is reproduced above. + +Copyright (c) 2010 Ryan McGrath. + +The source code for wrench-js is available at: +https://github.com/ryanmcgrath/wrench-js diff --git a/data/scripts/jsdoc/README.md b/data/scripts/jsdoc/README.md new file mode 100644 index 00000000..c4a05467 --- /dev/null +++ b/data/scripts/jsdoc/README.md @@ -0,0 +1,116 @@ +JSDoc 3 +======= +[![Build Status](https://secure.travis-ci.org/jsdoc3/jsdoc.png?branch=master)](http://travis-ci.org/jsdoc3/jsdoc) + +An inline API documentation processor for JavaScript. JSDoc 3 is intended to be +an upgrade to JsDoc Toolkit (JSDoc 2). + +Want to contribute to JSDoc? Please read `CONTRIBUTING.md`. + + +Installation +------------ + +Use git to clone the [official JSDoc repository](https://github.com/jsdoc3/jsdoc): + + git clone git@github.com:jsdoc3/jsdoc.git + +Alternatively, you can download a .zip file for the +[latest development version](https://github.com/jsdoc3/jsdoc/archive/master.zip) +or a [previous release](https://github.com/jsdoc3/jsdoc/tags). + +You can also install JSDoc within a Node.js project's `node_modules` directory +using npm. To install the latest development version, change directories to your +Node.js project, then run the following command: + + npm install git://github.com/jsdoc3/jsdoc.git + +Or to install JSDoc globally: + + npm install -g git://github.com/jsdoc3/jsdoc.git + +**Note**: Although you can install JSDoc with npm, JSDoc does not currently run +on Node.js. + + +Usage +----- + +This example assumes that your working directory is the JSDoc application base +directory: + + ./jsdoc yourSourceCodeFile.js + +For information about the supported command-line options, use the `--help` +option. + + ./jsdoc --help + +Generated documentation will appear in the folder specified by the +`--destination` option, or in a folder named "out" by default. + + +Dependencies +------------ + +JSDoc 3 uses 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 Mozilla Rhino that are only available in or +after version 1.7R3. In addition, JSDoc 3 requires several customizations to the +standard Rhino distribution. The customized version of Rhino is included with +JSDoc. + +In rare cases, users may have their Java CLASSPATH configured to override the +included Rhino and point to an older version of Rhino instead. If this is the +case, simply correct the CLASSPATH to remove the older Rhino. (On OS X, you may +need to remove the file `~/Library/Java/Extensions/js.jar`.) + +The version of Rhino distributed with JSDoc 3 can be found here: +https://github.com/hegemonic/rhino + + +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, run +the following command on Windows: + + jsdoc --debug + +Or on OS X, Linux, and other POSIX-compliant systems: + + ./jsdoc --debug + +If you can't get the short-form commands to work, try invoking Java directly: + + java -cp lib/js.jar org.mozilla.javascript.tools.debugger.Main \ + -debug -modules node_modules -modules rhino -modules lib -modules . \ + jsdoc.js your/script.js + +Note: `--debug` must be the first argument to the short-form command. + +This will open a debugging window. Click Debug > Break on Exceptions, then click +Run. If there is an error, you should see exactly where it is in the source +code. + + +See Also +-------- + +Project Documentation: (under development) +Project Documentation Source: +JSDoc User's Group: +JSDoc 3 Ant Task: +Project Announcements: + + +License +------- + +JSDoc 3 is copyright (c) 2011-2012 Michael Mathews . + +JSDoc 3 is free software, licensed under the Apache License, Version 2.0. See +the file `LICENSE.md` in this distribution for more details. diff --git a/data/scripts/jsdoc/apache-license-2.0.txt b/data/scripts/jsdoc/apache-license-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/data/scripts/jsdoc/apache-license-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/data/scripts/jsdoc/conf.json b/data/scripts/jsdoc/conf.json new file mode 100644 index 00000000..12bff5bd --- /dev/null +++ b/data/scripts/jsdoc/conf.json @@ -0,0 +1,17 @@ +{ + "tags": { + "allowUnknownTags": true + }, + "source": { + "includePattern": ".+\\.js(doc)?$", + "excludePattern": "(^|\\/|\\\\)_" + }, + "plugins": [], + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "default": { + "outputSourceFiles": true + } + } +} \ No newline at end of file diff --git a/data/scripts/jsdoc/docs/app.html b/data/scripts/jsdoc/docs/app.html new file mode 100644 index 00000000..9e6e9c81 --- /dev/null +++ b/data/scripts/jsdoc/docs/app.html @@ -0,0 +1,122 @@ + + + + + JSDoc: Namespace: app + + + + + + + + + + +
+ +

Namespace: app

+ + + + + +
+ +
+

+ app +

+ +
+ +
+
+ + + + +
Data that must be shared across the entire application.
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:54 GMT+0300 (EEST) +
+ + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/docs/bookAppointment.html b/data/scripts/jsdoc/docs/bookAppointment.html new file mode 100644 index 00000000..172edfff --- /dev/null +++ b/data/scripts/jsdoc/docs/bookAppointment.html @@ -0,0 +1,720 @@ + + + + + JSDoc: Class: bookAppointment + + + + + + + + + + +
+ +

Class: bookAppointment

+ + + + + +
+ +
+

+ bookAppointment +

+ +
Implelements the js part of the appointment booking page.
+ +
+ +
+
+ + + + +
+

new bookAppointment()

+ + +
+
+ + +
+ This class implements the book appointment page functionality. +Once the initialize() method is called the page is fully functional +and can serve the appointment booking process. +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + +

Methods

+ +
+ +
+

<static> bindEventHandlers()

+ + +
+
+ + +
+ This method binds the necessary event handlers +for the book appointments page. +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+

<static> calcEndDatetime() → {string}

+ + +
+
+ + +
+ This method calculates the end datetime of the current appointment. +End datetime is depending on the service and start datetime fieldss. +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+ Returns the end datetime in string format. +
+ + + +
+
+ Type +
+
+ +string + + +
+
+ + + + +
+ + + +
+

<static> getAvailableHours(selDate)

+ + +
+
+ + +
+ This function makes an ajax call and returns the available +hours for the selected service, provider and date. +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
selDate + + +string + + + + The selected date of which the available +hours we need to receive.
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+

<static> initialize(bindEventHandlers)

+ + +
+
+ + +
+ This method initializes the book appointment page. +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
bindEventHandlers + + +bool + + + + (OPTIONAL) Determines wether +the default event handlers will be binded to the dom elements.
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+

<static> updateConfirmData()

+ + +
+
+ + +
+ Every time this function is executed, it updates the confirmation +page with the latest customer settigns and input for the appointment +booking. +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+

<static> validateCustomerDataForm() → {bool}

+ + +
+
+ + +
+ This function validates the customer's data input. +It only checks for empty fields by the time. +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + +
Returns:
+ + +
+ Returns the validation result. +
+ + + +
+
+ Type +
+
+ +bool + + +
+
+ + + + +
+ +
+ + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:24:34 GMT+0300 (EEST) +
+ + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/docs/book_appointment.js.html b/data/scripts/jsdoc/docs/book_appointment.js.html new file mode 100644 index 00000000..8965b745 --- /dev/null +++ b/data/scripts/jsdoc/docs/book_appointment.js.html @@ -0,0 +1,362 @@ + + + + + JSDoc: Source: book_appointment.js + + + + + + + + + + +
+ +

Source: book_appointment.js

+ + + + + +
+
+
/**
+ * This class implements the book appointment page functionality. 
+ * Once the initialize() method is called the page is fully functional 
+ * and can serve the appointment booking process.
+ * 
+ * @class Implelements the js part of the appointment booking page.
+ */
+var bookAppointment = {
+    /**
+     * This method initializes the book appointment page.
+     * 
+     * @param {bool} bindEventHandlers (OPTIONAL) Determines wether 
+     * the default event handlers will be binded to the dom elements.
+     */
+    initialize : function(bindEventHandlers) {
+        if (bindEventHandlers == undefined) {
+            bindEventHandlers = true; // Default value
+        }
+        
+        // Initialize page's components (tooltips, datepickers etc).
+        $('.book-step').qtip({
+            position: {
+                my: 'top center',
+                at: 'bottom center'
+            },
+            style: {
+                classes: 'qtip-green qtip-shadow custom-qtip'
+            }
+        });
+        
+        $('#select-date').datepicker({
+            dateFormat  : 'dd-mm-yy',
+            minDate     : 0,
+            defaultDate : Date.today(),
+            onSelect    : function(dateText, instance) {
+                bookAppointment.getAvailableHours(dateText);
+                bookAppointment.updateConfirmData();
+            }
+        });
+       
+        // Bind the event handlers (might not be necessary every time
+        // we use this class).
+        if (bindEventHandlers) {
+            bookAppointment.bindEventHandlers();
+        }
+       
+        // Execute other necessary operations on startup.
+        $('#select-service').trigger('change');
+    },
+    
+    /**
+     * This method binds the necessary event handlers 
+     * for the book appointments page.
+     */
+    bindEventHandlers : function() {
+        /**
+         * Event : Selected Provider "Changed"
+         */
+        $('#select-provider').change(function() {
+            bookAppointment.getAvailableHours(Date.today().toString('dd-MM-yyyy'));
+            bookAppointment.updateConfirmData();
+        });
+        
+        /**
+         * Event : Selected Service "Changed"
+         * 
+         * When the user clicks on a service, its available providers should 
+         * become visible. 
+         */
+        $('#select-service').change(function() {
+            var currServiceId = $('#select-service').val();
+            $('#select-provider').empty();
+
+            $.each(GlobalVariables.providers, function(indexProvider, provider) {
+                $.each(provider['services'], function(indexService, serviceId) {
+                    // If the current provider is able to provide the selected 
+                    // service, add him to the listbox. 
+                    if (serviceId == currServiceId) { 
+                        var optionHtml = '<option value="' + provider['id'] + '">' 
+                            + provider['last_name']  + ' ' + provider['first_name'] 
+                            + '</option>';
+                        $('#select-provider').append(optionHtml);
+                    }
+                });
+            });
+
+            bookAppointment.getAvailableHours(Date.today().toString('dd-MM-yyyy'));
+            bookAppointment.updateConfirmData();
+        });
+        
+        /**
+         * Event : Next Step Button "Clicked"
+         * 
+         * This handler is triggered every time the user pressed the 
+         * "next" button on the book wizard. Some special tasks might 
+         * be perfomed, depending the current wizard step.
+         */
+        $('.button-next').click(function() {
+            // If we are on the 3rd tab then we will need to validate the user's 
+            // input before proceeding to the next step.
+            if ($(this).attr('data-step_index') == '3') {
+                if (!bookAppointment.validateCustomerDataForm()) {
+                    return; // Validation failed, do not continue.
+                } else {
+                    bookAppointment.updateConfirmData();
+                }
+            }
+            
+            // Display the next step tab (uses jquery animation effect).
+            var nextTabIndex = parseInt($(this).attr('data-step_index')) + 1;
+
+            $(this).parents().eq(1).hide('fade', function() {    
+                $('.active-step').removeClass('active-step');
+                $('#step-' + nextTabIndex).addClass('active-step');
+                $('#book-appointment-' + nextTabIndex).show('fade');
+            });
+        });
+
+        /**
+         * Event : Back Step Button "Clicked"
+         * 
+         * This handler is triggered every time the user pressed the 
+         * "back" button on the book wizard.
+         */
+        $('.button-back').click(function() {
+            var prevTabIndex = parseInt($(this).attr('data-step_index')) - 1;
+
+            $(this).parents().eq(1).hide('fade', function() {    
+                $('.active-step').removeClass('active-step');
+                $('#step-' + prevTabIndex).addClass('active-step');
+                $('#book-appointment-' + prevTabIndex).show('fade');
+            });
+        });
+
+        /**
+         * Event : Available Hour "Click"
+         * 
+         * Triggered whenever the user clicks on an available hour
+         * for his appointment.
+         */
+        $('#available-hours').on('click', '.available-hour', function() {
+            $('.selected-hour').removeClass('selected-hour');
+            $(this).addClass('selected-hour');
+            bookAppointment.updateConfirmData();
+        });
+    },
+    
+    /**
+     * This function makes an ajax call and returns the available 
+     * hours for the selected service, provider and date.
+     * 
+     * @param {string} selDate The selected date of which the available
+     * hours we need to receive.
+     */
+    getAvailableHours : function(selDate) {
+        // Find the selected service duration (it is going to 
+        // be send within the "postData" object.
+        var selServiceDuration = 15; // Default value of duration (in minutes).
+        $.each(GlobalVariables.services, function(index, service) {
+            if (service['id'] == $('#select-service').val()) {
+                selServiceDuration = service['duration']; 
+            }
+        });
+
+        var postData = {
+            'service_id'         : $('#select-service').val(),
+            'provider_id'        : $('#select-provider').val(),
+            'selected_date'      : selDate,
+            'service_duration'   : selServiceDuration
+        };
+
+        // Make ajax post request and get the available hours.
+        var ajaxurl = GlobalVariables.baseUrl + 'appointments/ajax_get_available_hours';
+        jQuery.post(ajaxurl, postData, function(postResponse) {
+            ////////////////////////////////////////////////////////////////////////////////
+            console.log('\n\n Get Available Hours Post Response :', postResponse, '\n\n');
+            ////////////////////////////////////////////////////////////////////////////////
+
+            try {
+                var jsonResponse = jQuery.parseJSON(postResponse);
+                ////////////////////////////////////////////////////////////////////////////////
+                //console.log('\n\n Get Available Hours JSON Response :', jsonResponse, '\n\n');
+                ////////////////////////////////////////////////////////////////////////////////
+
+                // Fill the available time div
+                var currColumn = 1;
+                $('#available-hours').html('<div style="width:50px; float:left;"></div>');
+                $.each(jsonResponse, function(index, availableHour) {
+                    if ((currColumn * 10) < (index + 1)) {
+                        currColumn++;
+                        $('#available-hours').append('<div style="width:50px; float:left;"></div>');
+                    }
+
+                    $('#available-hours div:eq(' + (currColumn - 1) + ')')
+                        .append('<span class="available-hour">' + availableHour + '</span><br/>');
+                });
+
+                // Set the first item as selected.
+                $('.available-hour:eq(0)').addClass('selected-hour');
+                bookAppointment.updateConfirmData();
+            } catch(exception) {
+                GeneralFunctions.displayMessageBox('Unexpected Error', 'An unexpected error occured ' 
+                    + 'during the available hours calculation. Please refresh the page and try again.');
+            }
+        });
+    },
+
+    /**
+     * This function validates the customer's data input.
+     * It only checks for empty fields by the time.
+     * 
+     * @return {bool} Returns the validation result.
+     */
+    validateCustomerDataForm : function() {
+        var validationResult = true;
+        $('.required').css('border', '');
+
+        $('.required').each(function() {
+            if ($(this).val() == '') {
+                validationResult = false; 
+                $(this).css('border', '2px solid red');
+            }
+        });
+
+        return validationResult;
+    },
+
+    /**
+     * Every time this function is executed, it updates the confirmation
+     * page with the latest customer settigns and input for the appointment 
+     * booking.
+     */
+    updateConfirmData : function() {
+        /*** SET APPOINTMENT INFO ***/
+        var selectedDate = $('#select-date').datepicker('getDate');
+        if (selectedDate !== null) {
+            selectedDate = Date.parse(selectedDate).toString('dd-MM-yyyy');
+        }
+
+        $('#appointment-info').html(
+            '<h4>' + $('#select-service option:selected').text() + '</h4>' +
+            $('#select-provider option:selected').text() + '<br/>' + 
+            '<strong class="text-info">' + selectedDate + ' ' 
+                    + $('.selected-hour').text() + '</strong>'
+        );
+
+        /*** SET CUSTOMER'S INFO ***/
+        $('#customer-info').html(
+            '<h4>' + $('#last-name').val() + ' ' + $('#first-name').val() + '</h4>' + 
+            'Phone: ' + $('#phone-number').val() + '<br/>' + 
+            'Email: ' + $('#email').val() + '<br/>' + 
+            'Address: ' + $('#address').val() + '<br/>' + 
+            'City: ' + $('#city').val() + '<br/>' + 
+            'Zip Code: ' + $('#zip-code').val()
+        );
+            
+        /*** UPDATE FORM POST DATA ***/
+        var postData = new Object();
+        
+        postData['customer'] = {
+            'last_name'      : $('#last-name').val(),
+            'first_name'     : $('#first-name').val(),
+            'email'          : $('#email').val(),
+            'phone_number'   : $('#phone-number').val(),
+            'address'        : $('#address').val(),
+            'city'           : $('#city').val(),
+            'zip_code'       : $('#zip-code').val()
+        };
+        
+        postData['appointment'] = {
+            'start_datetime'    : $('#select-date').datepicker('getDate').toString('yyyy-MM-dd') 
+                                        + ' ' + $('.selected-hour').text() + ':00',
+            'end_datetime'      : bookAppointment.calcEndDatetime(),
+            'notes'             : $('#notes').val(),
+            'id_users_provider' : $('#select-provider').val(),
+            'id_services'       : $('#select-service').val()
+        };
+        
+        $('input[name="post_data"]').val(JSON.stringify(postData));
+    },
+    
+    /** 
+     * This method calculates the end datetime of the current appointment. 
+     * End datetime is depending on the service and start datetime fieldss.
+     * 
+     * @return {string} Returns the end datetime in string format.
+     */
+    calcEndDatetime : function() {
+        // Find selected service duration. 
+        var selServiceDuration = undefined;
+        
+        $.each(GlobalVariables.services, function(index, service) {
+            if (service.id == $('#select-service').val()) {
+                selServiceDuration = service.duration;
+                return; // Stop searching ... 
+            }
+        });
+        
+        // Add the duration to the start datetime.
+        var startDatetime = $('#select-date').datepicker('getDate').toString('dd-MM-yyyy') 
+                + ' ' + $('.selected-hour').text();
+        startDatetime = Date.parseExact(startDatetime, 'dd-MM-yyyy HH:mm');
+        var endDatetime = undefined;
+        
+        if (selServiceDuration !== undefined && startDatetime !== null) {
+            endDatetime = startDatetime.add({ 'minutes' : parseInt(selServiceDuration) });
+        } else {
+            endDatetime = new Date();
+        }
+        
+        return endDatetime.toString('yyyy-MM-dd HH:mm:ss');
+    }
+}
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:24:34 GMT+0300 (EEST) +
+ + + + + diff --git a/data/scripts/jsdoc/docs/env.html b/data/scripts/jsdoc/docs/env.html new file mode 100644 index 00000000..ac7feda5 --- /dev/null +++ b/data/scripts/jsdoc/docs/env.html @@ -0,0 +1,441 @@ + + + + + JSDoc: Namespace: env + + + + + + + + + + +
+ +

Namespace: env

+ + + + + +
+ +
+

+ env +

+ +
+ +
+
+ + + + +
Data representing the environment in which this app is running.
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + +

Members

+ +
+ +
+

<static> args :Array

+ + +
+
+ +
+ The command-line arguments passed into JSDoc. +
+ + + +
Type:
+
    +
  • + +Array + + +
  • +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
+ + + +
+

<static> conf :Object

+ + +
+
+ +
+ The parsed JSON data from the configuration file. +
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
+ + + +
+

<static> opts :Object

+ + +
+
+ +
+ The command-line arguments, parsed into a key/value hash. +
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Example
+ +
 if (env.opts.help) { console.log('Helpful message.'); }
+ + +
+ + + +
+

<static> run

+ + +
+
+ +
+ Running start and finish times. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
+ + + +
+

<static> version :Object

+ + +
+
+ +
+ The JSDoc version number and revision date. +
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
+ +
+ + + + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:54 GMT+0300 (EEST) +
+ + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/docs/global.html b/data/scripts/jsdoc/docs/global.html new file mode 100644 index 00000000..d57729c3 --- /dev/null +++ b/data/scripts/jsdoc/docs/global.html @@ -0,0 +1,195 @@ + + + + + JSDoc: Global + + + + + + + + + + +
+ +

Global

+ + + + + +
+ +
+

+ +

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

Methods

+ +
+ +
+

main()

+ + +
+
+ + +
+ Run the jsdoc application. +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
To Do:
+
+
    +
  • Refactor function (and require statements) into smaller functions
  • +
+
+ +
+ + + + + + + + + + + + + +
+ +
+ + + + + +
+ +
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:54 GMT+0300 (EEST) +
+ + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/docs/index.html b/data/scripts/jsdoc/docs/index.html new file mode 100644 index 00000000..08bfd681 --- /dev/null +++ b/data/scripts/jsdoc/docs/index.html @@ -0,0 +1,63 @@ + + + + + JSDoc: Index + + + + + + + + + + +
+ +

Index

+ + + + + + + +

+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:24:34 GMT+0300 (EEST) +
+ + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/docs/jsdoc.js.html b/data/scripts/jsdoc/docs/jsdoc.js.html new file mode 100644 index 00000000..2a819f5c --- /dev/null +++ b/data/scripts/jsdoc/docs/jsdoc.js.html @@ -0,0 +1,358 @@ + + + + + JSDoc: Source: jsdoc.js + + + + + + + + + + +
+ +

Source: jsdoc.js

+ + + + + +
+
+
/*global app: true, args: true, env: true, publish: true */
+/**
+ * @project jsdoc
+ * @author Michael Mathews <micmath@gmail.com>
+ * @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
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
+
+/**
+ * Data representing the environment in which this app is running.
+ *
+ * @namespace
+ * @name env
+ */
+require('lib/jsdoc/util/global').env = {
+    /**
+     * Running start and finish times.
+     * 
+     * @memberof env
+     */
+    run: {
+        start: new Date(),
+        finish: null
+    },
+
+    /**
+     * The command-line arguments passed into JSDoc.
+     *
+     * @type Array
+     * @memberof env
+     */
+    args: [],
+
+    /**
+     * The parsed JSON data from the configuration file.
+     * 
+     * @type Object
+     * @memberof env
+     */
+    conf: {},
+
+    /**
+     * The absolute path to the base directory of the JSDoc application.
+     * 
+     * @private
+     * @deprecated Use `__dirname` instead.
+     * @type string
+     * @memberof env
+     */
+    dirname: '.',
+
+    /**
+     * The command-line arguments, parsed into a key/value hash.
+     * 
+     * @type Object
+     * @memberof env
+     * @example if (env.opts.help) { console.log('Helpful message.'); }
+    */
+    opts: {},
+
+    /**
+     * The JSDoc version number and revision date.
+     * 
+     * @type Object
+     * @memberof env
+     */
+    version: {}
+};
+
+// initialize the environment for the current JavaScript VM
+(function(args) {
+    var vm = require('jsdoc/util/vm').vm;
+    // TODO: may need to move this file to support Node.js
+    require('initialize')[vm](args);
+})( Array.prototype.slice.call(arguments, 0) );
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
+
+/**
+ * Data that must be shared across the entire application.
+ * @namespace
+ * @name app
+ */
+require('lib/jsdoc/util/global').app = {
+    jsdoc: {
+        scanner: new (require('jsdoc/src/scanner').Scanner)(),
+        parser: new (require('jsdoc/src/parser').Parser)(),
+        name: require('jsdoc/name')
+    }
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
+
+/**
+    Try to recursively print out all key/values in an object.
+    @global
+    @private
+    @param {Object} ... Object/s to dump out to console.
+ */
+function dump() {
+    var doop = require('jsdoc/util/doop').doop;
+    var _dump = require('jsdoc/util/dumper').dump;
+    for (var i = 0, l = arguments.length; i < l; i++) {
+        console.log( _dump(doop(arguments[i])) );
+    }
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
+
+/**
+ * Run the jsdoc application.
+ * @todo Refactor function (and require statements) into smaller functions
+ */
+function main() {
+    var _ = require('underscore');
+    var fs = require('jsdoc/fs');
+    var path = require('jsdoc/path');
+    var taffy = require('taffydb').taffy;
+
+    var jsdoc = {
+        augment: require('jsdoc/augment'),
+        borrow: require('jsdoc/borrow'),
+        Config: require('jsdoc/config'),
+        opts: {
+            args: require('jsdoc/opts/args')
+        },
+        'package': require('jsdoc/package'),
+        plugins: require('jsdoc/plugins'),
+        Readme: require('jsdoc/readme'),
+        src: {
+            filter: require('jsdoc/src/filter'),
+            handlers: require('jsdoc/src/handlers')
+        },
+        tutorial: {
+            resolver: require('jsdoc/tutorial/resolver')
+        },
+        util: {
+            include: require('jsdoc/util/include')
+        }
+    };
+
+    var confPath;
+    var defaultOpts;
+    var docs;
+    var filter;
+    var i;
+    var info;
+    var l;
+    var packageDocs;
+    var packageJson;
+    var sourceFiles;
+    var template;
+
+
+    defaultOpts = {
+        destination: './out/',
+        encoding: 'utf8'
+    };
+
+    // get JSDoc version number
+    info = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
+    env.version = {
+        number: info.version,
+        revision: new Date(parseInt(info.revision, 10)).toUTCString()
+    };
+
+    env.opts = jsdoc.opts.args.parse(env.args);
+
+    confPath = env.opts.configure || path.join(__dirname, 'conf.json');
+    if ( !fs.statSync(confPath).isFile() ) {
+        confPath = path.join(__dirname, 'conf.json.EXAMPLE');
+    }
+
+    try {
+        env.conf = new jsdoc.Config( fs.readFileSync(confPath, 'utf8') )
+            .get();
+    }
+    catch (e) {
+        throw new Error('Cannot parse the config file ' + confPath + ': ' + e);
+    }
+
+    // look for options on the command line, in the config file, and in the defaults, in that order
+    env.opts = _.defaults(env.opts, env.conf.opts, defaultOpts);
+
+    if (env.opts.help) {
+        console.log( jsdoc.opts.args.help() );
+        process.exit(0);
+    } else if (env.opts.test) {
+        jsdoc.util.include('test/runner.js');
+        process.exit(0);
+    } else if (env.opts.version) {
+        console.log('JSDoc ' + env.version.number + ' (' + env.version.revision + ')');
+        process.exit(0);
+    }
+
+    if (env.conf.plugins) {
+        jsdoc.plugins.installPlugins(env.conf.plugins, app.jsdoc.parser);
+    }
+    
+    if (env.conf.source && env.conf.source.include) {
+        env.opts._ = (env.opts._ || []).concat(env.conf.source.include);
+    }
+
+    // any source file named package.json or README.md is treated special
+    for (i = 0, l = env.opts._.length; i < l; i++ ) {
+        if (/\bpackage\.json$/i.test(env.opts._[i])) {
+            packageJson = fs.readFileSync( env.opts._[i], 'utf8' );
+            env.opts._.splice(i--, 1);
+        }
+        
+        if (/(\bREADME|\.md)$/i.test(env.opts._[i])) {
+            env.opts.readme = new jsdoc.Readme(env.opts._[i]).html;
+            env.opts._.splice(i--, 1);
+        }
+    }
+    
+    if (env.conf.source && env.opts._.length > 0) { // are there any files to scan and parse?
+        filter = new jsdoc.src.filter.Filter(env.conf.source);
+
+        sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), filter);
+
+        jsdoc.src.handlers.attachTo(app.jsdoc.parser);
+
+        docs = app.jsdoc.parser.parse(sourceFiles, env.opts.encoding);
+
+        //The files are ALWAYS useful for the templates to have
+        //If there is no package.json, just create an empty package
+        packageDocs = new jsdoc.package.Package(packageJson);
+        packageDocs.files = sourceFiles || [];
+        docs.push(packageDocs);
+
+        jsdoc.borrow.indexAll(docs);
+
+        jsdoc.augment.addInherited(docs);
+        jsdoc.borrow.resolveBorrows(docs);
+
+        if (env.opts.explain) {
+            dump(docs);
+            process.exit(0);
+        }
+
+        if (env.opts.tutorials) {
+            jsdoc.tutorial.resolver.load(env.opts.tutorials);
+            jsdoc.tutorial.resolver.resolve();
+        }
+
+        env.opts.template = (function() {
+            var publish = env.opts.template || 'templates/default';
+            // if we don't find it, keep the user-specified value so the error message is useful
+            return path.getResourcePath(publish) || env.opts.template;
+        })();
+
+        try {
+            template = require(env.opts.template + '/publish');
+        }
+        catch(e) {
+            throw new Error('Unable to load template: ' + e.message || e);
+        }
+
+        // templates should include a publish.js file that exports a "publish" function
+        if (template.publish && typeof template.publish === 'function') {
+            // convert this from a URI back to a path if necessary
+            env.opts.template = path._uriToPath(env.opts.template);
+            template.publish(
+                taffy(docs),
+                env.opts,
+                jsdoc.tutorial.resolver.root
+            );
+        }
+        else {
+            // old templates define a global "publish" function, which is deprecated
+            jsdoc.util.include(env.opts.template + '/publish.js');
+            if (publish && typeof publish === 'function') {
+                console.log( env.opts.template + ' uses a global "publish" function, which is ' +
+                    'deprecated and may not be supported in future versions. ' +
+                    'Please update the template to use "exports.publish" instead.' );
+                // convert this from a URI back to a path if necessary
+                env.opts.template = path._uriToPath(env.opts.template);
+                publish(
+                    taffy(docs),
+                    env.opts,
+                    jsdoc.tutorial.resolver.root
+                );
+            }
+            else {
+                throw new Error( env.opts.template + ' does not export a "publish" function.' );
+            }
+        }
+    }
+}
+
+try {
+    main();
+    env.run.finish = new Date();
+    process.exit(0);
+}
+catch(e) {
+    env.run.finish = new Date();
+    if (e.rhinoException != null) {
+        e.rhinoException.printStackTrace();
+        process.exit(1);
+    } else {
+        throw e;
+    }
+}
+
+
+
+ + + + +
+ + + +
+ +
+ Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:54 GMT+0300 (EEST) +
+ + + + + diff --git a/data/scripts/jsdoc/docs/scripts/linenumber.js b/data/scripts/jsdoc/docs/scripts/linenumber.js new file mode 100644 index 00000000..a0c570d5 --- /dev/null +++ b/data/scripts/jsdoc/docs/scripts/linenumber.js @@ -0,0 +1,17 @@ +(function() { + var counter = 0; + var numbered; + var source = document.getElementsByClassName('prettyprint source'); + + if (source && source[0]) { + source = source[0].getElementsByTagName('code')[0]; + + numbered = source.innerHTML.split('\n'); + numbered = numbered.map(function(item) { + counter++; + return '' + item; + }); + + source.innerHTML = numbered.join('\n'); + } +})(); diff --git a/data/scripts/jsdoc/docs/scripts/prettify/Apache-License-2.0.txt b/data/scripts/jsdoc/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/data/scripts/jsdoc/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/data/scripts/jsdoc/docs/scripts/prettify/lang-css.js b/data/scripts/jsdoc/docs/scripts/prettify/lang-css.js new file mode 100644 index 00000000..041e1f59 --- /dev/null +++ b/data/scripts/jsdoc/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/data/scripts/jsdoc/docs/scripts/prettify/prettify.js b/data/scripts/jsdoc/docs/scripts/prettify/prettify.js new file mode 100644 index 00000000..eef5ad7e --- /dev/null +++ b/data/scripts/jsdoc/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p/dev/null || cygpath -w $(pwd) 2>/dev/null || pwd))" +if [ "${BASEPATH%${BASEPATH#?}}" != "/" ] ; then + BASEPATH="$( echo "$BASEPATH" | sed -e 's@\\@/@g' )" + # We need a extra slash for URLs + UBASEPATH="/$BASEPATH" +else + UBASEPATH="$BASEPATH" +fi + +# for whatever reason, Rhino requires module paths to be valid URIs +URLPATH="file://$UBASEPATH" +URLPATH=`echo "$URLPATH" | sed -e 's/ /%20/g'` +ENCODEDBASEPATH=`echo "$BASEPATH" | sed -e 's/ /%20/g'` + +if test "$1" = "--debug" +then + echo "Running Debug" + CMD="org.mozilla.javascript.tools.debugger.Main -debug" + # strip --debug argument + shift +else + CMD="org.mozilla.javascript.tools.shell.Main" +fi + +#Conditionally execute different command lines depending on whether we're running tests or not +if test "$1" = "-T" +then + echo "Running Tests" + cd -P "$(dirname "$SOURCE")" + java -classpath "${BASEPATH}/rhino/js.jar" ${CMD} -opt -1 -modules "${URLPATH}/node_modules" -modules "${URLPATH}/rhino" -modules "${URLPATH}/lib" -modules "${URLPATH}" "${BASEPATH}/jsdoc.js" "$@" --dirname="${BASEPATH}/" + +else + # normal mode should be quiet + java -classpath "${BASEPATH}/rhino/js.jar" ${CMD} -modules "${URLPATH}/node_modules" -modules "${URLPATH}/rhino" -modules "${URLPATH}/lib" -modules "${URLPATH}" "${BASEPATH}/jsdoc.js" "$@" --dirname="${BASEPATH}/" +fi diff --git a/data/scripts/jsdoc/jsdoc.cmd b/data/scripts/jsdoc/jsdoc.cmd new file mode 100644 index 00000000..a2ef8f11 --- /dev/null +++ b/data/scripts/jsdoc/jsdoc.cmd @@ -0,0 +1,45 @@ +@ECHO OFF + +SETLOCAL + +REM jsdoc.js expects Unix-style paths without a trailing slash +SET _BASEPATH=%~dp0 +SET _BASEPATH=%_BASEPATH:\=/% +SET _BASEPATH=%_BASEPATH:~0,-1% + +REM for whatever reason, Rhino requires module paths to be valid URIs +SET _URLPATH=file:/%_BASEPATH% + +IF "%_URLPATH%"=="%_URLPATH: =%" GOTO NO_SPACES +:ESCAPE_SPACE +SET _TRAILING=%_URLPATH:* =% +CALL SET _URLPATH=%%_URLPATH: %_TRAILING%=%% +SET _URLPATH=%_URLPATH%%%20%_TRAILING% +IF NOT "%_URLPATH%"=="%_URLPATH: =%" GOTO ESCAPE_SPACE +:NO_SPACES + +IF [%1]==[--debug] ( + ECHO Running Debug + SET CMD=org.mozilla.javascript.tools.debugger.Main -debug + + REM `SHIFT` doesn't affect %* + :COLLECT_ARGS + IF [%2]==[] GOTO LAST_ARG + SET ARGS=%ARGS% %2 + SHIFT + GOTO COLLECT_ARGS +) ELSE ( + SET CMD=org.mozilla.javascript.tools.shell.Main + SET ARGS=%* +) +:LAST_ARG + +IF [%1]==[-T] ( + ECHO Running Tests + java -classpath "%_BASEPATH%/rhino/js.jar" %CMD% -opt -1 -modules "%_URLPATH%/node_modules" -modules "%_URLPATH%/rhino" -modules "%_URLPATH%/lib" -modules "%_URLPATH%" "%_BASEPATH%/jsdoc.js" %ARGS% --nocolor --dirname="%_BASEPATH%/ +) ELSE ( + REM normal mode should be quiet + java -classpath "%_BASEPATH%/rhino/js.jar" %CMD% -modules "%_URLPATH%/node_modules" -modules "%_URLPATH%/rhino" -modules "%_URLPATH%/lib" -modules "%_URLPATH%" "%_BASEPATH%/jsdoc.js" %ARGS% --dirname="%_BASEPATH%/ +) + +ENDLOCAL diff --git a/data/scripts/jsdoc/jsdoc.js b/data/scripts/jsdoc/jsdoc.js new file mode 100644 index 00000000..0204ff61 --- /dev/null +++ b/data/scripts/jsdoc/jsdoc.js @@ -0,0 +1,308 @@ +/*global app: true, args: true, env: true, publish: true */ +/** + * @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 + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +/** + * Data representing the environment in which this app is running. + * + * @namespace + * @name env + */ +require('lib/jsdoc/util/global').env = { + /** + * Running start and finish times. + * + * @memberof env + */ + run: { + start: new Date(), + finish: null + }, + + /** + * The command-line arguments passed into JSDoc. + * + * @type Array + * @memberof env + */ + args: [], + + /** + * The parsed JSON data from the configuration file. + * + * @type Object + * @memberof env + */ + conf: {}, + + /** + * The absolute path to the base directory of the JSDoc application. + * + * @private + * @deprecated Use `__dirname` instead. + * @type string + * @memberof env + */ + dirname: '.', + + /** + * The command-line arguments, parsed into a key/value hash. + * + * @type Object + * @memberof env + * @example if (env.opts.help) { console.log('Helpful message.'); } + */ + opts: {}, + + /** + * The JSDoc version number and revision date. + * + * @type Object + * @memberof env + */ + version: {} +}; + +// initialize the environment for the current JavaScript VM +(function(args) { + var vm = require('jsdoc/util/vm').vm; + // TODO: may need to move this file to support Node.js + require('initialize')[vm](args); +})( Array.prototype.slice.call(arguments, 0) ); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +/** + * Data that must be shared across the entire application. + * @namespace + * @name app + */ +require('lib/jsdoc/util/global').app = { + jsdoc: { + scanner: new (require('jsdoc/src/scanner').Scanner)(), + parser: new (require('jsdoc/src/parser').Parser)(), + name: require('jsdoc/name') + } +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +/** + Try to recursively print out all key/values in an object. + @global + @private + @param {Object} ... Object/s to dump out to console. + */ +function dump() { + var doop = require('jsdoc/util/doop').doop; + var _dump = require('jsdoc/util/dumper').dump; + for (var i = 0, l = arguments.length; i < l; i++) { + console.log( _dump(doop(arguments[i])) ); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +/** + * Run the jsdoc application. + * @todo Refactor function (and require statements) into smaller functions + */ +function main() { + var _ = require('underscore'); + var fs = require('jsdoc/fs'); + var path = require('jsdoc/path'); + var taffy = require('taffydb').taffy; + + var jsdoc = { + augment: require('jsdoc/augment'), + borrow: require('jsdoc/borrow'), + Config: require('jsdoc/config'), + opts: { + args: require('jsdoc/opts/args') + }, + 'package': require('jsdoc/package'), + plugins: require('jsdoc/plugins'), + Readme: require('jsdoc/readme'), + src: { + filter: require('jsdoc/src/filter'), + handlers: require('jsdoc/src/handlers') + }, + tutorial: { + resolver: require('jsdoc/tutorial/resolver') + }, + util: { + include: require('jsdoc/util/include') + } + }; + + var confPath; + var defaultOpts; + var docs; + var filter; + var i; + var info; + var l; + var packageDocs; + var packageJson; + var sourceFiles; + var template; + + + defaultOpts = { + destination: './out/', + encoding: 'utf8' + }; + + // get JSDoc version number + info = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')); + env.version = { + number: info.version, + revision: new Date(parseInt(info.revision, 10)).toUTCString() + }; + + env.opts = jsdoc.opts.args.parse(env.args); + + confPath = env.opts.configure || path.join(__dirname, 'conf.json'); + if ( !fs.statSync(confPath).isFile() ) { + confPath = path.join(__dirname, 'conf.json.EXAMPLE'); + } + + try { + env.conf = new jsdoc.Config( fs.readFileSync(confPath, 'utf8') ) + .get(); + } + catch (e) { + throw new Error('Cannot parse the config file ' + confPath + ': ' + e); + } + + // look for options on the command line, in the config file, and in the defaults, in that order + env.opts = _.defaults(env.opts, env.conf.opts, defaultOpts); + + if (env.opts.help) { + console.log( jsdoc.opts.args.help() ); + process.exit(0); + } else if (env.opts.test) { + jsdoc.util.include('test/runner.js'); + process.exit(0); + } else if (env.opts.version) { + console.log('JSDoc ' + env.version.number + ' (' + env.version.revision + ')'); + process.exit(0); + } + + if (env.conf.plugins) { + jsdoc.plugins.installPlugins(env.conf.plugins, app.jsdoc.parser); + } + + if (env.conf.source && env.conf.source.include) { + env.opts._ = (env.opts._ || []).concat(env.conf.source.include); + } + + // any source file named package.json or README.md is treated special + for (i = 0, l = env.opts._.length; i < l; i++ ) { + if (/\bpackage\.json$/i.test(env.opts._[i])) { + packageJson = fs.readFileSync( env.opts._[i], 'utf8' ); + env.opts._.splice(i--, 1); + } + + if (/(\bREADME|\.md)$/i.test(env.opts._[i])) { + env.opts.readme = new jsdoc.Readme(env.opts._[i]).html; + env.opts._.splice(i--, 1); + } + } + + if (env.conf.source && env.opts._.length > 0) { // are there any files to scan and parse? + filter = new jsdoc.src.filter.Filter(env.conf.source); + + sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), filter); + + jsdoc.src.handlers.attachTo(app.jsdoc.parser); + + docs = app.jsdoc.parser.parse(sourceFiles, env.opts.encoding); + + //The files are ALWAYS useful for the templates to have + //If there is no package.json, just create an empty package + packageDocs = new jsdoc.package.Package(packageJson); + packageDocs.files = sourceFiles || []; + docs.push(packageDocs); + + jsdoc.borrow.indexAll(docs); + + jsdoc.augment.addInherited(docs); + jsdoc.borrow.resolveBorrows(docs); + + if (env.opts.explain) { + dump(docs); + process.exit(0); + } + + if (env.opts.tutorials) { + jsdoc.tutorial.resolver.load(env.opts.tutorials); + jsdoc.tutorial.resolver.resolve(); + } + + env.opts.template = (function() { + var publish = env.opts.template || 'templates/default'; + // if we don't find it, keep the user-specified value so the error message is useful + return path.getResourcePath(publish) || env.opts.template; + })(); + + try { + template = require(env.opts.template + '/publish'); + } + catch(e) { + throw new Error('Unable to load template: ' + e.message || e); + } + + // templates should include a publish.js file that exports a "publish" function + if (template.publish && typeof template.publish === 'function') { + // convert this from a URI back to a path if necessary + env.opts.template = path._uriToPath(env.opts.template); + template.publish( + taffy(docs), + env.opts, + jsdoc.tutorial.resolver.root + ); + } + else { + // old templates define a global "publish" function, which is deprecated + jsdoc.util.include(env.opts.template + '/publish.js'); + if (publish && typeof publish === 'function') { + console.log( env.opts.template + ' uses a global "publish" function, which is ' + + 'deprecated and may not be supported in future versions. ' + + 'Please update the template to use "exports.publish" instead.' ); + // convert this from a URI back to a path if necessary + env.opts.template = path._uriToPath(env.opts.template); + publish( + taffy(docs), + env.opts, + jsdoc.tutorial.resolver.root + ); + } + else { + throw new Error( env.opts.template + ' does not export a "publish" function.' ); + } + } + } +} + +try { + main(); + env.run.finish = new Date(); + process.exit(0); +} +catch(e) { + env.run.finish = new Date(); + if (e.rhinoException != null) { + e.rhinoException.printStackTrace(); + process.exit(1); + } else { + throw e; + } +} diff --git a/data/scripts/jsdoc/lib/initialize.js b/data/scripts/jsdoc/lib/initialize.js new file mode 100644 index 00000000..774b9d76 --- /dev/null +++ b/data/scripts/jsdoc/lib/initialize.js @@ -0,0 +1,40 @@ +/*global env: true */ +exports.rhino = function(args) { + var myGlobal = require('jsdoc/util/global'); + + // note: mutates args + function getDirname() { + var dirname; + + // Rhino has no native way to get the base dirname of the current script, + // so this information must be manually passed in from the command line. + for (var i = 0; i < args.length; i++) { + if ( /^--dirname(?:=(.+?)(\/|\/\.)?)?$/i.test(args[i]) ) { + if (RegExp.$1) { + dirname = RegExp.$1; // last wins + args.splice(i--, 1); // remove --dirname opt from arguments + } + else { + dirname = args[i + 1]; + args.splice(i--, 2); + } + } + } + + return dirname; + } + + myGlobal.__dirname = env.dirname = getDirname(); + env.args = args; + + require('jsdoc/util/include')(__dirname + '/rhino/rhino-shim.js'); +}; + +exports.nodejs = function(args) { + throw new Error('Node.js is not currently supported!'); + /* + env.dirname = __dirname; + env.args = args; + // TODO: add lib/ to the library paths + */ +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/augment.js b/data/scripts/jsdoc/lib/jsdoc/augment.js new file mode 100644 index 00000000..eaa00a48 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/augment.js @@ -0,0 +1,138 @@ +var hasOwnProp = Object.prototype.hasOwnProperty; + +function mapDependencies(index) { + var doclets, doc, len, dependencies = {}; + + Object.keys(index).forEach(function(name) { + doclets = index[name]; + for (var i = 0, ii = doclets.length; i < ii; ++i) { + doc = doclets[i]; + if (doc.kind === "class" || doc.kind === "external") { + dependencies[name] = {}; + len = doc.augments && doc.augments.length || 0; + for (var j = 0; j < len; ++j) { + dependencies[name][doc.augments[j]] = true; + } + } + } + }); + + return dependencies; +} + +function Sorter(dependencies) { + this.dependencies = dependencies; + this.visited = {}; + this.sorted = []; +} + +Sorter.prototype.visit = function(key) { + var self = this; + + if (!(key in this.visited)) { + this.visited[key] = true; + + if (this.dependencies[key]) { + Object.keys(this.dependencies[key]).forEach(function(path) { + self.visit(path); + }); + } + + this.sorted.push(key); + } +}; + +Sorter.prototype.sort = function() { + var self = this; + + Object.keys(this.dependencies).forEach(function(key) { + self.visit(key); + }); + + return this.sorted; +}; + +function sort(dependencies) { + var sorter = new Sorter(dependencies); + return sorter.sort(); +} + +function getMembers(longname, docs) { + var candidate, members = []; + for (var i = 0, ii = docs.length; i < ii; ++i) { + candidate = docs[i]; + if (candidate.memberof === longname && candidate.scope === "instance") { + members.push(candidate); + } + } + return members; +} + +function getAdditions(doclets, docs, longnames) { + var doop = require("jsdoc/util/doop").doop; + + var additions = []; + var doc; + var parents; + var members; + var member; + var parts; + + // doclets will be undefined if the inherited symbol isn't documented + doclets = doclets || []; + + for (var i = 0, ii = doclets.length; i < ii; i++) { + doc = doclets[i]; + parents = doc.augments; + if (parents && doc.kind === "class") { + for (var j = 0, jj = parents.length; j < jj; j++) { + members = getMembers(parents[j], docs); + for (var k = 0, kk = members.length; k < kk; k++) { + member = doop(members[k]); + + member.inherits = member.longname; + member.inherited = true; + + member.memberof = doc.longname; + parts = member.longname.split("#"); + parts[0] = doc.longname; + member.longname = parts.join("#"); + + // if the child doesn't override the parent member, add the parent member + if (longnames.indexOf(member.longname) === -1) { + additions.push(member); + } + } + } + } + } + return additions; +} + +exports.addInherited = function(docs) { + var dependencies = mapDependencies(docs.index); + var sorted = sort(dependencies); + var longnames = []; + + // only build the list of longnames if we'll actually need it + if (sorted.length) { + longnames = docs.map(function(doc) { + if (doc.longname) { + return doc.longname; + } + }); + } + + sorted.forEach(function(name) { + var doclets = docs.index[name]; + var additions = getAdditions(doclets, docs, longnames); + additions.forEach(function(doc) { + var name = doc.longname; + if ( !hasOwnProp.call(docs.index, name) ) { + docs.index[name] = []; + } + docs.index[name].push(doc); + docs.push(doc); + }); + }); +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/borrow.js b/data/scripts/jsdoc/lib/jsdoc/borrow.js new file mode 100644 index 00000000..5cc13ca5 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/borrow.js @@ -0,0 +1,65 @@ +/** + A collection of functions relating to resolving @borrows tags in JSDoc symbols. + @module jsdoc/borrow + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var doop = require("jsdoc/util/doop").doop; + +var hasOwnProp = Object.prototype.hasOwnProperty; + +exports.indexAll = function(docs) { + var lookupTable = {}; + + docs.forEach(function(doc) { + if ( !hasOwnProp.call(lookupTable, doc.longname) ) { + lookupTable[doc.longname] = []; + } + lookupTable[doc.longname].push(doc); + }); + docs.index = lookupTable; +}; + +// 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; + } + }); +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/config.js b/data/scripts/jsdoc/lib/jsdoc/config.js new file mode 100644 index 00000000..9b6a52c4 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/config.js @@ -0,0 +1,58 @@ +/** + @overview + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +/** + @module jsdoc/config + */ + +function mergeRecurse(target, source) { + Object.keys(source).forEach(function(p) { + if ( source[p].constructor === Object ) { + if ( !target[p] ) { target[p] = {}; } + mergeRecurse(target[p], source[p]); + } + else { + target[p] = source[p]; + } + }); + + return target; +} + +// required config values, override these defaults in your config.json if necessary +const defaults = { + "tags": { + "allowUnknownTags": true + }, + "templates": { + "monospaceLinks": false, + "cleverLinks": false + }, + "source": { + "includePattern": ".+\\.js(doc)?$", + "excludePattern": "(^|\\/)_" + }, + "plugins": [] +}; + +/** + @class + @classdesc Represents a JSDoc application configuration. + @param {string} [json] - The contents of config.json. + */ +function Config(json) { + json = JSON.parse( (json || "{}") ); + this._config = mergeRecurse(defaults, json); +} + +module.exports = Config; + +/** + Get the merged configuration values. + */ +Config.prototype.get = function() { + return this._config; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/doclet.js b/data/scripts/jsdoc/lib/jsdoc/doclet.js new file mode 100644 index 00000000..47f833c0 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/doclet.js @@ -0,0 +1,337 @@ +/** + @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 + */ + +var jsdoc = { + tag: { + Tag: require('jsdoc/tag').Tag, + dictionary: require('jsdoc/tag/dictionary') + }, + name: require('jsdoc/name') +}; +var path = require('path'); + + +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 'member'; + } + + return kind; +} + +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 split(docletSrc) { + var tagSrcs = [], + tagText, + tagTitle; + + // 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) { + // we don't need parsedTag[0] + tagTitle = parsedTag[1]; + tagText = parsedTag[2]; + + if (tagTitle) { + tagSrcs.push({ + title: tagTitle, + text: tagText + }); + } + } + } + }); + + return tagSrcs; +} + +/** + Convert the raw source of the doclet comment into an array of Tag objects. + @private + */ +function toTags(docletSrc) { + var tagSrcs, + tags = []; + + tagSrcs = split(docletSrc); + + for (var i = 0, l = tagSrcs.length; i < l; i++) { + tags.push( {title: tagSrcs[i].title, text: tagSrcs[i].text} ); + } + + return tags; +} + +function fixDescription(docletSrc) { + if (!/^\s*@/.test(docletSrc)) { + docletSrc = '@description ' + docletSrc; + } + return docletSrc; +} + +/** + @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) ); + } + + if (this.variation && this.longname && !/\)$/.test(this.longname) ) { + this.longname += '('+this.variation+')'; + } + + // add in any missing param names + if (this.params && this.meta && this.meta.code && this.meta.code.paramnames) { + for (var i = 0, len = this.params.length; i < len; i++) { + if (!this.params[i].name) { + this.params[i].name = this.meta.code.paramnames[i] || ''; + } + } + } +}; + +/** 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); +}; + +exports.Doclet.prototype.mix = function(source) { + if (!this.mixes) { + /** + A list of symbols that are mixed into this one, if any. + @type Array. + */ + this.mixes = []; + } + this.mixes.push(source); +}; + +/** 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.range) { + /** + The positions of the first and last characters of the code associated with this doclet. + @type Array. + */ + this.meta.range = meta.range.slice(0); + } + + if (meta.lineno) { + /** + The name of the file containing the code associated with this doclet. + @type string + */ + this.meta.filename = path.basename(meta.filename); + /** + The line number of the code associated with this doclet. + @type number + */ + this.meta.lineno = meta.lineno; + + var pathname = path.dirname(meta.filename); + if (pathname && pathname !== '.') { + this.meta.path = pathname; + } + } + + /** + 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; + } + if (meta.code.paramnames) { + this.meta.code.paramnames = meta.code.paramnames.concat([]); + } + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/fs.js b/data/scripts/jsdoc/lib/jsdoc/fs.js new file mode 100644 index 00000000..ee365a8d --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/fs.js @@ -0,0 +1,14 @@ +/** + * Extended version of the standard `fs` module. + * @module jsdoc/fs + */ + +var fs = exports.fs = require('fs'); +var vm = require('jsdoc/util/vm'); + +// export the VM-specific implementations of the extra methods +// TODO: document extra methods here +var extras = vm.getModule('fs'); +Object.keys(extras).forEach(function(extra) { + exports[extra] = extras[extra]; +}); diff --git a/data/scripts/jsdoc/lib/jsdoc/name.js b/data/scripts/jsdoc/lib/jsdoc/name.js new file mode 100644 index 00000000..b3b97d4b --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/name.js @@ -0,0 +1,258 @@ +/** + 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. + */ + +var jsdoc = { + tagDictionary: require('jsdoc/tag/dictionary') +}; + +var puncToScope = { '.': 'static', '~': 'inner', '#': 'instance' }, + scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' }; + +var DEFAULT_SCOPE = 'static'; + +/** + 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; + + doclet.name = 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 || doclet.forceMemberof) { // @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 && name !== memberof) { + about = exports.shorten(name, (doclet.forceMemberof ? memberof : undefined)); + } + // the name and memberof are identical and refer to a module, + // like @name module:foo, @memberof module:foo (probably a member like 'var exports') + else if (name && name === memberof && name.indexOf('module:') === 0) { + about = exports.shorten(name, (doclet.forceMemberof ? memberof : undefined)); + } + // the name and memberof are identical, like @name foo, @memberof foo + else if (name && name === memberof) { + doclet.scope = doclet.scope || DEFAULT_SCOPE; + name = memberof + scopeToPunc[doclet.scope] + name; + about = exports.shorten(name, (doclet.forceMemberof ? memberof : undefined)); + } + // like @memberof foo# or @memberof foo~ + else if (name && /([#.~])$/.test(memberof) ) { + about = exports.shorten(memberof + name, (doclet.forceMemberof ? memberof : undefined)); + } + else if (name && doclet.scope) { + about = exports.shorten(memberof + (scopeToPunc[doclet.scope] || '') + name, + (doclet.forceMemberof ? memberof : undefined)); + } + } + 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) { + if (about.memberof === '') { // via @memberof ? + doclet.scope = 'global'; + } + else { + doclet.scope = puncToScope[about.scope]; + } + } + else { + if (doclet.name && doclet.memberof && !doclet.longname) { + if ( /^([#.~])/.test(doclet.name) ) { + doclet.scope = puncToScope[RegExp.$1]; + doclet.name = doclet.name.substr(1); + } + else { + doclet.scope = DEFAULT_SCOPE; + } + + doclet.setLongname(doclet.memberof + scopeToPunc[doclet.scope] + doclet.name); + } + } + + if (about.variation) { + doclet.variation = about.variation; + } +}; + +/** + @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 + @param {string} forcedMemberof + @returns {object} Representing the properties of the given name. + */ +exports.shorten = function(longname, forcedMemberof) { + // quoted strings in a longname are atomic, convert to tokens + var atoms = [], token; + + // handle quoted names like foo["bar"] or 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}@ + }); + + var name = '', + scope = '', // ., ~, or # + memberof = '', + parts, + variation; + + longname = longname.replace( /\.prototype\.?/g, '#' ); + + if (typeof forcedMemberof !== 'undefined') { + name = longname.substr(forcedMemberof.length); + parts = forcedMemberof.match(/^(.*?)([#.~]?)$/); + + if (parts[1]) { memberof = parts[1] || forcedMemberof; } + if (parts[2]) { scope = parts[2]; } + } + else { + parts = longname? + (longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse() + : ['']; + + name = parts[0] || ''; // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24] + scope = parts[1] || ''; // ., ~, or # + memberof = parts[2] || ''; + } + + // 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}; +}; + +/** + Split a string that starts with a name and ends with a description, into its parts. + @param {string} nameDesc + @returns {object} Hash with "name" and "description" properties. + */ +exports.splitName = function(nameDesc) { + var name = '', + desc = '', + thisChar = '', + inQuote = false; + + for (var i = 0, len = nameDesc.length; i < len; i++) { + thisChar = nameDesc.charAt(i); + + if (thisChar === '\\') { + name += thisChar + nameDesc.charAt(++i); + continue; + } + + if (thisChar === '"') { + inQuote = !inQuote; + } + + if (inQuote) { + name += thisChar; + continue; + } + + if (!inQuote) { + if ( /\s/.test(thisChar) ) { + desc = nameDesc.substr(i); + desc = desc.replace(/^[\s\-\s]+/, '').trim(); + break; + } + else { + name += thisChar; + } + } + } + + return { name: name, description: desc }; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/opts/argparser.js b/data/scripts/jsdoc/lib/jsdoc/opts/argparser.js new file mode 100644 index 00000000..7e68580b --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/opts/argparser.js @@ -0,0 +1,179 @@ +/** + Parse the command line arguments. + @module jsdoc/opts/argparser + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var _ = require('underscore'); + +var hasOwnProp = Object.prototype.hasOwnProperty; + +/** + Create an instance of the parser. + @classdesc A parser to interpret the key-value pairs entered on the command + line. + @constructor + */ +var ArgParser = function() { + this._options = []; + this._shortNameIndex = {}; + this._longNameIndex = {}; +}; + +ArgParser.prototype._getOptionByShortName = function(name) { + if (hasOwnProp.call(this._shortNameIndex, name)) { + return this._options[this._shortNameIndex[name]]; + } + return null; +}; + +ArgParser.prototype._getOptionByLongName = function(name) { + if (hasOwnProp.call(this._longNameIndex, name)) { + return this._options[this._longNameIndex[name]]; + } + 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 A brief description of the option. + * @param {boolean} [canHaveMultiple=false] Set to `true` if the option can be provided more than once. + * @param {function} [coercer] A function to coerce the given value to a specific type. + * @example + * myParser.addOption('t', 'template', true, 'The path to the template.'); + * myParser.addOption('h', 'help', false, 'Show the help message.'); + */ +ArgParser.prototype.addOption = function(shortName, longName, hasValue, helpText, canHaveMultiple, coercer) { + this._options.push({ + shortName: shortName, + longName: longName, + hasValue: hasValue, + helpText: helpText, + canHaveMultiple: (canHaveMultiple || false), + coercer: coercer + }); + + if (shortName) { + this._shortNameIndex[shortName] = this._options.length - 1; + } + if (longName) { + this._longNameIndex[longName] = this._options.length - 1; + } +}; + +/** + Generate a summary of all the options with corresponding help text. + @returns {string} + */ +ArgParser.prototype.help = function() { + var helpArr = ['OPTIONS:'], + option, optionHelp; + + for (var i = 0, leni = this._options.length; i < leni; i++) { + option = this._options[i]; + optionHelp = '\t'; + + if (option.shortName) { + optionHelp += '-' + option.shortName + (option.longName ? ', ' : ''); + } + + if (option.longName) { + optionHelp += '--' + option.longName; + } + + if (option.hasValue) { + optionHelp += ' '; + } + + optionHelp += '\t\t' + option.helpText; + helpArr.push(optionHelp); + } + + return helpArr.join('\n'); +}; + +/** + 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. + */ +ArgParser.prototype.parse = function(args, defaults) { + var result = defaults && _.defaults({}, 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 = null, + 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; + } + + if (typeof option.coercer === 'function') { + value = option.coercer(value); + } + + // Allow for multiple options of the same type to be present + if (option.canHaveMultiple && hasOwnProp.call(result, name)) { + var val = result[name]; + + if (val instanceof Array) { + val.push(value); + } else { + result[name] = [val, value]; + } + } + else { + result[name] = value; + } + } + else { + result._.push(arg); + } + } + + return result; +}; + +module.exports = ArgParser; diff --git a/data/scripts/jsdoc/lib/jsdoc/opts/args.js b/data/scripts/jsdoc/lib/jsdoc/opts/args.js new file mode 100644 index 00000000..ddc3e620 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/opts/args.js @@ -0,0 +1,127 @@ +/** + @module jsdoc/opts/args + @requires jsdoc/opts/argparser + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var ArgParser = require('jsdoc/opts/argparser'), + argParser = new ArgParser(), + hasOwnProp = Object.prototype.hasOwnProperty, + ourOptions, + querystring = require('querystring'), + util = require('util'); + + +// cast strings to booleans or integers where appropriate +function castTypes(item) { + var result = item; + + switch (result) { + case 'true': + return true; + case 'false': + return false; + default: + // might be an integer + var integer = parseInt(result, 10); + if (String(integer) === result && integer !== 'NaN') { + return integer; + } else { + return result; + } + } +} + +// check for strings that we need to cast to other types +function fixTypes(item) { + var result = item; + + // recursively process arrays and objects + if ( util.isArray(result) ) { + for (var i = 0, l = result.length; i < l; i++) { + result[i] = fixTypes(result[i]); + } + } else if (typeof result === 'object') { + Object.keys(result).forEach(function(prop) { + result[prop] = fixTypes(result[prop]); + }); + } else { + result = castTypes(result); + } + + return result; +} + +function parseQuery(str) { + var result = querystring.parse(str); + + Object.keys(result).forEach(function(prop) { + result[prop] = fixTypes(result[prop]); + }); + + return result; +} + +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 __dirname + /conf.json'); +argParser.addOption('e', 'encoding', true, 'Assume this encoding when reading all source files. Default: utf8'); +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: ./out/'); +argParser.addOption('p', 'private', false, 'Display symbols marked with the @private tag. Default: false'); +argParser.addOption('r', 'recurse', false, 'Recurse into subdirectories when scanning for source code files.'); +argParser.addOption('l', 'lenient', false, 'Continue to generate output if a doclet is incomplete or contains errors. Default: false'); +argParser.addOption('h', 'help', false, 'Print this message and quit.'); +argParser.addOption('X', 'explain', false, 'Dump all found doclet internals to console and quit.'); +argParser.addOption('q', 'query', true, 'A query string to parse and store in env.opts.query. Example: foo=bar&baz=true', false, parseQuery); +argParser.addOption('u', 'tutorials', true, 'Directory in which JSDoc should search for tutorials.'); +argParser.addOption('v', 'version', false, 'Display the version number and quit.'); + +//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? + +//Here are options specific to tests +argParser.addOption(null, 'verbose', false, 'Display verbose output for tests'); +argParser.addOption(null, 'match', true, 'Only run tests containing ', true); +argParser.addOption(null, 'nocolor', false, 'Do not use color in console output from tests'); + +/** + 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); + + 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]; + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/package.js b/data/scripts/jsdoc/lib/jsdoc/package.js new file mode 100644 index 00000000..9b9cf99d --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/package.js @@ -0,0 +1,69 @@ +/** + @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 + */ + +/** + @class + @classdesc Represents a JavaScript package. + @param {string} json - The contents of package.json. + */ +exports.Package = function(json) { + json = 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; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/path.js b/data/scripts/jsdoc/lib/jsdoc/path.js new file mode 100644 index 00000000..4ddb031d --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/path.js @@ -0,0 +1,136 @@ +/** + * Extended version of the standard `path` module. + * @module jsdoc/path + */ + +var fs = require('fs'); +var path = require('path'); +var vm = require('jsdoc/util/vm'); + + +function prefixReducer(previousPath, current) { + var currentPath = []; + + // if previousPath is defined, but has zero length, there's no common prefix; move along + if (previousPath && !previousPath.length) { + return currentPath; + } + + currentPath = path.resolve( process.cwd(), path.dirname(current) ).split(path.sep) || []; + + if (previousPath && currentPath.length) { + // remove chunks that exceed the previous path's length + currentPath = currentPath.slice(0, previousPath.length); + + // if a chunk doesn't match the previous path, remove everything from that chunk on + for (var i = 0, l = currentPath.length; i < l; i++) { + if (currentPath[i] !== previousPath[i]) { + currentPath.splice(i, currentPath.length - i); + break; + } + } + } + + return currentPath; +} + +/** + * Find the common prefix for an array of paths. If there is a common prefix, a trailing separator + * is appended to the prefix. Relative paths are resolved relative to the current working directory. + * + * For example, assuming that the current working directory is `/Users/jsdoc`: + * + * + For paths `foo/bar/baz/qux.js`, `foo/bar/baz/quux.js`, and `foo/bar/baz.js`, the common prefix + * is `/Users/jsdoc/foo/bar/`. + * + For paths `../jsdoc/foo/bar/baz/qux/quux/test.js`, `/Users/jsdoc/foo/bar/bazzy.js`, and + * `../../Users/jsdoc/foo/bar/foobar.js`, the common prefix is `/Users/jsdoc/foo/bar/`. + * + For paths `foo/bar/baz/qux.js` and `../../Library/foo/bar/baz.js`, there is no common prefix, + * and an empty string is returned. + * + * @param {Array.} paths - The paths to search for a common prefix. + * @return {string} The common prefix, or an empty string if there is no common prefix. + */ +exports.commonPrefix = function(paths) { + var common = paths.reduce(prefixReducer, undefined); + + // if there's anything left (other than a placeholder for a leading slash), add a placeholder + // for a trailing slash + if ( common.length && (common.length > 1 || common[0] !== '') ) { + common.push(''); + } + + return common.join(path.sep); +}; + +// TODO: do we need this? +/** + * If required by the current VM, convert a path to a URI that meets the operating system's + * requirements. Otherwise, return the original path. + * @function + * @private + * @param {string} path The path to convert. + * @return {string} A URI that meets the operating system's requirements, or the original path. + */ +var pathToUri = vm.getModule('jsdoc').pathToUri; + +// TODO: do we need this? if so, any way to stop exporting it? +/** + * If required by the current VM, convert a URI to a path that meets the operating system's + * requirements. Otherwise, assume the "URI" is really a path, and return the original path. + * @function + * @private + * @param {string} uri The URI to convert. + * @return {string} A path that meets the operating system's requirements. + */ +exports._uriToPath = vm.getModule('jsdoc').uriToPath; + +/** + * Retrieve the fully qualified path to the requested resource. + * + * If the resource path is specified as a relative path, JSDoc searches for the path in the current + * working directory, then in the JSDoc directory. + * + * If the resource path is specified as a fully qualified path, JSDoc uses the path as-is. + * + * @param {string} filepath - The path to the requested resource. May be an absolute path; a path + * relative to the JSDoc directory; or a path relative to the current working directory. + * @param {string} [filename] - The filename of the requested resource. + * @return {string} The fully qualified path (or, on Rhino, a URI) to the requested resource. + * Includes the filename if one was provided. + */ +exports.getResourcePath = function(filepath, filename) { + var result; + + function pathExists(_path) { + try { + fs.readdirSync(_path); + } + catch(e) { + return false; + } + + return true; + } + + // first, try resolving it relative to the current working directory (or just normalize it + // if it's an absolute path) + result = path.resolve(filepath); + if ( !pathExists(result) ) { + // next, try resolving it relative to the JSDoc directory + result = path.resolve(__dirname, filepath); + if ( !pathExists(result) ) { + result = null; + } + } + + if (result) { + result = filename ? path.join(result, filename) : result; + result = pathToUri(result); + } + + return result; +}; + +Object.keys(path).forEach(function(member) { + exports[member] = path[member]; +}); diff --git a/data/scripts/jsdoc/lib/jsdoc/plugins.js b/data/scripts/jsdoc/lib/jsdoc/plugins.js new file mode 100644 index 00000000..5e2637bc --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/plugins.js @@ -0,0 +1,45 @@ +/*global app: true */ +/** + * Utility functions to support the JSDoc plugin framework. + * @module jsdoc/plugins + */ + +var error = require('jsdoc/util/error'); +var path = require('jsdoc/path'); + +exports.installPlugins = function(plugins, p) { + var dictionary = require('jsdoc/tag/dictionary'); + var parser = p; + + var eventName; + var plugin; + var pluginPath; + + for (var i = 0, l = plugins.length; i < l; i++) { + pluginPath = path.getResourcePath(path.dirname(plugins[i]), path.basename(plugins[i])); + if (!pluginPath) { + error.handle(new Error('Unable to find the plugin "' + plugins[i] + '"')); + } + else { + plugin = require(pluginPath); + + // allow user-defined plugins to... + //...register event handlers + if (plugin.handlers) { + Object.keys(plugin.handlers).forEach(function(eventName) { + parser.on(eventName, plugin.handlers[eventName]); + }); + } + + //...define tags + if (plugin.defineTags) { + plugin.defineTags(dictionary); + } + + //...add a node visitor + if (plugin.nodeVisitor) { + parser.addNodeVisitor(plugin.nodeVisitor); + } + } + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/readme.js b/data/scripts/jsdoc/lib/jsdoc/readme.js new file mode 100644 index 00000000..0e3ca21e --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/readme.js @@ -0,0 +1,25 @@ +/*global env: true */ + +/** + * Make the contents of a README file available to include in the output. + * @module jsdoc/readme + * @author Michael Mathews + * @author Ben Blank + */ + +var fs = require('jsdoc/fs'), + markdown = require('jsdoc/util/markdown'); + +/** + * @class + * @classdesc Represents a README file. + * @param {string} path - The filepath to the README. + */ +function ReadMe(path) { + var content = fs.readFileSync(path, env.opts.encoding), + parse = markdown.getParser(); + + this.html = parse(content); +} + +module.exports = ReadMe; diff --git a/data/scripts/jsdoc/lib/jsdoc/schema.js b/data/scripts/jsdoc/lib/jsdoc/schema.js new file mode 100644 index 00000000..2fa32dde --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/schema.js @@ -0,0 +1,308 @@ +/** + @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" + } + }, + "tutorials": { // extended tutorials + "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", "member", "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"] + }, + "virtual": { // is a member left to be implemented during inheritance? + "type": "boolean", + "optional": true, + "default": false + }, + "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/data/scripts/jsdoc/lib/jsdoc/src/filter.js b/data/scripts/jsdoc/lib/jsdoc/src/filter.js new file mode 100644 index 00000000..4bd18055 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/src/filter.js @@ -0,0 +1,53 @@ +/** + @module jsdoc/src/filter + + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var path = require('jsdoc/path'); + +/** + @constructor + @param {object} opts + @param {string[]} opts.exclude - Specific files to exclude. + @param {string|RegExp} opts.includePattern + @param {string|RegExp} opts.excludePattern + */ +exports.Filter = function(opts) { + var cwd = process.cwd(); + + this.exclude = opts.exclude && Array.isArray(opts.exclude) ? + opts.exclude.map(function($) { + return path.resolve(cwd, $); + }) : + null; + this.includePattern = opts.includePattern? + typeof opts.includePattern === 'string'? new RegExp(opts.includePattern) : opts.includePattern + : null; + this.excludePattern = opts.excludePattern? + typeof opts.excludePattern === 'string'? new RegExp(opts.excludePattern) : opts.excludePattern + : null; +}; + +/** + @param {string} filepath - The filepath to check. + @returns {boolean} Should the given file be included? + */ +exports.Filter.prototype.isIncluded = function(filepath) { + filepath = path.resolve(process.cwd(), filepath); + + if ( this.includePattern && !this.includePattern.test(filepath) ) { + return false; + } + + if ( this.excludePattern && this.excludePattern.test(filepath) ) { + return false; + } + + if ( this.exclude && this.exclude.indexOf(filepath) > -1 ) { + return false; + } + + return true; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/src/handlers.js b/data/scripts/jsdoc/lib/jsdoc/src/handlers.js new file mode 100644 index 00000000..fef4bf13 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/src/handlers.js @@ -0,0 +1,200 @@ +/** + * @module jsdoc/src/handlers + */ + + +var currentModule = null; + +function getNewDoclet(comment, e) { + var jsdoc = {doclet: require('jsdoc/doclet')}; + var util = require('util'); + var err; + + try { + return new jsdoc.doclet.Doclet(comment, e); + } + catch (error) { + err = new Error( util.format('cannot create a doclet for the comment "%s": %s', + comment.replace(/[\r\n]/g, ''), error.message) ); + require('jsdoc/util/error').handle(err); + return new jsdoc.doclet.Doclet('', {}); + } +} + +/** + * Attach these event handlers to a particular instance of a parser. + * @param parser + */ +exports.attachTo = function(parser) { + var jsdoc = {doclet: require('jsdoc/doclet'), name: require('jsdoc/name')}; + + // handles JSDoc comments that include a @name tag -- the code is ignored in such a case + parser.on('jsdocCommentFound', function(e) { + var newDoclet = getNewDoclet(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; + + //resolveProperties(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); + } + }); + + // TODO: for clarity, decompose into smaller functions + function newSymbolDoclet(docletSrc, e) { + var memberofName = null, + newDoclet = getNewDoclet(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 = getNewDoclet(e.comment, e); + } + + if (newDoclet.alias) { + if (newDoclet.alias === '{@thisClass}') { + memberofName = this.resolveThis(e.astnode); + + // "class" refers to the owner of the prototype, not the prototype itself + if ( /^(.+?)(\.prototype|#)$/.test(memberofName) ) { + memberofName = RegExp.$1; + } + newDoclet.alias = memberofName; + } + 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 basename = null, + 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( Array.isArray(memberofName) ) { + basename = memberofName[1]; + memberofName = memberofName[0]; + } + } + + if (memberofName) { + newDoclet.addTag('memberof', memberofName); + if (basename) { + newDoclet.name = newDoclet.name.replace(new RegExp('^' + RegExp.escape(basename) + '.'), ''); + } + } + else { + // add @inner and @memberof tags unless the current module exports only this symbol + if (currentModule && currentModule !== newDoclet.name) { + // add @inner unless the current module exports only this symbol + if (!newDoclet.scope) { + newDoclet.addTag('inner'); + } + + if (!newDoclet.memberof && newDoclet.scope !== 'global') { + newDoclet.addTag('memberof', currentModule); + } + } + } + } + + newDoclet.postProcess(); + } + else { + return false; + } + + //resolveProperties(newDoclet); + + // set the scope to global unless a) the doclet is a memberof something or b) the current + // module exports only this symbol + if (!newDoclet.memberof && currentModule !== newDoclet.name) { + newDoclet.scope = 'global'; + } + + addDoclet.call(this, newDoclet); + e.doclet = newDoclet; + } + + //parser.on('fileBegin', function(e) { }); + + parser.on('fileComplete', function(e) { + currentModule = null; + }); + + function addDoclet(newDoclet) { + var e; + if (newDoclet) { + e = { doclet: newDoclet }; + this.emit('newDoclet', e); + + if ( !e.defaultPrevented && !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; + } + + function resolveProperties(newDoclet) {} +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/src/parser.js b/data/scripts/jsdoc/lib/jsdoc/src/parser.js new file mode 100644 index 00000000..d98b0829 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/src/parser.js @@ -0,0 +1,779 @@ +/*global env: true, Packages: true */ +/** + * @module jsdoc/src/parser + * @requires fs + * @requires events + */ + +var Token = Packages.org.mozilla.javascript.Token; +var hasOwnProp = Object.prototype.hasOwnProperty; + +/** + * @class + * @mixes module:events + * + * @example Create a new parser. + * var jsdocParser = new (require('jsdoc/src/parser').Parser)(); + */ +exports.Parser = function() { + this._currentSourceName = ''; + this._resultBuffer = []; + this._comments = { + original: [], + modified: [] + }; + //Initialize a global ref to store global members + this.refs = { + __global__: { + meta: {} + } + }; + this._visitors = []; +}; +exports.Parser.prototype = Object.create( require('events').EventEmitter.prototype ); + +/** + * 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) { + encoding = encoding || env.conf.encoding || 'utf8'; + + const SCHEMA = 'javascript:'; + + var filename = ''; + var sourceCode = ''; + var parsedFiles = []; + var e = {}; + + if (typeof sourceFiles === 'string') { + sourceFiles = [sourceFiles]; + } + + e.sourcefiles = sourceFiles; + + this.emit('parseBegin', e); + + for (var i = 0, l = sourceFiles.length; i < l; i++) { + sourceCode = ''; + + if (sourceFiles[i].indexOf(SCHEMA) === 0) { + sourceCode = sourceFiles[i].substr(SCHEMA.length); + filename = '[[string' + i + ']]'; + } + else { + filename = sourceFiles[i]; + try { + sourceCode = require('jsdoc/fs').readFileSync(filename, encoding); + } + catch(e) { + console.log('FILE READ ERROR: in module:jsdoc/parser.parseFiles: "' + filename + + '" ' + e); + continue; + } + } + + if (sourceCode.length) { + this._parseSourceCode(sourceCode, filename); + parsedFiles.push(filename); + } + } + + this.emit('parseComplete', { + sourcefiles: parsedFiles + }); + + 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() { + this._currentSourceName = ''; + this._resultBuffer = []; + this._comments = { + original: [], + modified: [] + }; +}; + +/** + * Adds a node visitor to use in parsing + */ +exports.Parser.prototype.addNodeVisitor = function(visitor) { + this._visitors.push(visitor); +}; + +/** + * Get the node visitors used in parsing + */ +exports.Parser.prototype.getVisitors = function() { + return this._visitors; +}; + +function pretreat(code) { + return code + // make starbangstar comments look like real jsdoc comments + .replace(/\/\*\!\*/g, '/**') + + // merge adjacent doclets + .replace(/\*\/\/\*\*+/g, '@also') + // make lent object literals documentable by giving them a dummy name + // like return @lends { + .replace(/(\/\*\*[^\*\/]*?[\*\s]*@lends\s(?:[^\*]|\*(?!\/))*\*\/\s*)\{/g, '$1 ____ = {') + // like @lends return { + .replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/)(\s*)return(\s*)\{/g, + '$2$3 return $1 ____ = {'); +} + +var tkn = { + NAMEDFUNCTIONSTATEMENT: -1001 +}; +exports.Parser.tkn = tkn; + +/** @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()); +} + +/** @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 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 if (node.type === Token.NEG || node.type === Token.TRUE || node.type === Token.FALSE) { + str = node.toSource(); // like -1 + } + else { + str = getTypeName(node); + } + + return '' + str; +} + +/** + * Attempts to find the name and type of the given node. + * @private + * @memberof module:src/parser.Parser + */ +function aboutNode(node) { + var about = {}; + + if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) { + about.name = node.type == tkn.NAMEDFUNCTIONSTATEMENT? '' : '' + node.name; + about.type = 'function'; + about.node = node; + } + else 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); + if (about.type === 'FUNCTION' && about.node.name) { + about.node.type = tkn.NAMEDFUNCTIONSTATEMENT; + } + } + else { // like var i; + about.node = node.target; + about.value = nodeToString(about.node); + about.type = 'undefined'; + } + } + else if (node.type === Token.ASSIGN || node.type === Token.COLON || + node.type === Token.GET || node.type === Token.SET) { + 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); + + // Getter and setter functions should be treated as properties + if (node.type === Token.GET || node.type === Token.SET) { + about.type = getTypeName(node); + } else { + about.type = getTypeName(node.right); + } + + if (about.type === 'FUNCTION' && about.node.name) { + about.node.type = tkn.NAMEDFUNCTIONSTATEMENT; + } + } + else if (node.type === Token.GETPROP) { + about.node = node; + about.name = nodeToString(about.node); + about.type = getTypeName(node); + } + else { + // type 39 (NAME) + var string = nodeToString(node); + if (string) { + about.name = string; + } + } + + // get names of the formal parameters declared for this function + if (about.node && about.node.getParamCount) { + var paramCount = about.node.getParamCount(); + if (typeof paramCount === 'number') { + about.node.flattenSymbolTable(true); + var paramNames = []; + for (var i = 0, l = paramCount; i < l; i++) { + paramNames.push( '' + about.node.getParamOrVarName(i) ); + } + about.paramnames = paramNames; + } + } + + return about; +} + +/** @private + @memberof module:src/parser.Parser +*/ +function isValidJsdoc(commentSrc) { + /*** ignore comments that start with many stars ***/ + return commentSrc && commentSrc.indexOf('/***') !== 0; +} + +/** @private + * @memberof module:src/parser.Parser + */ +function makeVarsFinisher(funcDoc) { + return function(e) { + //no need to evaluate all things related to funcDoc again, just use it + if (funcDoc && e.doclet && e.doclet.alias) { + funcDoc.meta.vars[e.code.name] = e.doclet.longname; + } + }; +} + +/** @private + * @memberof module:src/parser.Parser + * @param {string} name Full symbol name. + * @return {string} Basename. + */ +function getBasename(name) { + if (name !== undefined) { + return name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); + } + return name; +} + +/** @private + * @memberof module:src/parser.Parser + * @param {object} node + * @return {Array.} Start and end lines. + */ +function getRange(node) { + var range = []; + + range[0] = parseInt(String(node.getAbsolutePosition()), 10); + range[1] = range[0] + parseInt(String(node.getLength()), 10); + + return range; +} + +/** @private + * @memberof module:src/parser.Parser + */ +exports.Parser.prototype._makeEvent = function(node, extras) { + extras = extras || {}; + + // fill in default values as needed. if we're overriding a property, don't execute the default + // code for that property, since it might blow up. + var result = { + id: extras.id || 'astnode' + node.hashCode(), + comment: extras.comment || String(node.getJsDoc() || '@undocumented'), + lineno: extras.lineno || node.left.getLineno(), + range: extras.range || getRange(node), + filename: extras.filename || this._currentSourceName, + astnode: extras.astnode || node, + code: extras.code || aboutNode(node), + event: extras.event || 'symbolFound', + finishers: extras.finishers || [this.addDocletRef] + }; + + // use the modified version of the comment + var idx = this._comments.original.indexOf(result.comment); + if (idx !== -1) { + result.comment = this._comments.modified[idx]; + } + + // make sure the result includes extras that don't have default values + Object.keys(extras).forEach(function(prop) { + result[prop] = extras[prop]; + }); + + return result; +}; + +/** @private + * @memberof module:src/parser.Parser + */ +exports.Parser.prototype._trackVars = function(node, e) { + // keep track of vars in a function or global scope + var func = '__global__'; + var funcDoc = null; + + if (node.enclosingFunction) { + func = 'astnode' + node.enclosingFunction.hashCode(); + } + + funcDoc = this.refs[func]; + if (funcDoc) { + funcDoc.meta.vars = funcDoc.meta.vars || {}; + funcDoc.meta.vars[e.code.name] = false; + e.finishers.push(makeVarsFinisher(funcDoc)); + } +}; + +/** @private */ +exports.Parser.prototype._visitComment = function(comment) { + var e; + var original = String( comment.toSource() ); + var modified; + + if ( original && isValidJsdoc(original) ) { + this._comments.original.push(original); + + e = { + comment: original, + lineno: comment.getLineno(), + filename: this._currentSourceName, + range: getRange(comment) + }; + + this.emit('jsdocCommentFound', e, this); + + if (e.comment !== original) { + modified = e.comment; + } + + this._comments.modified.push(modified || original); + } + + return true; +}; + +/** @private */ +exports.Parser.prototype._visitNode = function(node) { + var e, + extras, + basename, + func, + funcDoc, + i, + l; + + if (node.type === Token.ASSIGN) { + e = this._makeEvent(node); + + basename = getBasename(e.code.name); + + if (basename !== 'this') { + e.code.funcscope = this.resolveVar(node, basename); + } + } + else if (node.type === Token.COLON) { // assignment within an object literal + extras = { + comment: String(node.left.getJsDoc() || '@undocumented'), + finishers: [this.addDocletRef, this.resolveEnum] + }; + e = this._makeEvent(node, extras); + } + else if (node.type === Token.GET || node.type === Token.SET) { // assignment within an object literal + extras = { + comment: String(node.left.getJsDoc() || '@undocumented') + }; + e = this._makeEvent(node, extras); + } + else if (node.type === Token.GETPROP) { // like 'obj.prop' in '/** @typedef {string} */ obj.prop;' + // this COULD be a Closure Compiler-style typedef, but it's probably not; to avoid filling + // the parse results with junk, only fire an event if there's a JSDoc comment attached + extras = { + lineno: node.getLineno() + }; + if ( node.getJsDoc() ) { + e = this._makeEvent(node, extras); + } + } + 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 + if (typeof node.setJsDoc !== 'undefined') { node.setJsDoc( node.parent.getJsDoc() ); } + } + + extras = { + lineno: node.getLineno() + }; + e = this._makeEvent(node, extras); + + this._trackVars(node, e); + } + else if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) { + extras = { + lineno: node.getLineno() + }; + e = this._makeEvent(node, extras); + + e.code.name = (node.type == tkn.NAMEDFUNCTIONSTATEMENT)? '' : String(node.name) || ''; + + this._trackVars(node, e); + + basename = getBasename(e.code.name); + e.code.funcscope = this.resolveVar(node, basename); + } + + if (!e) { + e = { + finishers: [] + }; + } + + for (i = 0, l = this._visitors.length; i < l; i++) { + this._visitors[i].visitNode(node, e, this, this._currentSourceName); + if (e.stopPropagation) { break; } + } + + if (!e.preventDefault && isValidJsdoc(e.comment)) { + this.emit(e.event, e, this); + } + + for (i = 0, l = e.finishers.length; i < l; i++) { + e.finishers[i].call(this, e); + } + + return true; +}; + +/** @private */ +exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) { + var NodeVisitor = Packages.org.mozilla.javascript.ast.NodeVisitor; + + var ast; + var e = { + filename: sourceName + }; + + this.emit('fileBegin', e); + + if (!e.defaultPrevented) { + e = { + filename: sourceName, + source: sourceCode + }; + this.emit('beforeParse', e); + sourceCode = e.source; + this._currentSourceName = sourceName = e.filename; + + sourceCode = pretreat(e.source); + + ast = parserFactory().parse(sourceCode, sourceName, 1); + + ast.visitComments( + new NodeVisitor({ + visit: this._visitComment.bind(this) + }) + ); + + ast.visit( + new NodeVisitor({ + visit: this._visitNode.bind(this) + }) + ); + } + + this.emit('fileComplete', e); + + this._currentSourceName = ''; +}; + +/** + * 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 id, + doclet, + alias; + + if (node.type === Token.VAR || node.type === Token.FUNCTION || + node.type == tkn.NAMEDFUNCTIONSTATEMENT) { + if (node.enclosingFunction) { // an inner var or func + id = 'astnode' + node.enclosingFunction.hashCode(); + doclet = this.refs[id]; + if (!doclet) { + return '~'; + } + return (doclet.longname || doclet.name) + '~'; + } + } + else { + // check local references for aliases + var scope = node, + basename = getBasename(nodeToString(node.left)); + while(scope.enclosingFunction) { + id = 'astnode' + scope.enclosingFunction.hashCode(); + doclet = this.refs[id]; + if ( doclet && doclet.meta.vars && hasOwnProp.call(doclet.meta.vars, basename) ) { + return [doclet.meta.vars[basename], basename]; + } + // move up + scope = scope.enclosingFunction; + } + // First check to see if we have a global scope alias + doclet = this.refs.__global__; + if ( doclet && doclet.meta.vars && hasOwnProp.call(doclet.meta.vars, basename) ) { + return [doclet.meta.vars[basename], basename]; + } + + id = 'astnode' + node.parent.hashCode(); + doclet = this.refs[id]; + if (!doclet) { + return ''; // global? + } + return doclet.longname || doclet.name; + } +}; + +/** + * Resolve what "this" refers to 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 = {}; + var parent; + + if (node.type !== Token.COLON && node.enclosingFunction) { + // get documentation for the enclosing function + 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){ + // memberof.doclet.meta.code.val + return this.resolveThis(node.enclosingFunction); + } + else { + return ''; // TODO handle global this? + } + } + } + else if (node.parent) { + if (node.parent.type === Token.COLON) { + parent = node.parent.parent; + } + else { + parent = node.parent; + } + + 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? + } +}; + +/** + * Given 'var foo = { x: 1 }', find foo from x. + */ +exports.Parser.prototype.resolvePropertyParent = function(node) { + var memberof = {}; + var parent; + + if (node.parent && node.parent.type === Token.COLON) { + parent = node.parent.parent; + } + else { + parent = node.parent; + } + + if (parent) { + memberof.id = 'astnode' + parent.hashCode(); + memberof.doclet = this.refs[memberof.id]; + + if (memberof.doclet) { + return memberof; + } + } +}; + +/** + * 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; + var enclosingFunction = node.enclosingFunction; + + if (!enclosingFunction) { + return ''; // global + } + + doclet = this.refs['astnode'+enclosingFunction.hashCode()]; + if (doclet && doclet.meta.vars && basename in doclet.meta.vars) { + return doclet.longname; + } + + return this.resolveVar(enclosingFunction, basename); +}; + +exports.Parser.prototype.addDocletRef = function(e) { + var node = e.code.node; + // allow lookup from value => doclet + if (e.doclet) { + this.refs['astnode' + node.hashCode()] = e.doclet; + } + // keep references to undocumented anonymous functions, too, as they might have scoped vars + else if ((node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) && + !this.refs['astnode' + node.hashCode()]) { + this.refs['astnode' + node.hashCode()] = { + longname: '', + meta: { + code: e.code + } + }; + } +}; + +exports.Parser.prototype.resolveEnum = function(e) { + var parent = this.resolvePropertyParent(e.code.node); + + if (parent && parent.doclet.isEnum) { + if (!parent.doclet.properties) { + parent.doclet.properties = []; + } + + // members of an enum inherit the enum's type + if (parent.doclet.type && !e.doclet.type) { + e.doclet.type = parent.doclet.type; + + } + + delete e.doclet.undocumented; + e.doclet.defaultvalue = e.doclet.meta.code.value; + + // add the doclet to the parent's properties + // use a copy of the doclet to avoid circular references + parent.doclet.properties.push( require('jsdoc/util/doop').doop(e.doclet) ); + } +}; + +/** + 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/data/scripts/jsdoc/lib/jsdoc/src/scanner.js b/data/scripts/jsdoc/lib/jsdoc/src/scanner.js new file mode 100644 index 00000000..914cf7fe --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/src/scanner.js @@ -0,0 +1,58 @@ +/** + @module jsdoc/src/scanner + @requires module:fs + + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + + +var fs = require('jsdoc/fs'); +var path = require('jsdoc/path'); + +/** + @constructor + @mixes module:events + */ +exports.Scanner = function() {}; +exports.Scanner.prototype = Object.create( require('events').EventEmitter.prototype ); + +/** + Recursively searches the given searchPaths for js files. + @param {Array.} searchPaths + @param {number} [depth=1] + @fires sourceFileFound + */ +exports.Scanner.prototype.scan = function(searchPaths, depth, filter) { + var cwd = process.cwd(), + filePaths = [], + self = this; + + searchPaths = searchPaths || []; + depth = depth || 1; + + searchPaths.forEach(function($) { + var filepath = decodeURIComponent($); + if ( fs.statSync(filepath).isFile() ) { + filePaths.push( path.resolve(cwd, filepath) ); + } + else { + filePaths = filePaths.concat( fs.ls(filepath, depth).map(function(item) { + return path.resolve(cwd, item); + }) ); + } + }); + + filePaths = filePaths.filter(function($) { + return filter.isIncluded($); + }); + + filePaths = filePaths.filter(function($) { + var e = { fileName: $ }; + self.emit('sourceFileFound', e); + + return !e.defaultPrevented; + }); + + return filePaths; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/tag.js b/data/scripts/jsdoc/lib/jsdoc/tag.js new file mode 100644 index 00000000..ca59f95d --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tag.js @@ -0,0 +1,103 @@ +/*global env: true */ +/** + @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 + */ + +var jsdoc = { + tag: { + dictionary: require('jsdoc/tag/dictionary'), + validator: require('jsdoc/tag/validator'), + type: require('jsdoc/tag/type') + } +}; + +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, ''); + } +} + +function processTagText(tag, tagDef) { + var tagType; + + if (tagDef.onTagText) { + tag.text = tagDef.onTagText(tag.text); + } + + if (tagDef.canHaveType || tagDef.canHaveName) { + /** The value property represents the result of parsing the tag text. */ + tag.value = {}; + + tagType = jsdoc.tag.type.parse(tag.text, tagDef.canHaveName, tagDef.canHaveType); + + // It is possible for a tag to *not* have a type but still have + // optional or defaultvalue, e.g. '@param [foo]'. + // Although tagType.type.length == 0 we should still copy the other properties. + if (tagType.type) { + if (tagType.type.length) { + tag.value.type = { + names: tagType.type + }; + } + tag.value.optional = tagType.optional; + tag.value.nullable = tagType.nullable; + tag.value.variable = tagType.variable; + tag.value.defaultvalue = tagType.defaultvalue; + } + + if (tagType.text && tagType.text.length) { + tag.value.description = tagType.text; + } + + if (tagDef.canHaveName) { + // note the dash is a special case: as a param name it means "no name" + if (tagType.name && tagType.name !== '-') { tag.value.name = tagType.name; } + } + } + else { + tag.value = tag.text; + } +} + +/** + 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 + */ +var Tag = exports.Tag = function(tagTitle, tagBody, meta) { + meta = meta || {}; + + this.originalTitle = trim(tagTitle); + + /** The title part of the tag: @title text */ + this.title = jsdoc.tag.dictionary.normalise(this.originalTitle); + + var tagDef = jsdoc.tag.dictionary.lookUp(this.title); + + /** The text part of the tag: @title text */ + this.text = trim(tagBody, tagDef.keepsWhitespace); + + if (this.text) { + processTagText(this, tagDef); + } + + jsdoc.tag.validator.validate(this, tagDef, meta); +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/tag/dictionary.js b/data/scripts/jsdoc/lib/jsdoc/tag/dictionary.js new file mode 100644 index 00000000..e87cc88c --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tag/dictionary.js @@ -0,0 +1,85 @@ +/** + @overview + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var hasOwnProp = Object.prototype.hasOwnProperty; + +var _tags = {}; +var _tagSynonyms = {}; +var _namespaces = []; +var dictionary; + + +/** @private */ +function TagDefinition(title, etc) { + var self = this; + etc = etc || {}; + + this.title = dictionary.normalise(title); + + Object.keys(etc).forEach(function(p) { + self[p] = etc[p]; + }); +} + +/** @private */ +TagDefinition.prototype.synonym = function(synonymName) { + _tagSynonyms[synonymName.toLowerCase()] = this.title; + return this; // chainable +}; + +/** @exports jsdoc/tag/dictionary */ +dictionary = { + /** @function */ + defineTag: function(title, opts) { + var def = new TagDefinition(title, opts); + // all the other dictionary functions use normalised names; we should too. + _tags[def.title] = def; + + if (opts.isNamespace) { + _namespaces.push(def.title); + } + + return _tags[def.title]; + }, + + /** @function */ + lookUp: function(title) { + title = dictionary.normalise(title); + + if ( hasOwnProp.call(_tags, title) ) { + return _tags[title]; + } + + return false; + }, + + /** @function */ + isNamespace: function(kind) { + if (kind) { + kind = dictionary.normalise(kind); + if ( _namespaces.indexOf(kind) !== -1) { + return true; + } + } + + return false; + }, + + /** @function */ + normalise: function(title) { + var canonicalName = title.toLowerCase(); + + if ( hasOwnProp.call(_tagSynonyms, canonicalName) ) { + return _tagSynonyms[canonicalName]; + } + + return canonicalName; + } +}; + +require('jsdoc/tag/dictionary/definitions').defineTags(dictionary); + +module.exports = dictionary; diff --git a/data/scripts/jsdoc/lib/jsdoc/tag/dictionary/definitions.js b/data/scripts/jsdoc/lib/jsdoc/tag/dictionary/definitions.js new file mode 100644 index 00000000..137b0286 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tag/dictionary/definitions.js @@ -0,0 +1,677 @@ +/*global app: true, env: true */ +/** + 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. + */ + +var path = require('path'); + +/** @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) { + var name = ''; + if (doclet.meta.filename) { + // TODO: find the shortest path shared by all input files, and remove that from + // doclet.meta.path + name += path.basename(doclet.meta.path) + '/'; + doclet.addTag( 'name', name + doclet.meta.filename ); + } +} + +function setDocletMemberof(doclet, tag) { + if (tag.value && tag.value !== '') { + doclet.setMemberof(tag.value); + } +} + +function applyNamespace(docletOrNs, tag) { + if (typeof docletOrNs === 'string') { // ns + tag.value = app.jsdoc.name.applyNamespace(tag.value, docletOrNs); + } + else { // doclet + if (!docletOrNs.name) { + return; // error? + } + + //doclet.displayname = doclet.name; + docletOrNs.longname = app.jsdoc.name.applyNamespace(docletOrNs.name, tag.title); + } +} + +function setDocletNameToFilename(doclet, tag) { + // TODO: find the shortest path shared by all input files, and remove that from doclet.meta.path + var name = doclet.meta.path ? path.basename(doclet.meta.path) + '/' : ''; + 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 { target: m[1], source: m[2] }; + } + else if (m[1]) { + return { target: m[1] }; + } + } else { + return {}; + } +} + +function firstWordOf(string) { + var m = /^(\S+)/.exec(string); + if (m) { return m[1]; } + else { return ''; } +} + +/** Populate the given dictionary with all known JSDoc tag definitions. + @param {module:jsdoc/tag/dictionary} dictionary +*/ +exports.defineTags = function(dictionary) { + + dictionary.defineTag('abstract', { + mustNotHaveValue: true, + onTagged: function(doclet, tag) { + // since "abstract" is reserved word in JavaScript let's use "virtual" in code + doclet.virtual = true; + } + }) + .synonym('virtual'); + + 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('author', { + mustHaveValue: true, + onTagged: function(doclet, tag) { + if (!doclet.author) { doclet.author = []; } + doclet.author.push(tag.value); + } + }); + + // I add on to that + dictionary.defineTag('augments', { + mustHaveValue: true, + // Allow augments value to be specified as a normal type, e.g. {Type} + onTagText: function(text) { + var type = require('jsdoc/tag/type'), + tagType = type.parse(text, false, true); + return tagType.typeExpression || text; + }, + 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 borrows = parseBorrows(doclet, tag); + doclet.borrow(borrows.target, borrows.source); + } + }); + + // that adds all of it's members to me + dictionary.defineTag('mixes', { + mustHaveValue: true, + onTagged: function(doclet, tag) { + var source = firstWordOf(tag.value); + doclet.mix(source); + } + }); + + 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', { + onTagged: function(doclet, tag) { + var ownerClassName; + if (!tag.value) { + ownerClassName = '{@thisClass}'; // this can be resolved later in the handlers + } + else { + ownerClassName = firstWordOf(tag.value); + } + doclet.addTag('alias', ownerClassName); + doclet.addTag('kind', 'class'); + } + }); + + dictionary.defineTag('default', { + 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('defaultvalue'); + + dictionary.defineTag('deprecated', { + // value is optional + onTagged: function(doclet, tag) { + doclet.deprecated = tag.value || true; + } + }); + + dictionary.defineTag('description', { + mustHaveValue: true + }) + .synonym('desc'); + + dictionary.defineTag('enum', { + canHaveType: true, + onTagged: function(doclet, tag) { + doclet.kind = 'member'; + doclet.isEnum = true; + if (tag.value && tag.value.type) { doclet.type = tag.value.type; } + } + }); + + dictionary.defineTag('event', { + isNamespace: true, + onTagged: function(doclet, tag) { + setDocletKindToTitle(doclet, tag); + setDocletNameToValue(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('external', { + canHaveType: true, + isNamespace: true, + onTagged: function(doclet, tag) { + setDocletKindToTitle(doclet, tag); + setDocletNameToValue(doclet, tag); + if (tag.value && tag.value.type) { + doclet.type = tag.value.type; + } + } + }) + .synonym('host'); + + 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 = []; } + applyNamespace('event', tag); + doclet.fires.push(tag.value); + } + }) + .synonym('emits'); + + dictionary.defineTag('function', { + onTagged: function(doclet, tag) { + setDocletKindToTitle(doclet, tag); + setDocletNameToValue(doclet, tag); + } + }) + .synonym('func') + .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('lends', { + onTagged: function(doclet, tag) { + doclet.alias = tag.value || ''; + doclet.addTag('undocumented'); + } + }); + + dictionary.defineTag('license', { + mustHaveValue: true, + onTagged: function(doclet, tag) { + doclet.license = tag.value; + } + }); + + dictionary.defineTag('listens', { + mustHaveValue: true, + onTagged: function (doclet, tag) { + if (!doclet.listens) { doclet.listens = []; } + applyNamespace('event', tag); + doclet.listens.push(tag.value); + // TODO: verify that parameters match the event parameters? + } + }); + + dictionary.defineTag('member', { + canHaveType: true, + onTagged: function(doclet, tag) { + setDocletKindToTitle(doclet, tag); + setDocletNameToValue(doclet, tag); + if (tag.value && tag.value.type) { + doclet.type = tag.value.type; + } + } + }) + .synonym('var'); + + dictionary.defineTag('memberof', { + mustHaveValue: true, + onTagged: function(doclet, tag) { + if (tag.originalTitle === 'memberof!') { + doclet.forceMemberof = true; + if (tag.value === '') { + doclet.addTag('global'); + delete doclet.memberof; + } + } + setDocletMemberof(doclet, tag); + } + }) + .synonym('memberof!'); + + 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); + if (!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, // param name can be found in the source code if not provided + 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', { + mustHaveValue: true, + canHaveType: true, + canHaveName: true, + onTagged: function(doclet, tag) { + if (!doclet.properties) { doclet.properties = []; } + doclet.properties.push(tag.value); + } + }) + .synonym('prop'); + + 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('static', { + onTagged: function(doclet, tag) { + setDocletScopeToTitle(doclet, tag); + } + }); + + 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('todo', { + mustHaveValue: true, + onTagged: function(doclet, tag) { + if (!doclet.todo) { doclet.todo = []; } + doclet.todo.push(tag.value); + } + }); + + dictionary.defineTag('tutorial', { + mustHaveValue: true, + onTagged: function(doclet, tag) { + if (!doclet.tutorials) { doclet.tutorials = []; } + doclet.tutorials.push(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); + } + + // callbacks are always type {function} + if (tag.originalTitle === 'callback') { + doclet.type = { + names: [ + 'function' + ] + }; + } + else if (tag.value.type) { + doclet.type = tag.value.type; + } + } + } + }) + .synonym('callback'); + + 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; + } + }); +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/tag/inline.js b/data/scripts/jsdoc/lib/jsdoc/tag/inline.js new file mode 100644 index 00000000..b425ef87 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tag/inline.js @@ -0,0 +1,107 @@ +/** + * @module jsdoc/tag/inline + * + * @author Jeff Williams + * @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +/** + * Information about an inline tag that was found within a string. + * + * @typedef {Object} InlineTagInfo + * @memberof module:jsdoc/tag/inline + * @property {?string} completeTag - The entire inline tag, including its enclosing braces. + * @property {?string} tag - The tag whose text was found. + * @property {?string} text - The tag text that was found. + */ + +/** + * Information about the results of replacing inline tags within a string. + * + * @typedef {Object} InlineTagResult + * @memberof module:jsdoc/tag/inline + * @property {Array.} tags - The inline tags that were found. + * @property {string} newString - The updated text string after extracting or replacing the inline + * tags. + */ + +/** + * Text-replacing function for strings that contain an inline tag. + * + * @callback InlineTagReplacer + * @memberof module:jsdoc/tag/inline + * @param {string} string - The complete string containing the inline tag. + * @param {module:jsdoc/tag/inline.InlineTagInfo} tagInfo - Information about the inline tag. + * @return {string} An updated version of the complete string. + */ + +/** + * Replace all instances of multiple inline tags with other text. + * + * @param {string} string - The string in which to replace the inline tags. + * @param {Object} replacers - The functions that are used to replace text in the string. The keys + * must contain tag names (for example, `link`), and the values must contain functions with the + * type {@link module:jsdoc/tag/inline.InlineTagReplacer}. + * @return {module:jsdoc/tag/inline.InlineTagResult} The updated string, as well as information + * about the inline tags that were found. + */ +exports.replaceInlineTags = function(string, replacers) { + var tagInfo = []; + + function replaceMatch(replacer, tag, match, text) { + var matchedTag = { + completeTag: match, + tag: tag, + text: text + }; + tagInfo.push(matchedTag); + + return replacer.call(this, string, matchedTag); + } + + string = string || ''; + Object.keys(replacers).forEach(function(replacer) { + var tagRegExp = new RegExp('\\{@' + replacer + '\\s+(.+?)\\}', 'gi'); + var matches; + // call the replacer once for each match + while ( (matches = tagRegExp.exec(string)) !== null ) { + string = replaceMatch(replacers[replacer], replacer, matches[0], matches[1]); + } + }); + + return { + tags: tagInfo, + newString: string.trim() + }; +}; + +/** + * Replace all instances of an inline tag with other text. + * + * @param {string} string - The string in which to replace the inline tag. + * @param {string} tag - The name of the inline tag to replace. + * @param {module:jsdoc/tag/inline.InlineTagReplacer} replacer - The function that is used to + * replace text in the string. + * @return {module:jsdoc/tag/inline.InlineTagResult} The updated string, as well as information + * about the inline tags that were found. + */ +exports.replaceInlineTag = function(string, tag, replacer) { + var replacers = {}; + replacers[tag] = replacer; + + return exports.replaceInlineTags(string, replacers); +}; + +/** + * Extract inline tags from a string, replacing them with an empty string. + * + * @param {string} string - The string from which to extract text. + * @param {?string} tag - The inline tag to extract. + * @return {module:jsdoc/tag/inline.InlineTagResult} The updated string, as well as information + * about the inline tags that were found. + */ +exports.extractInlineTag = function(string, tag) { + return exports.replaceInlineTag(string, tag, function(str, tagInfo) { + return str.replace(tagInfo.completeTag, ''); + }); +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/tag/type.js b/data/scripts/jsdoc/lib/jsdoc/tag/type.js new file mode 100644 index 00000000..a14df5be --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tag/type.js @@ -0,0 +1,286 @@ +/** + * @module jsdoc/tag/type + * + * @author Michael Mathews + * @author Jeff Williams + * @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +/** + * Information about a type expression extracted from tag text. + * + * @typedef TypeExpressionInfo + * @memberof module:jsdoc/tag/type + * @property {string} expression - The type expression. + * @property {string} text - The updated tag text. + */ + +/** @private */ +function unescapeBraces(text) { + return text.replace(/\\\{/g, '{') + .replace(/\\\}/g, '}'); +} + +/** + * Extract a type expression from the tag text. + * + * @private + * @param {string} string - The tag text. + * @return {module:jsdoc/tag/type.TypeExpressionInfo} The type expression and updated tag text. + */ + function extractTypeExpression(string) { + string = string || ''; + + var completeExpression; + var count = 0; + var position = 0; + var expression = ''; + var startIndex = string.indexOf('{'); + var textStartIndex; + + if (startIndex !== -1) { + // advance to the first character in the type expression + position = textStartIndex = startIndex + 1; + count++; + + while (position < string.length) { + switch (string[position]) { + case '\\': + // backslash is an escape character, so skip the next character + position++; + break; + case '{': + count++; + break; + case '}': + count--; + break; + default: + // do nothing + } + + if (count === 0) { + completeExpression = string.slice(startIndex, position + 1); + expression = string.slice(textStartIndex, position).trim(); + break; + } + + position++; + } + } + + string = completeExpression ? string.replace(completeExpression, '') : string; + + return { + expression: unescapeBraces(expression), + newString: string.trim() + }; +} + +/** @private */ +function getTagInfo(tagValue, canHaveName, canHaveType) { + var name = ''; + var typeExpression = ''; + var text = tagValue; + var expressionAndText; + var typeOverride; + + if (canHaveType) { + expressionAndText = extractTypeExpression(text); + typeExpression = expressionAndText.expression; + text = expressionAndText.newString; + } + + if (canHaveName) { + // like: name, [name], name text, [name] text, name - text, or [name] - text + text.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/); + name = RegExp.$1; + text = RegExp.$3; + } + + // an inline @type tag, like {@type Foo}, overrides the type expression + if (canHaveType) { + typeOverride = require('jsdoc/tag/inline').extractInlineTag(text, 'type'); + if (typeOverride.tags && typeOverride.tags[0]) { + typeExpression = typeOverride.tags[0].text || typeExpression; + } + text = typeOverride.newString; + } + + return { + name: name, + typeExpression: typeExpression, + text: text + }; +} + +/** + * Information provided in a JSDoc tag. + * + * @typedef {Object} TagInfo + * @memberof module:jsdoc/tag/type + * @property {string} TagInfo.defaultvalue - The default value of the member. + * @property {string} TagInfo.name - The name of the member (for example, `myParamName`). + * @property {boolean} TagInfo.nullable - Indicates whether the member can be set to `null` or + * `undefined`. + * @property {boolean} TagInfo.optional - Indicates whether the member is optional. + * @property {string} TagInfo.text - Descriptive text for the member (for example, `The user's email + * address.`). + * @property {Array.} TagInfo.type - The type or types that the member can contain (for + * example, `string` or `MyNamespace.MyClass`). + * @property {string} TagInfo.typeExpression - The type expression that was parsed to identify the + * types. + * @property {boolean} TagInfo.variable - Indicates whether the number of members that are provided + * can vary (for example, in a function that accepts any number of parameters). + */ + +/** + * Extract JSDoc-style type information from the name specified in the tag info, including the + * member name; whether the member is optional; and the default value of the member. + * + * @private + * @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag. + * @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag. + */ +function parseName(tagInfo) { + // like '[foo]' or '[ foo ]' or '[foo=bar]' or '[ foo=bar ]' or '[ foo = bar ]' + if ( /^\[\s*(.+?)\s*\]$/.test(tagInfo.name) ) { + tagInfo.name = RegExp.$1; + tagInfo.optional = true; + + // like 'foo=bar' or 'foo = bar' + if ( /^(.+?)\s*=\s*(.+)$/.test(tagInfo.name) ) { + tagInfo.name = RegExp.$1; + tagInfo.defaultvalue = RegExp.$2; + } + } + + return tagInfo; +} + +/** @private */ +function getTypeStrings(parsedType) { + var types = []; + + var catharsis = require('catharsis'); + var TYPES = catharsis.Types; + var util = require('util'); + + switch(parsedType.type) { + case TYPES.AllLiteral: + types.push('*'); + break; + case TYPES.FunctionType: + types.push('function'); + break; + case TYPES.NameExpression: + types.push(parsedType.name); + break; + case TYPES.NullLiteral: + types.push('null'); + break; + case TYPES.RecordType: + types.push('Object'); + break; + case TYPES.TypeApplication: + types.push( catharsis.stringify(parsedType) ); + break; + case TYPES.TypeUnion: + parsedType.elements.forEach(function(element) { + types = types.concat( getTypeStrings(element) ); + }); + break; + case TYPES.UndefinedLiteral: + types.push('undefined'); + break; + case TYPES.UnknownLiteral: + types.push('?'); + break; + default: + // this shouldn't happen + throw new Error( util.format('unrecognized type %s in parsed type: %j', parsedType.type, + parsedType) ); + } + + return types; +} + +/** + * Extract JSDoc-style and Closure Compiler-style type information from the type expression + * specified in the tag info. + * + * @private + * @param {module:jsdoc/tag/type.TagInfo} tagInfo - Information contained in the tag. + * @return {module:jsdoc/tag/type.TagInfo} Updated information from the tag. + */ +function parseTypeExpression(tagInfo) { + var catharsis = require('catharsis'); + var util = require('util'); + + var errorMessage; + var parsedType; + + // don't try to parse empty type expressions + if (!tagInfo.typeExpression) { + return tagInfo; + } + + try { + parsedType = catharsis.parse(tagInfo.typeExpression, {jsdoc: true}); + } + catch (e) { + // always re-throw so the caller has a chance to report which file was bad + throw new Error( util.format('unable to parse the type expression "%s": %s', + tagInfo.typeExpression, e.message) ); + } + + if (parsedType) { + tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType) ); + + // Catharsis and JSDoc use the same names for 'optional' and 'nullable'... + ['optional', 'nullable'].forEach(function(key) { + if (parsedType[key] !== null && parsedType[key] !== undefined) { + tagInfo[key] = parsedType[key]; + } + }); + + // ...but not 'variable'. + if (parsedType.repeatable !== null && parsedType.repeatable !== undefined) { + tagInfo.variable = parsedType.repeatable; + } + } + + return tagInfo; +} + +// TODO: allow users to add/remove type parsers (perhaps via plugins) +var typeParsers = [parseName, parseTypeExpression]; + +/** + * Parse the value of a JSDoc tag. + * + * @param {string} tagValue - The value of the tag. For example, the tag `@param {string} name` has + * a value of `{string} name`. + * @param {boolean} canHaveName - Indicates whether the value can include a symbol name. + * @param {boolean} canHaveType - Indicates whether the value can include a type expression that + * describes the symbol. + * @return {module:jsdoc/tag/type.TagInfo} Information obtained from the tag. + * @throws {Error} Thrown if a type expression cannot be parsed. + */ +exports.parse = function(tagValue, canHaveName, canHaveType) { + if (typeof tagValue !== 'string') { tagValue = ''; } + + var tagInfo = getTagInfo(tagValue, canHaveName, canHaveType); + tagInfo.type = tagInfo.type || []; + + typeParsers.forEach(function(parser) { + tagInfo = parser.call(this, tagInfo); + }); + + // if we wanted a type, but the parsers didn't add any type names, use the type expression + if (canHaveType && !tagInfo.type.length && tagInfo.typeExpression) { + tagInfo.type = [tagInfo.typeExpression]; + } + + return tagInfo; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/tag/validator.js b/data/scripts/jsdoc/lib/jsdoc/tag/validator.js new file mode 100644 index 00000000..5621cced --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tag/validator.js @@ -0,0 +1,62 @@ +/*global env: true */ +/** + @module jsdoc/tag/validator + @requires jsdoc/tag/dictionary + + @author Michael Mathews + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + + +var dictionary = require('jsdoc/tag/dictionary'); +var format = require('util').format; + +function buildMessage(tagName, meta, desc) { + var result = format('The @%s tag %s. File: %s, line: %s', tagName, desc, meta.filename, + meta.lineno); + if (meta.comment) { + result += '\n' + meta.comment; + } + return result; +} + +function UnknownTagError(tagName, meta) { + this.name = 'UnknownTagError'; + this.message = buildMessage(tagName, meta, 'is not a known tag'); +} +UnknownTagError.prototype = new Error(); +UnknownTagError.prototype.constructor = UnknownTagError; + +function TagValueRequiredError(tagName, meta) { + this.name = 'TagValueRequiredError'; + this.message = buildMessage(tagName, meta, 'requires a value'); +} +TagValueRequiredError.prototype = new Error(); +TagValueRequiredError.prototype.constructor = TagValueRequiredError; + +function TagValueNotPermittedError(tagName, meta) { + this.name = 'TagValueNotPermittedError'; + this.message = buildMessage(tagName, meta, 'does not permit a value'); +} +TagValueNotPermittedError.prototype = new Error(); +TagValueNotPermittedError.prototype.constructor = TagValueNotPermittedError; + +/** + * Validate the given tag. + */ +exports.validate = function(tag, tagDef, meta) { + if (!tagDef && !env.conf.tags.allowUnknownTags) { + require('jsdoc/util/error').handle( new UnknownTagError(tag.title, meta) ); + } + + if (!tag.text) { + if (tagDef.mustHaveValue) { + require('jsdoc/util/error').handle( new TagValueRequiredError(tag.title, meta) ); + } + } + else { + if (tagDef.mustNotHaveValue) { + require('jsdoc/util/error').handle( new TagValueNotPermittedError(tag.title, meta) ); + } + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/template.js b/data/scripts/jsdoc/lib/jsdoc/template.js new file mode 100644 index 00000000..3fb2e762 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/template.js @@ -0,0 +1,84 @@ +/** + * @file Wrapper for underscore's template utility to allow loading templates from files. + * @author Rafał Wrzeszcz + * @author Matthew Christopher Kastor-Inare III + * @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var _ = require('underscore'), + fs = require('jsdoc/fs'), + path = require('path'); + + +/** + @module jsdoc/template + */ + +/** + @class + @classdesc Underscore template helper. + @param {string} path - Templates directory. + */ +exports.Template = function(path) { + this.path = path; + this.layout = null; + this.cache = {}; + // override default template tag settings + this.settings = { + evaluate : /<\?js([\s\S]+?)\?>/g, + interpolate: /<\?js=([\s\S]+?)\?>/g, + escape : /<\?js~([\s\S]+?)\?>/g + }; + +}; + +/** Loads template from given file. + @param {string} file - Template filename. + @return {function} Returns template closure. + */ +exports.Template.prototype.load = function(file) { + var _path = path.join(this.path, file); + return _.template(fs.readFileSync(_path, 'utf8'), null, this.settings); +}; + + +/** + Renders template using given data. + + This is low-level function, for rendering full templates use {@link Template.render()}. + + @param {string} file - Template filename. + @param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use). + @return {string} Rendered template. + */ +exports.Template.prototype.partial = function(file, data) { + // load template into cache + if (!(file in this.cache)) { + this.cache[file] = this.load(file); + } + + // keep template helper context + return this.cache[file].call(this, data); +}; + +/** + Renders template with given data. + + This method automaticaly applies layout if set. + + @param {string} file - Template filename. + @param {object} data - Template variables (doesn't have to be object, but passing variables dictionary is best way and most common use). + @return {string} Rendered template. + */ +exports.Template.prototype.render = function(file, data) { + // main content + var content = this.partial(file, data); + + // apply layout + if (this.layout) { + data.content = content; + content = this.partial(this.layout, data); + } + + return content; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/tutorial.js b/data/scripts/jsdoc/lib/jsdoc/tutorial.js new file mode 100644 index 00000000..81ef5b5b --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tutorial.js @@ -0,0 +1,110 @@ +/** + @overview + @author Rafał Wrzeszcz + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var markdown = require('jsdoc/util/markdown'); + +/** Removes child tutorial from the parent. Does *not* unset child.parent though. + @param {Tutorial} parent - parent tutorial. + @param {Tutorial} child - Old child. + @private + */ +function removeChild(parent, child) { + var index = parent.children.indexOf(child); + if (index != -1) { + parent.children.splice(index, 1); + } +} + +/** Adds a child to the parent tutorial. Does *not* set child.parent though. + @param {Tutorial} parent - parent tutorial. + @param {Tutorial} child - New child. + @private + */ +function addChild(parent, child) { + parent.children.push(child); +} + +/** + @module jsdoc/tutorial + */ + +/** + @class + @classdesc Represents a single JSDoc tutorial. + @param {string} name - Tutorial name. + @param {string} content - Text content. + @param {number} type - Source formating. + */ +exports.Tutorial = function(name, content, type) { + this.title = this.name = name; + this.content = content; + this.type = type; + + // default values + this.parent = null; + this.children = []; +}; + +/** Moves children from current parent to different one. + @param {?Tutorial} parent - New parent. If null, the tutorial has no parent. + */ +exports.Tutorial.prototype.setParent = function(parent) { + // removes node from old parent + if (this.parent) { + removeChild(this.parent, this); + } + + this.parent = parent; + if (parent) { + addChild(parent, this); + } +}; + +/** Removes children from current node. + @param {Tutorial} child - Old child. + */ +exports.Tutorial.prototype.removeChild = function(child) { + child.setParent(null); +}; + +/** Adds new children to current node. + @param {Tutorial} child - New child. + */ +exports.Tutorial.prototype.addChild = function(child) { + child.setParent(this); +}; + +/** Prepares source. + @return {string} HTML source. + */ +exports.Tutorial.prototype.parse = function() { + switch (this.type) { + // nothing to do + case exports.TYPES.HTML: + return this.content; + + // markdown + case exports.TYPES.MARKDOWN: + var mdParse = markdown.getParser(); + return mdParse(this.content) + .replace(/&/g, '&') // because markdown escapes these + .replace(/</g, '<') + .replace(/>/g, '>'); + + // uhm... should we react somehow? + // if not then this case can be merged with TYPES.HTML + default: + return this.content; + } +}; + +/** Tutorial source types. + @enum {number} + */ +exports.TYPES = { + HTML: 1, + MARKDOWN: 2 +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/tutorial/resolver.js b/data/scripts/jsdoc/lib/jsdoc/tutorial/resolver.js new file mode 100644 index 00000000..c07a3f7e --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/tutorial/resolver.js @@ -0,0 +1,190 @@ +/*global env: true */ +/** + @overview + @author Rafał Wrzeszcz + @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +/** + @module jsdoc/tutorial/resolver + */ + +var tutorial = require('jsdoc/tutorial'), + fs = require('jsdoc/fs'), + error = require('jsdoc/util/error'), + path = require('path'), + hasOwnProp = Object.prototype.hasOwnProperty, + conf = {}, + tutorials = {}, + finder = /^(.*)\.(x(?:ht)?ml|html?|md|markdown|json)$/i; + +/** checks if `conf` is the metadata for a single tutorial. + * A tutorial's metadata has a property 'title' and/or a property 'children'. + * @param {object} json - the object we want to test (typically from JSON.parse) + * @returns {boolean} whether `json` could be the metadata for a tutorial. + */ +function isTutorialJSON(json) { + // if conf.title exists or conf.children exists, it is metadata for a tutorial + return (hasOwnProp.call(json, 'title') || hasOwnProp.call(json, 'children')); +} + +/** Helper function that adds tutorial configuration to the `conf` variable. + * This helps when multiple tutorial configurations are specified in one object, + * or when a tutorial's children are specified as tutorial configurations as + * opposed to an array of tutorial names. + * + * Recurses as necessary to ensure all tutorials are added. + * + * @param {string} name - if `meta` is a configuration for a single tutorial, + * this is that tutorial's name. + * @param {object} meta - object that contains tutorial information. + * Can either be for a single tutorial, or for multiple + * (where each key in `meta` is the tutorial name and each + * value is the information for a single tutorial). + * Additionally, a tutorial's 'children' property may + * either be an array of strings (names of the child tutorials), + * OR an object giving the configuration for the child tutorials. + */ +function addTutorialConf(name, meta) { + var names, i; + if (isTutorialJSON(meta)) { + // if the children are themselves tutorial defintions as opposed to an + // array of strings, add each child. + if (hasOwnProp.call(meta, 'children') && !Array.isArray(meta.children)) { + names = Object.keys(meta.children); + for (i = 0; i < names.length; ++i) { + addTutorialConf(names[i], meta.children[names[i]]); + } + // replace with an array of names. + meta.children = names; + } + // check if the tutorial has already been defined... + if (hasOwnProp.call(conf, name)) { + error.handle(new Error("Tutorial " + name + "'s metadata is defined multiple times, only the first will be used.")); + } else { + conf[name] = meta; + } + } else { + // it's an object of tutorials, the keys are th etutorial names. + names = Object.keys(meta); + for (i = 0; i < names.length; ++i) { + addTutorialConf(names[i], meta[names[i]]); + } + } +} + +/** Adds new tutorial. + @param {tutorial.Tutorial} current - New tutorial. + */ +exports.addTutorial = function(current) { + if (hasOwnProp.call(tutorials, current.name)) { + error.handle(new Error("Tutorial with name " + current.name + " exists more than once, not adding (same name, different file extensions?)")); + } else { + tutorials[current.name] = current; + + // default temporary parent + current.setParent(exports.root); + } +}; + +/** Root tutorial. + @type tutorial.Tutorial + */ +exports.root = new tutorial.Tutorial('', ''); + +/** Additional instance method for root node. + @param {string} name - Tutorial name. + @return {tutorial.Tutorial} Tutorial instance. + */ +exports.root.getByName = function(name) { + return hasOwnProp.call(tutorials, name) && tutorials[name]; +}; + +/** Load tutorials from given path. + @param {string} _path - Tutorials directory. + */ +exports.load = function(_path) { + var match, + type, + name, + content, + current, + files = fs.ls(_path); + + // tutorials handling + files.forEach(function(file) { + match = file.match(finder); + + // any filetype that can apply to tutorials + if (match) { + name = path.basename(match[1]); + content = fs.readFileSync(file, env.opts.encoding); + + switch (match[2].toLowerCase()) { + // HTML type + case 'xml': + case 'xhtml': + case 'html': + case 'htm': + type = tutorial.TYPES.HTML; + break; + + // Markdown typs + case 'md': + case 'markdown': + type = tutorial.TYPES.MARKDOWN; + break; + + // configuration file + case 'json': + var meta = JSON.parse(content); + addTutorialConf(name, meta); + // don't add this as a tutorial + return; + + // how can it be? check `finder' regexp + default: + // not a file we want to work with + return; + } + + current = new tutorial.Tutorial(name, content, type); + exports.addTutorial(current); + } + }); +}; + +/** Resolves hierarchical structure. + */ +exports.resolve = function() { + var item, + current; + for (var name in conf) { + if ( hasOwnProp.call(conf, name) ) { + // TODO: should we complain about this? + if (!hasOwnProp.call(tutorials, name)) { + continue; + } + + item = conf[name]; + current = tutorials[name]; + + // set title + if (item.title) { + current.title = item.title; + } + + // add children + if (item.children) { + item.children.forEach(function(child) { + if (!hasOwnProp.call(tutorials, child)) { + error.handle( new Error("Missing child tutorial: " + child) ); + } + else { + tutorials[child].setParent(current); + } + }); + } + } + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/util/doop.js b/data/scripts/jsdoc/lib/jsdoc/util/doop.js new file mode 100644 index 00000000..d3c148cb --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/doop.js @@ -0,0 +1,20 @@ +/** + Deep clone a simple object. + @private + */ +var doop = exports.doop = function(o) { + var clone, + prop; + + if (o instanceof Object && o.constructor != Function) { + clone = o instanceof Array ? [] : {}; + + Object.keys(o).forEach(function(prop) { + clone[prop] = (o[prop] instanceof Object) ? doop(o[prop]) : o[prop]; + }); + + return clone; + } + + return o; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/util/dumper.js b/data/scripts/jsdoc/lib/jsdoc/util/dumper.js new file mode 100644 index 00000000..e22279dc --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/dumper.js @@ -0,0 +1,94 @@ +/** + * Recursively print out all names and values in a data structure. + * @module jsdoc/util/dumper + * @author Michael Mathews + * @license Apache License 2.0 - See file 'LICENSE.md' in this project. + */ + +var util = require('util'); + +var seenItems = []; +function seen(object) { + if (seenItems.indexOf(object) !== -1) { + return true; + } + + return false; +} + +// some objects are unwalkable, like Java native objects +function isUnwalkable(o) { + return (o && typeof o === 'object' && typeof o.constructor === 'undefined'); +} + +function isFunction(o) { + return (o && typeof o === 'function' || o instanceof Function); +} + +function isObject(o) { + return o && o instanceof Object || + (o && typeof o.constructor !== 'undefined' && o.constructor.name === 'Object'); +} + +function checkCircularRefs(o, func) { + if ( seen(o) ) { + return ''; + } + else { + seenItems.push(o); + return func.call(this, o); + } +} + +function walk(o) { + var result; + + if ( isUnwalkable(o) ) { + result = ''; + } + else if ( o === undefined ) { + result = 'undefined'; + } + else if ( Array.isArray(o) ) { + result = checkCircularRefs(o, function(arr) { + var newArray = []; + arr.forEach(function(item) { + newArray.push( walk(item) ); + }); + + return newArray; + }); + } + else if ( util.isRegExp(o) ) { + result = ''; + } + else if ( util.isDate(o) ) { + result = ''; + } + else if ( isFunction(o) ) { + result = ''; + } + else if ( isObject(o) && o !== null ) { + result = checkCircularRefs(o, function(obj) { + var newObj = {}; + Object.keys(obj).forEach(function(key) { + newObj[key] = walk(obj[key]); + }); + + return newObj; + }); + } + // should be safe to JSON.stringify() everything else + else { + result = o; + } + + return result; +} + +/** + * @param {*} object + */ +exports.dump = function(object) { + return JSON.stringify(walk(object), null, 4); +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/util/error.js b/data/scripts/jsdoc/lib/jsdoc/util/error.js new file mode 100644 index 00000000..57d950b3 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/error.js @@ -0,0 +1,32 @@ +/*global env: true */ +/** + Helper functions for handling errors. + @module jsdoc/util/error + */ + +/** + Handle an exception appropriately based on whether lenient mode is enabled: + + + If lenient mode is enabled, log the exception to the console. + + If lenient mode is not enabled, re-throw the exception. + @param {Error} e - The exception to handle. + @exception {Error} Re-throws the original exception unless lenient mode is enabled. + @memberof module:jsdoc/util/error + */ +exports.handle = function(e) { + var msg; + + if (env.opts.lenient) { + msg = e.message || JSON.stringify(e); + + // include the error type if it's an Error object + if (e instanceof Error) { + msg = e.name + ': ' + msg; + } + + console.log(msg); + } + else { + throw e; + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/util/global.js b/data/scripts/jsdoc/lib/jsdoc/util/global.js new file mode 100644 index 00000000..dc20169d --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/global.js @@ -0,0 +1,7 @@ +/** + * The global object to which functions and properties can be assigned. + * @private + */ +module.exports = (function() { + return this; +}).call(null); diff --git a/data/scripts/jsdoc/lib/jsdoc/util/include.js b/data/scripts/jsdoc/lib/jsdoc/util/include.js new file mode 100644 index 00000000..54d1efa4 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/include.js @@ -0,0 +1,19 @@ +var path = require('path'); +var vm = require('jsdoc/util/vm'); + +/** + * Read and execute a JavaScript file in global scope. + * @private + * @param {string} filepath The path to the JavaScript file. May contain an absolute path or a + * path relative to env.dirname. + */ +module.exports = function(filepath) { + filepath = path.resolve(__dirname, filepath); + + try { + vm.getModule('jsdoc/util/include')(filepath); + } + catch (e) { + console.log('Cannot include ' + filepath + ': ' + e); + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/util/markdown.js b/data/scripts/jsdoc/lib/jsdoc/util/markdown.js new file mode 100644 index 00000000..3fdd217d --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/markdown.js @@ -0,0 +1,112 @@ +/*global env: true */ + +/** + * Provides access to Markdown-related functions. + * @module jsdoc/util/markdown + * @author Michael Mathews + * @author Ben Blank + */ + +/** + * Enumeration of Markdown parsers that are available. + * @enum {String} + */ +var parsers = { + /** The "[markdown-js](https://github.com/evilstreak/markdown-js)" (aka "evilstreak") parser. */ + evilstreak: "markdown", + /** + * The "GitHub-flavored Markdown" parser. + * @deprecated Replaced by "marked." + */ + gfm: "marked", + /** + * The "[Marked](https://github.com/chjj/marked)" parser. + */ + marked: "marked" +}; + +/** + * Escape underscores that occur within {@ ... } in order to protect them + * from the markdown parser(s). + * @param {String} source the source text to sanitize. + * @returns {String} `source` where underscores within {@ ... } have been + * protected with a preceding backslash (i.e. \_) -- the markdown parsers + * will strip the backslash and protect the underscore. + */ +function escapeUnderscores(source) { + return source.replace(/\{@[^}\r\n]+\}/g, function (wholeMatch) { + return wholeMatch.replace(/(^|[^\\])_/g, '$1\\_'); + }); +} + +/** + * Retrieve a function that accepts a single parameter containing Markdown source. The function uses + * the specified parser to transform the Markdown source to HTML, then returns the HTML as a string. + * + * @private + * @param {String} parser The name of the selected parser. + * @param {Object} [conf] Configuration for the selected parser, if any. + * @returns {Function} A function that accepts Markdown source, feeds it to the selected parser, and + * returns the resulting HTML. + * @throws {Error} If the name does not correspond to a known parser. + */ +function getParseFunction(parser, conf) { + conf = conf || {}; + var parse; + + if (parser === parsers.marked) { + parser = require(parser); + parser.setOptions({ + gfm: true, + tables: true, + breaks: false, + pedantic: false, + sanitize: true, + smartLists: true, + langPrefix: 'lang-' + }); + parse = function(source) { + source = escapeUnderscores(source); + return parser(source) + .replace(/\s+$/, '') + .replace(/'/g, "'"); + }; + parse._parser = parsers.marked; + return parse; + } else if (parser === parsers.evilstreak) { + parser = require(parser).markdown; + + parse = function(source) { + source = escapeUnderscores(source); + // evilstreak parser expects line endings to be \n + source = source.replace(/\r\n|\r/g, '\n'); + return parser.toHTML(source, conf.dialect); + }; + parse._parser = parsers.evilstreak; + return parse; + } else { + throw new Error("unknown Markdown parser: '" + parser + "'"); + } +} + +/** + * Retrieve a Markdown parsing function based on the value of the `conf.json` file's + * `env.conf.markdown` property. The parsing function accepts a single parameter containing Markdown + * source. The function uses the parser specified in `conf.json` to transform the Markdown source to + * HTML, then returns the HTML as a string. + * @returns {Function} A function that accepts Markdown source, feeds it to the selected parser, and + * returns the resulting HTML. + * @throws {Error} If the value of `env.conf.markdown.parser` does not correspond to a known parser. + */ +exports.getParser = function() { + var conf = env.conf.markdown; + if (conf && conf.parser) { + return getParseFunction(parsers[conf.parser], conf); + } else if (conf && conf.githubRepoOwner && conf.githubRepoName) { + // use GitHub-friendly parser if GitHub-specific options are present + return getParseFunction(parsers.gfm, conf); + } else { + // evilstreak is the default parser + return getParseFunction(parsers.evilstreak, conf); + } +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/util/templateHelper.js b/data/scripts/jsdoc/lib/jsdoc/util/templateHelper.js new file mode 100644 index 00000000..61b1ecc5 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/templateHelper.js @@ -0,0 +1,711 @@ +/*global env: true */ +/** + * @module jsdoc/util/templateHelper + */ + +var dictionary = require('jsdoc/tag/dictionary'); +var util = require('util'); + +var hasOwnProp = Object.prototype.hasOwnProperty; + +var files = {}; + +// each container gets its own html file +var containers = ['class', 'module', 'external', 'namespace', 'mixin']; + +var tutorials; + +/** Sets tutorials map. + @param {jsdoc.tutorial.Tutorial} root - Root tutorial node. + */ +exports.setTutorials = function(root) { + tutorials = root; +}; + +exports.globalName = 'global'; +exports.fileExtension = '.html'; +exports.scopeToPunc = { 'static': '.', 'inner': '~', 'instance': '#' }; + +function getNamespace(kind) { + if (dictionary.isNamespace(kind)) { + return kind + ':'; + } + return ''; +} + +function makeFilenameUnique(filename, str) { + var key = filename.toLowerCase(); + var nonUnique = true; + + // append enough underscores to make the filename unique + while (nonUnique) { + if ( files[key] && hasOwnProp.call(files, key) ) { + filename += '_'; + key = filename.toLowerCase(); + } else { + nonUnique = false; + } + } + + files[key] = str; + return filename; +} + +// compute it here just once +var nsprefix = /^(event|module|external):/; + +var htmlsafe = exports.htmlsafe = function(str) { + return str.replace(/` or `Array.<(MyClass|YourClass)>`. In these examples, the method attempts to + * replace `Array`, `MyClass`, and `YourClass` with links to the appropriate types. The link text + * is ignored for type applications. + * + * @param {string} longname - The longname (or URL) that is the target of the link. + * @param {string=} linkText - The text to display for the link, or `longname` if no text is + * provided. + * @param {Object} options - Options for building the link. + * @param {string=} options.cssClass - The CSS class (or classes) to include in the link's `` + * tag. + * @param {string=} options.fragmentId - The fragment identifier (for example, `name` in + * `foo.html#name`) to append to the link target. + * @param {string=} options.linkMap - The link map in which to look up the longname. + * @param {boolean=} options.monospace - Indicates whether to display the link text in a monospace + * font. + * @return {string} The HTML link, or the link text if the link is not available. + */ +function buildLink(longname, linkText, options) { + var catharsis = require('catharsis'); + + var classString = options.cssClass ? util.format(' class="%s"', options.cssClass) : ''; + var fragmentString = options.fragmentId ? '#' + options.fragmentId : ''; + var stripped; + var text; + var url; + var parsedType; + + // handle cases like: + // @see + // @see http://example.org + stripped = longname ? longname.replace(/^<|>$/g, '') : ''; + if ( hasUrlPrefix(stripped) ) { + url = stripped; + text = linkText || stripped; + } + // handle complex type expressions that may require multiple links + // (but skip anything that looks like an inline tag) + else if (longname && longname.search(/[<{(]/) !== -1 && /\{\@.+\}/.test(longname) === false) { + parsedType = parseType(longname); + return stringifyType(parsedType, options.cssClass, options.linkMap); + } + else { + url = hasOwnProp.call(options.linkMap, longname) ? options.linkMap[longname] : ''; + text = linkText || longname; + } + + text = options.monospace ? '' + text + '' : text; + + if (!url) { + return text; + } + else { + return util.format('%s', url, fragmentString, classString, text); + } +} + +/** + * Retrieve an HTML link to the symbol with the specified longname. If the longname is not + * associated with a URL, this method simply returns the link text, if provided, or the longname. + * + * The `longname` parameter can also contain a URL rather than a symbol's longname. + * + * This method supports type applications that can contain one or more types, such as + * `Array.` or `Array.<(MyClass|YourClass)>`. In these examples, the method attempts to + * replace `Array`, `MyClass`, and `YourClass` with links to the appropriate types. The link text + * is ignored for type applications. + * + * @param {string} longname - The longname (or URL) that is the target of the link. + * @param {string=} linkText - The text to display for the link, or `longname` if no text is + * provided. + * @param {string=} cssClass - The CSS class (or classes) to include in the link's `` tag. + * @param {string=} fragmentId - The fragment identifier (for example, `name` in `foo.html#name`) to + * append to the link target. + * @return {string} The HTML link, or a plain-text string if the link is not available. + */ +var linkto = exports.linkto = function(longname, linkText, cssClass, fragmentId) { + return buildLink(longname, linkText, { + cssClass: cssClass, + fragmentId: fragmentId, + linkMap: longnameToUrl + }); +}; + +function useMonospace(tag, text) { + var cleverLinks; + var monospaceLinks; + var result; + + if ( hasUrlPrefix(text) ) { + result = false; + } + else if (tag === 'linkplain') { + result = false; + } + else if (tag === 'linkcode') { + result = true; + } + else { + cleverLinks = env.conf.templates.cleverLinks; + monospaceLinks = env.conf.templates.monospaceLinks; + + if (monospaceLinks || cleverLinks) { + result = true; + } + } + + return result || false; +} + +function splitLinkText(text) { + var linkText; + var target; + var splitIndex; + + // if a pipe is not present, we split on the first space + splitIndex = text.indexOf('|'); + if (splitIndex === -1) { + splitIndex = text.indexOf(' '); + } + + if (splitIndex !== -1) { + linkText = text.substr(splitIndex + 1); + target = text.substr(0, splitIndex); + } + + return { + linkText: linkText, + target: target || text + }; +} + +var tutorialToUrl = exports.tutorialToUrl = function(tutorial) { + var node = tutorials.getByName(tutorial); + // no such tutorial + if (!node) { + require('jsdoc/util/error').handle( new Error('No such tutorial: '+tutorial) ); + return; + } + + var url; + // define the URL if necessary + if (!hasOwnProp.call(tutorialLinkMap.nameToUrl, node.name)) { + url = 'tutorial-' + getUniqueFilename(node.name); + tutorialLinkMap.nameToUrl[node.name] = url; + tutorialLinkMap.urlToName[url] = node.name; + } + + return tutorialLinkMap.nameToUrl[node.name]; +}; + +/** + * Retrieve a link to a tutorial, or the name of the tutorial if the tutorial is missing. If the + * `missingOpts` parameter is supplied, the names of missing tutorials will be prefixed by the + * specified text and wrapped in the specified HTML tag and CSS class. + * + * @todo Deprecate missingOpts once we have a better error-reporting mechanism. + * @param {string} tutorial The name of the tutorial. + * @param {string} content The link text to use. + * @param {object} [missingOpts] Options for displaying the name of a missing tutorial. + * @param {string} missingOpts.classname The CSS class to wrap around the tutorial name. + * @param {string} missingOpts.prefix The prefix to add to the tutorial name. + * @param {string} missingOpts.tag The tag to wrap around the tutorial name. + * @return {string} An HTML link to the tutorial, or the name of the tutorial with the specified + * options. + */ +var toTutorial = exports.toTutorial = function(tutorial, content, missingOpts) { + if (!tutorial) { + require('jsdoc/util/error').handle( new Error('Missing required parameter: tutorial') ); + return; + } + + var node = tutorials.getByName(tutorial); + // no such tutorial + if (!node) { + missingOpts = missingOpts || {}; + var tag = missingOpts.tag; + var classname = missingOpts.classname; + + var link = tutorial; + if (missingOpts.prefix) { + link = missingOpts.prefix + link; + } + if (tag) { + link = '<' + tag + (classname ? (' class="' + classname + '">') : '>') + link; + link += ''; + } + return link; + } + + content = content || node.title; + + return '' + content + ''; +}; + +/** Find symbol {@link ...} and {@tutorial ...} strings in text and turn into html links */ +exports.resolveLinks = function(str) { + var replaceInlineTags = require('jsdoc/tag/inline').replaceInlineTags; + + function extractLeadingText(string, completeTag) { + var tagIndex = string.indexOf(completeTag); + var leadingText = null; + var leadingTextInfo = /\[(.+?)\]/.exec(string); + + // did we find leading text, and if so, does it immediately precede the tag? + if ( leadingTextInfo && leadingTextInfo.index && + (leadingTextInfo.index + leadingTextInfo[0].length === tagIndex) ) { + string = string.replace(leadingTextInfo[0], ''); + leadingText = leadingTextInfo[1]; + } + + return { + leadingText: leadingText, + string: string + }; + } + + function processLink(string, tagInfo) { + var leading = extractLeadingText(string, tagInfo.completeTag); + var linkText = leading.leadingText; + var monospace; + var split; + var target; + string = leading.string; + + split = splitLinkText(tagInfo.text); + target = split.target; + linkText = linkText || split.linkText; + + monospace = useMonospace(tagInfo.tag, tagInfo.text); + + return string.replace( tagInfo.completeTag, buildLink(target, linkText, { + linkMap: longnameToUrl, + monospace: monospace + }) ); + } + + function processTutorial(string, tagInfo) { + var leading = extractLeadingText(string, tagInfo.completeTag); + string = leading.string; + + return string.replace( tagInfo.completeTag, toTutorial(tagInfo.text, leading.leadingText) ); + } + + var replacers = { + link: processLink, + linkcode: processLink, + linkplain: processLink, + tutorial: processTutorial + }; + + return replaceInlineTags(str, replacers).newString; +}; + +/** Convert tag text like "Jane Doe " into a mailto link */ +exports.resolveAuthorLinks = function(str) { + var author; + var matches = str.match(/^\s?([\s\S]+)\b\s+<(\S+@\S+)>\s?$/); + if (matches && matches.length === 3) { + author = '' + htmlsafe(matches[1]) + ''; + } + else { + author = htmlsafe(str); + } + + return author; +}; + +/** + * Find items in a TaffyDB database that match the specified key-value pairs. + * @param {TAFFY} data The TaffyDB database to search. + * @param {object|function} spec Key-value pairs to match against (for example, + * `{ longname: 'foo' }`), or a function that returns `true` if a value matches or `false` if it + * does not match. + * @return {array} The matching items. + */ +var find = exports.find = function(data, spec) { + return data(spec).get(); +}; + +/** + * Check whether a symbol is a function and is the only symbol exported by a module (as in + * `module.exports = function() {};`). + * + * @private + * @param {module:jsdoc/doclet.Doclet} doclet - The doclet for the symbol. + * @return {boolean} `true` if the symbol is a function and is the only symbol exported by a module; + * otherwise, `false`. + */ +function isModuleFunction(doclet) { + return doclet.longname && doclet.longname === doclet.name && + doclet.longname.indexOf('module:') === 0 && doclet.kind === 'function'; +} + +/** + * Retrieve all of the following types of members from a set of doclets: + * + * + Classes + * + Externals + * + Globals + * + Mixins + * + Modules + * + Namespaces + * + Events + * @param {TAFFY} data The TaffyDB database to search. + * @return {object} An object with `classes`, `externals`, `globals`, `mixins`, `modules`, + * `events`, and `namespaces` properties. Each property contains an array of objects. + */ +exports.getMembers = function(data) { + var members = { + classes: find( data, {kind: 'class'} ), + externals: find( data, {kind: 'external'} ), + events: find( data, {kind: 'event'} ), + globals: find(data, { + kind: ['member', 'function', 'constant', 'typedef'], + memberof: { isUndefined: true } + }), + mixins: find( data, {kind: 'mixin'} ), + modules: find( data, {kind: 'module'} ), + namespaces: find( data, {kind: 'namespace'} ) + }; + + // functions that are also modules (as in "module.exports = function() {};") are not globals + members.globals = members.globals.filter(function(doclet) { + if ( isModuleFunction(doclet) ) { + return false; + } + + return true; + }); + + return members; +}; + +/** + * Retrieve the member attributes for a doclet (for example, `virtual`, `static`, and + * `readonly`). + * @param {object} d The doclet whose attributes will be retrieved. + * @return {array} The member attributes for the doclet. + */ +exports.getAttribs = function(d) { + var attribs = []; + + if (d.virtual) { + attribs.push('virtual'); + } + + if (d.access && d.access !== 'public') { + attribs.push(d.access); + } + + if (d.scope && d.scope !== 'instance' && d.scope !== 'global') { + if (d.kind == 'function' || d.kind == 'member' || d.kind == 'constant') { + attribs.push(d.scope); + } + } + + if (d.readonly === true) { + if (d.kind == 'member') { + attribs.push('readonly'); + } + } + + if (d.kind === 'constant') { + attribs.push('constant'); + } + + return attribs; +}; + +/** + * Retrieve links to allowed types for the member. + * + * @param {Object} d - The doclet whose types will be retrieved. + * @param {string} [cssClass] - The CSS class to include in the `class` attribute for each link. + * @return {Array.} HTML links to allowed types for the member. + */ +exports.getSignatureTypes = function(d, cssClass) { + var types = []; + + if (d.type && d.type.names) { + types = d.type.names; + } + + if (types && types.length) { + types = types.map(function(t) { + return linkto(t, htmlsafe(t), cssClass); + }); + } + + return types; +}; + +/** + * Retrieve names of the parameters that the member accepts. If a value is provided for `optClass`, + * the names of optional parameters will be wrapped in a `` tag with that class. + * @param {object} d The doclet whose parameter names will be retrieved. + * @param {string} [optClass] The class to assign to the `` tag that is wrapped around the + * names of optional parameters. If a value is not provided, optional parameter names will not be + * wrapped with a `` tag. Must be a legal value for a CSS class name. + * @return {array} An array of parameter names, with or without `` tags wrapping the + * names of optional parameters. + */ +exports.getSignatureParams = function(d, optClass) { + var pnames = []; + + if (d.params) { + d.params.forEach(function(p) { + if (p.name && p.name.indexOf('.') === -1) { + if (p.optional && optClass) { + pnames.push('' + p.name + ''); + } + else { + pnames.push(p.name); + } + } + }); + } + + return pnames; +}; + +/** + * Retrieve links to types that the member can return. + * + * @param {Object} d - The doclet whose types will be retrieved. + * @param {string} [cssClass] - The CSS class to include in the `class` attribute for each link. + * @return {Array.} HTML links to types that the member can return. + */ +exports.getSignatureReturns = function(d, cssClass) { + var returnTypes = []; + + if (d.returns) { + d.returns.forEach(function(r) { + if (r.type && r.type.names) { + if (!returnTypes.length) { + returnTypes = r.type.names; + } + } + }); + } + + if (returnTypes && returnTypes.length) { + returnTypes = returnTypes.map(function(r) { + return linkto(r, htmlsafe(r), cssClass); + }); + } + + return returnTypes; +}; + +/** + * Retrieve links to a member's ancestors. + * + * @param {TAFFY} data - The TaffyDB database to search. + * @param {Object} doclet - The doclet whose ancestors will be retrieved. + * @param {string} [cssClass] - The CSS class to include in the `class` attribute for each link. + * @return {Array.} HTML links to a member's ancestors. + */ +exports.getAncestorLinks = function(data, doclet, cssClass) { + var ancestors = [], + doc = doclet.memberof; + + while (doc) { + doc = find( data, {longname: doc}, false ); + if (doc) { doc = doc[0]; } + if (!doc) { break; } + ancestors.unshift( linkto(doc.longname, (exports.scopeToPunc[doc.scope] || '') + doc.name, + cssClass) ); + doc = doc.memberof; + } + if (ancestors.length) { + ancestors[ancestors.length - 1] += (exports.scopeToPunc[doclet.scope] || ''); + } + return ancestors; +}; + +/** + * Iterates through all the doclets in `data`, ensuring that if a method + * @listens to an event, then that event has a 'listeners' array with the + * longname of the listener in it. + * + * @param {TAFFY} data - The TaffyDB database to search. + */ +exports.addEventListeners = function(data) { + // TODO: do this on the *pruned* data + // find all doclets that @listen to something. + var listeners = find(data, function () { return this.listens && this.listens.length; }); + + if (!listeners.length) { + return; + } + + var doc, + l, + _events = {}; // just a cache to prevent me doing so many lookups + + listeners.forEach(function (listener) { + l = listener.listens; + l.forEach(function (eventLongname) { + doc = _events[eventLongname] || find(data, {longname: eventLongname, kind: 'event'})[0]; + if (doc) { + if (!doc.listeners) { + doc.listeners = [listener.longname]; + } else { + doc.listeners.push(listener.longname); + } + _events[eventLongname] = _events[eventLongname] || doc; + } + }); + }); +}; + +/** + * Remove members that will not be included in the output, including: + * + * + Undocumented members. + * + Members tagged `@ignore`. + * + Members of anonymous classes. + * + Members tagged `@private`, unless the `private` option is enabled. + * @param {TAFFY} data The TaffyDB database to prune. + * @return {TAFFY} The pruned database. + */ +exports.prune = function(data) { + data({undocumented: true}).remove(); + data({ignore: true}).remove(); + if (!env.opts.private) { data({access: 'private'}).remove(); } + data({memberof: ''}).remove(); + + return data; +}; + +var registerLink = exports.registerLink = function(longname, url) { + linkMap.longnameToUrl[longname] = url; + linkMap.urlToLongname[url] = longname; +}; + +/** + * Get a longname's filename if one has been registered; otherwise, generate a unique filename, then + * register the filename. + * @private + */ +function getFilename(longname) { + var url; + + if ( longnameToUrl[longname] && hasOwnProp.call(longnameToUrl, longname) ) { + url = longnameToUrl[longname]; + } else { + url = getUniqueFilename(longname); + registerLink(longname, url); + } + + return url; +} + +/** Turn a doclet into a URL. */ +exports.createLink = function(doclet) { + var url = ''; + var longname = doclet.longname; + var filename; + + if ( containers.indexOf(doclet.kind) !== -1 || isModuleFunction(doclet) ) { + url = getFilename(longname); + } + else { + filename = getFilename(doclet.memberof || exports.globalName); + url = filename + '#' + getNamespace(doclet.kind) + doclet.name; + } + + return url; +}; diff --git a/data/scripts/jsdoc/lib/jsdoc/util/vm.js b/data/scripts/jsdoc/lib/jsdoc/util/vm.js new file mode 100644 index 00000000..eb7048b1 --- /dev/null +++ b/data/scripts/jsdoc/lib/jsdoc/util/vm.js @@ -0,0 +1,83 @@ +/*global Packages: true */ +/** + * Helper functions to enable JSDoc to run on multiple JavaScript virtual machines. + * @module jsdoc/util/vm + */ + + var os = require('os'); + +// These strings represent directory names; do not modify them! +/** @private */ +const RHINO = exports.RHINO = 'rhino'; +/** @private */ +const NODEJS = exports.NODEJS = 'nodejs'; + +// hacky conversion from Windows path to URI +function pathToUri(filepath) { + var uri = filepath; + + // get drive + var drive = uri.match(/^[A-Za-z]/)[0] || ''; + // strip drive/colon (if present; UNC paths won't have either) + uri = uri.replace(/^:?([A-Za-z]\:)?/, ''); + // replace spaces with %20 + uri = uri.replace(/\s/g, '%20'); + // prepend drive (if present) + if (drive) { + uri = drive + ':' + uri; + } + // prepend URI scheme + uri = 'file:/' + uri; + + return uri; +} + +/** + * The JavaScript VM that is executing JSDoc: + * + * + `module:jsdoc/util/vm.RHINO`: Mozilla Rhino. + * + `module:jsdoc/util/vm.NODEJS`: Node.js (not currently supported). + */ +var vm = exports.vm = (function() { + if (Packages && typeof Packages === 'object' && + Object.prototype.toString.call(Packages) === '[object JavaPackage]') { + return RHINO; + } else if ( require && require.main && module && (require.main === module) ) { + return NODEJS; + } else { + // unknown VM + throw new Error('Unable to identify the current JavaScript VM.'); + } +})(); + +/** + * Load the VM-specific implementation of a module. + * + * @param {string} modulePath - The relative path to the module. Use the same format as when calling + * `require()`. + * @return {object} An object containing VM-specific functions for the requested module. + */ +exports.getModule = function(modulePath) { + modulePath = [__dirname, vm, modulePath].join('/').replace(/ /g, '%20'); + if (os.platform() === 'win32') { + modulePath = pathToUri(modulePath); + } + + return require(modulePath); +}; + +/** + * Check whether Mozilla Rhino is running JSDoc. + * @return {boolean} Set to `true` if the current VM is Mozilla Rhino. + */ +exports.isRhino = function() { + return vm === RHINO; +}; + +/** + * Check whether Node.js is running JSDoc. (Node.js is not currently supported.) + * @return {boolean} Set to `true` if the current VM is Node.js. + */ +exports.isNodejs = function() { + return vm === NODEJS; +}; diff --git a/data/scripts/jsdoc/node_modules/async/LICENSE b/data/scripts/jsdoc/node_modules/async/LICENSE new file mode 100644 index 00000000..b7f9d500 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/async/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Caolan McMahon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/data/scripts/jsdoc/node_modules/async/index.js b/data/scripts/jsdoc/node_modules/async/index.js new file mode 100644 index 00000000..8e238453 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/async/index.js @@ -0,0 +1,3 @@ +// This file is just added for convenience so this repository can be +// directly checked out into a project's deps folder +module.exports = require('./lib/async'); diff --git a/data/scripts/jsdoc/node_modules/async/lib/async.js b/data/scripts/jsdoc/node_modules/async/lib/async.js new file mode 100644 index 00000000..7cc4f5ea --- /dev/null +++ b/data/scripts/jsdoc/node_modules/async/lib/async.js @@ -0,0 +1,692 @@ +/*global setTimeout: false, console: false */ +(function () { + + var async = {}; + + // global on the server, window in the browser + var root = this, + previous_async = root.async; + + if (typeof module !== 'undefined' && module.exports) { + module.exports = async; + } + else { + root.async = async; + } + + async.noConflict = function () { + root.async = previous_async; + return async; + }; + + //// cross-browser compatiblity functions //// + + var _forEach = function (arr, iterator) { + if (arr.forEach) { + return arr.forEach(iterator); + } + for (var i = 0; i < arr.length; i += 1) { + iterator(arr[i], i, arr); + } + }; + + var _map = function (arr, iterator) { + if (arr.map) { + return arr.map(iterator); + } + var results = []; + _forEach(arr, function (x, i, a) { + results.push(iterator(x, i, a)); + }); + return results; + }; + + var _reduce = function (arr, iterator, memo) { + if (arr.reduce) { + return arr.reduce(iterator, memo); + } + _forEach(arr, function (x, i, a) { + memo = iterator(memo, x, i, a); + }); + return memo; + }; + + var _keys = function (obj) { + if (Object.keys) { + return Object.keys(obj); + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } + } + return keys; + }; + + //// exported async module functions //// + + //// nextTick implementation with browser-compatible fallback //// + if (typeof process === 'undefined' || !(process.nextTick)) { + async.nextTick = function (fn) { + setTimeout(fn, 0); + }; + } + else { + async.nextTick = process.nextTick; + } + + async.forEach = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + _forEach(arr, function (x) { + iterator(x, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(null); + } + } + }); + }); + }; + + async.forEachSeries = function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length) { + return callback(); + } + var completed = 0; + var iterate = function () { + iterator(arr[completed], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed === arr.length) { + callback(null); + } + else { + iterate(); + } + } + }); + }; + iterate(); + }; + + async.forEachLimit = function (arr, limit, iterator, callback) { + callback = callback || function () {}; + if (!arr.length || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed === arr.length) { + return callback(); + } + + while (running < limit && started < arr.length) { + started += 1; + running += 1; + iterator(arr[started - 1], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed === arr.length) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + + + var doParallel = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEach].concat(args)); + }; + }; + var doSeries = function (fn) { + return function () { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, [async.forEachSeries].concat(args)); + }; + }; + + + var _asyncMap = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (err, v) { + results[x.index] = v; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + }; + async.map = doParallel(_asyncMap); + async.mapSeries = doSeries(_asyncMap); + + + // reduce only has a series version, as doing reduce in parallel won't + // work in many situations. + async.reduce = function (arr, memo, iterator, callback) { + async.forEachSeries(arr, function (x, callback) { + iterator(memo, x, function (err, v) { + memo = v; + callback(err); + }); + }, function (err) { + callback(err, memo); + }); + }; + // inject alias + async.inject = async.reduce; + // foldl alias + async.foldl = async.reduce; + + async.reduceRight = function (arr, memo, iterator, callback) { + var reversed = _map(arr, function (x) { + return x; + }).reverse(); + async.reduce(reversed, memo, iterator, callback); + }; + // foldr alias + async.foldr = async.reduceRight; + + var _filter = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.filter = doParallel(_filter); + async.filterSeries = doSeries(_filter); + // select alias + async.select = async.filter; + async.selectSeries = async.filterSeries; + + var _reject = function (eachfn, arr, iterator, callback) { + var results = []; + arr = _map(arr, function (x, i) { + return {index: i, value: x}; + }); + eachfn(arr, function (x, callback) { + iterator(x.value, function (v) { + if (!v) { + results.push(x); + } + callback(); + }); + }, function (err) { + callback(_map(results.sort(function (a, b) { + return a.index - b.index; + }), function (x) { + return x.value; + })); + }); + }; + async.reject = doParallel(_reject); + async.rejectSeries = doSeries(_reject); + + var _detect = function (eachfn, arr, iterator, main_callback) { + eachfn(arr, function (x, callback) { + iterator(x, function (result) { + if (result) { + main_callback(x); + main_callback = function () {}; + } + else { + callback(); + } + }); + }, function (err) { + main_callback(); + }); + }; + async.detect = doParallel(_detect); + async.detectSeries = doSeries(_detect); + + async.some = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (v) { + main_callback(true); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(false); + }); + }; + // any alias + async.any = async.some; + + async.every = function (arr, iterator, main_callback) { + async.forEach(arr, function (x, callback) { + iterator(x, function (v) { + if (!v) { + main_callback(false); + main_callback = function () {}; + } + callback(); + }); + }, function (err) { + main_callback(true); + }); + }; + // all alias + async.all = async.every; + + async.sortBy = function (arr, iterator, callback) { + async.map(arr, function (x, callback) { + iterator(x, function (err, criteria) { + if (err) { + callback(err); + } + else { + callback(null, {value: x, criteria: criteria}); + } + }); + }, function (err, results) { + if (err) { + return callback(err); + } + else { + var fn = function (left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }; + callback(null, _map(results.sort(fn), function (x) { + return x.value; + })); + } + }); + }; + + async.auto = function (tasks, callback) { + callback = callback || function () {}; + var keys = _keys(tasks); + if (!keys.length) { + return callback(null); + } + + var results = {}; + + var listeners = []; + var addListener = function (fn) { + listeners.unshift(fn); + }; + var removeListener = function (fn) { + for (var i = 0; i < listeners.length; i += 1) { + if (listeners[i] === fn) { + listeners.splice(i, 1); + return; + } + } + }; + var taskComplete = function () { + _forEach(listeners.slice(0), function (fn) { + fn(); + }); + }; + + addListener(function () { + if (_keys(results).length === keys.length) { + callback(null, results); + callback = function () {}; + } + }); + + _forEach(keys, function (k) { + var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; + var taskCallback = function (err) { + if (err) { + callback(err); + // stop subsequent errors hitting callback multiple times + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + taskComplete(); + } + }; + var requires = task.slice(0, Math.abs(task.length - 1)) || []; + var ready = function () { + return _reduce(requires, function (a, x) { + return (a && results.hasOwnProperty(x)); + }, true) && !results.hasOwnProperty(k); + }; + if (ready()) { + task[task.length - 1](taskCallback, results); + } + else { + var listener = function () { + if (ready()) { + removeListener(listener); + task[task.length - 1](taskCallback, results); + } + }; + addListener(listener); + } + }); + }; + + async.waterfall = function (tasks, callback) { + callback = callback || function () {}; + if (!tasks.length) { + return callback(); + } + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } + else { + args.push(callback); + } + async.nextTick(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(async.iterator(tasks))(); + }; + + async.parallel = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.map(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.forEach(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.series = function (tasks, callback) { + callback = callback || function () {}; + if (tasks.constructor === Array) { + async.mapSeries(tasks, function (fn, callback) { + if (fn) { + fn(function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + callback.call(null, err, args); + }); + } + }, callback); + } + else { + var results = {}; + async.forEachSeries(_keys(tasks), function (k, callback) { + tasks[k](function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (args.length <= 1) { + args = args[0]; + } + results[k] = args; + callback(err); + }); + }, function (err) { + callback(err, results); + }); + } + }; + + async.iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function () { + return (index < tasks.length - 1) ? makeCallback(index + 1): null; + }; + return fn; + }; + return makeCallback(0); + }; + + async.apply = function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + return function () { + return fn.apply( + null, args.concat(Array.prototype.slice.call(arguments)) + ); + }; + }; + + var _concat = function (eachfn, arr, fn, callback) { + var r = []; + eachfn(arr, function (x, cb) { + fn(x, function (err, y) { + r = r.concat(y || []); + cb(err); + }); + }, function (err) { + callback(err, r); + }); + }; + async.concat = doParallel(_concat); + async.concatSeries = doSeries(_concat); + + async.whilst = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.whilst(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.until = function (test, iterator, callback) { + if (!test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + async.until(test, iterator, callback); + }); + } + else { + callback(); + } + }; + + async.queue = function (worker, concurrency) { + var workers = 0; + var q = { + tasks: [], + concurrency: concurrency, + saturated: null, + empty: null, + drain: null, + push: function (data, callback) { + if(data.constructor !== Array) { + data = [data]; + } + _forEach(data, function(task) { + q.tasks.push({ + data: task, + callback: typeof callback === 'function' ? callback : null + }); + if (q.saturated && q.tasks.length == concurrency) { + q.saturated(); + } + async.nextTick(q.process); + }); + }, + process: function () { + if (workers < q.concurrency && q.tasks.length) { + var task = q.tasks.shift(); + if(q.empty && q.tasks.length == 0) q.empty(); + workers += 1; + worker(task.data, function () { + workers -= 1; + if (task.callback) { + task.callback.apply(task, arguments); + } + if(q.drain && q.tasks.length + workers == 0) q.drain(); + q.process(); + }); + } + }, + length: function () { + return q.tasks.length; + }, + running: function () { + return workers; + } + }; + return q; + }; + + var _console_fn = function (name) { + return function (fn) { + var args = Array.prototype.slice.call(arguments, 1); + fn.apply(null, args.concat([function (err) { + var args = Array.prototype.slice.call(arguments, 1); + if (typeof console !== 'undefined') { + if (err) { + if (console.error) { + console.error(err); + } + } + else if (console[name]) { + _forEach(args, function (x) { + console[name](x); + }); + } + } + }])); + }; + }; + async.log = _console_fn('log'); + async.dir = _console_fn('dir'); + /*async.info = _console_fn('info'); + async.warn = _console_fn('warn'); + async.error = _console_fn('error');*/ + + async.memoize = function (fn, hasher) { + var memo = {}; + var queues = {}; + hasher = hasher || function (x) { + return x; + }; + var memoized = function () { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var key = hasher.apply(null, args); + if (key in memo) { + callback.apply(null, memo[key]); + } + else if (key in queues) { + queues[key].push(callback); + } + else { + queues[key] = [callback]; + fn.apply(null, args.concat([function () { + memo[key] = arguments; + var q = queues[key]; + delete queues[key]; + for (var i = 0, l = q.length; i < l; i++) { + q[i].apply(null, arguments); + } + }])); + } + }; + memoized.unmemoized = fn; + return memoized; + }; + + async.unmemoize = function (fn) { + return function () { + return (fn.unmemoized || fn).apply(null, arguments); + }; + }; + +}()); diff --git a/data/scripts/jsdoc/node_modules/async/package.json b/data/scripts/jsdoc/node_modules/async/package.json new file mode 100644 index 00000000..1e3814d8 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/async/package.json @@ -0,0 +1,30 @@ +{ + "name": "async", + "description": "Higher-order functions and common patterns for asynchronous code", + "main": "./index", + "author": { + "name": "Caolan McMahon" + }, + "version": "0.1.22", + "repository": { + "type": "git", + "url": "http://github.com/caolan/async.git" + }, + "bugs": { + "url": "http://github.com/caolan/async/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/caolan/async/raw/master/LICENSE" + } + ], + "devDependencies": { + "nodeunit": ">0.0.0", + "uglify-js": "1.2.x", + "nodelint": ">0.0.0" + }, + "readme": "# Async.js\n\nAsync is a utility module which provides straight-forward, powerful functions\nfor working with asynchronous JavaScript. Although originally designed for\nuse with [node.js](http://nodejs.org), it can also be used directly in the\nbrowser.\n\nAsync provides around 20 functions that include the usual 'functional'\nsuspects (map, reduce, filter, forEach…) as well as some common patterns\nfor asynchronous control flow (parallel, series, waterfall…). All these\nfunctions assume you follow the node.js convention of providing a single\ncallback as the last argument of your async function.\n\n\n## Quick Examples\n\n async.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n });\n\n async.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n });\n\n async.parallel([\n function(){ ... },\n function(){ ... }\n ], callback);\n\n async.series([\n function(){ ... },\n function(){ ... }\n ]);\n\nThere are many more functions available so take a look at the docs below for a\nfull list. This module aims to be comprehensive, so if you feel anything is\nmissing please create a GitHub issue for it.\n\n\n## Download\n\nReleases are available for download from\n[GitHub](http://github.com/caolan/async/downloads).\nAlternatively, you can install using Node Package Manager (npm):\n\n npm install async\n\n\n__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 17.5kb Uncompressed\n\n__Production:__ [async.min.js](https://github.com/caolan/async/raw/master/dist/async.min.js) - 1.7kb Packed and Gzipped\n\n\n## In the Browser\n\nSo far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:\n\n \n \n\n\n## Documentation\n\n### Collections\n\n* [forEach](#forEach)\n* [map](#map)\n* [filter](#filter)\n* [reject](#reject)\n* [reduce](#reduce)\n* [detect](#detect)\n* [sortBy](#sortBy)\n* [some](#some)\n* [every](#every)\n* [concat](#concat)\n\n### Control Flow\n\n* [series](#series)\n* [parallel](#parallel)\n* [whilst](#whilst)\n* [until](#until)\n* [waterfall](#waterfall)\n* [queue](#queue)\n* [auto](#auto)\n* [iterator](#iterator)\n* [apply](#apply)\n* [nextTick](#nextTick)\n\n### Utils\n\n* [memoize](#memoize)\n* [unmemoize](#unmemoize)\n* [log](#log)\n* [dir](#dir)\n* [noConflict](#noConflict)\n\n\n## Collections\n\n\n### forEach(arr, iterator, callback)\n\nApplies an iterator function to each item in an array, in parallel.\nThe iterator is called with an item from the list and a callback for when it\nhas finished. If the iterator passes an error to this callback, the main\ncallback for the forEach function is immediately called with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n // assuming openFiles is an array of file names and saveFile is a function\n // to save the modified contents of that file:\n\n async.forEach(openFiles, saveFile, function(err){\n // if any of the saves produced an error, err would equal that error\n });\n\n---------------------------------------\n\n\n### forEachSeries(arr, iterator, callback)\n\nThe same as forEach only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. This means the iterator functions will complete in order.\n\n\n---------------------------------------\n\n\n### forEachLimit(arr, limit, iterator, callback)\n\nThe same as forEach only the iterator is applied to batches of items in the\narray, in series. The next batch of iterators is only called once the current\none has completed processing.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* limit - How many items should be in each batch.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(err) - A callback which is called after all the iterator functions\n have finished, or an error has occurred.\n\n__Example__\n\n // Assume documents is an array of JSON objects and requestApi is a\n // function that interacts with a rate-limited REST api.\n\n async.forEachLimit(documents, 20, requestApi, function(err){\n // if any of the saves produced an error, err would equal that error\n });\n---------------------------------------\n\n\n### map(arr, iterator, callback)\n\nProduces a new array of values by mapping each value in the given array through\nthe iterator function. The iterator is called with an item from the array and a\ncallback for when it has finished processing. The callback takes 2 arguments, \nan error and the transformed item from the array. If the iterator passes an\nerror to this callback, the main callback for the map function is immediately\ncalled with the error.\n\nNote, that since this function applies the iterator to each item in parallel\nthere is no guarantee that the iterator functions will complete in order, however\nthe results array will be in the same order as the original array.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and a transformed item.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array of the\n transformed items from the original array.\n\n__Example__\n\n async.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n });\n\n---------------------------------------\n\n\n### mapSeries(arr, iterator, callback)\n\nThe same as map only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n\n---------------------------------------\n\n\n### filter(arr, iterator, callback)\n\n__Alias:__ select\n\nReturns a new array of all the values which pass an async truth test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. This operation is\nperformed in parallel, but the results array will be in the same order as the\noriginal.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(results) - A callback which is called after all the iterator\n functions have finished.\n\n__Example__\n\n async.filter(['file1','file2','file3'], path.exists, function(results){\n // results now equals an array of the existing files\n });\n\n---------------------------------------\n\n\n### filterSeries(arr, iterator, callback)\n\n__alias:__ selectSeries\n\nThe same as filter only the iterator is applied to each item in the array in\nseries. The next iterator is only called once the current one has completed\nprocessing. The results array will be in the same order as the original.\n\n---------------------------------------\n\n\n### reject(arr, iterator, callback)\n\nThe opposite of filter. Removes values that pass an async truth test.\n\n---------------------------------------\n\n\n### rejectSeries(arr, iterator, callback)\n\nThe same as filter, only the iterator is applied to each item in the array\nin series.\n\n\n---------------------------------------\n\n\n### reduce(arr, memo, iterator, callback)\n\n__aliases:__ inject, foldl\n\nReduces a list of values into a single value using an async iterator to return\neach successive step. Memo is the initial state of the reduction. This\nfunction only operates in series. For performance reasons, it may make sense to\nsplit a call to this function into a parallel map, then use the normal\nArray.prototype.reduce on the results. This function is for situations where\neach step in the reduction needs to be async, if you can get the data before\nreducing it then its probably a good idea to do so.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* memo - The initial state of the reduction.\n* iterator(memo, item, callback) - A function applied to each item in the\n array to produce the next step in the reduction. The iterator is passed a\n callback which accepts an optional error as its first argument, and the state\n of the reduction as the second. If an error is passed to the callback, the\n reduction is stopped and the main callback is immediately called with the\n error.\n* callback(err, result) - A callback which is called after all the iterator\n functions have finished. Result is the reduced value.\n\n__Example__\n\n async.reduce([1,2,3], 0, function(memo, item, callback){\n // pointless async:\n process.nextTick(function(){\n callback(null, memo + item)\n });\n }, function(err, result){\n // result is now equal to the last value of memo, which is 6\n });\n\n---------------------------------------\n\n\n### reduceRight(arr, memo, iterator, callback)\n\n__Alias:__ foldr\n\nSame as reduce, only operates on the items in the array in reverse order.\n\n\n---------------------------------------\n\n\n### detect(arr, iterator, callback)\n\nReturns the first value in a list that passes an async truth test. The\niterator is applied in parallel, meaning the first iterator to return true will\nfire the detect callback with that result. That means the result might not be\nthe first item in the original array (in terms of order) that passes the test.\n\nIf order within the original array is important then look at detectSeries.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n the first item in the array that passes the truth test (iterator) or the\n value undefined if none passed.\n\n__Example__\n\n async.detect(['file1','file2','file3'], path.exists, function(result){\n // result now equals the first file in the list that exists\n });\n\n---------------------------------------\n\n\n### detectSeries(arr, iterator, callback)\n\nThe same as detect, only the iterator is applied to each item in the array\nin series. This means the result is always the first in the original array (in\nterms of array order) that passes the truth test.\n\n\n---------------------------------------\n\n\n### sortBy(arr, iterator, callback)\n\nSorts a list by the results of running each value through an async iterator.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and a value to use as the sort criteria.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is the items from\n the original array sorted by the values returned by the iterator calls.\n\n__Example__\n\n async.sortBy(['file1','file2','file3'], function(file, callback){\n fs.stat(file, function(err, stats){\n callback(err, stats.mtime);\n });\n }, function(err, results){\n // results is now the original array of files sorted by\n // modified date\n });\n\n\n---------------------------------------\n\n\n### some(arr, iterator, callback)\n\n__Alias:__ any\n\nReturns true if at least one element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists. Once any iterator\ncall returns true, the main callback is immediately called.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called as soon as any iterator returns\n true, or after all the iterator functions have finished. Result will be\n either true or false depending on the values of the async tests.\n\n__Example__\n\n async.some(['file1','file2','file3'], path.exists, function(result){\n // if result is true then at least one of the files exists\n });\n\n---------------------------------------\n\n\n### every(arr, iterator, callback)\n\n__Alias:__ all\n\nReturns true if every element in the array satisfies an async test.\n_The callback for each iterator call only accepts a single argument of true or\nfalse, it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like path.exists.\n\n__Arguments__\n\n* arr - An array to iterate over.\n* iterator(item, callback) - A truth test to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed.\n* callback(result) - A callback which is called after all the iterator\n functions have finished. Result will be either true or false depending on\n the values of the async tests.\n\n__Example__\n\n async.every(['file1','file2','file3'], path.exists, function(result){\n // if result is true then every file exists\n });\n\n---------------------------------------\n\n\n### concat(arr, iterator, callback)\n\nApplies an iterator to each item in a list, concatenating the results. Returns the\nconcatenated list. The iterators are called in parallel, and the results are\nconcatenated as they return. There is no guarantee that the results array will\nbe returned in the original order of the arguments passed to the iterator function.\n\n__Arguments__\n\n* arr - An array to iterate over\n* iterator(item, callback) - A function to apply to each item in the array.\n The iterator is passed a callback which must be called once it has completed\n with an error (which can be null) and an array of results.\n* callback(err, results) - A callback which is called after all the iterator\n functions have finished, or an error has occurred. Results is an array containing\n the concatenated results of the iterator function.\n\n__Example__\n\n async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){\n // files is now a list of filenames that exist in the 3 directories\n });\n\n---------------------------------------\n\n\n### concatSeries(arr, iterator, callback)\n\nSame as async.concat, but executes in series instead of parallel.\n\n\n## Control Flow\n\n\n### series(tasks, [callback])\n\nRun an array of functions in series, each one running once the previous\nfunction has completed. If any functions in the series pass an error to its\ncallback, no more functions are run and the callback for the series is\nimmediately called with the value of the error. Once the tasks have completed,\nthe results are passed to the final callback as an array.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.series.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed\n a callback it must call on completion.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets an array of all the arguments passed to\n the callbacks used in the array.\n\n__Example__\n\n async.series([\n function(callback){\n // do some stuff ...\n callback(null, 'one');\n },\n function(callback){\n // do some more stuff ...\n callback(null, 'two');\n },\n ],\n // optional callback\n function(err, results){\n // results is now equal to ['one', 'two']\n });\n\n\n // an example using an object instead of an array\n async.series({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n },\n },\n function(err, results) {\n // results is now equal to: {one: 1, two: 2}\n });\n\n\n---------------------------------------\n\n\n### parallel(tasks, [callback])\n\nRun an array of functions in parallel, without waiting until the previous\nfunction has completed. If any of the functions pass an error to its\ncallback, the main callback is immediately called with the value of the error.\nOnce the tasks have completed, the results are passed to the final callback as an\narray.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final callback as an object\ninstead of an array. This can be a more readable way of handling results from\nasync.parallel.\n\n\n__Arguments__\n\n* tasks - An array or object containing functions to run, each function is passed a\n callback it must call on completion.\n* callback(err, results) - An optional callback to run once all the functions\n have completed. This function gets an array of all the arguments passed to\n the callbacks used in the array.\n\n__Example__\n\n async.parallel([\n function(callback){\n setTimeout(function(){\n callback(null, 'one');\n }, 200);\n },\n function(callback){\n setTimeout(function(){\n callback(null, 'two');\n }, 100);\n },\n ],\n // optional callback\n function(err, results){\n // the results array will equal ['one','two'] even though\n // the second function had a shorter timeout.\n });\n\n\n // an example using an object instead of an array\n async.parallel({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n },\n },\n function(err, results) {\n // results is now equals to: {one: 1, two: 2}\n });\n\n\n---------------------------------------\n\n\n### whilst(test, fn, callback)\n\nRepeatedly call fn, while test returns true. Calls the callback when stopped,\nor an error occurs.\n\n__Arguments__\n\n* test() - synchronous truth test to perform before each execution of fn.\n* fn(callback) - A function to call each time the test passes. The function is\n passed a callback which must be called once it has completed with an optional\n error as the first argument.\n* callback(err) - A callback which is called after the test fails and repeated\n execution of fn has stopped.\n\n__Example__\n\n var count = 0;\n\n async.whilst(\n function () { return count < 5; },\n function (callback) {\n count++;\n setTimeout(callback, 1000);\n },\n function (err) {\n // 5 seconds have passed\n }\n );\n\n\n---------------------------------------\n\n\n### until(test, fn, callback)\n\nRepeatedly call fn, until test returns true. Calls the callback when stopped,\nor an error occurs.\n\nThe inverse of async.whilst.\n\n\n---------------------------------------\n\n\n### waterfall(tasks, [callback])\n\nRuns an array of functions in series, each passing their results to the next in\nthe array. However, if any of the functions pass an error to the callback, the\nnext function is not executed and the main callback is immediately called with\nthe error.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a callback it\n must call on completion.\n* callback(err, [results]) - An optional callback to run once all the functions\n have completed. This will be passed the results of the last task's callback.\n\n\n\n__Example__\n\n async.waterfall([\n function(callback){\n callback(null, 'one', 'two');\n },\n function(arg1, arg2, callback){\n callback(null, 'three');\n },\n function(arg1, callback){\n // arg1 now equals 'three'\n callback(null, 'done');\n }\n ], function (err, result) {\n // result now equals 'done' \n });\n\n\n---------------------------------------\n\n\n### queue(worker, concurrency)\n\nCreates a queue object with the specified concurrency. Tasks added to the\nqueue will be processed in parallel (up to the concurrency limit). If all\nworkers are in progress, the task is queued until one is available. Once\na worker has completed a task, the task's callback is called.\n\n__Arguments__\n\n* worker(task, callback) - An asynchronous function for processing a queued\n task.\n* concurrency - An integer for determining how many worker functions should be\n run in parallel.\n\n__Queue objects__\n\nThe queue object returned by this function has the following properties and\nmethods:\n\n* length() - a function returning the number of items waiting to be processed.\n* concurrency - an integer for determining how many worker functions should be\n run in parallel. This property can be changed after a queue is created to\n alter the concurrency on-the-fly.\n* push(task, [callback]) - add a new task to the queue, the callback is called\n once the worker has finished processing the task.\n instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.\n* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued\n* empty - a callback that is called when the last item from the queue is given to a worker\n* drain - a callback that is called when the last item from the queue has returned from the worker\n\n__Example__\n\n // create a queue object with concurrency 2\n\n var q = async.queue(function (task, callback) {\n console.log('hello ' + task.name);\n callback();\n }, 2);\n\n\n // assign a callback\n q.drain = function() {\n console.log('all items have been processed');\n }\n\n // add some items to the queue\n\n q.push({name: 'foo'}, function (err) {\n console.log('finished processing foo');\n });\n q.push({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n });\n\n // add some items to the queue (batch-wise)\n\n q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {\n console.log('finished processing bar');\n });\n\n\n---------------------------------------\n\n\n### auto(tasks, [callback])\n\nDetermines the best order for running functions based on their requirements.\nEach function can optionally depend on other functions being completed first,\nand each function is run as soon as its requirements are satisfied. If any of\nthe functions pass an error to their callback, that function will not complete\n(so any other functions depending on it will not run) and the main callback\nwill be called immediately with the error. Functions also receive an object\ncontaining the results of functions which have completed so far.\n\n__Arguments__\n\n* tasks - An object literal containing named functions or an array of\n requirements, with the function itself the last item in the array. The key\n used for each function or array is used when specifying requirements. The\n syntax is easier to understand by looking at the example.\n* callback(err, results) - An optional callback which is called when all the\n tasks have been completed. The callback will receive an error as an argument\n if any tasks pass an error to their callback. If all tasks complete\n successfully, it will receive an object containing their results.\n\n__Example__\n\n async.auto({\n get_data: function(callback){\n // async code to get some data\n },\n make_folder: function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n },\n write_file: ['get_data', 'make_folder', function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n callback(null, filename);\n }],\n email_link: ['write_file', function(callback, results){\n // once the file is written let's email a link to it...\n // results.write_file contains the filename returned by write_file.\n }]\n });\n\nThis is a fairly trivial example, but to do this using the basic parallel and\nseries functions would look like this:\n\n async.parallel([\n function(callback){\n // async code to get some data\n },\n function(callback){\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n }\n ],\n function(results){\n async.series([\n function(callback){\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n },\n email_link: function(callback){\n // once the file is written let's email a link to it...\n }\n ]);\n });\n\nFor a complicated series of async tasks using the auto function makes adding\nnew tasks much easier and makes the code more readable.\n\n\n---------------------------------------\n\n\n### iterator(tasks)\n\nCreates an iterator function which calls the next function in the array,\nreturning a continuation to call the next one after that. Its also possible to\n'peek' the next iterator by doing iterator.next().\n\nThis function is used internally by the async module but can be useful when\nyou want to manually control the flow of functions in series.\n\n__Arguments__\n\n* tasks - An array of functions to run, each function is passed a callback it\n must call on completion.\n\n__Example__\n\n var iterator = async.iterator([\n function(){ sys.p('one'); },\n function(){ sys.p('two'); },\n function(){ sys.p('three'); }\n ]);\n\n node> var iterator2 = iterator();\n 'one'\n node> var iterator3 = iterator2();\n 'two'\n node> iterator3();\n 'three'\n node> var nextfn = iterator2.next();\n node> nextfn();\n 'three'\n\n\n---------------------------------------\n\n\n### apply(function, arguments..)\n\nCreates a continuation function with some arguments already applied, a useful\nshorthand when combined with other control flow functions. Any arguments\npassed to the returned function are added to the arguments originally passed\nto apply.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to automatically apply when the\n continuation is called.\n\n__Example__\n\n // using apply\n\n async.parallel([\n async.apply(fs.writeFile, 'testfile1', 'test1'),\n async.apply(fs.writeFile, 'testfile2', 'test2'),\n ]);\n\n\n // the same process without using apply\n\n async.parallel([\n function(callback){\n fs.writeFile('testfile1', 'test1', callback);\n },\n function(callback){\n fs.writeFile('testfile2', 'test2', callback);\n },\n ]);\n\nIt's possible to pass any number of additional arguments when calling the\ncontinuation:\n\n node> var fn = async.apply(sys.puts, 'one');\n node> fn('two', 'three');\n one\n two\n three\n\n---------------------------------------\n\n\n### nextTick(callback)\n\nCalls the callback on a later loop around the event loop. In node.js this just\ncalls process.nextTick, in the browser it falls back to setTimeout(callback, 0),\nwhich means other higher priority events may precede the execution of the callback.\n\nThis is used internally for browser-compatibility purposes.\n\n__Arguments__\n\n* callback - The function to call on a later loop around the event loop.\n\n__Example__\n\n var call_order = [];\n async.nextTick(function(){\n call_order.push('two');\n // call_order now equals ['one','two]\n });\n call_order.push('one')\n\n\n## Utils\n\n\n### memoize(fn, [hasher])\n\nCaches the results of an async function. When creating a hash to store function\nresults against, the callback is omitted from the hash and an optional hash\nfunction can be used.\n\n__Arguments__\n\n* fn - the function you to proxy and cache results from.\n* hasher - an optional function for generating a custom hash for storing\n results, it has all the arguments applied to it apart from the callback, and\n must be synchronous.\n\n__Example__\n\n var slow_fn = function (name, callback) {\n // do something\n callback(null, result);\n };\n var fn = async.memoize(slow_fn);\n\n // fn can now be used as if it were slow_fn\n fn('some name', function () {\n // callback\n });\n\n\n### unmemoize(fn)\n\nUndoes a memoized function, reverting it to the original, unmemoized\nform. Comes handy in tests.\n\n__Arguments__\n\n* fn - the memoized function\n\n\n### log(function, arguments)\n\nLogs the result of an async function to the console. Only works in node.js or\nin browsers that support console.log and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.log is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n var hello = function(name, callback){\n setTimeout(function(){\n callback(null, 'hello ' + name);\n }, 1000);\n };\n\n node> async.log(hello, 'world');\n 'hello world'\n\n\n---------------------------------------\n\n\n### dir(function, arguments)\n\nLogs the result of an async function to the console using console.dir to\ndisplay the properties of the resulting object. Only works in node.js or\nin browsers that support console.dir and console.error (such as FF and Chrome).\nIf multiple arguments are returned from the async function, console.dir is\ncalled on each argument in order.\n\n__Arguments__\n\n* function - The function you want to eventually apply all arguments to.\n* arguments... - Any number of arguments to apply to the function.\n\n__Example__\n\n var hello = function(name, callback){\n setTimeout(function(){\n callback(null, {hello: name});\n }, 1000);\n };\n\n node> async.dir(hello, 'world');\n {hello: 'world'}\n\n\n---------------------------------------\n\n\n### noConflict()\n\nChanges the value of async back to its original value, returning a reference to the\nasync object.\n", + "_id": "async@0.1.22", + "_from": "async" +} diff --git a/data/scripts/jsdoc/node_modules/catharsis/LICENSE b/data/scripts/jsdoc/node_modules/catharsis/LICENSE new file mode 100644 index 00000000..9a454a29 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/catharsis/LICENSE @@ -0,0 +1,16 @@ +Copyright (c) 2012-2013 Jeff Williams + +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/data/scripts/jsdoc/node_modules/catharsis/catharsis.js b/data/scripts/jsdoc/node_modules/catharsis/catharsis.js new file mode 100644 index 00000000..a9ac7c35 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/catharsis/catharsis.js @@ -0,0 +1,123 @@ +/** + * Catharsis + * A parser for Google Closure Compiler type expressions, powered by PEG.js. + * + * @author Jeff Williams + * @license MIT License + */ + +'use strict'; + +var parse = require('./lib/parser').parse; +var stringify = require('./lib/stringify'); + +var typeExpressionCache = { + normal: {}, + jsdoc: {} +}; + +var parsedTypeCache = { + normal: {}, + htmlSafe: {} +}; + +function getTypeExpressionCache(options) { + if (options.useCache === false) { + return null; + } else if (options.jsdoc === true) { + return typeExpressionCache.jsdoc; + } else { + return typeExpressionCache.normal; + } +} + +function getParsedTypeCache(options) { + if (options.useCache === false || options.links !== null || options.links !== undefined) { + return null; + } else if (options.htmlSafe === true) { + return parsedTypeCache.htmlSafe; + } else { + return parsedTypeCache.normal; + } +} + +// can't return the original if any of the following are true: +// 1. restringification was requested +// 2. htmlSafe option was requested +// 3. links option was provided +// 4. typeExpression property is missing +function canReturnOriginalExpression(parsedType, options) { + return options.restringify !== true && options.htmlSafe !== true && + (options.links === null || options.links === undefined) && + Object.prototype.hasOwnProperty.call(parsedType, 'typeExpression'); +} + +function cachedParse(expr, options) { + var cache = getTypeExpressionCache(options); + var parsedType; + + if (cache && cache[expr]) { + return cache[expr]; + } else { + parsedType = parse(expr, options); + + Object.defineProperties(parsedType, { + typeExpression: { + value: expr + }, + jsdoc: { + value: options.jsdoc === true ? true : false + } + }); + parsedType = Object.freeze(parsedType); + + if (cache) { + cache[expr] = parsedType; + } + + return parsedType; + } +} + +function cachedStringify(parsedType, options) { + var cache = getParsedTypeCache(options); + var json; + + if (canReturnOriginalExpression(parsedType, options)) { + return parsedType.typeExpression; + } else if (cache) { + json = JSON.stringify(parsedType); + cache[json] = cache[json] || stringify(parsedType, options); + return cache[json]; + } else { + return stringify(parsedType, options); + } +} + +function Catharsis() { + this.Types = require('./lib/types'); +} + +Catharsis.prototype.parse = function(typeExpr, options) { + options = options || {}; + + typeExpr = typeExpr.replace(/[\r\n]/g, '') + .replace(/\s+/g, ' ') + .trim(); + + return cachedParse(typeExpr, options); +}; + +Catharsis.prototype.stringify = function(parsedType, options) { + options = options || {}; + var result; + + result = cachedStringify(parsedType, options); + if (options.validate) { + this.parse(result, options); + } + + return result; +}; + +module.exports = new Catharsis(); diff --git a/data/scripts/jsdoc/node_modules/catharsis/lib/parser.js b/data/scripts/jsdoc/node_modules/catharsis/lib/parser.js new file mode 100644 index 00000000..413fa56b --- /dev/null +++ b/data/scripts/jsdoc/node_modules/catharsis/lib/parser.js @@ -0,0 +1,3 @@ +module.exports=function(){function peg$subclass(child,parent){function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor}function SyntaxError(expected,found,offset,line,column){function buildMessage(expected,found){function stringEscape(s){function hex(ch){return ch.charCodeAt(0).toString(16).toUpperCase()}return s.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\x08/g,"\\b").replace(/\t/g,"\\t").replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/[\x00-\x07\x0B\x0E\x0F]/g,function(ch){return"\\x0"+hex(ch)}).replace(/[\x10-\x1F\x80-\xFF]/g,function(ch){return"\\x"+hex(ch)}).replace(/[\u0180-\u0FFF]/g,function(ch){return"\\u0"+hex(ch)}).replace(/[\u1080-\uFFFF]/g,function(ch){return"\\u"+hex(ch)})}var expectedDesc,foundDesc;switch(expected.length){case 0:expectedDesc="end of input";break;case 1:expectedDesc=expected[0];break;default:expectedDesc=expected.slice(0,-1).join(", ")+" or "+expected[expected.length-1]}foundDesc=found?'"'+stringEscape(found)+'"':"end of input";return"Expected "+expectedDesc+" but "+foundDesc+" found."}this.expected=expected;this.found=found;this.offset=offset;this.line=line;this.column=column;this.name="SyntaxError";this.message=buildMessage(expected,found)}peg$subclass(SyntaxError,Error);function parse(input){var options=arguments.length>1?arguments[1]:{},peg$startRuleFunctions={TypeExpression:peg$parseTypeExpression},peg$startRuleFunction=peg$parseTypeExpression,peg$c0=null,peg$c1="",peg$c2=function(unk){return unk},peg$c3="?",peg$c4='"?"',peg$c5="!",peg$c6='"!"',peg$c7=function(prefix,expr,postfix){if(prefix!==""&&postfix!==""){return null}[prefix,postfix].forEach(function(modifier){if(modifier!==""){expr.nullable=modifier==="?"?true:false}});return expr},peg$c8=function(repeat,lit,opt){var result=lit;if(opt.optional){result.optional=true}if(repeat.repeatable){result.repeatable=true}return result},peg$c9="*",peg$c10='"*"',peg$c11=function(){return{type:Types.AllLiteral}},peg$c12=function(){return{type:Types.NullLiteral}},peg$c13=function(){return{type:Types.UndefinedLiteral}},peg$c14="...",peg$c15='"..."',peg$c16=function(){return{repeatable:true}},peg$c17="=",peg$c18='"="',peg$c19=function(){return{optional:true}},peg$c20="[]",peg$c21='"[]"',peg$c22=function(name){var result;if(!options.jsdoc){return null}result={type:Types.TypeApplication,expression:{type:Types.NameExpression,name:"Array"},applications:[name]};result.applications[0].type=Types.NameExpression;return result},peg$c23=function(exp,appl,opt){var result={};var nameExp={type:Types.NameExpression,name:exp.name};if(appl.length){result.type=Types.TypeApplication;result.expression=nameExp;result.applications=appl}else{result=nameExp}if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c24=function(name){if(!options.jsdoc){return null}return name},peg$c25=function(exp,opt){var result={type:Types.NameExpression,name:exp.name,reservedWord:true};if(exp.repeatable){result.repeatable=true}if(opt.optional){result.optional=true}return result},peg$c26=".",peg$c27='"."',peg$c28="<",peg$c29='"<"',peg$c30=">",peg$c31='">"',peg$c32=function(sep,l){if(sep===""&&!options.jsdoc){return null}return l},peg$c33=[],peg$c34=",",peg$c35='","',peg$c36=function(expr,list){var result=[expr];for(var i=0,l=list.length;ipos){peg$cachedPos=0;peg$cachedPosDetails={line:1,column:1,seenCR:false}}peg$cachedPos=pos;advance(peg$cachedPosDetails,peg$cachedPos)}return peg$cachedPosDetails}function peg$fail(expected){if(peg$currPospeg$maxFailPos){peg$maxFailPos=peg$currPos;peg$maxFailExpected=[]}peg$maxFailExpected.push(expected)}function peg$cleanupExpected(expected){var i=0;expected.sort();while(ipeg$currPos){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c271)}}return s0}function peg$parseHexEscapeSequence(){var s0,s1,s2,s3,s4,s5;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===120){s1=peg$c269;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c270)}}if(s1!==null){s2=peg$currPos;s3=peg$currPos;s4=peg$parseHexDigit();if(s4!==null){s5=peg$parseHexDigit();if(s5!==null){s4=[s4,s5];s3=s4}else{peg$currPos=s3;s3=peg$c0}}else{peg$currPos=s3;s3=peg$c0}if(s3!==null){s3=input.substring(s2,peg$currPos)}s2=s3;if(s2!==null){peg$reportedPos=s0;s1=peg$c216(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parseLineContinuation(){var s0,s1,s2;s0=peg$currPos;if(input.charCodeAt(peg$currPos)===92){s1=peg$c212;peg$currPos++}else{s1=null;if(peg$silentFails===0){peg$fail(peg$c213)}}if(s1!==null){s2=peg$parseLineTerminatorSequence();if(s2!==null){peg$reportedPos=s0;s1=peg$c272(s2);if(s1===null){peg$currPos=s0;s0=s1}else{s0=s1}}else{peg$currPos=s0;s0=peg$c0}}else{peg$currPos=s0;s0=peg$c0}return s0}function peg$parse_(){var s0,s1;peg$silentFails++;s0=[];s1=peg$parseWhitespace();while(s1!==null){s0.push(s1);s1=peg$parseWhitespace()}peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c273)}}return s0}function peg$parse__(){var s0,s1;peg$silentFails++;s0=peg$c1;peg$silentFails--;if(s0===null){s1=null;if(peg$silentFails===0){peg$fail(peg$c274)}}return s0}function peg$parseWhitespace(){var s0;if(peg$c275.test(input.charAt(peg$currPos))){s0=input.charAt(peg$currPos);peg$currPos++}else{s0=null;if(peg$silentFails===0){peg$fail(peg$c276)}}if(s0===null){s0=peg$parseUnicodeZs()}return s0}var Types=require("./types");peg$result=peg$startRuleFunction();if(peg$result!==null&&peg$currPos===input.length){return peg$result}else{peg$cleanupExpected(peg$maxFailExpected);peg$reportedPos=Math.max(peg$currPos,peg$maxFailPos);throw new SyntaxError(peg$maxFailExpected,peg$reportedPos'; + + return result; +}; + +Stringifier.prototype.elements = function(elements) { + if (!elements) { + return ''; + } + + var result = []; + + for (var i = 0, l = elements.length; i < l; i++) { + result.push(this.type(elements[i])); + } + + return '(' + result.join('|') + ')'; +}; + +Stringifier.prototype.name = function(name) { + return name || ''; +}; + +Stringifier.prototype['new'] = function(funcNew) { + return funcNew ? 'new:' + this.type(funcNew) : ''; +}; + +Stringifier.prototype.nullable = function(nullable) { + switch (nullable) { + case true: + return '?'; + case false: + return '!'; + default: + return ''; + } +}; + +Stringifier.prototype.optional = function(optional) { + /*jshint boss: true */ // TODO: remove after JSHint releases the fix for jshint/jshint#878 + if (optional === true) { + return '='; + } else { + return ''; + } +}; + +Stringifier.prototype.params = function(params) { + if (!params || params.length === 0) { + return ''; + } + + var result = []; + + var param; + for (var i = 0, l = params.length; i < l; i++) { + result.push(this.type(params[i])); + } + + return result.join(', '); +}; + +Stringifier.prototype.properties = function(props) { + if (!props) { + return ''; + } + + var result = []; + + for (var i = 0, l = props.length; i < l; i++) { + result.push(this._formatNameAndType(props[i].name, props[i].type)); + } + + return result; +}; + +Stringifier.prototype.result = function(result) { + return result ? ': ' + this.type(result) : ''; +}; + +Stringifier.prototype['this'] = function(funcThis) { + return funcThis ? 'this:' + this.type(funcThis) : ''; +}; + +Stringifier.prototype.type = function(type) { + if (!type) { + return ''; + } + + // nullable comes first + var result = this.nullable(type.nullable); + + // next portion varies by type + switch(type.type) { + case Types.AllLiteral: + result += this._formatNameAndType(type, '*'); + break; + case Types.FunctionType: + result += this._signature(type); + break; + case Types.NullLiteral: + result += this._formatNameAndType(type, 'null'); + break; + case Types.RecordType: + result += this._record(type); + break; + case Types.TypeApplication: + result += this.type(type.expression); + result += this.applications(type.applications); + break; + case Types.UndefinedLiteral: + result += this._formatNameAndType(type, 'undefined'); + break; + case Types.TypeUnion: + result += this.elements(type.elements); + break; + case Types.UnknownLiteral: + result += this._formatNameAndType(type, '?'); + break; + default: + result += this._formatNameAndType(type); + } + + // finally, optionality + result += this.optional(type.optional); + + return result; +}; + +Stringifier.prototype.stringify = Stringifier.prototype.type; + +Stringifier.prototype.key = Stringifier.prototype.type; + +Stringifier.prototype._record = function(type) { + var fields = this._recordFields(type.fields); + + return '{' + fields.join(', ') + '}'; +}; + +Stringifier.prototype._recordFields = function(fields) { + if (!fields) { + return ''; + } + + var result = []; + + var field; + var keyAndValue; + + for (var i = 0, l = fields.length; i < l; i++) { + field = fields[i]; + + keyAndValue = this.key(field.key); + keyAndValue += field.value ? ': ' + this.type(field.value) : ''; + + result.push(keyAndValue); + } + + return result; +}; + +function combineNameAndType(nameString, typeString) { + var separator = (nameString && typeString) ? ':' : ''; + return nameString + separator + typeString; +} + +Stringifier.prototype._formatRepeatable = function(nameString, typeString) { + var open = this._inFunctionSignatureParams ? '...[' : '...'; + var close = this._inFunctionSignatureParams ? ']' : ''; + + return open + combineNameAndType(nameString, typeString) + close; +}; + +Stringifier.prototype._formatNameAndType = function(type, literal) { + var nameString = type.name || literal || ''; + var typeString = type.type ? this.type(type.type) : ''; + var cssClass; + var openTag; + + // replace the type with an HTML link if necessary + if (this._options.links && Object.prototype.hasOwnProperty.call(this._options.links, + nameString)) { + cssClass = this._options.cssClass ? ' class="' + this._options.cssClass + '"' : ''; + + openTag = ''; + nameString = openTag + nameString + ''; + } + + if (type.repeatable === true) { + return this._formatRepeatable(nameString, typeString); + } else { + return combineNameAndType(nameString, typeString); + } +}; + +Stringifier.prototype._signature = function(type) { + var params = []; + var param; + var result; + + // these go within the signature's parens, in this order + var props = [ + 'new', + 'this', + 'params' + ]; + var prop; + + this._inFunctionSignatureParams = true; + for (var i = 0, l = props.length; i < l; i++) { + prop = props[i]; + param = this[prop](type[prop]); + if (param.length > 0) { + params.push(param); + } + } + this._inFunctionSignatureParams = false; + + result = 'function(' + params.join(', ') + ')'; + result += this.result(type.result); + + return result; +}; + + +module.exports = function(type, options) { + return new Stringifier(options).stringify(type); +}; diff --git a/data/scripts/jsdoc/node_modules/catharsis/lib/types.js b/data/scripts/jsdoc/node_modules/catharsis/lib/types.js new file mode 100644 index 00000000..017aba6b --- /dev/null +++ b/data/scripts/jsdoc/node_modules/catharsis/lib/types.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = Object.freeze({ + // `*` + AllLiteral: 'AllLiteral', + // like `blah` in `{blah: string}` + FieldType: 'FieldType', + // like `function(string): string` + FunctionType: 'FunctionType', + // any string literal, such as `string` or `My.Namespace` + NameExpression: 'NameExpression', + // null + NullLiteral: 'NullLiteral', + // like `{foo: string}` + RecordType: 'RecordType', + // like `Array.` + TypeApplication: 'TypeApplication', + // like `(number|string)` + TypeUnion: 'TypeUnion', + // undefined + UndefinedLiteral: 'UndefinedLiteral', + // `?` + UnknownLiteral: 'UnknownLiteral' +}); diff --git a/data/scripts/jsdoc/node_modules/catharsis/package.json b/data/scripts/jsdoc/node_modules/catharsis/package.json new file mode 100644 index 00000000..49046e01 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/catharsis/package.json @@ -0,0 +1,44 @@ +{ + "version": "0.5.6", + "name": "catharsis", + "description": "A JavaScript parser for Google Closure Compiler and JSDoc type expressions.", + "author": { + "name": "Jeff Williams", + "email": "jeffrey.l.williams@gmail.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/hegemonic/catharsis" + }, + "bugs": "https://github.com/hegemonic/catharsis/issues", + "main": "catharsis.js", + "devDependencies": { + "mocha": "1.6.0", + "pegjs": "git+ssh://git@github.com:dmajda/pegjs.git#76cc5d55", + "should": "1.2.2", + "uglify-js": "2.2.5", + "underscore": "1.4.4" + }, + "engines": { + "node": ">= 0.6" + }, + "scripts": { + "build": "pegjs ./lib/parser.pegjs", + "prepublish": "pegjs ./lib/parser.pegjs; uglifyjs ./lib/parser.js -o ./lib/parser.js", + "test": "mocha" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/hegemonic/catharsis/raw/master/LICENSE" + } + ], + "readme": "# Catharsis #\n\nA JavaScript parser for\n[Google Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types)\nand [JSDoc](https://github.com/jsdoc3/jsdoc) type expressions.\n\nCatharsis is designed to be:\n\n+ **Accurate**. Catharsis is based on a [PEG.js](http://pegjs.majda.cz/) grammar that's designed to\nhandle any valid type expression. It uses a [Mocha](http://visionmedia.github.com/mocha/) test suite\nto verify the parser's accuracy.\n+ **Fast**. Parse results are cached, so the parser is invoked only when necessary.\n+ **Flexible**. Catharsis can convert parse results back into type expressions. In addition, it can\nparse [JSDoc](https://github.com/jsdoc3/jsdoc)-style type expressions.\n\n\n## Example ##\n\n\tvar catharsis = require('catharsis');\n\n var type;\n var jsdocType;\n var parsedType;\n var parsedJsdocType;\n\n // Google Closure Compiler parsing\n try {\n type = '!Object';\n parsedType = catharsis.parse(type);\n console.log('%j', parsedType); // {\"type\":\"NameExpression,\"name\":\"Object\",\"nullable\":false}\n }\n catch(e) {\n console.error('unable to parse %s: %s', type, e);\n }\n\n // JSDoc-style type expressions enabled\n try {\n jsdocType = 'string[]'; // Closure Compiler expects Array.\n parsedJsdocType = catharsis.parse(jsdocType, {jsdoc: true});\n }\n catch (e) {\n console.error('unable to parse %s: %s', jsdocType, e);\n }\n\n console.log(catharsis.stringify(parsedType)); // !Object\n console.log(catharsis.stringify(parsedJsdocType)); // string[]\n console.log(catharsis.stringify(parsedJsdocType, // Array.\n {restringify: true}));\n\n\nSee the `test/specs/` directory for more examples of Catharsis' parse results.\n\n\n## Methods ##\n\n### parse(typeExpression, options) ###\nParse `typeExpression`, and return the parse results. Throws an error if the type expression cannot\nbe parsed.\n\nWhen called without options, Catharsis attempts to parse type expressions in the same way as\nClosure Compiler. When the `jsdoc` option is enabled, Catharsis can also parse several kinds of\ntype expressions that are permitted in [JSDoc](https://github.com/jsdoc3/jsdoc):\n\n+ The string `function` is treated as a function type with no parameters.\n+ In a function type with repeatable parameters, the names of repeatable parameters are not required\nto be enclosed in square brackets (for example, `function(...foo)` is allowed).\n+ The period may be omitted from type applications. For example, `Array.` and\n`Array` will be parsed in the same way.\n+ You may append `[]` to a name expression (for example, `string[]`) to interpret it as a type\napplication with the expression `Array` (for example, `Array.`).\n+ Name expressions may contain the characters `#`, `~`, `:`, and `/`.\n+ Name expressions may contain a suffix that is similar to a function signature (for example,\n`MyClass(foo, bar)`).\n+ Name expressions may contain a reserved word.\n+ Record types may use types other than name expressions for keys.\n\n#### Parameters ####\n+ `type`: A string containing a Closure Compiler type expression.\n+ `options`: Options for parsing the type expression.\n + `options.jsdoc`: Specifies whether to enable parsing of JSDoc-style type expressions. Defaults\n to `false`.\n + `options.useCache`: Specifies whether to use the cache of parsed types. Defaults to `true`.\n\n#### Returns ####\nAn object containing the parse results. See the `test/specs/` directory for examples of the parse\nresults for different type expressions.\n\nThe object also includes two non-enumerable properties:\n\n+ `jsdoc`: A boolean indicating whether the type expression was parsed with JSDoc support enabled.\n+ `typeExpression`: A string containing the type expression that was parsed.\n\n### stringify(parsedType, options) ###\nStringify `parsedType`, and return the type expression. If validation is enabled, throws an error if\nthe stringified type expression cannot be parsed.\n\n#### Parameters ####\n+ `parsedType`: An object containing a parsed Closure Compiler type expression.\n+ `options`: Options for stringifying the parse results.\n + `options.cssClass`: A CSS class to add to HTML links. Used only if `options.links` is\n provided. By default, no CSS class is added.\n + `options.htmlSafe`: Specifies whether to return an HTML-safe string that replaces left angle\n brackets (`<`) with the corresponding entity (`<`). **Note**: Characters in name expressions\n are not escaped.\n + `options.links`: An object whose keys are name expressions and whose values are URIs. If a\n name expression matches a key in `options.links`, the name expression will be wrapped in an\n HTML `` tag that links to the URI. If `options.cssClass` is specified, the `` tag will\n include a `class` attribute. **Note**: When using this option, parsed types are always\n restringified, and the resulting string is not cached.\n + `options.restringify`: Forces Catharsis to restringify the parsed type. If this option is not\n specified, and the parsed type object includes a `typeExpression` property, Catharsis will\n return the `typeExpression` property without modification when possible. Defaults to `false`.\n + `options.useCache`: Specifies whether to use the cache of stringified parse results. Defaults\n to `true`.\n + `options.validate`: Specifies whether to validate the stringified parse results by attempting\n to parse them as a type expression. If the stringified results are not parsable by default, you\n must also provide the appropriate options to pass to the `parse()` method. Defaults to `false`.\n\n#### Returns ####\nA string containing the type expression.\n\n\n## Installation ##\n\nWith [npm](http://npmjs.org):\n\n npm install catharsis\n\nOr without:\n\n git clone git://github.com/hegemonic/catharsis.git\n\n\n## Roadmap and known issues ##\n\nTake a look at the [issue tracker](https://github.com/hegemonic/catharsis/issues) to see what's in\nstore for Catharsis.\n\nBug reports, feature requests, and pull requests are always welcome! If you're working on a large\npull request, please contact me in advance so I can help things go smoothly.\n\n**Note**: The parse tree's format should not be considered final until Catharsis reaches version\n1.0. I'll do my best to provide release notes for any changes.\n\n\n## Changelog ##\n\n+ 0.5.6 (April 2013):\n + For consistency with Google Closure Library, parentheses are no longer required around type\n unions. (In previous versions, the parentheses could be omitted when JSDoc support was enabled.)\n + For consistency with Google Closure Library, you can now use postfix notation for the `?`\n (nullable) and `!` (non-nullable) modifiers. For example, `?string` and `string?` are now\n treated as equivalent.\n + String literals and numeric literals are now allowed as property names within name\n expressions. For example, the name expression `Foo.\"bar\"` is now parsed correctly.\n+ 0.5.5 (April 2013): Corrected a parsing issue with name expressions that end with a value enclosed\nin parentheses.\n+ 0.5.4 (April 2013):\n + Repeatable literals (for example, `...*`) are now parsed correctly.\n + When JSDoc-style type expressions are enabled, a name expression can now contain a value\n enclosed in parentheses at the end of the name expression (for example, `MyClass(2)`).\n+ 0.5.3 (March 2013): The `parse()` method now correctly parses name expressions that contain\nhyphens.\n+ 0.5.2 (March 2013): The `parse()` method now correctly parses function types when JSDoc-style type\nexpressions are enabled.\n+ 0.5.1 (March 2013): Newlines and extra spaces are now removed from type expressions before they\nare parsed.\n+ 0.5.0 (March 2013):\n + The `parse()` method's `lenient` option has been renamed to `jsdoc`. **Note**: This change is\n not backwards-compatible with previous versions.\n + The `stringify()` method now accepts `cssClass` and `links` options, which you can use to\n add HTML links to a type expression.\n+ 0.4.3 (March 2013):\n + The `stringify()` method no longer caches HTML-safe type expressions as if they were normal\n type expressions.\n + The `stringify()` method's options parameter may now include an `options.restringify`\n property, and the behavior of the `options.useCache` property has changed.\n+ 0.4.2 (March 2013):\n + When lenient parsing is enabled, name expressions can now contain the characters `:` and `/`.\n + When lenient parsing is enabled, a name expression followed by `[]` (for example, `string[]`)\n will be interpreted as a type application with the expression `Array` (for example,\n `Array.`).\n+ 0.4.1 (March 2013):\n + The `parse()` and `stringify()` methods now honor all of the specified options.\n + When lenient parsing is enabled, name expressions can now contain a reserved word.\n+ 0.4.0 (March 2013):\n + Catharsis now supports a lenient parsing option that can parse several kinds of malformed type\n expressions. See the documentation for details.\n + The objects containing parse results are now frozen.\n + The objects containing parse results now have two non-enumerable properties:\n + `lenient`: A boolean indicating whether the type expression was parsed in lenient mode.\n + `typeExpression`: A string containing the original type expression.\n + The `stringify()` method now honors the `useCache` option. If a parsed type includes a\n `typeExpression` property, and `useCache` is not set to `false`, the stringified type will be\n identical to the original type expression.\n+ 0.3.1 (March 2013): Type expressions that begin with a reserved word, such as `integer`, are now\nparsed correctly.\n+ 0.3.0 (March 2013):\n + The `parse()` and `stringify()` methods are now synchronous, and the `parseSync()` and\n `stringifySync()` methods have been removed. **Note**: This change is not backwards-compatible\n with previous versions.\n + The parse results now use a significantly different format from previous versions. The new\n format is more expressive and is similar, but not identical, to the format used by the\n [doctrine](https://github.com/Constellation/doctrine) parser. **Note**: This change is not\n backwards-compatible with previous versions.\n + Name expressions that contain a reserved word now include a `reservedWord: true` property.\n + Union types that are optional or nullable, or that can be passed a variable number of times,\n are now parsed and stringified correctly.\n + Optional function types and record types are now parsed and stringified correctly.\n + Function types now longer include `new` or `this` properties unless the properties are defined\n in the type expression. In addition, the `new` and `this` properties can now use any type\n expression.\n + In record types, the key for a field type can now use any type expression.\n + Standalone single-character literals, such as ALL (`*`), are now parsed and stringified\n correctly.\n + `null` and `undefined` literals with additional properties, such as `repeatable`, are now\n stringified correctly.\n+ 0.2.0 (November 2012):\n + Added `stringify()` and `stringifySync()` methods, which convert a parsed type to a type\n expression.\n + Simplified the parse results for function signatures. **Note**: This change is not\n backwards-compatible with previous versions.\n + Corrected minor errors in README.md.\n+ 0.1.1 (November 2012): Added `opts` argument to `parse()` and `parseSync()` methods. **Note**: The\nchange to `parse()` is not backwards-compatible with previous versions.\n+ 0.1.0 (November 2012): Initial release.\n\n## License ##\n\n[MIT license](https://github.com/hegemonic/catharsis/blob/master/LICENSE).\n", + "readmeFilename": "README.md", + "_id": "catharsis@0.5.6", + "dist": { + "shasum": "210a0cfa23c5d09fa994d6fafe1a76383c170cbb" + }, + "_from": "catharsis@0.5.6", + "_resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.5.6.tgz" +} diff --git a/data/scripts/jsdoc/node_modules/crypto-browserify/index.js b/data/scripts/jsdoc/node_modules/crypto-browserify/index.js new file mode 100644 index 00000000..a6762ed4 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/crypto-browserify/index.js @@ -0,0 +1,68 @@ +var sha = require('./sha') +var rng = require('./rng') + +var algorithms = { + sha1: { + hex: sha.hex_sha1, + binary: sha.b64_sha1, + ascii: sha.str_sha1 + } +} + +function error () { + var m = [].slice.call(arguments).join(' ') + throw new Error([ + m, + 'we accept pull requests', + 'http://github.com/dominictarr/crypto-browserify' + ].join('\n')) +} + +exports.createHash = function (alg) { + alg = alg || 'sha1' + if(!algorithms[alg]) + error('algorithm:', alg, 'is not yet supported') + var s = '' + var _alg = algorithms[alg] + return { + update: function (data) { + s += data + return this + }, + digest: function (enc) { + enc = enc || 'binary' + var fn + if(!(fn = _alg[enc])) + error('encoding:', enc , 'is not yet supported for algorithm', alg) + var r = fn(s) + s = null //not meant to use the hash after you've called digest. + return r + } + } +} + +exports.randomBytes = function(size, callback) { + if (callback && callback.call) { + try { + callback.call(this, undefined, rng(size)); + } catch (err) { callback(err); } + } else { + return rng(size); + } +} + +// the least I can do is make error messages for the rest of the node.js/crypto api. +;['createCredentials' +, 'createHmac' +, 'createCypher' +, 'createCypheriv' +, 'createDecipher' +, 'createDecipheriv' +, 'createSign' +, 'createVerify' +, 'createDeffieHellman' +, 'pbkdf2'].forEach(function (name) { + exports[name] = function () { + error('sorry,', name, 'is not implemented yet') + } +}) diff --git a/data/scripts/jsdoc/node_modules/crypto-browserify/package.json b/data/scripts/jsdoc/node_modules/crypto-browserify/package.json new file mode 100644 index 00000000..99a6930c --- /dev/null +++ b/data/scripts/jsdoc/node_modules/crypto-browserify/package.json @@ -0,0 +1,29 @@ +{ + "author": { + "name": "Dominic Tarr", + "email": "dominic.tarr@gmail.com", + "url": "dominictarr.com" + }, + "name": "crypto-browserify", + "description": "partial implementation of crypto for the browser", + "version": "0.1.1", + "homepage": "https://github.com/dominictarr/crypto-browserify", + "repository": { + "url": "" + }, + "scripts": { + "test": "node test/simple.js" + }, + "engines": { + "node": "*" + }, + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "readme": "# crypto-browserify\n\nA (partial) port of `crypto` to the browser.\n\nBasically, I found some crypto implemented in JS lieing on the internet somewhere\nand wrapped it in the part of the `crypto` api that I am currently using.\n\nIn a way that will be compatible with [browserify](https://github.com/substack/node-browserify/).\n\nI will extend this if I need more features, or if anyone else wants to extend this,\nI will add you as a maintainer.\n\nProvided that you agree that it should replicate the [node.js/crypto](http://nodejs.org/api/crypto.html) api exactly, of course.\n\n", + "_id": "crypto-browserify@0.1.1", + "dist": { + "shasum": "251b240c6bd0e95db0654fbc8b178b855cbef45e" + }, + "_from": "crypto-browserify@git://github.com/dominictarr/crypto-browserify.git#95c5d505" +} diff --git a/data/scripts/jsdoc/node_modules/crypto-browserify/rng.js b/data/scripts/jsdoc/node_modules/crypto-browserify/rng.js new file mode 100644 index 00000000..2160788a --- /dev/null +++ b/data/scripts/jsdoc/node_modules/crypto-browserify/rng.js @@ -0,0 +1,37 @@ +// Original code adapted from Robert Kieffer. +// details at https://github.com/broofa/node-uuid +(function() { + var _global = this; + + var mathRNG, whatwgRNG; + + // NOTE: Math.random() does not guarantee "cryptographic quality" + mathRNG = function(size) { + var bytes = new Array(size); + var r; + + for (var i = 0, r; i < size; i++) { + if ((i & 0x03) == 0) r = Math.random() * 0x100000000; + bytes[i] = r >>> ((i & 0x03) << 3) & 0xff; + } + + return bytes; + } + + // currently only available in webkit-based browsers. + if (_global.crypto && crypto.getRandomValues) { + var _rnds = new Uint32Array(4); + whatwgRNG = function(size) { + var bytes = new Array(size); + crypto.getRandomValues(_rnds); + + for (var c = 0 ; c < size; c++) { + bytes[c] = _rnds[c >> 2] >>> ((c & 0x03) * 8) & 0xff; + } + return bytes; + } + } + + module.exports = whatwgRNG || mathRNG; + +}()) \ No newline at end of file diff --git a/data/scripts/jsdoc/node_modules/crypto-browserify/sha.js b/data/scripts/jsdoc/node_modules/crypto-browserify/sha.js new file mode 100644 index 00000000..4f9cc3e9 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/crypto-browserify/sha.js @@ -0,0 +1,210 @@ +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +exports.hex_sha1 = hex_sha1; +exports.b64_sha1 = b64_sha1; +exports.str_sha1 = str_sha1; +exports.hex_hmac_sha1 = hex_hmac_sha1; +exports.b64_hmac_sha1 = b64_hmac_sha1; +exports.str_hmac_sha1 = str_hmac_sha1; + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for(var j = 0; j < 80; j++) + { + if(j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); + +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if(t < 20) return (b & c) | ((~b) & d); + if(t < 40) return b ^ c ^ d; + if(t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + diff --git a/data/scripts/jsdoc/node_modules/js2xmlparser/LICENSE.md b/data/scripts/jsdoc/node_modules/js2xmlparser/LICENSE.md new file mode 100644 index 00000000..0cb547eb --- /dev/null +++ b/data/scripts/jsdoc/node_modules/js2xmlparser/LICENSE.md @@ -0,0 +1,16 @@ +js2xmlparser is licensed under the MIT license: + +> Copyright (C) 2012 Michael Kourlas +> +> 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/data/scripts/jsdoc/node_modules/js2xmlparser/lib/js2xmlparser.js b/data/scripts/jsdoc/node_modules/js2xmlparser/lib/js2xmlparser.js new file mode 100644 index 00000000..cf8110f1 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/js2xmlparser/lib/js2xmlparser.js @@ -0,0 +1,223 @@ +var xmlDeclaration = true; +var xmlVersion = "1.0"; +var xmlEncoding = "UTF-8"; +var attributeString = "@"; +var valueString = "#"; +var prettyPrinting = true; +var indentString = "\t"; + +module.exports = function (root, data, options) { + + return toXML(init(root, data, options)); +}; + +// Initialization +var init = function(root, data, options) { + + // Error checking for root element + if (typeof root !== "string") + throw new Error("root element must be a string"); + + // Error checking and variable initialization for options + if (typeof options === "object" && options !== null) { + + if ("declaration" in options) { + + if ("include" in options.declaration) { + + if (typeof options.declaration.include === "boolean") + xmlDeclaration = options.declaration.include; + else + throw new Error("declaration.include option must be a boolean"); + } + + if ("encoding" in options.declaration) { + + if (typeof options.declaration.encoding === "string" || options.declaration.encoding === null) + xmlEncoding = options.declaration.encoding; + else + throw new Error("declaration.encoding option must be a string or null"); + } + } + if ("attributeString" in options) { + + if (typeof options.attributeString === "string") + attributeString = options.attributeString; + else + throw new Error("attributeString option must be a string"); + } + if ("valueString" in options) { + + if (typeof options.valueString === "string") + valueString = options.valueString; + else + throw new Error("valueString option must be a string"); + } + if ("prettyPrinting" in options) { + + if ("enabled" in options.prettyPrinting) { + + if (typeof options.prettyPrinting.enabled === "boolean") + prettyPrinting = options.prettyPrinting.enabled; + else + throw new Error("prettyPrinting.enabled option must be a boolean"); + } + + if ("indentString" in options.prettyPrinting) { + + if (typeof options.prettyPrinting.indentString === "string") + indentString = options.prettyPrinting.indentString; + else + throw new Error("prettyPrinting.indentString option must be a string"); + } + } + } + + // Error checking and variable initialization for data + if (typeof data !== "string" && typeof data !== "object") + throw new Error("data must be an object or a string"); + + if (typeof data === "string") + data = JSON.parse(data); + + var tempData = {}; + tempData[root] = data; // Add root element to object + + return tempData; +}; + +// Convert object to XML +var toXML = function(object) { + + // Initialize arguments, if necessary + var xml = arguments[1] || ""; + var level = arguments[2] || 0; + + for (var property in object) { + + // Arrays + if (Object.prototype.toString.call(object[property]) === "[object Array]") { + + // Create separate object for each array element and pass to this function + for (var i = 0; i < object[property].length; i++) { + + var obj = {}; + obj[property] = object[property][i]; + + xml = toXML(obj, xml, level); + } + } + // JSON-type objects with properties + else if (Object.prototype.toString.call(object[property]) === "[object Object]") { + + xml += addIndent("<" + property, level); + + // Add attributes + var lengthExcludingAttributes = Object.keys(object[property]).length; + if (Object.prototype.toString.call(object[property][attributeString]) === "[object Object]") { + + lengthExcludingAttributes -= 1; + for (var attribute in object[property][attributeString]) + xml += " " + attribute + "=\"" + toString(object[property][attributeString][attribute]) + "\""; + } + + if (lengthExcludingAttributes === 0) // Empty object + xml += addBreak("/>"); + else if (lengthExcludingAttributes === 1 && valueString in object[property]) // Value string only + xml += addBreak(">" + toString(object[property][valueString]) + ""); + else { // Object with properties + + xml += addBreak(">"); + + // Create separate object for each property and pass to this function + for (var subProperty in object[property]) { + + if (subProperty !== attributeString) { + + var tempObject = {}; + tempObject[subProperty] = object[property][subProperty]; + + xml = toXML(tempObject, xml, level + 1); + } + } + + xml += addBreak(addIndent("", level)); + } + } + // Everything else + else { + + xml += addBreak(addIndent("<" + property + ">" + toString(object[property]) + "", level)); + } + } + + // Finalize XML at end of process + if (level === 0) { + + // Remove extra line break at end of file + xml = xml.substring(0, xml.length - 1); + + // Add XML declaration + if (xmlDeclaration) + if (xmlEncoding === null) + xml = addBreak("") + xml; + else + xml = addBreak("") + xml; + } + + return xml; +}; + +// Add indenting to data for pretty printing +var addIndent = function(data, level) { + + if (prettyPrinting) { + + var indent = ""; + for (var i = 0; i < level; i++) { + indent += indentString; + } + data = indent + data; + } + + return data; +}; + +// Add line break to data for pretty printing +var addBreak = function(data) { + + return prettyPrinting ? data + "\n" : data; +}; + +// Convert anything into a valid XML string representation +var toString = function(data) { + + // Recursive function used to handle nested functions + var functionHelper = function(data) { + + if (Object.prototype.toString.call(data) === "[object Function]") + return (data() === undefined) ? data.toString() : functionHelper(data()); + else + return data; + }; + + // Functions + if (Object.prototype.toString.call(data) === "[object Function]") + data = functionHelper(data); + // Empty objects + else if (Object.prototype.toString.call(data) === "[object Object]" && Object.keys(data).length === 0) + data = ""; + + // Cast data to string + if (typeof data !== "string") + data = data.toString(); + + // Escape illegal XML characters + data = data.replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + + return data; +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/node_modules/js2xmlparser/package.json b/data/scripts/jsdoc/node_modules/js2xmlparser/package.json new file mode 100644 index 00000000..d4384f36 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/js2xmlparser/package.json @@ -0,0 +1,30 @@ +{ + "name": "js2xmlparser", + "description": "Parses JavaScript objects into XML", + "keywords": [ + "convert", + "converter", + "js", + "json", + "object", + "objects", + "parse", + "parser", + "xml" + ], + "homepage": "http://www.kourlas.net", + "version": "0.1.0", + "author": { + "name": "Michael Kourlas", + "email": "michaelkourlas@gmail.com" + }, + "main": "./lib/js2xmlparser.js", + "repository": { + "type": "git", + "url": "git://github.com/michaelkourlas/node-js2xmlparser.git" + }, + "license": "MIT", + "readme": "# node-js2xmlparser #\r\n\r\n## Overview ##\r\n\r\njs2xmlparser is a Node.js module that parses JavaScript objects into XML.\r\n\r\n## Features ##\r\n\r\nSince XML is a data-interchange format, js2xmlparser is designed primarily for JSON-type objects, arrays and primitive\r\ndata types, like many of the other JavaScript to XML parsers currently available for Node.js.\r\n\r\nHowever, js2xmlparser is capable of parsing any object, including native JavaScript objects such as Date and RegExp, by\r\ntaking advantage of each object's toString function. Functions are a special case where the return value of the function\r\nitself is used instead of the toString function, if available.\r\n\r\njs2xmlparser also supports a number of constructs unique to XML:\r\n\r\n* attributes (through a unique attribute property in objects)\r\n* mixed content (through a unique value property in objects)\r\n* multiple elements with the same name (through arrays)\r\n\r\njs2xmlparser can also pretty-print the XML it outputs with the option of customizing the indent string.\r\n\r\n## Installation ##\r\n\r\nThe easiest way to install js2xmlparser is to use npm: `npm install js2xmlparser`.\r\n\r\nAlternatively, you may download the source from GitHub and copy it to a folder named \"js2xmlparser\" within your\r\n\"node_modules\" directory.\r\n\r\n## Usage ##\r\n\r\nThe js2xmlparser module contains one function which takes the following arguments:\r\n\r\n* `root` - string containing the root element of the XML\r\n* `data` - object or JSON string to be converted to XML\r\n* `options` - object containing options (optional)\r\n * `declaration` - XML declaration options object (optional)\r\n * `include` - boolean representing whether an XML declaration is included (optional, default: true)\r\n * `encoding` - string representing the XML encoding for the corresponding attribute in the declaration; a value\r\n of null represents no encoding attribute (optional, default: \"UTF-8\")\r\n * `attributeString` - string containing the attribute property (optional, default: \"@\")\r\n * `valueString` - string containing the value property (optional, default: \"#\")\r\n * `prettyPrinting` - pretty-printing options object (optional)\r\n * `enabled` - boolean representing whether pretty-printing is enabled (optional, default: true)\r\n * `indentString` - string representing the indent (optional, default: \"\\t\")\r\n\r\n## Example ##\r\n\r\nThe following example illustrates the basic usage of js2xmlparser:\r\n\r\n var js2xmlparser = require(\"js2xmlparser\");\r\n\r\n var data = {\r\n \"firstName\": \"John\",\r\n \"lastName\": \"Smith\"\r\n };\r\n\r\n console.log(js2xmlparser(\"person\", data));\r\n\r\n > \r\n > \r\n > John\r\n > Smith\r\n > \r\n\r\nHere's a more complex example that builds on the first:\r\n\r\n var js2xmlparser = require(\"js2xmlparser\");\r\n\r\n var data = {\r\n \"firstName\": \"John\",\r\n \"lastName\": \"Smith\",\r\n \"dateOfBirth\": new Date(1964, 07, 26),\r\n \"address\": {\r\n \"@\": {\r\n \"type\": \"home\"\r\n },\r\n \"streetAddress\": \"3212 22nd St\",\r\n \"city\": \"Chicago\",\r\n \"state\": \"Illinois\",\r\n \"zip\": 10000\r\n },\r\n \"phone\": [\r\n {\r\n \"@\": {\r\n \"type\": \"home\"\r\n },\r\n \"#\": \"123-555-4567\"\r\n },\r\n {\r\n \"@\": {\r\n \"type\": \"cell\"\r\n },\r\n \"#\": \"456-555-7890\"\r\n }\r\n ],\r\n \"email\": function() {return \"john@smith.com\";}\r\n }\r\n\r\n console.log(js2xmlparser(\"person\", data));\r\n\r\n > \r\n > \r\n > John\r\n > Smith\r\n > Wed Aug 26 1964 00:00:00 GMT-0400 (Eastern Daylight Time)\r\n >
\r\n > 3212 22nd St\r\n > Chicago\r\n > Illinois\r\n > 10000\r\n >
\r\n > 123-555-4567\r\n > 456-555-7890\r\n > john@smith.com\r\n >
", + "_id": "js2xmlparser@0.1.0", + "_from": "js2xmlparser" +} diff --git a/data/scripts/jsdoc/node_modules/jsdoc b/data/scripts/jsdoc/node_modules/jsdoc new file mode 100644 index 00000000..bc705d5f --- /dev/null +++ b/data/scripts/jsdoc/node_modules/jsdoc @@ -0,0 +1 @@ +../lib/jsdoc \ No newline at end of file diff --git a/data/scripts/jsdoc/node_modules/jshint/LICENSE b/data/scripts/jsdoc/node_modules/jshint/LICENSE new file mode 100644 index 00000000..0b310fe7 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/jshint/LICENSE @@ -0,0 +1,4 @@ +** Licensed Under ** + + The MIT License + http://www.opensource.org/licenses/mit-license.php diff --git a/data/scripts/jsdoc/node_modules/jshint/package.json b/data/scripts/jsdoc/node_modules/jshint/package.json new file mode 100644 index 00000000..75d12516 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/jshint/package.json @@ -0,0 +1,43 @@ +{ + "name": "jshint", + "version": "0.9.1", + "description": "A CLI for JSHint", + "homepage": "http://github.com/jshint/node-jshint", + "author": { + "name": "Brent Lintner", + "email": "brent.lintner@gmail.com", + "url": "http://github.com/brentlintner" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/mit-license.php" + } + ], + "bin": { + "jshint": "./bin/hint" + }, + "main": "packages/jshint/jshint", + "files": [ + "packages/jshint/README.markdown", + "packages/jshint/jshint.js", + "README.md", + "LICENSE", + "bin/hint", + "lib" + ], + "dependencies": { + "cli": "0.4.3", + "minimatch": "0.0.x" + }, + "devDependencies": { + "jasmine-node": "1.0.x" + }, + "preferGlobal": true, + "readme": "# node-jshint\n\nA command line interface and npm package for jshint.\n\n## Install\n\nTo use jshint from any location (for npm v1.x) you need to install using the global (-g) flag.\n\n npm install -g jshint\n\n## Usage\n\n jshint -h\n\nYou can also require JSHint itself as a module.\n\n var jshint = require('jshint');\n\nNote: If you are using npm v1.x be sure to install jshint locally (without the -g flag) or link it globally.\n\n## Text Editor Plugins\n\n* [gedit-node-jshint](https://github.com/niftylettuce/gedit-node-jshint) - Simply use CTRL+J in gedit to run JSHint using `node-jshint`.\n* [vim syntastic](https://github.com/scrooloose/syntastic) - Run node-jshint at each file save.\n* [sublime-jshint](https://github.com/uipoet/sublime-jshint) - `F7` or `command-B` on any .js file. `F4` next error line,column. `shift-F4` previous error line,column.\n\n## Custom Reporters\n\nSpecify a custom reporter module (see example/reporter.js).\n\n --reporter path/to/reporter.js\n\nUse a jslint compatible xml reporter.\n\n --jslint-reporter\n\nShow additional non-error data generated by jshint (unused globals etc).\n\n --show-non-errors\n\n## Configuration Options\n\n**Note:** This behavior described below is very different from versions prior to `0.6`.\n\nThe CLI uses the default options that come with JSHint. \n\nOnly one extra option is unique to node-jshint: `globals` \nallows you to define an object of globals that get ignored for every file.\nTo see an example you can look at how whitelisted globals are defined\nin the [jshint code](https://github.com/jshint/jshint/blob/c047ea1b01097fcc220fcd1a55c41f67ae2e6e81/jshint.js#L556)\n\nTo have your own configuration apply, there are several methods you can use:\n\n### Specify Manually\n\nSetting the `--config=/path/to/your/config` command line option to specify your own configuration file outside of the directory tree for your project.\n\n### Within your Project's Directory Tree\n\nWhen the CLI is called, and a configuration file isn't specified already, `node-jshint` will attempt to locate one for you starting in `pwd`. (or \"present working directory\") If this does not yield a `.jshintrc` file, it will move one level up (`..`) the directory tree all the way up to the filesystem root. If a file is found, it stops immediately and uses that set of configuration.\n\nThis setup allows you to set up **one** configuration file for your entire project. (place it in the root folder) As long as you run `jshint` from anywhere within your project directory tree, the same configuration file will be used.\n\n### Home Directory\n\nIf all the methods above do not yield a `.jshintrc` to use, the last place that will be checked is your user's `$HOME` directory.\n\n## File Extensions\n\nDefault extension for files is \".js\". If you want to use JSHint with other file extensions (.json), you need to pass this extra extension as an option :\n\n --extra-ext .json\n\n## Ignoring Files and Directories\n\nIf there is a .jshintignore file in your project's directory tree, (also provided you run `jshint` from within your project's directory) then any directories or files specified will be skipped over. (behaves just like a `.gitignore` file)\n\n**Note:** Pattern matching uses minimatch, with the nocase [option](https://github.com/isaacs/minimatch). When there is no match, it performs a left side match (when no forward slashes present and path is a directory).\n\n## Installing dependencies for development\n\n ./configure\n\n## Build Commands\n\n jake -T\n\n## Project Guidelines\n\n* All tests are passing.\n* No (new) JSHint errors are introduced.\n", + "_id": "jshint@0.9.1", + "dist": { + "shasum": "ea05fb7d6dae4f2d0d0392ad89b06f7b72eb497e" + }, + "_from": "jshint" +} diff --git a/data/scripts/jsdoc/node_modules/jshint/packages/jshint/jshint.js b/data/scripts/jsdoc/node_modules/jshint/packages/jshint/jshint.js new file mode 100644 index 00000000..53724f5c --- /dev/null +++ b/data/scripts/jsdoc/node_modules/jshint/packages/jshint/jshint.js @@ -0,0 +1,4832 @@ +/*! + * JSHint, by JSHint Community. + * + * Licensed under the same slightly modified MIT license that JSLint is. + * It stops evil-doers everywhere. + * + * JSHint is a derivative work of JSLint: + * + * Copyright (c) 2002 Douglas Crockford (www.JSLint.com) + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * The Software shall be used for Good, not Evil. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * JSHint was forked from the 2010-12-16 edition of JSLint. + * + */ + +/* + JSHINT is a global function. It takes two parameters. + + var myResult = JSHINT(source, option); + + The first parameter is either a string or an array of strings. If it is a + string, it will be split on '\n' or '\r'. If it is an array of strings, it + is assumed that each string represents one line. The source can be a + JavaScript text or a JSON text. + + The second parameter is an optional object of options which control the + operation of JSHINT. Most of the options are booleans: They are all + optional and have a default value of false. One of the options, predef, + can be an array of names, which will be used to declare global variables, + or an object whose keys are used as global names, with a boolean value + that determines if they are assignable. + + If it checks out, JSHINT returns true. Otherwise, it returns false. + + If false, you can inspect JSHINT.errors to find out the problems. + JSHINT.errors is an array of objects containing these members: + + { + line : The line (relative to 1) at which the lint was found + character : The character (relative to 1) at which the lint was found + reason : The problem + evidence : The text line in which the problem occurred + raw : The raw message before the details were inserted + a : The first detail + b : The second detail + c : The third detail + d : The fourth detail + } + + If a fatal error was found, a null will be the last element of the + JSHINT.errors array. + + You can request a data structure which contains JSHint's results. + + var myData = JSHINT.data(); + + It returns a structure with this form: + + { + errors: [ + { + line: NUMBER, + character: NUMBER, + reason: STRING, + evidence: STRING + } + ], + functions: [ + name: STRING, + line: NUMBER, + character: NUMBER, + last: NUMBER, + lastcharacter: NUMBER, + param: [ + STRING + ], + closure: [ + STRING + ], + var: [ + STRING + ], + exception: [ + STRING + ], + outer: [ + STRING + ], + unused: [ + STRING + ], + global: [ + STRING + ], + label: [ + STRING + ] + ], + globals: [ + STRING + ], + member: { + STRING: NUMBER + }, + unused: [ + { + name: STRING, + line: NUMBER + } + ], + implieds: [ + { + name: STRING, + line: NUMBER + } + ], + urls: [ + STRING + ], + json: BOOLEAN + } + + Empty arrays will not be included. + +*/ + +/*jshint + evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true, + undef: true, maxlen: 100, indent: 4, quotmark: double, unused: true +*/ + +/*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)", + "(breakage)", "(character)", "(context)", "(error)", "(explicitNewcap)", "(global)", + "(identifier)", "(last)", "(lastcharacter)", "(line)", "(loopage)", "(metrics)", + "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "(tokens)", "(catch)", + "*", "+", "++", "-", "--", "\/", "<", "<=", "==", + "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax, + __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio, + Autocompleter, Asset, Boolean, Builder, Buffer, Browser, Blob, COM, CScript, Canvas, + CustomAnimation, Class, Control, ComplexityCount, Chain, Color, Cookie, Core, DataView, Date, + Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMEvent, DOMReady, DOMParser, + Drag, E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event, + Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form, + FormField, Frame, FormData, Function, Fx, GetObject, Group, Hash, HotKey, + HTMLElement, HTMLAnchorElement, HTMLBaseElement, HTMLBlockquoteElement, + HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDirectoryElement, + HTMLDivElement, HTMLDListElement, HTMLFieldSetElement, + HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement, + HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement, + HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLIsIndexElement, + HTMLLabelElement, HTMLLayerElement, HTMLLegendElement, HTMLLIElement, + HTMLLinkElement, HTMLMapElement, HTMLMenuElement, HTMLMetaElement, + HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement, + HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement, + HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLStyleElement, + HtmlTable, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement, + HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement, + HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLVideoElement, + Iframe, IframeShim, Image, importScripts, Int16Array, Int32Array, Int8Array, + Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E, + MAX_VALUE, MIN_VALUE, Map, Mask, Math, MenuItem, MessageChannel, MessageEvent, MessagePort, + MoveAnimation, MooTools, MutationObserver, NaN, Native, NEGATIVE_INFINITY, Node, NodeFilter, + Number, Object, ObjectRange, + Option, Options, OverText, PI, POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype, + RangeError, Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation, Set, + SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion, + ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller, + Slick, Slider, Selector, SharedWorker, String, Style, SyntaxError, Sortable, Sortables, + SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template, + Timer, Tips, Type, TypeError, Toggle, Try, "use strict", unescape, URI, URIError, URL, + VBArray, WeakMap, WSH, WScript, XDomainRequest, Web, Window, XMLDOM, XMLHttpRequest, XMLSerializer, + XPathEvaluator, XPathException, XPathExpression, XPathNamespace, XPathNSResolver, XPathResult, + "\\", a, abs, addEventListener, address, alert, apply, applicationCache, arguments, arity, + asi, atob, b, basic, basicToken, bitwise, blacklist, block, blur, boolOptions, boss, + browser, btoa, c, call, callee, caller, camelcase, cases, charAt, charCodeAt, character, + clearInterval, clearTimeout, close, closed, closure, comment, complexityCount, condition, + confirm, console, constructor, content, couch, create, css, curly, d, data, datalist, dd, debug, + decodeURI, decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document, + dojo, dijit, dojox, define, else, emit, encodeURI, encodeURIComponent, elem, + eqeq, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil, + ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus, forEach, + forin, fragment, frames, from, fromCharCode, fud, funcscope, funct, function, functions, + g, gc, getComputedStyle, getRow, getter, getterToken, GLOBAL, global, globals, globalstrict, + hasOwnProperty, help, history, i, id, identifier, immed, implieds, importPackage, include, + indent, indexOf, init, ins, internals, instanceOf, isAlpha, isApplicationRunning, isArray, + isDigit, isFinite, isNaN, iterator, java, join, jshint, + JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastcharacter, lastsemic, laxbreak, + laxcomma, latedef, lbp, led, left, length, line, load, loadClass, localStorage, location, + log, loopfunc, m, match, max, maxcomplexity, maxdepth, maxerr, maxlen, maxstatements, maxparams, + member, message, meta, module, moveBy, moveTo, mootools, multistr, name, navigator, new, newcap, + nestedBlockDepth, noarg, node, noempty, nomen, nonew, nonstandard, nud, onbeforeunload, onblur, + onerror, onevar, onecase, onfocus, onload, onresize, onunload, open, openDatabase, openURL, + opener, opera, options, outer, param, parent, parseFloat, parseInt, passfail, plusplus, + postMessage, pop, predef, print, process, prompt, proto, prototype, prototypejs, provides, push, + quit, quotmark, range, raw, reach, reason, regexp, readFile, readUrl, regexdash, + removeEventListener, replace, report, require, reserved, resizeBy, resizeTo, resolvePath, + resumeUpdates, respond, rhino, right, runCommand, scroll, scope, screen, scripturl, scrollBy, + scrollTo, scrollbar, search, seal, self, send, serialize, sessionStorage, setInterval, setTimeout, + setter, setterToken, shift, slice, smarttabs, sort, spawn, split, statement, statementCount, stack, + status, start, strict, sub, substr, supernew, shadow, supplant, sum, sync, test, toLowerCase, + toString, toUpperCase, toint32, token, tokens, top, trailing, type, typeOf, Uint16Array, + Uint32Array, Uint8Array, undef, undefs, unused, urls, validthis, value, valueOf, var, vars, + version, verifyMaxParametersPerFunction, verifyMaxStatementsPerFunction, + verifyMaxComplexityPerFunction, verifyMaxNestedBlockDepthPerFunction, WebSocket, withstmt, white, + window, windows, Worker, worker, wsh, yui, YUI, Y, YUI_config*/ + +/*global exports: false */ + +// We build the application inside a function so that we produce only a single +// global variable. That function will be invoked immediately, and its return +// value is the JSHINT function itself. + +var JSHINT = (function () { + "use strict"; + + var anonname, // The guessed name for anonymous functions. + +// These are operators that should not be used with the ! operator. + + bang = { + "<" : true, + "<=" : true, + "==" : true, + "===": true, + "!==": true, + "!=" : true, + ">" : true, + ">=" : true, + "+" : true, + "-" : true, + "*" : true, + "/" : true, + "%" : true + }, + + // These are the JSHint boolean options. + boolOptions = { + asi : true, // if automatic semicolon insertion should be tolerated + bitwise : true, // if bitwise operators should not be allowed + boss : true, // if advanced usage of assignments should be allowed + browser : true, // if the standard browser globals should be predefined + camelcase : true, // if identifiers should be required in camel case + couch : true, // if CouchDB globals should be predefined + curly : true, // if curly braces around all blocks should be required + debug : true, // if debugger statements should be allowed + devel : true, // if logging globals should be predefined (console, + // alert, etc.) + dojo : true, // if Dojo Toolkit globals should be predefined + eqeqeq : true, // if === should be required + eqnull : true, // if == null comparisons should be tolerated + es5 : true, // if ES5 syntax should be allowed + esnext : true, // if es.next specific syntax should be allowed + evil : true, // if eval should be allowed + expr : true, // if ExpressionStatement should be allowed as Programs + forin : true, // if for in statements must filter + funcscope : true, // if only function scope should be used for scope tests + globalstrict: true, // if global "use strict"; should be allowed (also + // enables 'strict') + immed : true, // if immediate invocations must be wrapped in parens + iterator : true, // if the `__iterator__` property should be allowed + jquery : true, // if jQuery globals should be predefined + lastsemic : true, // if semicolons may be ommitted for the trailing + // statements inside of a one-line blocks. + latedef : true, // if the use before definition should not be tolerated + laxbreak : true, // if line breaks should not be checked + laxcomma : true, // if line breaks should not be checked around commas + loopfunc : true, // if functions should be allowed to be defined within + // loops + mootools : true, // if MooTools globals should be predefined + multistr : true, // allow multiline strings + newcap : true, // if constructor names must be capitalized + noarg : true, // if arguments.caller and arguments.callee should be + // disallowed + node : true, // if the Node.js environment globals should be + // predefined + noempty : true, // if empty blocks should be disallowed + nonew : true, // if using `new` for side-effects should be disallowed + nonstandard : true, // if non-standard (but widely adopted) globals should + // be predefined + nomen : true, // if names should be checked + onevar : true, // if only one var statement per function should be + // allowed + onecase : true, // if one case switch statements should be allowed + passfail : true, // if the scan should stop on first error + plusplus : true, // if increment/decrement should not be allowed + proto : true, // if the `__proto__` property should be allowed + prototypejs : true, // if Prototype and Scriptaculous globals should be + // predefined + regexdash : true, // if unescaped first/last dash (-) inside brackets + // should be tolerated + regexp : true, // if the . should not be allowed in regexp literals + rhino : true, // if the Rhino environment globals should be predefined + undef : true, // if variables should be declared before used + unused : true, // if variables should be always used + scripturl : true, // if script-targeted URLs should be tolerated + shadow : true, // if variable shadowing should be tolerated + smarttabs : true, // if smarttabs should be tolerated + // (http://www.emacswiki.org/emacs/SmartTabs) + strict : true, // require the "use strict"; pragma + sub : true, // if all forms of subscript notation are tolerated + supernew : true, // if `new function () { ... };` and `new Object;` + // should be tolerated + trailing : true, // if trailing whitespace rules apply + validthis : true, // if 'this' inside a non-constructor function is valid. + // This is a function scoped option only. + withstmt : true, // if with statements should be allowed + white : true, // if strict whitespace rules apply + worker : true, // if Web Worker script symbols should be allowed + wsh : true, // if the Windows Scripting Host environment globals + // should be predefined + yui : true // YUI variables should be predefined + }, + + // These are the JSHint options that can take any value + // (we use this object to detect invalid options) + valOptions = { + maxlen : false, + indent : false, + maxerr : false, + predef : false, + quotmark : false, //'single'|'double'|true + scope : false, + maxstatements: false, // {int} max statements per function + maxdepth : false, // {int} max nested block depth per function + maxparams : false, // {int} max params per function + maxcomplexity: false // {int} max cyclomatic complexity per function + }, + + // These are JSHint boolean options which are shared with JSLint + // where the definition in JSHint is opposite JSLint + invertedOptions = { + bitwise : true, + forin : true, + newcap : true, + nomen : true, + plusplus : true, + regexp : true, + undef : true, + white : true, + + // Inverted and renamed, use JSHint name here + eqeqeq : true, + onevar : true + }, + + // These are JSHint boolean options which are shared with JSLint + // where the name has been changed but the effect is unchanged + renamedOptions = { + eqeq : "eqeqeq", + vars : "onevar", + windows : "wsh" + }, + + + // browser contains a set of global names which are commonly provided by a + // web browser environment. + browser = { + ArrayBuffer : false, + ArrayBufferView : false, + Audio : false, + Blob : false, + addEventListener : false, + applicationCache : false, + atob : false, + blur : false, + btoa : false, + clearInterval : false, + clearTimeout : false, + close : false, + closed : false, + DataView : false, + DOMParser : false, + defaultStatus : false, + document : false, + event : false, + FileReader : false, + Float32Array : false, + Float64Array : false, + FormData : false, + focus : false, + frames : false, + getComputedStyle : false, + HTMLElement : false, + HTMLAnchorElement : false, + HTMLBaseElement : false, + HTMLBlockquoteElement : false, + HTMLBodyElement : false, + HTMLBRElement : false, + HTMLButtonElement : false, + HTMLCanvasElement : false, + HTMLDirectoryElement : false, + HTMLDivElement : false, + HTMLDListElement : false, + HTMLFieldSetElement : false, + HTMLFontElement : false, + HTMLFormElement : false, + HTMLFrameElement : false, + HTMLFrameSetElement : false, + HTMLHeadElement : false, + HTMLHeadingElement : false, + HTMLHRElement : false, + HTMLHtmlElement : false, + HTMLIFrameElement : false, + HTMLImageElement : false, + HTMLInputElement : false, + HTMLIsIndexElement : false, + HTMLLabelElement : false, + HTMLLayerElement : false, + HTMLLegendElement : false, + HTMLLIElement : false, + HTMLLinkElement : false, + HTMLMapElement : false, + HTMLMenuElement : false, + HTMLMetaElement : false, + HTMLModElement : false, + HTMLObjectElement : false, + HTMLOListElement : false, + HTMLOptGroupElement : false, + HTMLOptionElement : false, + HTMLParagraphElement : false, + HTMLParamElement : false, + HTMLPreElement : false, + HTMLQuoteElement : false, + HTMLScriptElement : false, + HTMLSelectElement : false, + HTMLStyleElement : false, + HTMLTableCaptionElement : false, + HTMLTableCellElement : false, + HTMLTableColElement : false, + HTMLTableElement : false, + HTMLTableRowElement : false, + HTMLTableSectionElement : false, + HTMLTextAreaElement : false, + HTMLTitleElement : false, + HTMLUListElement : false, + HTMLVideoElement : false, + history : false, + Int16Array : false, + Int32Array : false, + Int8Array : false, + Image : false, + length : false, + localStorage : false, + location : false, + MessageChannel : false, + MessageEvent : false, + MessagePort : false, + moveBy : false, + moveTo : false, + MutationObserver : false, + name : false, + Node : false, + NodeFilter : false, + navigator : false, + onbeforeunload : true, + onblur : true, + onerror : true, + onfocus : true, + onload : true, + onresize : true, + onunload : true, + open : false, + openDatabase : false, + opener : false, + Option : false, + parent : false, + print : false, + removeEventListener : false, + resizeBy : false, + resizeTo : false, + screen : false, + scroll : false, + scrollBy : false, + scrollTo : false, + sessionStorage : false, + setInterval : false, + setTimeout : false, + SharedWorker : false, + status : false, + top : false, + Uint16Array : false, + Uint32Array : false, + Uint8Array : false, + WebSocket : false, + window : false, + Worker : false, + XMLHttpRequest : false, + XMLSerializer : false, + XPathEvaluator : false, + XPathException : false, + XPathExpression : false, + XPathNamespace : false, + XPathNSResolver : false, + XPathResult : false + }, + + couch = { + "require" : false, + respond : false, + getRow : false, + emit : false, + send : false, + start : false, + sum : false, + log : false, + exports : false, + module : false, + provides : false + }, + + declared, // Globals that were declared using /*global ... */ syntax. + + devel = { + alert : false, + confirm : false, + console : false, + Debug : false, + opera : false, + prompt : false + }, + + dojo = { + dojo : false, + dijit : false, + dojox : false, + define : false, + "require" : false + }, + + funct, // The current function + + functionicity = [ + "closure", "exception", "global", "label", + "outer", "unused", "var" + ], + + functions, // All of the functions + + global, // The global scope + implied, // Implied globals + inblock, + indent, + jsonmode, + + jquery = { + "$" : false, + jQuery : false + }, + + lines, + lookahead, + member, + membersOnly, + + mootools = { + "$" : false, + "$$" : false, + Asset : false, + Browser : false, + Chain : false, + Class : false, + Color : false, + Cookie : false, + Core : false, + Document : false, + DomReady : false, + DOMEvent : false, + DOMReady : false, + Drag : false, + Element : false, + Elements : false, + Event : false, + Events : false, + Fx : false, + Group : false, + Hash : false, + HtmlTable : false, + Iframe : false, + IframeShim : false, + InputValidator : false, + instanceOf : false, + Keyboard : false, + Locale : false, + Mask : false, + MooTools : false, + Native : false, + Options : false, + OverText : false, + Request : false, + Scroller : false, + Slick : false, + Slider : false, + Sortables : false, + Spinner : false, + Swiff : false, + Tips : false, + Type : false, + typeOf : false, + URI : false, + Window : false + }, + + nexttoken, + + node = { + __filename : false, + __dirname : false, + Buffer : false, + console : false, + exports : true, // In Node it is ok to exports = module.exports = foo(); + GLOBAL : false, + global : false, + module : false, + process : false, + require : false, + setTimeout : false, + clearTimeout : false, + setInterval : false, + clearInterval : false + }, + + noreach, + option, + predefined, // Global variables defined by option + prereg, + prevtoken, + + prototypejs = { + "$" : false, + "$$" : false, + "$A" : false, + "$F" : false, + "$H" : false, + "$R" : false, + "$break" : false, + "$continue" : false, + "$w" : false, + Abstract : false, + Ajax : false, + Class : false, + Enumerable : false, + Element : false, + Event : false, + Field : false, + Form : false, + Hash : false, + Insertion : false, + ObjectRange : false, + PeriodicalExecuter: false, + Position : false, + Prototype : false, + Selector : false, + Template : false, + Toggle : false, + Try : false, + Autocompleter : false, + Builder : false, + Control : false, + Draggable : false, + Draggables : false, + Droppables : false, + Effect : false, + Sortable : false, + SortableObserver : false, + Sound : false, + Scriptaculous : false + }, + + quotmark, + + rhino = { + defineClass : false, + deserialize : false, + gc : false, + help : false, + importPackage: false, + "java" : false, + load : false, + loadClass : false, + print : false, + quit : false, + readFile : false, + readUrl : false, + runCommand : false, + seal : false, + serialize : false, + spawn : false, + sync : false, + toint32 : false, + version : false + }, + + scope, // The current scope + stack, + + // standard contains the global names that are provided by the + // ECMAScript standard. + standard = { + Array : false, + Boolean : false, + Date : false, + decodeURI : false, + decodeURIComponent : false, + encodeURI : false, + encodeURIComponent : false, + Error : false, + "eval" : false, + EvalError : false, + Function : false, + hasOwnProperty : false, + isFinite : false, + isNaN : false, + JSON : false, + Map : false, + Math : false, + NaN : false, + Number : false, + Object : false, + parseInt : false, + parseFloat : false, + RangeError : false, + ReferenceError : false, + RegExp : false, + Set : false, + String : false, + SyntaxError : false, + TypeError : false, + URIError : false, + WeakMap : false + }, + + // widely adopted global names that are not part of ECMAScript standard + nonstandard = { + escape : false, + unescape : false + }, + + directive, + syntax = {}, + tab, + token, + unuseds, + urls, + useESNextSyntax, + warnings, + + worker = { + importScripts : true, + postMessage : true, + self : true + }, + + wsh = { + ActiveXObject : true, + Enumerator : true, + GetObject : true, + ScriptEngine : true, + ScriptEngineBuildVersion : true, + ScriptEngineMajorVersion : true, + ScriptEngineMinorVersion : true, + VBArray : true, + WSH : true, + WScript : true, + XDomainRequest : true + }, + + yui = { + YUI : false, + Y : false, + YUI_config : false + }; + // Regular expressions. Some of these are stupidly long. + var ax, cx, tx, nx, nxg, lx, ix, jx, ft; + (function () { + /*jshint maxlen:300 */ + + // unsafe comment or string + ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i; + + // unsafe characters that are silently deleted by one or more browsers + cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; + + // token + tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/=(?!(\S*\/[gim]?))|\/(\*(jshint|jslint|members?|global)?|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/; + + // characters in strings that need escapement + nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/; + nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + // star slash + lx = /\*\//; + + // identifier + ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; + + // javascript url + jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; + + // catches /* falls through */ comments + ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/; + }()); + + function F() {} // Used by Object.create + + function is_own(object, name) { + // The object.hasOwnProperty method fails when the property under consideration + // is named 'hasOwnProperty'. So we have to use this more convoluted form. + return Object.prototype.hasOwnProperty.call(object, name); + } + + function checkOption(name, t) { + if (valOptions[name] === undefined && boolOptions[name] === undefined) { + warning("Bad option: '" + name + "'.", t); + } + } + + function isString(obj) { + return Object.prototype.toString.call(obj) === "[object String]"; + } + + // Provide critical ES5 functions to ES3. + + if (typeof Array.isArray !== "function") { + Array.isArray = function (o) { + return Object.prototype.toString.apply(o) === "[object Array]"; + }; + } + + if (!Array.prototype.forEach) { + Array.prototype.forEach = function (fn, scope) { + var len = this.length; + + for (var i = 0; i < len; i++) { + fn.call(scope || this, this[i], i, this); + } + }; + } + + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { + if (this === null || this === undefined) { + throw new TypeError(); + } + + var t = new Object(this); + var len = t.length >>> 0; + + if (len === 0) { + return -1; + } + + var n = 0; + if (arguments.length > 0) { + n = Number(arguments[1]); + if (n != n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n !== 0 && n != Infinity && n != -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + + if (n >= len) { + return -1; + } + + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + + return -1; + }; + } + + if (typeof Object.create !== "function") { + Object.create = function (o) { + F.prototype = o; + return new F(); + }; + } + + if (typeof Object.keys !== "function") { + Object.keys = function (o) { + var a = [], k; + for (k in o) { + if (is_own(o, k)) { + a.push(k); + } + } + return a; + }; + } + + // Non standard methods + + function isAlpha(str) { + return (str >= "a" && str <= "z\uffff") || + (str >= "A" && str <= "Z\uffff"); + } + + function isDigit(str) { + return (str >= "0" && str <= "9"); + } + + function isIdentifier(token, value) { + if (!token) + return false; + + if (!token.identifier || token.value !== value) + return false; + + return true; + } + + function supplant(str, data) { + return str.replace(/\{([^{}]*)\}/g, function (a, b) { + var r = data[b]; + return typeof r === "string" || typeof r === "number" ? r : a; + }); + } + + function combine(t, o) { + var n; + for (n in o) { + if (is_own(o, n) && !is_own(JSHINT.blacklist, n)) { + t[n] = o[n]; + } + } + } + + function updatePredefined() { + Object.keys(JSHINT.blacklist).forEach(function (key) { + delete predefined[key]; + }); + } + + function assume() { + if (option.couch) { + combine(predefined, couch); + } + + if (option.rhino) { + combine(predefined, rhino); + } + + if (option.prototypejs) { + combine(predefined, prototypejs); + } + + if (option.node) { + combine(predefined, node); + option.globalstrict = true; + } + + if (option.devel) { + combine(predefined, devel); + } + + if (option.dojo) { + combine(predefined, dojo); + } + + if (option.browser) { + combine(predefined, browser); + } + + if (option.nonstandard) { + combine(predefined, nonstandard); + } + + if (option.jquery) { + combine(predefined, jquery); + } + + if (option.mootools) { + combine(predefined, mootools); + } + + if (option.worker) { + combine(predefined, worker); + } + + if (option.wsh) { + combine(predefined, wsh); + } + + if (option.esnext) { + useESNextSyntax(); + } + + if (option.globalstrict && option.strict !== false) { + option.strict = true; + } + + if (option.yui) { + combine(predefined, yui); + } + } + + + // Produce an error warning. + function quit(message, line, chr) { + var percentage = Math.floor((line / lines.length) * 100); + + throw { + name: "JSHintError", + line: line, + character: chr, + message: message + " (" + percentage + "% scanned).", + raw: message + }; + } + + function isundef(scope, m, t, a) { + return JSHINT.undefs.push([scope, m, t, a]); + } + + function warning(m, t, a, b, c, d) { + var ch, l, w; + t = t || nexttoken; + if (t.id === "(end)") { // `~ + t = token; + } + l = t.line || 0; + ch = t.from || 0; + w = { + id: "(error)", + raw: m, + evidence: lines[l - 1] || "", + line: l, + character: ch, + scope: JSHINT.scope, + a: a, + b: b, + c: c, + d: d + }; + w.reason = supplant(m, w); + JSHINT.errors.push(w); + if (option.passfail) { + quit("Stopping. ", l, ch); + } + warnings += 1; + if (warnings >= option.maxerr) { + quit("Too many errors.", l, ch); + } + return w; + } + + function warningAt(m, l, ch, a, b, c, d) { + return warning(m, { + line: l, + from: ch + }, a, b, c, d); + } + + function error(m, t, a, b, c, d) { + warning(m, t, a, b, c, d); + } + + function errorAt(m, l, ch, a, b, c, d) { + return error(m, { + line: l, + from: ch + }, a, b, c, d); + } + + // Tracking of "internal" scripts, like eval containing a static string + function addInternalSrc(elem, src) { + var i; + i = { + id: "(internal)", + elem: elem, + value: src + }; + JSHINT.internals.push(i); + return i; + } + + +// lexical analysis and token construction + + var lex = (function lex() { + var character, from, line, s; + +// Private lex methods + + function nextLine() { + var at, + match, + tw; // trailing whitespace check + + if (line >= lines.length) + return false; + + character = 1; + s = lines[line]; + line += 1; + + // If smarttabs option is used check for spaces followed by tabs only. + // Otherwise check for any occurence of mixed tabs and spaces. + // Tabs and one space followed by block comment is allowed. + if (option.smarttabs) { + // negative look-behind for "//" + match = s.match(/(\/\/)? \t/); + at = match && !match[1] ? 0 : -1; + } else { + at = s.search(/ \t|\t [^\*]/); + } + + if (at >= 0) + warningAt("Mixed spaces and tabs.", line, at + 1); + + s = s.replace(/\t/g, tab); + at = s.search(cx); + + if (at >= 0) + warningAt("Unsafe character.", line, at); + + if (option.maxlen && option.maxlen < s.length) + warningAt("Line too long.", line, s.length); + + // Check for trailing whitespaces + tw = option.trailing && s.match(/^(.*?)\s+$/); + if (tw && !/^\s+$/.test(s)) { + warningAt("Trailing whitespace.", line, tw[1].length + 1); + } + return true; + } + +// Produce a token object. The token inherits from a syntax symbol. + + function it(type, value) { + var i, t; + + function checkName(name) { + if (!option.proto && name === "__proto__") { + warningAt("The '{a}' property is deprecated.", line, from, name); + return; + } + + if (!option.iterator && name === "__iterator__") { + warningAt("'{a}' is only available in JavaScript 1.7.", line, from, name); + return; + } + + // Check for dangling underscores unless we're in Node + // environment and this identifier represents built-in + // Node globals with underscores. + + var hasDangling = /^(_+.*|.*_+)$/.test(name); + + if (option.nomen && hasDangling && name !== "_") { + if (option.node && token.id !== "." && /^(__dirname|__filename)$/.test(name)) + return; + + warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", name); + return; + } + + // Check for non-camelcase names. Names like MY_VAR and + // _myVar are okay though. + + if (option.camelcase) { + if (name.replace(/^_+/, "").indexOf("_") > -1 && !name.match(/^[A-Z0-9_]*$/)) { + warningAt("Identifier '{a}' is not in camel case.", line, from, value); + } + } + } + + if (type === "(color)" || type === "(range)") { + t = {type: type}; + } else if (type === "(punctuator)" || + (type === "(identifier)" && is_own(syntax, value))) { + t = syntax[value] || syntax["(error)"]; + } else { + t = syntax[type]; + } + + t = Object.create(t); + + if (type === "(string)" || type === "(range)") { + if (!option.scripturl && jx.test(value)) { + warningAt("Script URL.", line, from); + } + } + + if (type === "(identifier)") { + t.identifier = true; + checkName(value); + } + + t.value = value; + t.line = line; + t.character = character; + t.from = from; + i = t.id; + if (i !== "(endline)") { + prereg = i && + (("(,=:[!&|?{};".indexOf(i.charAt(i.length - 1)) >= 0) || + i === "return" || + i === "case"); + } + return t; + } + + // Public lex methods + return { + init: function (source) { + if (typeof source === "string") { + lines = source + .replace(/\r\n/g, "\n") + .replace(/\r/g, "\n") + .split("\n"); + } else { + lines = source; + } + + // If the first line is a shebang (#!), make it a blank and move on. + // Shebangs are used by Node scripts. + if (lines[0] && lines[0].substr(0, 2) === "#!") + lines[0] = ""; + + line = 0; + nextLine(); + from = 1; + }, + + range: function (begin, end) { + var c, value = ""; + from = character; + if (s.charAt(0) !== begin) { + errorAt("Expected '{a}' and instead saw '{b}'.", + line, character, begin, s.charAt(0)); + } + for (;;) { + s = s.slice(1); + character += 1; + c = s.charAt(0); + switch (c) { + case "": + errorAt("Missing '{a}'.", line, character, c); + break; + case end: + s = s.slice(1); + character += 1; + return it("(range)", value); + case "\\": + warningAt("Unexpected '{a}'.", line, character, c); + } + value += c; + } + + }, + + + // token -- this is called by advance to get the next token + token: function () { + var b, c, captures, d, depth, high, i, l, low, q, t, isLiteral, isInRange, n; + + function match(x) { + var r = x.exec(s), r1; + + if (r) { + l = r[0].length; + r1 = r[1]; + c = r1.charAt(0); + s = s.substr(l); + from = character + l - r1.length; + character += l; + return r1; + } + } + + function string(x) { + var c, j, r = "", allowNewLine = false; + + if (jsonmode && x !== "\"") { + warningAt("Strings must use doublequote.", + line, character); + } + + if (option.quotmark) { + if (option.quotmark === "single" && x !== "'") { + warningAt("Strings must use singlequote.", + line, character); + } else if (option.quotmark === "double" && x !== "\"") { + warningAt("Strings must use doublequote.", + line, character); + } else if (option.quotmark === true) { + quotmark = quotmark || x; + if (quotmark !== x) { + warningAt("Mixed double and single quotes.", + line, character); + } + } + } + + function esc(n) { + var i = parseInt(s.substr(j + 1, n), 16); + j += n; + if (i >= 32 && i <= 126 && + i !== 34 && i !== 92 && i !== 39) { + warningAt("Unnecessary escapement.", line, character); + } + character += n; + c = String.fromCharCode(i); + } + + j = 0; +unclosedString: for (;;) { + while (j >= s.length) { + j = 0; + + var cl = line, cf = from; + if (!nextLine()) { + errorAt("Unclosed string.", cl, cf); + break unclosedString; + } + + if (allowNewLine) { + allowNewLine = false; + } else { + warningAt("Unclosed string.", cl, cf); + } + } + + c = s.charAt(j); + if (c === x) { + character += 1; + s = s.substr(j + 1); + return it("(string)", r, x); + } + + if (c < " ") { + if (c === "\n" || c === "\r") { + break; + } + warningAt("Control character in string: {a}.", + line, character + j, s.slice(0, j)); + } else if (c === "\\") { + j += 1; + character += 1; + c = s.charAt(j); + n = s.charAt(j + 1); + switch (c) { + case "\\": + case "\"": + case "/": + break; + case "\'": + if (jsonmode) { + warningAt("Avoid \\'.", line, character); + } + break; + case "b": + c = "\b"; + break; + case "f": + c = "\f"; + break; + case "n": + c = "\n"; + break; + case "r": + c = "\r"; + break; + case "t": + c = "\t"; + break; + case "0": + c = "\0"; + // Octal literals fail in strict mode + // check if the number is between 00 and 07 + // where 'n' is the token next to 'c' + if (n >= 0 && n <= 7 && directive["use strict"]) { + warningAt( + "Octal literals are not allowed in strict mode.", + line, character); + } + break; + case "u": + esc(4); + break; + case "v": + if (jsonmode) { + warningAt("Avoid \\v.", line, character); + } + c = "\v"; + break; + case "x": + if (jsonmode) { + warningAt("Avoid \\x-.", line, character); + } + esc(2); + break; + case "": + // last character is escape character + // always allow new line if escaped, but show + // warning if option is not set + allowNewLine = true; + if (option.multistr) { + if (jsonmode) { + warningAt("Avoid EOL escapement.", line, character); + } + c = ""; + character -= 1; + break; + } + warningAt("Bad escapement of EOL. Use option multistr if needed.", + line, character); + break; + case "!": + if (s.charAt(j - 2) === "<") + break; + /*falls through*/ + default: + warningAt("Bad escapement.", line, character); + } + } + r += c; + character += 1; + j += 1; + } + } + + for (;;) { + if (!s) { + return it(nextLine() ? "(endline)" : "(end)", ""); + } + + t = match(tx); + + if (!t) { + t = ""; + c = ""; + while (s && s < "!") { + s = s.substr(1); + } + if (s) { + errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); + s = ""; + } + } else { + + // identifier + + if (isAlpha(c) || c === "_" || c === "$") { + return it("(identifier)", t); + } + + // number + + if (isDigit(c)) { + if (!isFinite(Number(t))) { + warningAt("Bad number '{a}'.", + line, character, t); + } + if (isAlpha(s.substr(0, 1))) { + warningAt("Missing space after '{a}'.", + line, character, t); + } + if (c === "0") { + d = t.substr(1, 1); + if (isDigit(d)) { + if (token.id !== ".") { + warningAt("Don't use extra leading zeros '{a}'.", + line, character, t); + } + } else if (jsonmode && (d === "x" || d === "X")) { + warningAt("Avoid 0x-. '{a}'.", + line, character, t); + } + } + if (t.substr(t.length - 1) === ".") { + warningAt( +"A trailing decimal point can be confused with a dot '{a}'.", line, character, t); + } + return it("(number)", t); + } + switch (t) { + + // string + + case "\"": + case "'": + return string(t); + + // // comment + + case "//": + s = ""; + token.comment = true; + break; + + // /* comment + + case "/*": + for (;;) { + i = s.search(lx); + if (i >= 0) { + break; + } + if (!nextLine()) { + errorAt("Unclosed comment.", line, character); + } + } + s = s.substr(i + 2); + token.comment = true; + break; + + // /*members /*jshint /*global + + case "/*members": + case "/*member": + case "/*jshint": + case "/*jslint": + case "/*global": + case "*/": + return { + value: t, + type: "special", + line: line, + character: character, + from: from + }; + + case "": + break; + // / + case "/": + if (s.charAt(0) === "=") { + errorAt("A regular expression literal can be confused with '/='.", + line, from); + } + + if (prereg) { + depth = 0; + captures = 0; + l = 0; + for (;;) { + b = true; + c = s.charAt(l); + l += 1; + switch (c) { + case "": + errorAt("Unclosed regular expression.", line, from); + return quit("Stopping.", line, from); + case "/": + if (depth > 0) { + warningAt("{a} unterminated regular expression " + + "group(s).", line, from + l, depth); + } + c = s.substr(0, l - 1); + q = { + g: true, + i: true, + m: true + }; + while (q[s.charAt(l)] === true) { + q[s.charAt(l)] = false; + l += 1; + } + character += l; + s = s.substr(l); + q = s.charAt(0); + if (q === "/" || q === "*") { + errorAt("Confusing regular expression.", + line, from); + } + return it("(regexp)", c); + case "\\": + c = s.charAt(l); + if (c < " ") { + warningAt( +"Unexpected control character in regular expression.", line, from + l); + } else if (c === "<") { + warningAt( +"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); + } + l += 1; + break; + case "(": + depth += 1; + b = false; + if (s.charAt(l) === "?") { + l += 1; + switch (s.charAt(l)) { + case ":": + case "=": + case "!": + l += 1; + break; + default: + warningAt( +"Expected '{a}' and instead saw '{b}'.", line, from + l, ":", s.charAt(l)); + } + } else { + captures += 1; + } + break; + case "|": + b = false; + break; + case ")": + if (depth === 0) { + warningAt("Unescaped '{a}'.", + line, from + l, ")"); + } else { + depth -= 1; + } + break; + case " ": + q = 1; + while (s.charAt(l) === " ") { + l += 1; + q += 1; + } + if (q > 1) { + warningAt( +"Spaces are hard to count. Use {{a}}.", line, from + l, q); + } + break; + case "[": + c = s.charAt(l); + if (c === "^") { + l += 1; + if (s.charAt(l) === "]") { + errorAt("Unescaped '{a}'.", + line, from + l, "^"); + } + } + if (c === "]") { + warningAt("Empty class.", line, + from + l - 1); + } + isLiteral = false; + isInRange = false; +klass: do { + c = s.charAt(l); + l += 1; + switch (c) { + case "[": + case "^": + warningAt("Unescaped '{a}'.", + line, from + l, c); + if (isInRange) { + isInRange = false; + } else { + isLiteral = true; + } + break; + case "-": + if (isLiteral && !isInRange) { + isLiteral = false; + isInRange = true; + } else if (isInRange) { + isInRange = false; + } else if (s.charAt(l) === "]") { + isInRange = true; + } else { + if (option.regexdash !== (l === 2 || (l === 3 && + s.charAt(1) === "^"))) { + warningAt("Unescaped '{a}'.", + line, from + l - 1, "-"); + } + isLiteral = true; + } + break; + case "]": + if (isInRange && !option.regexdash) { + warningAt("Unescaped '{a}'.", + line, from + l - 1, "-"); + } + break klass; + case "\\": + c = s.charAt(l); + if (c < " ") { + warningAt( +"Unexpected control character in regular expression.", line, from + l); + } else if (c === "<") { + warningAt( +"Unexpected escaped character '{a}' in regular expression.", line, from + l, c); + } + l += 1; + + // \w, \s and \d are never part of a character range + if (/[wsd]/i.test(c)) { + if (isInRange) { + warningAt("Unescaped '{a}'.", + line, from + l, "-"); + isInRange = false; + } + isLiteral = false; + } else if (isInRange) { + isInRange = false; + } else { + isLiteral = true; + } + break; + case "/": + warningAt("Unescaped '{a}'.", + line, from + l - 1, "/"); + + if (isInRange) { + isInRange = false; + } else { + isLiteral = true; + } + break; + case "<": + if (isInRange) { + isInRange = false; + } else { + isLiteral = true; + } + break; + default: + if (isInRange) { + isInRange = false; + } else { + isLiteral = true; + } + } + } while (c); + break; + case ".": + if (option.regexp) { + warningAt("Insecure '{a}'.", line, + from + l, c); + } + break; + case "]": + case "?": + case "{": + case "}": + case "+": + case "*": + warningAt("Unescaped '{a}'.", line, + from + l, c); + } + if (b) { + switch (s.charAt(l)) { + case "?": + case "+": + case "*": + l += 1; + if (s.charAt(l) === "?") { + l += 1; + } + break; + case "{": + l += 1; + c = s.charAt(l); + if (c < "0" || c > "9") { + warningAt( +"Expected a number and instead saw '{a}'.", line, from + l, c); + break; // No reason to continue checking numbers. + } + l += 1; + low = +c; + for (;;) { + c = s.charAt(l); + if (c < "0" || c > "9") { + break; + } + l += 1; + low = +c + (low * 10); + } + high = low; + if (c === ",") { + l += 1; + high = Infinity; + c = s.charAt(l); + if (c >= "0" && c <= "9") { + l += 1; + high = +c; + for (;;) { + c = s.charAt(l); + if (c < "0" || c > "9") { + break; + } + l += 1; + high = +c + (high * 10); + } + } + } + if (s.charAt(l) !== "}") { + warningAt( +"Expected '{a}' and instead saw '{b}'.", line, from + l, "}", c); + } else { + l += 1; + } + if (s.charAt(l) === "?") { + l += 1; + } + if (low > high) { + warningAt( +"'{a}' should not be greater than '{b}'.", line, from + l, low, high); + } + } + } + } + c = s.substr(0, l - 1); + character += l; + s = s.substr(l); + return it("(regexp)", c); + } + return it("(punctuator)", t); + + // punctuator + + case "#": + return it("(punctuator)", t); + default: + return it("(punctuator)", t); + } + } + } + } + }; + }()); + + + function addlabel(t, type, token) { + if (t === "hasOwnProperty") { + warning("'hasOwnProperty' is a really bad name."); + } + + // Define t in the current function in the current scope. + if (type === "exception") { + if (is_own(funct["(context)"], t)) { + if (funct[t] !== true && !option.node) { + warning("Value of '{a}' may be overwritten in IE.", nexttoken, t); + } + } + } + + if (is_own(funct, t) && !funct["(global)"]) { + if (funct[t] === true) { + if (option.latedef) + warning("'{a}' was used before it was defined.", nexttoken, t); + } else { + if (!option.shadow && type !== "exception") { + warning("'{a}' is already defined.", nexttoken, t); + } + } + } + + funct[t] = type; + + if (token) { + funct["(tokens)"][t] = token; + } + + if (funct["(global)"]) { + global[t] = funct; + if (is_own(implied, t)) { + if (option.latedef) + warning("'{a}' was used before it was defined.", nexttoken, t); + delete implied[t]; + } + } else { + scope[t] = funct; + } + } + + + function doOption() { + var nt = nexttoken; + var o = nt.value; + var quotmarkValue = option.quotmark; + var predef = {}; + var b, obj, filter, t, tn, v, minus; + + switch (o) { + case "*/": + error("Unbegun comment."); + break; + case "/*members": + case "/*member": + o = "/*members"; + if (!membersOnly) { + membersOnly = {}; + } + obj = membersOnly; + option.quotmark = false; + break; + case "/*jshint": + case "/*jslint": + obj = option; + filter = boolOptions; + break; + case "/*global": + obj = predef; + break; + default: + error("What?"); + } + + t = lex.token(); +loop: for (;;) { + minus = false; + for (;;) { + if (t.type === "special" && t.value === "*/") { + break loop; + } + if (t.id !== "(endline)" && t.id !== ",") { + break; + } + t = lex.token(); + } + + if (o === "/*global" && t.value === "-") { + minus = true; + t = lex.token(); + } + + if (t.type !== "(string)" && t.type !== "(identifier)" && o !== "/*members") { + error("Bad option.", t); + } + + v = lex.token(); + if (v.id === ":") { + v = lex.token(); + + if (obj === membersOnly) { + error("Expected '{a}' and instead saw '{b}'.", t, "*/", ":"); + } + + if (o === "/*jshint") { + checkOption(t.value, t); + } + + var numericVals = [ + "maxstatements", + "maxparams", + "maxdepth", + "maxcomplexity", + "maxerr", + "maxlen", + "indent" + ]; + + if (numericVals.indexOf(t.value) > -1 && (o === "/*jshint" || o === "/*jslint")) { + b = +v.value; + + if (typeof b !== "number" || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { + error("Expected a small integer and instead saw '{a}'.", v, v.value); + } + + if (t.value === "indent") + obj.white = true; + + obj[t.value] = b; + } else if (t.value === "validthis") { + if (funct["(global)"]) { + error("Option 'validthis' can't be used in a global scope."); + } else { + if (v.value === "true" || v.value === "false") + obj[t.value] = v.value === "true"; + else + error("Bad option value.", v); + } + } else if (t.value === "quotmark" && (o === "/*jshint")) { + switch (v.value) { + case "true": + obj.quotmark = true; + break; + case "false": + obj.quotmark = false; + break; + case "double": + case "single": + obj.quotmark = v.value; + break; + default: + error("Bad option value.", v); + } + } else if (v.value === "true" || v.value === "false") { + if (o === "/*jslint") { + tn = renamedOptions[t.value] || t.value; + obj[tn] = v.value === "true"; + if (invertedOptions[tn] !== undefined) { + obj[tn] = !obj[tn]; + } + } else { + obj[t.value] = v.value === "true"; + } + + if (t.value === "newcap") + obj["(explicitNewcap)"] = true; + } else { + error("Bad option value.", v); + } + t = lex.token(); + } else { + if (o === "/*jshint" || o === "/*jslint") { + error("Missing option value.", t); + } + + obj[t.value] = false; + + if (o === "/*global" && minus === true) { + JSHINT.blacklist[t.value] = t.value; + updatePredefined(); + } + + t = v; + } + } + + if (o === "/*members") { + option.quotmark = quotmarkValue; + } + + combine(predefined, predef); + + for (var key in predef) { + if (is_own(predef, key)) { + declared[key] = nt; + } + } + + if (filter) { + assume(); + } + } + + +// We need a peek function. If it has an argument, it peeks that much farther +// ahead. It is used to distinguish +// for ( var i in ... +// from +// for ( var i = ... + + function peek(p) { + var i = p || 0, j = 0, t; + + while (j <= i) { + t = lookahead[j]; + if (!t) { + t = lookahead[j] = lex.token(); + } + j += 1; + } + return t; + } + + + +// Produce the next token. It looks for programming errors. + + function advance(id, t) { + switch (token.id) { + case "(number)": + if (nexttoken.id === ".") { + warning("A dot following a number can be confused with a decimal point.", token); + } + break; + case "-": + if (nexttoken.id === "-" || nexttoken.id === "--") { + warning("Confusing minusses."); + } + break; + case "+": + if (nexttoken.id === "+" || nexttoken.id === "++") { + warning("Confusing plusses."); + } + break; + } + + if (token.type === "(string)" || token.identifier) { + anonname = token.value; + } + + if (id && nexttoken.id !== id) { + if (t) { + if (nexttoken.id === "(end)") { + warning("Unmatched '{a}'.", t, t.id); + } else { + warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", + nexttoken, id, t.id, t.line, nexttoken.value); + } + } else if (nexttoken.type !== "(identifier)" || + nexttoken.value !== id) { + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, id, nexttoken.value); + } + } + + prevtoken = token; + token = nexttoken; + for (;;) { + nexttoken = lookahead.shift() || lex.token(); + if (nexttoken.id === "(end)" || nexttoken.id === "(error)") { + return; + } + if (nexttoken.type === "special") { + doOption(); + } else { + if (nexttoken.id !== "(endline)") { + break; + } + } + } + } + + +// This is the heart of JSHINT, the Pratt parser. In addition to parsing, it +// is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is +// like .nud except that it is only used on the first token of a statement. +// Having .fud makes it much easier to define statement-oriented languages like +// JavaScript. I retained Pratt's nomenclature. + +// .nud Null denotation +// .fud First null denotation +// .led Left denotation +// lbp Left binding power +// rbp Right binding power + +// They are elements of the parsing method called Top Down Operator Precedence. + + function expression(rbp, initial) { + var left, isArray = false, isObject = false; + + if (nexttoken.id === "(end)") + error("Unexpected early end of program.", token); + + advance(); + if (initial) { + anonname = "anonymous"; + funct["(verb)"] = token.value; + } + if (initial === true && token.fud) { + left = token.fud(); + } else { + if (token.nud) { + left = token.nud(); + } else { + if (nexttoken.type === "(number)" && token.id === ".") { + warning("A leading decimal point can be confused with a dot: '.{a}'.", + token, nexttoken.value); + advance(); + return token; + } else { + error("Expected an identifier and instead saw '{a}'.", + token, token.id); + } + } + while (rbp < nexttoken.lbp) { + isArray = token.value === "Array"; + isObject = token.value === "Object"; + + // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object() + // Line breaks in IfStatement heads exist to satisfy the checkJSHint + // "Line too long." error. + if (left && (left.value || (left.first && left.first.value))) { + // If the left.value is not "new", or the left.first.value is a "." + // then safely assume that this is not "new Array()" and possibly + // not "new Object()"... + if (left.value !== "new" || + (left.first && left.first.value && left.first.value === ".")) { + isArray = false; + // ...In the case of Object, if the left.value and token.value + // are not equal, then safely assume that this not "new Object()" + if (left.value !== token.value) { + isObject = false; + } + } + } + + advance(); + if (isArray && token.id === "(" && nexttoken.id === ")") + warning("Use the array literal notation [].", token); + if (isObject && token.id === "(" && nexttoken.id === ")") + warning("Use the object literal notation {}.", token); + if (token.led) { + left = token.led(left); + } else { + error("Expected an operator and instead saw '{a}'.", + token, token.id); + } + } + } + return left; + } + + +// Functions for conformance of style. + + function adjacent(left, right) { + left = left || token; + right = right || nexttoken; + if (option.white) { + if (left.character !== right.from && left.line === right.line) { + left.from += (left.character - left.from); + warning("Unexpected space after '{a}'.", left, left.value); + } + } + } + + function nobreak(left, right) { + left = left || token; + right = right || nexttoken; + if (option.white && (left.character !== right.from || left.line !== right.line)) { + warning("Unexpected space before '{a}'.", right, right.value); + } + } + + function nospace(left, right) { + left = left || token; + right = right || nexttoken; + if (option.white && !left.comment) { + if (left.line === right.line) { + adjacent(left, right); + } + } + } + + function nonadjacent(left, right) { + if (option.white) { + left = left || token; + right = right || nexttoken; + if (left.value === ";" && right.value === ";") { + return; + } + if (left.line === right.line && left.character === right.from) { + left.from += (left.character - left.from); + warning("Missing space after '{a}'.", + left, left.value); + } + } + } + + function nobreaknonadjacent(left, right) { + left = left || token; + right = right || nexttoken; + if (!option.laxbreak && left.line !== right.line) { + warning("Bad line breaking before '{a}'.", right, right.id); + } else if (option.white) { + left = left || token; + right = right || nexttoken; + if (left.character === right.from) { + left.from += (left.character - left.from); + warning("Missing space after '{a}'.", + left, left.value); + } + } + } + + function indentation(bias) { + var i; + if (option.white && nexttoken.id !== "(end)") { + i = indent + (bias || 0); + if (nexttoken.from !== i) { + warning( +"Expected '{a}' to have an indentation at {b} instead at {c}.", + nexttoken, nexttoken.value, i, nexttoken.from); + } + } + } + + function nolinebreak(t) { + t = t || token; + if (t.line !== nexttoken.line) { + warning("Line breaking error '{a}'.", t, t.value); + } + } + + + function comma() { + if (token.line !== nexttoken.line) { + if (!option.laxcomma) { + if (comma.first) { + warning("Comma warnings can be turned off with 'laxcomma'"); + comma.first = false; + } + warning("Bad line breaking before '{a}'.", token, nexttoken.id); + } + } else if (!token.comment && token.character !== nexttoken.from && option.white) { + token.from += (token.character - token.from); + warning("Unexpected space after '{a}'.", token, token.value); + } + advance(","); + nonadjacent(token, nexttoken); + } + + +// Functional constructors for making the symbols that will be inherited by +// tokens. + + function symbol(s, p) { + var x = syntax[s]; + if (!x || typeof x !== "object") { + syntax[s] = x = { + id: s, + lbp: p, + value: s + }; + } + return x; + } + + + function delim(s) { + return symbol(s, 0); + } + + + function stmt(s, f) { + var x = delim(s); + x.identifier = x.reserved = true; + x.fud = f; + return x; + } + + + function blockstmt(s, f) { + var x = stmt(s, f); + x.block = true; + return x; + } + + + function reserveName(x) { + var c = x.id.charAt(0); + if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) { + x.identifier = x.reserved = true; + } + return x; + } + + + function prefix(s, f) { + var x = symbol(s, 150); + reserveName(x); + x.nud = (typeof f === "function") ? f : function () { + this.right = expression(150); + this.arity = "unary"; + if (this.id === "++" || this.id === "--") { + if (option.plusplus) { + warning("Unexpected use of '{a}'.", this, this.id); + } else if ((!this.right.identifier || this.right.reserved) && + this.right.id !== "." && this.right.id !== "[") { + warning("Bad operand.", this); + } + } + return this; + }; + return x; + } + + + function type(s, f) { + var x = delim(s); + x.type = s; + x.nud = f; + return x; + } + + + function reserve(s, f) { + var x = type(s, f); + x.identifier = x.reserved = true; + return x; + } + + + function reservevar(s, v) { + return reserve(s, function () { + if (typeof v === "function") { + v(this); + } + return this; + }); + } + + + function infix(s, f, p, w) { + var x = symbol(s, p); + reserveName(x); + x.led = function (left) { + if (!w) { + nobreaknonadjacent(prevtoken, token); + nonadjacent(token, nexttoken); + } + if (s === "in" && left.id === "!") { + warning("Confusing use of '{a}'.", left, "!"); + } + if (typeof f === "function") { + return f(left, this); + } else { + this.left = left; + this.right = expression(p); + return this; + } + }; + return x; + } + + + function relation(s, f) { + var x = symbol(s, 100); + x.led = function (left) { + nobreaknonadjacent(prevtoken, token); + nonadjacent(token, nexttoken); + var right = expression(100); + + if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) { + warning("Use the isNaN function to compare with NaN.", this); + } else if (f) { + f.apply(this, [left, right]); + } + if (left.id === "!") { + warning("Confusing use of '{a}'.", left, "!"); + } + if (right.id === "!") { + warning("Confusing use of '{a}'.", right, "!"); + } + this.left = left; + this.right = right; + return this; + }; + return x; + } + + + function isPoorRelation(node) { + return node && + ((node.type === "(number)" && +node.value === 0) || + (node.type === "(string)" && node.value === "") || + (node.type === "null" && !option.eqnull) || + node.type === "true" || + node.type === "false" || + node.type === "undefined"); + } + + + function assignop(s) { + symbol(s, 20).exps = true; + + return infix(s, function (left, that) { + that.left = left; + + if (predefined[left.value] === false && + scope[left.value]["(global)"] === true) { + warning("Read only.", left); + } else if (left["function"]) { + warning("'{a}' is a function.", left, left.value); + } + + if (left) { + if (option.esnext && funct[left.value] === "const") { + warning("Attempting to override '{a}' which is a constant", left, left.value); + } + + if (left.id === "." || left.id === "[") { + if (!left.left || left.left.value === "arguments") { + warning("Bad assignment.", that); + } + that.right = expression(19); + return that; + } else if (left.identifier && !left.reserved) { + if (funct[left.value] === "exception") { + warning("Do not assign to the exception parameter.", left); + } + that.right = expression(19); + return that; + } + + if (left === syntax["function"]) { + warning( +"Expected an identifier in an assignment and instead saw a function invocation.", + token); + } + } + + error("Bad assignment.", that); + }, 20); + } + + + function bitwise(s, f, p) { + var x = symbol(s, p); + reserveName(x); + x.led = (typeof f === "function") ? f : function (left) { + if (option.bitwise) { + warning("Unexpected use of '{a}'.", this, this.id); + } + this.left = left; + this.right = expression(p); + return this; + }; + return x; + } + + + function bitwiseassignop(s) { + symbol(s, 20).exps = true; + return infix(s, function (left, that) { + if (option.bitwise) { + warning("Unexpected use of '{a}'.", that, that.id); + } + nonadjacent(prevtoken, token); + nonadjacent(token, nexttoken); + if (left) { + if (left.id === "." || left.id === "[" || + (left.identifier && !left.reserved)) { + expression(19); + return that; + } + if (left === syntax["function"]) { + warning( +"Expected an identifier in an assignment, and instead saw a function invocation.", + token); + } + return that; + } + error("Bad assignment.", that); + }, 20); + } + + + function suffix(s) { + var x = symbol(s, 150); + x.led = function (left) { + if (option.plusplus) { + warning("Unexpected use of '{a}'.", this, this.id); + } else if ((!left.identifier || left.reserved) && + left.id !== "." && left.id !== "[") { + warning("Bad operand.", this); + } + this.left = left; + return this; + }; + return x; + } + + + // fnparam means that this identifier is being defined as a function + // argument (see identifier()) + function optionalidentifier(fnparam) { + if (nexttoken.identifier) { + advance(); + if (token.reserved && !option.es5) { + // `undefined` as a function param is a common pattern to protect + // against the case when somebody does `undefined = true` and + // help with minification. More info: https://gist.github.com/315916 + if (!fnparam || token.value !== "undefined") { + warning("Expected an identifier and instead saw '{a}' (a reserved word).", + token, token.id); + } + } + return token.value; + } + } + + // fnparam means that this identifier is being defined as a function + // argument + function identifier(fnparam) { + var i = optionalidentifier(fnparam); + if (i) { + return i; + } + if (token.id === "function" && nexttoken.id === "(") { + warning("Missing name in function declaration."); + } else { + error("Expected an identifier and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + } + + + function reachable(s) { + var i = 0, t; + if (nexttoken.id !== ";" || noreach) { + return; + } + for (;;) { + t = peek(i); + if (t.reach) { + return; + } + if (t.id !== "(endline)") { + if (t.id === "function") { + if (!option.latedef) { + break; + } + warning( +"Inner functions should be listed at the top of the outer function.", t); + break; + } + warning("Unreachable '{a}' after '{b}'.", t, t.value, s); + break; + } + i += 1; + } + } + + + function statement(noindent) { + var i = indent, r, s = scope, t = nexttoken; + + if (t.id === ";") { + advance(";"); + return; + } + + // Is this a labelled statement? + + if (t.identifier && !t.reserved && peek().id === ":") { + advance(); + advance(":"); + scope = Object.create(s); + addlabel(t.value, "label"); + + if (!nexttoken.labelled && nexttoken.value !== "{") { + warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value); + } + + if (jx.test(t.value + ":")) { + warning("Label '{a}' looks like a javascript url.", t, t.value); + } + + nexttoken.label = t.value; + t = nexttoken; + } + + // Is it a lonely block? + + if (t.id === "{") { + block(true, true); + return; + } + + // Parse the statement. + + if (!noindent) { + indentation(); + } + r = expression(0, true); + + // Look for the final semicolon. + + if (!t.block) { + if (!option.expr && (!r || !r.exps)) { + warning("Expected an assignment or function call and instead saw an expression.", + token); + } else if (option.nonew && r.id === "(" && r.left.id === "new") { + warning("Do not use 'new' for side effects.", t); + } + + if (nexttoken.id === ",") { + return comma(); + } + + if (nexttoken.id !== ";") { + if (!option.asi) { + // If this is the last statement in a block that ends on + // the same line *and* option lastsemic is on, ignore the warning. + // Otherwise, complain about missing semicolon. + if (!option.lastsemic || nexttoken.id !== "}" || + nexttoken.line !== token.line) { + warningAt("Missing semicolon.", token.line, token.character); + } + } + } else { + adjacent(token, nexttoken); + advance(";"); + nonadjacent(token, nexttoken); + } + } + +// Restore the indentation. + + indent = i; + scope = s; + return r; + } + + + function statements(startLine) { + var a = [], p; + + while (!nexttoken.reach && nexttoken.id !== "(end)") { + if (nexttoken.id === ";") { + p = peek(); + if (!p || p.id !== "(") { + warning("Unnecessary semicolon."); + } + advance(";"); + } else { + a.push(statement(startLine === nexttoken.line)); + } + } + return a; + } + + + /* + * read all directives + * recognizes a simple form of asi, but always + * warns, if it is used + */ + function directives() { + var i, p, pn; + + for (;;) { + if (nexttoken.id === "(string)") { + p = peek(0); + if (p.id === "(endline)") { + i = 1; + do { + pn = peek(i); + i = i + 1; + } while (pn.id === "(endline)"); + + if (pn.id !== ";") { + if (pn.id !== "(string)" && pn.id !== "(number)" && + pn.id !== "(regexp)" && pn.identifier !== true && + pn.id !== "}") { + break; + } + warning("Missing semicolon.", nexttoken); + } else { + p = pn; + } + } else if (p.id === "}") { + // directive with no other statements, warn about missing semicolon + warning("Missing semicolon.", p); + } else if (p.id !== ";") { + break; + } + + indentation(); + advance(); + if (directive[token.value]) { + warning("Unnecessary directive \"{a}\".", token, token.value); + } + + if (token.value === "use strict") { + if (!option["(explicitNewcap)"]) + option.newcap = true; + option.undef = true; + } + + // there's no directive negation, so always set to true + directive[token.value] = true; + + if (p.id === ";") { + advance(";"); + } + continue; + } + break; + } + } + + + /* + * Parses a single block. A block is a sequence of statements wrapped in + * braces. + * + * ordinary - true for everything but function bodies and try blocks. + * stmt - true if block can be a single statement (e.g. in if/for/while). + * isfunc - true if block is a function body + */ + function block(ordinary, stmt, isfunc) { + var a, + b = inblock, + old_indent = indent, + m, + s = scope, + t, + line, + d; + + inblock = ordinary; + + if (!ordinary || !option.funcscope) + scope = Object.create(scope); + + nonadjacent(token, nexttoken); + t = nexttoken; + + var metrics = funct["(metrics)"]; + metrics.nestedBlockDepth += 1; + metrics.verifyMaxNestedBlockDepthPerFunction(); + + if (nexttoken.id === "{") { + advance("{"); + line = token.line; + if (nexttoken.id !== "}") { + indent += option.indent; + while (!ordinary && nexttoken.from > indent) { + indent += option.indent; + } + + if (isfunc) { + m = {}; + for (d in directive) { + if (is_own(directive, d)) { + m[d] = directive[d]; + } + } + directives(); + + if (option.strict && funct["(context)"]["(global)"]) { + if (!m["use strict"] && !directive["use strict"]) { + warning("Missing \"use strict\" statement."); + } + } + } + + a = statements(line); + + metrics.statementCount += a.length; + + if (isfunc) { + directive = m; + } + + indent -= option.indent; + if (line !== nexttoken.line) { + indentation(); + } + } else if (line !== nexttoken.line) { + indentation(); + } + advance("}", t); + indent = old_indent; + } else if (!ordinary) { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, "{", nexttoken.value); + } else { + if (!stmt || option.curly) + warning("Expected '{a}' and instead saw '{b}'.", + nexttoken, "{", nexttoken.value); + + noreach = true; + indent += option.indent; + // test indentation only if statement is in new line + a = [statement(nexttoken.line === token.line)]; + indent -= option.indent; + noreach = false; + } + funct["(verb)"] = null; + if (!ordinary || !option.funcscope) scope = s; + inblock = b; + if (ordinary && option.noempty && (!a || a.length === 0)) { + warning("Empty block."); + } + metrics.nestedBlockDepth -= 1; + return a; + } + + + function countMember(m) { + if (membersOnly && typeof membersOnly[m] !== "boolean") { + warning("Unexpected /*member '{a}'.", token, m); + } + if (typeof member[m] === "number") { + member[m] += 1; + } else { + member[m] = 1; + } + } + + + function note_implied(token) { + var name = token.value, line = token.line, a = implied[name]; + if (typeof a === "function") { + a = false; + } + + if (!a) { + a = [line]; + implied[name] = a; + } else if (a[a.length - 1] !== line) { + a.push(line); + } + } + + + // Build the syntax table by declaring the syntactic elements of the language. + + type("(number)", function () { + return this; + }); + + type("(string)", function () { + return this; + }); + + syntax["(identifier)"] = { + type: "(identifier)", + lbp: 0, + identifier: true, + nud: function () { + var v = this.value, + s = scope[v], + f; + + if (typeof s === "function") { + // Protection against accidental inheritance. + s = undefined; + } else if (typeof s === "boolean") { + f = funct; + funct = functions[0]; + addlabel(v, "var"); + s = funct; + funct = f; + } + + // The name is in scope and defined in the current function. + if (funct === s) { + // Change 'unused' to 'var', and reject labels. + switch (funct[v]) { + case "unused": + funct[v] = "var"; + break; + case "unction": + funct[v] = "function"; + this["function"] = true; + break; + case "function": + this["function"] = true; + break; + case "label": + warning("'{a}' is a statement label.", token, v); + break; + } + } else if (funct["(global)"]) { + // The name is not defined in the function. If we are in the global + // scope, then we have an undefined variable. + // + // Operators typeof and delete do not raise runtime errors even if + // the base object of a reference is null so no need to display warning + // if we're inside of typeof or delete. + + if (option.undef && typeof predefined[v] !== "boolean") { + // Attempting to subscript a null reference will throw an + // error, even within the typeof and delete operators + if (!(anonname === "typeof" || anonname === "delete") || + (nexttoken && (nexttoken.value === "." || nexttoken.value === "["))) { + + isundef(funct, "'{a}' is not defined.", token, v); + } + } + + note_implied(token); + } else { + // If the name is already defined in the current + // function, but not as outer, then there is a scope error. + + switch (funct[v]) { + case "closure": + case "function": + case "var": + case "unused": + warning("'{a}' used out of scope.", token, v); + break; + case "label": + warning("'{a}' is a statement label.", token, v); + break; + case "outer": + case "global": + break; + default: + // If the name is defined in an outer function, make an outer entry, + // and if it was unused, make it var. + if (s === true) { + funct[v] = true; + } else if (s === null) { + warning("'{a}' is not allowed.", token, v); + note_implied(token); + } else if (typeof s !== "object") { + // Operators typeof and delete do not raise runtime errors even + // if the base object of a reference is null so no need to + // display warning if we're inside of typeof or delete. + if (option.undef) { + // Attempting to subscript a null reference will throw an + // error, even within the typeof and delete operators + if (!(anonname === "typeof" || anonname === "delete") || + (nexttoken && + (nexttoken.value === "." || nexttoken.value === "["))) { + + isundef(funct, "'{a}' is not defined.", token, v); + } + } + funct[v] = true; + note_implied(token); + } else { + switch (s[v]) { + case "function": + case "unction": + this["function"] = true; + s[v] = "closure"; + funct[v] = s["(global)"] ? "global" : "outer"; + break; + case "var": + case "unused": + s[v] = "closure"; + funct[v] = s["(global)"] ? "global" : "outer"; + break; + case "closure": + funct[v] = s["(global)"] ? "global" : "outer"; + break; + case "label": + warning("'{a}' is a statement label.", token, v); + } + } + } + } + return this; + }, + led: function () { + error("Expected an operator and instead saw '{a}'.", + nexttoken, nexttoken.value); + } + }; + + type("(regexp)", function () { + return this; + }); + + +// ECMAScript parser + + delim("(endline)"); + delim("(begin)"); + delim("(end)").reach = true; + delim(""); + delim("(error)").reach = true; + delim("}").reach = true; + delim(")"); + delim("]"); + delim("\"").reach = true; + delim("'").reach = true; + delim(";"); + delim(":").reach = true; + delim(","); + delim("#"); + delim("@"); + reserve("else"); + reserve("case").reach = true; + reserve("catch"); + reserve("default").reach = true; + reserve("finally"); + reservevar("arguments", function (x) { + if (directive["use strict"] && funct["(global)"]) { + warning("Strict violation.", x); + } + }); + reservevar("eval"); + reservevar("false"); + reservevar("Infinity"); + reservevar("null"); + reservevar("this", function (x) { + if (directive["use strict"] && !option.validthis && ((funct["(statement)"] && + funct["(name)"].charAt(0) > "Z") || funct["(global)"])) { + warning("Possible strict violation.", x); + } + }); + reservevar("true"); + reservevar("undefined"); + assignop("=", "assign", 20); + assignop("+=", "assignadd", 20); + assignop("-=", "assignsub", 20); + assignop("*=", "assignmult", 20); + assignop("/=", "assigndiv", 20).nud = function () { + error("A regular expression literal can be confused with '/='."); + }; + assignop("%=", "assignmod", 20); + bitwiseassignop("&=", "assignbitand", 20); + bitwiseassignop("|=", "assignbitor", 20); + bitwiseassignop("^=", "assignbitxor", 20); + bitwiseassignop("<<=", "assignshiftleft", 20); + bitwiseassignop(">>=", "assignshiftright", 20); + bitwiseassignop(">>>=", "assignshiftrightunsigned", 20); + infix("?", function (left, that) { + that.left = left; + that.right = expression(10); + advance(":"); + that["else"] = expression(10); + return that; + }, 30); + + infix("||", "or", 40); + infix("&&", "and", 50); + bitwise("|", "bitor", 70); + bitwise("^", "bitxor", 80); + bitwise("&", "bitand", 90); + relation("==", function (left, right) { + var eqnull = option.eqnull && (left.value === "null" || right.value === "null"); + + if (!eqnull && option.eqeqeq) + warning("Expected '{a}' and instead saw '{b}'.", this, "===", "=="); + else if (isPoorRelation(left)) + warning("Use '{a}' to compare with '{b}'.", this, "===", left.value); + else if (isPoorRelation(right)) + warning("Use '{a}' to compare with '{b}'.", this, "===", right.value); + + return this; + }); + relation("==="); + relation("!=", function (left, right) { + var eqnull = option.eqnull && + (left.value === "null" || right.value === "null"); + + if (!eqnull && option.eqeqeq) { + warning("Expected '{a}' and instead saw '{b}'.", + this, "!==", "!="); + } else if (isPoorRelation(left)) { + warning("Use '{a}' to compare with '{b}'.", + this, "!==", left.value); + } else if (isPoorRelation(right)) { + warning("Use '{a}' to compare with '{b}'.", + this, "!==", right.value); + } + return this; + }); + relation("!=="); + relation("<"); + relation(">"); + relation("<="); + relation(">="); + bitwise("<<", "shiftleft", 120); + bitwise(">>", "shiftright", 120); + bitwise(">>>", "shiftrightunsigned", 120); + infix("in", "in", 120); + infix("instanceof", "instanceof", 120); + infix("+", function (left, that) { + var right = expression(130); + if (left && right && left.id === "(string)" && right.id === "(string)") { + left.value += right.value; + left.character = right.character; + if (!option.scripturl && jx.test(left.value)) { + warning("JavaScript URL.", left); + } + return left; + } + that.left = left; + that.right = right; + return that; + }, 130); + prefix("+", "num"); + prefix("+++", function () { + warning("Confusing pluses."); + this.right = expression(150); + this.arity = "unary"; + return this; + }); + infix("+++", function (left) { + warning("Confusing pluses."); + this.left = left; + this.right = expression(130); + return this; + }, 130); + infix("-", "sub", 130); + prefix("-", "neg"); + prefix("---", function () { + warning("Confusing minuses."); + this.right = expression(150); + this.arity = "unary"; + return this; + }); + infix("---", function (left) { + warning("Confusing minuses."); + this.left = left; + this.right = expression(130); + return this; + }, 130); + infix("*", "mult", 140); + infix("/", "div", 140); + infix("%", "mod", 140); + + suffix("++", "postinc"); + prefix("++", "preinc"); + syntax["++"].exps = true; + + suffix("--", "postdec"); + prefix("--", "predec"); + syntax["--"].exps = true; + prefix("delete", function () { + var p = expression(0); + if (!p || (p.id !== "." && p.id !== "[")) { + warning("Variables should not be deleted."); + } + this.first = p; + return this; + }).exps = true; + + prefix("~", function () { + if (option.bitwise) { + warning("Unexpected '{a}'.", this, "~"); + } + expression(150); + return this; + }); + + prefix("!", function () { + this.right = expression(150); + this.arity = "unary"; + if (bang[this.right.id] === true) { + warning("Confusing use of '{a}'.", this, "!"); + } + return this; + }); + prefix("typeof", "typeof"); + prefix("new", function () { + var c = expression(155), i; + if (c && c.id !== "function") { + if (c.identifier) { + c["new"] = true; + switch (c.value) { + case "Number": + case "String": + case "Boolean": + case "Math": + case "JSON": + warning("Do not use {a} as a constructor.", prevtoken, c.value); + break; + case "Function": + if (!option.evil) { + warning("The Function constructor is eval."); + } + break; + case "Date": + case "RegExp": + break; + default: + if (c.id !== "function") { + i = c.value.substr(0, 1); + if (option.newcap && (i < "A" || i > "Z") && !is_own(global, c.value)) { + warning("A constructor name should start with an uppercase letter.", + token); + } + } + } + } else { + if (c.id !== "." && c.id !== "[" && c.id !== "(") { + warning("Bad constructor.", token); + } + } + } else { + if (!option.supernew) + warning("Weird construction. Delete 'new'.", this); + } + adjacent(token, nexttoken); + if (nexttoken.id !== "(" && !option.supernew) { + warning("Missing '()' invoking a constructor.", + token, token.value); + } + this.first = c; + return this; + }); + syntax["new"].exps = true; + + prefix("void").exps = true; + + infix(".", function (left, that) { + adjacent(prevtoken, token); + nobreak(); + var m = identifier(); + if (typeof m === "string") { + countMember(m); + } + that.left = left; + that.right = m; + if (left && left.value === "arguments" && (m === "callee" || m === "caller")) { + if (option.noarg) + warning("Avoid arguments.{a}.", left, m); + else if (directive["use strict"]) + error("Strict violation."); + } else if (!option.evil && left && left.value === "document" && + (m === "write" || m === "writeln")) { + warning("document.write can be a form of eval.", left); + } + if (!option.evil && (m === "eval" || m === "execScript")) { + warning("eval is evil."); + } + return that; + }, 160, true); + + infix("(", function (left, that) { + if (prevtoken.id !== "}" && prevtoken.id !== ")") { + nobreak(prevtoken, token); + } + nospace(); + if (option.immed && !left.immed && left.id === "function") { + warning("Wrap an immediate function invocation in parentheses " + + "to assist the reader in understanding that the expression " + + "is the result of a function, and not the function itself."); + } + var n = 0, + p = []; + if (left) { + if (left.type === "(identifier)") { + if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { + if ("Number String Boolean Date Object".indexOf(left.value) === -1) { + if (left.value === "Math") { + warning("Math is not a function.", left); + } else if (option.newcap) { + warning("Missing 'new' prefix when invoking a constructor.", left); + } + } + } + } + } + if (nexttoken.id !== ")") { + for (;;) { + p[p.length] = expression(10); + n += 1; + if (nexttoken.id !== ",") { + break; + } + comma(); + } + } + advance(")"); + nospace(prevtoken, token); + if (typeof left === "object") { + if (left.value === "parseInt" && n === 1) { + warning("Missing radix parameter.", token); + } + if (!option.evil) { + if (left.value === "eval" || left.value === "Function" || + left.value === "execScript") { + warning("eval is evil.", left); + + if (p[0] && [0].id === "(string)") { + addInternalSrc(left, p[0].value); + } + } else if (p[0] && p[0].id === "(string)" && + (left.value === "setTimeout" || + left.value === "setInterval")) { + warning( + "Implied eval is evil. Pass a function instead of a string.", left); + addInternalSrc(left, p[0].value); + + // window.setTimeout/setInterval + } else if (p[0] && p[0].id === "(string)" && + left.value === "." && + left.left.value === "window" && + (left.right === "setTimeout" || + left.right === "setInterval")) { + warning( + "Implied eval is evil. Pass a function instead of a string.", left); + addInternalSrc(left, p[0].value); + } + } + if (!left.identifier && left.id !== "." && left.id !== "[" && + left.id !== "(" && left.id !== "&&" && left.id !== "||" && + left.id !== "?") { + warning("Bad invocation.", left); + } + } + that.left = left; + return that; + }, 155, true).exps = true; + + prefix("(", function () { + nospace(); + if (nexttoken.id === "function") { + nexttoken.immed = true; + } + var v = expression(0); + advance(")", this); + nospace(prevtoken, token); + if (option.immed && v.id === "function") { + if (nexttoken.id !== "(" && + (nexttoken.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { + warning( +"Do not wrap function literals in parens unless they are to be immediately invoked.", + this); + } + } + + return v; + }); + + infix("[", function (left, that) { + nobreak(prevtoken, token); + nospace(); + var e = expression(0), s; + if (e && e.type === "(string)") { + if (!option.evil && (e.value === "eval" || e.value === "execScript")) { + warning("eval is evil.", that); + } + countMember(e.value); + if (!option.sub && ix.test(e.value)) { + s = syntax[e.value]; + if (!s || !s.reserved) { + warning("['{a}'] is better written in dot notation.", + prevtoken, e.value); + } + } + } + advance("]", that); + nospace(prevtoken, token); + that.left = left; + that.right = e; + return that; + }, 160, true); + + prefix("[", function () { + var b = token.line !== nexttoken.line; + this.first = []; + if (b) { + indent += option.indent; + if (nexttoken.from === indent + option.indent) { + indent += option.indent; + } + } + while (nexttoken.id !== "(end)") { + while (nexttoken.id === ",") { + if (!option.es5) + warning("Extra comma."); + advance(","); + } + if (nexttoken.id === "]") { + break; + } + if (b && token.line !== nexttoken.line) { + indentation(); + } + this.first.push(expression(10)); + if (nexttoken.id === ",") { + comma(); + if (nexttoken.id === "]" && !option.es5) { + warning("Extra comma.", token); + break; + } + } else { + break; + } + } + if (b) { + indent -= option.indent; + indentation(); + } + advance("]", this); + return this; + }, 160); + + + function property_name() { + var id = optionalidentifier(true); + if (!id) { + if (nexttoken.id === "(string)") { + id = nexttoken.value; + advance(); + } else if (nexttoken.id === "(number)") { + id = nexttoken.value.toString(); + advance(); + } + } + return id; + } + + + function functionparams() { + var next = nexttoken; + var params = []; + var ident; + + advance("("); + nospace(); + + if (nexttoken.id === ")") { + advance(")"); + return; + } + + for (;;) { + ident = identifier(true); + params.push(ident); + addlabel(ident, "unused", token); + if (nexttoken.id === ",") { + comma(); + } else { + advance(")", next); + nospace(prevtoken, token); + return params; + } + } + } + + + function doFunction(name, statement) { + var f; + var oldOption = option; + var oldScope = scope; + + option = Object.create(option); + scope = Object.create(scope); + + funct = { + "(name)" : name || "\"" + anonname + "\"", + "(line)" : nexttoken.line, + "(character)": nexttoken.character, + "(context)" : funct, + "(breakage)" : 0, + "(loopage)" : 0, + "(metrics)" : createMetrics(nexttoken), + "(scope)" : scope, + "(statement)": statement, + "(tokens)" : {} + }; + + f = funct; + token.funct = funct; + + functions.push(funct); + + if (name) { + addlabel(name, "function"); + } + + funct["(params)"] = functionparams(); + funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); + + block(false, false, true); + + funct["(metrics)"].verifyMaxStatementsPerFunction(); + funct["(metrics)"].verifyMaxComplexityPerFunction(); + + scope = oldScope; + option = oldOption; + funct["(last)"] = token.line; + funct["(lastcharacter)"] = token.character; + funct = funct["(context)"]; + + return f; + } + + function createMetrics(functionStartToken) { + return { + statementCount: 0, + nestedBlockDepth: -1, + ComplexityCount: 1, + verifyMaxStatementsPerFunction: function () { + if (option.maxstatements && + this.statementCount > option.maxstatements) { + var message = "Too many statements per function (" + this.statementCount + ")."; + warning(message, functionStartToken); + } + }, + + verifyMaxParametersPerFunction: function (params) { + params = params || []; + + if (option.maxparams && params.length > option.maxparams) { + var message = "Too many parameters per function (" + params.length + ")."; + warning(message, functionStartToken); + } + }, + + verifyMaxNestedBlockDepthPerFunction: function () { + if (option.maxdepth && + this.nestedBlockDepth > 0 && + this.nestedBlockDepth === option.maxdepth + 1) { + var message = "Blocks are nested too deeply (" + this.nestedBlockDepth + ")."; + warning(message); + } + }, + + verifyMaxComplexityPerFunction: function () { + var max = option.maxcomplexity; + var cc = this.ComplexityCount; + if (max && cc > max) { + var message = "Cyclomatic complexity is too high per function (" + cc + ")."; + warning(message, functionStartToken); + } + } + }; + } + + function increaseComplexityCount() { + funct["(metrics)"].ComplexityCount += 1; + } + + + (function (x) { + x.nud = function () { + var b, f, i, p, t; + var props = {}; // All properties, including accessors + + function saveProperty(name, token) { + if (props[name] && is_own(props, name)) + warning("Duplicate member '{a}'.", nexttoken, i); + else + props[name] = {}; + + props[name].basic = true; + props[name].basicToken = token; + } + + function saveSetter(name, token) { + if (props[name] && is_own(props, name)) { + if (props[name].basic || props[name].setter) + warning("Duplicate member '{a}'.", nexttoken, i); + } else { + props[name] = {}; + } + + props[name].setter = true; + props[name].setterToken = token; + } + + function saveGetter(name) { + if (props[name] && is_own(props, name)) { + if (props[name].basic || props[name].getter) + warning("Duplicate member '{a}'.", nexttoken, i); + } else { + props[name] = {}; + } + + props[name].getter = true; + props[name].getterToken = token; + } + + b = token.line !== nexttoken.line; + if (b) { + indent += option.indent; + if (nexttoken.from === indent + option.indent) { + indent += option.indent; + } + } + for (;;) { + if (nexttoken.id === "}") { + break; + } + if (b) { + indentation(); + } + if (nexttoken.value === "get" && peek().id !== ":") { + advance("get"); + if (!option.es5) { + error("get/set are ES5 features."); + } + i = property_name(); + if (!i) { + error("Missing property name."); + } + saveGetter(i); + t = nexttoken; + adjacent(token, nexttoken); + f = doFunction(); + p = f["(params)"]; + if (p) { + warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); + } + adjacent(token, nexttoken); + } else if (nexttoken.value === "set" && peek().id !== ":") { + advance("set"); + if (!option.es5) { + error("get/set are ES5 features."); + } + i = property_name(); + if (!i) { + error("Missing property name."); + } + saveSetter(i, nexttoken); + t = nexttoken; + adjacent(token, nexttoken); + f = doFunction(); + p = f["(params)"]; + if (!p || p.length !== 1) { + warning("Expected a single parameter in set {a} function.", t, i); + } + } else { + i = property_name(); + saveProperty(i, nexttoken); + if (typeof i !== "string") { + break; + } + advance(":"); + nonadjacent(token, nexttoken); + expression(10); + } + + countMember(i); + if (nexttoken.id === ",") { + comma(); + if (nexttoken.id === ",") { + warning("Extra comma.", token); + } else if (nexttoken.id === "}" && !option.es5) { + warning("Extra comma.", token); + } + } else { + break; + } + } + if (b) { + indent -= option.indent; + indentation(); + } + advance("}", this); + + // Check for lonely setters if in the ES5 mode. + if (option.es5) { + for (var name in props) { + if (is_own(props, name) && props[name].setter && !props[name].getter) { + warning("Setter is defined without getter.", props[name].setterToken); + } + } + } + return this; + }; + x.fud = function () { + error("Expected to see a statement and instead saw a block.", token); + }; + }(delim("{"))); + +// This Function is called when esnext option is set to true +// it adds the `const` statement to JSHINT + + useESNextSyntax = function () { + var conststatement = stmt("const", function (prefix) { + var id, name, value; + + this.first = []; + for (;;) { + nonadjacent(token, nexttoken); + id = identifier(); + if (funct[id] === "const") { + warning("const '" + id + "' has already been declared"); + } + if (funct["(global)"] && predefined[id] === false) { + warning("Redefinition of '{a}'.", token, id); + } + addlabel(id, "const"); + if (prefix) { + break; + } + name = token; + this.first.push(token); + + if (nexttoken.id !== "=") { + warning("const " + + "'{a}' is initialized to 'undefined'.", token, id); + } + + if (nexttoken.id === "=") { + nonadjacent(token, nexttoken); + advance("="); + nonadjacent(token, nexttoken); + if (nexttoken.id === "undefined") { + warning("It is not necessary to initialize " + + "'{a}' to 'undefined'.", token, id); + } + if (peek(0).id === "=" && nexttoken.identifier) { + error("Constant {a} was not declared correctly.", + nexttoken, nexttoken.value); + } + value = expression(0); + name.first = value; + } + + if (nexttoken.id !== ",") { + break; + } + comma(); + } + return this; + }); + conststatement.exps = true; + }; + + var varstatement = stmt("var", function (prefix) { + // JavaScript does not have block scope. It only has function scope. So, + // declaring a variable in a block can have unexpected consequences. + var id, name, value; + + if (funct["(onevar)"] && option.onevar) { + warning("Too many var statements."); + } else if (!funct["(global)"]) { + funct["(onevar)"] = true; + } + + this.first = []; + + for (;;) { + nonadjacent(token, nexttoken); + id = identifier(); + + if (option.esnext && funct[id] === "const") { + warning("const '" + id + "' has already been declared"); + } + + if (funct["(global)"] && predefined[id] === false) { + warning("Redefinition of '{a}'.", token, id); + } + + addlabel(id, "unused", token); + + if (prefix) { + break; + } + + name = token; + this.first.push(token); + + if (nexttoken.id === "=") { + nonadjacent(token, nexttoken); + advance("="); + nonadjacent(token, nexttoken); + if (nexttoken.id === "undefined") { + warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); + } + if (peek(0).id === "=" && nexttoken.identifier) { + error("Variable {a} was not declared correctly.", + nexttoken, nexttoken.value); + } + value = expression(0); + name.first = value; + } + if (nexttoken.id !== ",") { + break; + } + comma(); + } + return this; + }); + varstatement.exps = true; + + blockstmt("function", function () { + if (inblock) { + warning("Function declarations should not be placed in blocks. " + + "Use a function expression or move the statement to the top of " + + "the outer function.", token); + + } + var i = identifier(); + if (option.esnext && funct[i] === "const") { + warning("const '" + i + "' has already been declared"); + } + adjacent(token, nexttoken); + addlabel(i, "unction", token); + + doFunction(i, { statement: true }); + if (nexttoken.id === "(" && nexttoken.line === token.line) { + error( +"Function declarations are not invocable. Wrap the whole function invocation in parens."); + } + return this; + }); + + prefix("function", function () { + var i = optionalidentifier(); + if (i) { + adjacent(token, nexttoken); + } else { + nonadjacent(token, nexttoken); + } + doFunction(i); + if (!option.loopfunc && funct["(loopage)"]) { + warning("Don't make functions within a loop."); + } + return this; + }); + + blockstmt("if", function () { + var t = nexttoken; + increaseComplexityCount(); + advance("("); + nonadjacent(this, t); + nospace(); + expression(20); + if (nexttoken.id === "=") { + if (!option.boss) + warning("Expected a conditional expression and instead saw an assignment."); + advance("="); + expression(20); + } + advance(")", t); + nospace(prevtoken, token); + block(true, true); + if (nexttoken.id === "else") { + nonadjacent(token, nexttoken); + advance("else"); + if (nexttoken.id === "if" || nexttoken.id === "switch") { + statement(true); + } else { + block(true, true); + } + } + return this; + }); + + blockstmt("try", function () { + var b; + + function doCatch() { + var oldScope = scope; + var e; + + advance("catch"); + nonadjacent(token, nexttoken); + advance("("); + + scope = Object.create(oldScope); + + e = nexttoken.value; + if (nexttoken.type !== "(identifier)") { + e = null; + warning("Expected an identifier and instead saw '{a}'.", nexttoken, e); + } + + advance(); + advance(")"); + + funct = { + "(name)" : "(catch)", + "(line)" : nexttoken.line, + "(character)": nexttoken.character, + "(context)" : funct, + "(breakage)" : funct["(breakage)"], + "(loopage)" : funct["(loopage)"], + "(scope)" : scope, + "(statement)": false, + "(metrics)" : createMetrics(nexttoken), + "(catch)" : true, + "(tokens)" : {} + }; + + if (e) { + addlabel(e, "exception"); + } + + token.funct = funct; + functions.push(funct); + + block(false); + + scope = oldScope; + + funct["(last)"] = token.line; + funct["(lastcharacter)"] = token.character; + funct = funct["(context)"]; + } + + block(false); + + if (nexttoken.id === "catch") { + increaseComplexityCount(); + doCatch(); + b = true; + } + + if (nexttoken.id === "finally") { + advance("finally"); + block(false); + return; + } else if (!b) { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, "catch", nexttoken.value); + } + + return this; + }); + + blockstmt("while", function () { + var t = nexttoken; + funct["(breakage)"] += 1; + funct["(loopage)"] += 1; + increaseComplexityCount(); + advance("("); + nonadjacent(this, t); + nospace(); + expression(20); + if (nexttoken.id === "=") { + if (!option.boss) + warning("Expected a conditional expression and instead saw an assignment."); + advance("="); + expression(20); + } + advance(")", t); + nospace(prevtoken, token); + block(true, true); + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + return this; + }).labelled = true; + + blockstmt("with", function () { + var t = nexttoken; + if (directive["use strict"]) { + error("'with' is not allowed in strict mode.", token); + } else if (!option.withstmt) { + warning("Don't use 'with'.", token); + } + + advance("("); + nonadjacent(this, t); + nospace(); + expression(0); + advance(")", t); + nospace(prevtoken, token); + block(true, true); + + return this; + }); + + blockstmt("switch", function () { + var t = nexttoken, + g = false; + funct["(breakage)"] += 1; + advance("("); + nonadjacent(this, t); + nospace(); + this.condition = expression(20); + advance(")", t); + nospace(prevtoken, token); + nonadjacent(token, nexttoken); + t = nexttoken; + advance("{"); + nonadjacent(token, nexttoken); + indent += option.indent; + this.cases = []; + for (;;) { + switch (nexttoken.id) { + case "case": + switch (funct["(verb)"]) { + case "break": + case "case": + case "continue": + case "return": + case "switch": + case "throw": + break; + default: + // You can tell JSHint that you don't use break intentionally by + // adding a comment /* falls through */ on a line just before + // the next `case`. + if (!ft.test(lines[nexttoken.line - 2])) { + warning( + "Expected a 'break' statement before 'case'.", + token); + } + } + indentation(-option.indent); + advance("case"); + this.cases.push(expression(20)); + increaseComplexityCount(); + g = true; + advance(":"); + funct["(verb)"] = "case"; + break; + case "default": + switch (funct["(verb)"]) { + case "break": + case "continue": + case "return": + case "throw": + break; + default: + if (!ft.test(lines[nexttoken.line - 2])) { + warning( + "Expected a 'break' statement before 'default'.", + token); + } + } + indentation(-option.indent); + advance("default"); + g = true; + advance(":"); + break; + case "}": + indent -= option.indent; + indentation(); + advance("}", t); + if (this.cases.length === 1 || this.condition.id === "true" || + this.condition.id === "false") { + if (!option.onecase) + warning("This 'switch' should be an 'if'.", this); + } + funct["(breakage)"] -= 1; + funct["(verb)"] = undefined; + return; + case "(end)": + error("Missing '{a}'.", nexttoken, "}"); + return; + default: + if (g) { + switch (token.id) { + case ",": + error("Each value should have its own case label."); + return; + case ":": + g = false; + statements(); + break; + default: + error("Missing ':' on a case clause.", token); + return; + } + } else { + if (token.id === ":") { + advance(":"); + error("Unexpected '{a}'.", token, ":"); + statements(); + } else { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, "case", nexttoken.value); + return; + } + } + } + } + }).labelled = true; + + stmt("debugger", function () { + if (!option.debug) { + warning("All 'debugger' statements should be removed."); + } + return this; + }).exps = true; + + (function () { + var x = stmt("do", function () { + funct["(breakage)"] += 1; + funct["(loopage)"] += 1; + increaseComplexityCount(); + + this.first = block(true); + advance("while"); + var t = nexttoken; + nonadjacent(token, t); + advance("("); + nospace(); + expression(20); + if (nexttoken.id === "=") { + if (!option.boss) + warning("Expected a conditional expression and instead saw an assignment."); + advance("="); + expression(20); + } + advance(")", t); + nospace(prevtoken, token); + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + return this; + }); + x.labelled = true; + x.exps = true; + }()); + + blockstmt("for", function () { + var s, t = nexttoken; + funct["(breakage)"] += 1; + funct["(loopage)"] += 1; + increaseComplexityCount(); + advance("("); + nonadjacent(this, t); + nospace(); + if (peek(nexttoken.id === "var" ? 1 : 0).id === "in") { + if (nexttoken.id === "var") { + advance("var"); + varstatement.fud.call(varstatement, true); + } else { + switch (funct[nexttoken.value]) { + case "unused": + funct[nexttoken.value] = "var"; + break; + case "var": + break; + default: + warning("Bad for in variable '{a}'.", + nexttoken, nexttoken.value); + } + advance(); + } + advance("in"); + expression(20); + advance(")", t); + s = block(true, true); + if (option.forin && s && (s.length > 1 || typeof s[0] !== "object" || + s[0].value !== "if")) { + warning("The body of a for in should be wrapped in an if statement to filter " + + "unwanted properties from the prototype.", this); + } + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + return this; + } else { + if (nexttoken.id !== ";") { + if (nexttoken.id === "var") { + advance("var"); + varstatement.fud.call(varstatement); + } else { + for (;;) { + expression(0, "for"); + if (nexttoken.id !== ",") { + break; + } + comma(); + } + } + } + nolinebreak(token); + advance(";"); + if (nexttoken.id !== ";") { + expression(20); + if (nexttoken.id === "=") { + if (!option.boss) + warning("Expected a conditional expression and instead saw an assignment."); + advance("="); + expression(20); + } + } + nolinebreak(token); + advance(";"); + if (nexttoken.id === ";") { + error("Expected '{a}' and instead saw '{b}'.", + nexttoken, ")", ";"); + } + if (nexttoken.id !== ")") { + for (;;) { + expression(0, "for"); + if (nexttoken.id !== ",") { + break; + } + comma(); + } + } + advance(")", t); + nospace(prevtoken, token); + block(true, true); + funct["(breakage)"] -= 1; + funct["(loopage)"] -= 1; + return this; + } + }).labelled = true; + + + stmt("break", function () { + var v = nexttoken.value; + + if (funct["(breakage)"] === 0) + warning("Unexpected '{a}'.", nexttoken, this.value); + + if (!option.asi) + nolinebreak(this); + + if (nexttoken.id !== ";") { + if (token.line === nexttoken.line) { + if (funct[v] !== "label") { + warning("'{a}' is not a statement label.", nexttoken, v); + } else if (scope[v] !== funct) { + warning("'{a}' is out of scope.", nexttoken, v); + } + this.first = nexttoken; + advance(); + } + } + reachable("break"); + return this; + }).exps = true; + + + stmt("continue", function () { + var v = nexttoken.value; + + if (funct["(breakage)"] === 0) + warning("Unexpected '{a}'.", nexttoken, this.value); + + if (!option.asi) + nolinebreak(this); + + if (nexttoken.id !== ";") { + if (token.line === nexttoken.line) { + if (funct[v] !== "label") { + warning("'{a}' is not a statement label.", nexttoken, v); + } else if (scope[v] !== funct) { + warning("'{a}' is out of scope.", nexttoken, v); + } + this.first = nexttoken; + advance(); + } + } else if (!funct["(loopage)"]) { + warning("Unexpected '{a}'.", nexttoken, this.value); + } + reachable("continue"); + return this; + }).exps = true; + + + stmt("return", function () { + if (this.line === nexttoken.line) { + if (nexttoken.id === "(regexp)") + warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); + + if (nexttoken.id !== ";" && !nexttoken.reach) { + nonadjacent(token, nexttoken); + if (peek().value === "=" && !option.boss) { + warningAt("Did you mean to return a conditional instead of an assignment?", + token.line, token.character + 1); + } + this.first = expression(0); + } + } else if (!option.asi) { + nolinebreak(this); // always warn (Line breaking error) + } + reachable("return"); + return this; + }).exps = true; + + + stmt("throw", function () { + nolinebreak(this); + nonadjacent(token, nexttoken); + this.first = expression(20); + reachable("throw"); + return this; + }).exps = true; + +// Superfluous reserved words + + reserve("class"); + reserve("const"); + reserve("enum"); + reserve("export"); + reserve("extends"); + reserve("import"); + reserve("super"); + + reserve("let"); + reserve("yield"); + reserve("implements"); + reserve("interface"); + reserve("package"); + reserve("private"); + reserve("protected"); + reserve("public"); + reserve("static"); + + +// Parse JSON + + function jsonValue() { + + function jsonObject() { + var o = {}, t = nexttoken; + advance("{"); + if (nexttoken.id !== "}") { + for (;;) { + if (nexttoken.id === "(end)") { + error("Missing '}' to match '{' from line {a}.", + nexttoken, t.line); + } else if (nexttoken.id === "}") { + warning("Unexpected comma.", token); + break; + } else if (nexttoken.id === ",") { + error("Unexpected comma.", nexttoken); + } else if (nexttoken.id !== "(string)") { + warning("Expected a string and instead saw {a}.", + nexttoken, nexttoken.value); + } + if (o[nexttoken.value] === true) { + warning("Duplicate key '{a}'.", + nexttoken, nexttoken.value); + } else if ((nexttoken.value === "__proto__" && + !option.proto) || (nexttoken.value === "__iterator__" && + !option.iterator)) { + warning("The '{a}' key may produce unexpected results.", + nexttoken, nexttoken.value); + } else { + o[nexttoken.value] = true; + } + advance(); + advance(":"); + jsonValue(); + if (nexttoken.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + } + + function jsonArray() { + var t = nexttoken; + advance("["); + if (nexttoken.id !== "]") { + for (;;) { + if (nexttoken.id === "(end)") { + error("Missing ']' to match '[' from line {a}.", + nexttoken, t.line); + } else if (nexttoken.id === "]") { + warning("Unexpected comma.", token); + break; + } else if (nexttoken.id === ",") { + error("Unexpected comma.", nexttoken); + } + jsonValue(); + if (nexttoken.id !== ",") { + break; + } + advance(","); + } + } + advance("]"); + } + + switch (nexttoken.id) { + case "{": + jsonObject(); + break; + case "[": + jsonArray(); + break; + case "true": + case "false": + case "null": + case "(number)": + case "(string)": + advance(); + break; + case "-": + advance("-"); + if (token.character !== nexttoken.from) { + warning("Unexpected space after '-'.", token); + } + adjacent(token, nexttoken); + advance("(number)"); + break; + default: + error("Expected a JSON value.", nexttoken); + } + } + + + // The actual JSHINT function itself. + var itself = function (s, o, g) { + var a, i, k, x; + var optionKeys; + var newOptionObj = {}; + + if (o && o.scope) { + JSHINT.scope = o.scope; + } else { + JSHINT.errors = []; + JSHINT.undefs = []; + JSHINT.internals = []; + JSHINT.blacklist = {}; + JSHINT.scope = "(main)"; + } + + predefined = Object.create(standard); + declared = Object.create(null); + combine(predefined, g || {}); + + if (o) { + a = o.predef; + if (a) { + if (!Array.isArray(a) && typeof a === "object") { + a = Object.keys(a); + } + a.forEach(function (item) { + var slice; + if (item[0] === "-") { + slice = item.slice(1); + JSHINT.blacklist[slice] = slice; + } else { + predefined[item] = true; + } + }); + } + + optionKeys = Object.keys(o); + for (x = 0; x < optionKeys.length; x++) { + newOptionObj[optionKeys[x]] = o[optionKeys[x]]; + + if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) + newOptionObj["(explicitNewcap)"] = true; + + if (optionKeys[x] === "indent") + newOptionObj.white = true; + } + } + + option = newOptionObj; + + option.indent = option.indent || 4; + option.maxerr = option.maxerr || 50; + + tab = ""; + for (i = 0; i < option.indent; i += 1) { + tab += " "; + } + indent = 1; + global = Object.create(predefined); + scope = global; + funct = { + "(global)": true, + "(name)": "(global)", + "(scope)": scope, + "(breakage)": 0, + "(loopage)": 0, + "(tokens)": {}, + "(metrics)": createMetrics(nexttoken) + }; + functions = [funct]; + urls = []; + stack = null; + member = {}; + membersOnly = null; + implied = {}; + inblock = false; + lookahead = []; + jsonmode = false; + warnings = 0; + lines = []; + unuseds = []; + + if (!isString(s) && !Array.isArray(s)) { + errorAt("Input is neither a string nor an array of strings.", 0); + return false; + } + + if (isString(s) && /^\s*$/g.test(s)) { + errorAt("Input is an empty string.", 0); + return false; + } + + if (s.length === 0) { + errorAt("Input is an empty array.", 0); + return false; + } + + lex.init(s); + + prereg = true; + directive = {}; + + prevtoken = token = nexttoken = syntax["(begin)"]; + + // Check options + for (var name in o) { + if (is_own(o, name)) { + checkOption(name, token); + } + } + + assume(); + + // combine the passed globals after we've assumed all our options + combine(predefined, g || {}); + + //reset values + comma.first = true; + quotmark = undefined; + + try { + advance(); + switch (nexttoken.id) { + case "{": + case "[": + option.laxbreak = true; + jsonmode = true; + jsonValue(); + break; + default: + directives(); + if (directive["use strict"] && !option.globalstrict) { + warning("Use the function form of \"use strict\".", prevtoken); + } + + statements(); + } + advance((nexttoken && nexttoken.value !== ".") ? "(end)" : undefined); + + var markDefined = function (name, context) { + do { + if (typeof context[name] === "string") { + // JSHINT marks unused variables as 'unused' and + // unused function declaration as 'unction'. This + // code changes such instances back 'var' and + // 'closure' so that the code in JSHINT.data() + // doesn't think they're unused. + + if (context[name] === "unused") + context[name] = "var"; + else if (context[name] === "unction") + context[name] = "closure"; + + return true; + } + + context = context["(context)"]; + } while (context); + + return false; + }; + + var clearImplied = function (name, line) { + if (!implied[name]) + return; + + var newImplied = []; + for (var i = 0; i < implied[name].length; i += 1) { + if (implied[name][i] !== line) + newImplied.push(implied[name][i]); + } + + if (newImplied.length === 0) + delete implied[name]; + else + implied[name] = newImplied; + }; + + var warnUnused = function (name, token) { + var line = token.line; + var chr = token.character; + + if (option.unused) + warningAt("'{a}' is defined but never used.", line, chr, name); + + unuseds.push({ + name: name, + line: line, + character: chr + }); + }; + + var checkUnused = function (func, key) { + var type = func[key]; + var token = func["(tokens)"][key]; + + if (key.charAt(0) === "(") + return; + + if (type !== "unused" && type !== "unction") + return; + + // Params are checked separately from other variables. + if (func["(params)"] && func["(params)"].indexOf(key) !== -1) + return; + + warnUnused(key, token); + }; + + // Check queued 'x is not defined' instances to see if they're still undefined. + for (i = 0; i < JSHINT.undefs.length; i += 1) { + k = JSHINT.undefs[i].slice(0); + + if (markDefined(k[2].value, k[0])) { + clearImplied(k[2].value, k[2].line); + } else { + warning.apply(warning, k.slice(1)); + } + } + + functions.forEach(function (func) { + for (var key in func) { + if (is_own(func, key)) { + checkUnused(func, key); + } + } + + if (!func["(params)"]) + return; + + var params = func["(params)"].slice(); + var param = params.pop(); + var type; + + while (param) { + type = func[param]; + + // 'undefined' is a special case for (function (window, undefined) { ... })(); + // patterns. + + if (param === "undefined") + return; + + if (type !== "unused" && type !== "unction") + return; + + warnUnused(param, func["(tokens)"][param]); + param = params.pop(); + } + }); + + for (var key in declared) { + if (is_own(declared, key) && !is_own(global, key)) { + warnUnused(key, declared[key]); + } + } + } catch (e) { + if (e) { + var nt = nexttoken || {}; + JSHINT.errors.push({ + raw : e.raw, + reason : e.message, + line : e.line || nt.line, + character : e.character || nt.from + }, null); + } + } + + // Loop over the listed "internals", and check them as well. + + if (JSHINT.scope === "(main)") { + o = o || {}; + + for (i = 0; i < JSHINT.internals.length; i += 1) { + k = JSHINT.internals[i]; + o.scope = k.elem; + itself(k.value, o, g); + } + } + + return JSHINT.errors.length === 0; + }; + + // Data summary. + itself.data = function () { + var data = { + functions: [], + options: option + }; + var implieds = []; + var members = []; + var fu, f, i, j, n, globals; + + if (itself.errors.length) { + data.errors = itself.errors; + } + + if (jsonmode) { + data.json = true; + } + + for (n in implied) { + if (is_own(implied, n)) { + implieds.push({ + name: n, + line: implied[n] + }); + } + } + + if (implieds.length > 0) { + data.implieds = implieds; + } + + if (urls.length > 0) { + data.urls = urls; + } + + globals = Object.keys(scope); + if (globals.length > 0) { + data.globals = globals; + } + + for (i = 1; i < functions.length; i += 1) { + f = functions[i]; + fu = {}; + + for (j = 0; j < functionicity.length; j += 1) { + fu[functionicity[j]] = []; + } + + for (j = 0; j < functionicity.length; j += 1) { + if (fu[functionicity[j]].length === 0) { + delete fu[functionicity[j]]; + } + } + + fu.name = f["(name)"]; + fu.param = f["(params)"]; + fu.line = f["(line)"]; + fu.character = f["(character)"]; + fu.last = f["(last)"]; + fu.lastcharacter = f["(lastcharacter)"]; + data.functions.push(fu); + } + + if (unuseds.length > 0) { + data.unused = unuseds; + } + + members = []; + for (n in member) { + if (typeof member[n] === "number") { + data.member = member; + break; + } + } + + return data; + }; + + itself.jshint = itself; + + return itself; +}()); + +// Make JSHINT a Node module, if possible. +if (typeof exports === "object" && exports) { + exports.JSHINT = JSHINT; +} diff --git a/data/scripts/jsdoc/node_modules/markdown/README.markdown b/data/scripts/jsdoc/node_modules/markdown/README.markdown new file mode 100644 index 00000000..67f39696 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/markdown/README.markdown @@ -0,0 +1,115 @@ +markdown-js +=========== + +Yet another markdown parser, this time for JavaScript. There's a few +options that precede this project but they all treat markdown to HTML +conversion as a single step process. You pass markdown in and get HTML +out, end of story. We had some pretty particular views on how the +process should actually look, which include: + + * producing well-formed HTML. This means that em and strong nesting is + important, as is the ability to output as both HTML and XHTML + + * having an intermediate representation to allow processing of parsed + data (we in fact have two, both [JsonML]: a markdown tree and an + HTML tree) + + * being easily extensible to add new dialects without having to + rewrite the entire parsing mechanics + + * having a good test suite. The only test suites we could find tested + massive blocks of input, and passing depended on outputting the HTML + with exactly the same whitespace as the original implementation + +[JsonML]: http://jsonml.org/ "JSON Markup Language" + +## Installation + +Just the `markdown` library: + + npm install markdown + +Also install `md2html` to `/usr/local/bin` (or wherever) + + npm install -g markdown + +## Usage + +The simple way to use it with CommonJS is: + + var input = "# Heading\n\nParagraph"; + var output = require( "markdown" ).markdown.toHTML( input ); + print( output ); + +If you want more control check out the documentation in +[lib/markdown.js] which details all the methods and parameters +available (including examples!). One day we'll get the docs generated +and hosted somewhere for nicer browsing. + +It also works in a browser; here is a complete example: + + + + + +
+ + + + + +### md2html + + md2html /path/to/doc.md > /path/to/doc.html + +[lib/markdown.js]: http://github.com/evilstreak/markdown-js/blob/master/lib/markdown.js + +## Intermediate Representation + +Internally the process to convert a chunk of markdown into a chunk of +HTML has three steps: + + 1. Parse the markdown into a JsonML tree. Any references found in the + parsing are stored in the attribute hash of the root node under the + key `references`. + + 2. Convert the markdown tree into an HTML tree. Rename any nodes that + need it (`bulletlist` to `ul` for example) and lookup any references + used by links or images. Remove the references attribute once done. + + 3. Stringify the HTML tree being careful not to wreck whitespace where + whitespace is important (surrounding inline elements for example). + +Each step of this process can be called individually if you need to do +some processing or modification of the data at an intermediate stage. +For example, you may want to grab a list of all URLs linked to in the +document before rendering it to HTML which you could do by recursing +through the HTML tree looking for `a` nodes. + +## Running tests + +To run the tests under node you will need tap installed (it's listed as a +devDependencies so `npm install` from the checkout should be enough), then do + + $ ./node_modules/.bin/tap test/*.t.js + +## Contributing + +Do the usual github fork and pull request dance. Add yourself to the +contributors section of package.json too if you want to. + +## License + +Released under the MIT license. diff --git a/data/scripts/jsdoc/node_modules/markdown/lib/index.js b/data/scripts/jsdoc/node_modules/markdown/lib/index.js new file mode 100644 index 00000000..8bb08734 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/markdown/lib/index.js @@ -0,0 +1,3 @@ +// super simple module for the most common nodejs use case. +exports.markdown = require("./markdown"); +exports.parse = exports.markdown.toHTML; diff --git a/data/scripts/jsdoc/node_modules/markdown/lib/markdown.js b/data/scripts/jsdoc/node_modules/markdown/lib/markdown.js new file mode 100644 index 00000000..817cfddc --- /dev/null +++ b/data/scripts/jsdoc/node_modules/markdown/lib/markdown.js @@ -0,0 +1,1616 @@ +// Released under MIT license +// Copyright (c) 2009-2010 Dominic Baggott +// Copyright (c) 2009-2010 Ash Berlin +// Copyright (c) 2011 Christoph Dorn (http://www.christophdorn.com) + +(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, [dialect] ) -> 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 , dialect , options ) { + var input = expose.toHTMLTree( source , dialect , options ); + + 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 , options ) { + // 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 , options ); + merge_text_nodes( html ); + return html; +}; + +// For Spidermonkey based engines +function mk_block_toSource() { + return "Markdown.mk_block( " + + uneval(this.toString()) + + ", " + + uneval(this.trailing) + + ", " + + uneval(this.lineNumber) + + " )"; +} + +// node +function mk_block_inspect() { + var util = require('util'); + return "Markdown.mk_block( " + + util.inspect(this.toString()) + + ", " + + util.inspect(this.trailing) + + ", " + + util.inspect(this.lineNumber) + + " )"; + +} + +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.inspect = mk_block_inspect; + s.toSource = mk_block_toSource; + + 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)/.exec(input) ) != null ) { + // skip (but count) leading blank lines + line_no += count_lines( m[0] ); + re.lastIndex = m[0].length; + } + + while ( ( m = re.exec(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 cbs.__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 ( !isArray(res) || ( res.length > 0 && !( isArray(res[0]) ) ) ) + 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); + if (typeof print !== "undefined") + print.apply( print, args ); + if (typeof console !== "undefined" && typeof console.log !== "undefined") + console.log.apply( null, 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.exec(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 } ]; + Array.prototype.push.apply(header, this.processInline(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 + if ( !block.match( re ) ) 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 + if ( !next[0].match( re ) ) 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.exec( 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.exec( 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 ], + i; + + // 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 = false; + for (i = 0; i < stack.length; i++) { + if ( stack[ i ].indent != m[1] ) continue; + list = stack[ i ].list; + stack.splice( i+1 ); + found = true; + break; + } + + 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 + forEach( stack, 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 + forEach( stack, 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 = new String(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 = { + + __oneElement__: function oneElement( text, patterns_or_re, previous_nodes ) { + var m, + res, + lastIndex = 0; + + patterns_or_re = patterns_or_re || this.dialect.inline.__patterns__; + var re = new RegExp( "([\\s\\S]*?)(" + (patterns_or_re.source || patterns_or_re) + ")" ); + + m = re.exec( text ); + if (!m) { + // Just boring text + return [ text.length, text ]; + } + else if ( m[1] ) { + // Some un-interesting text matched. Return that first + return [ m[1].length, m[1] ]; + } + + var res; + if ( m[2] in this.dialect.inline ) { + res = this.dialect.inline[ m[2] ].call( + this, + text.substr( m.index ), m, previous_nodes || [] ); + } + // Default for now to make dev easier. just slurp special and output it. + res = res || [ m[2].length, m[2] ]; + return res; + }, + + __call__: function inline( text, patterns ) { + + var out = [], + res; + + 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 ( text.length > 0 ) { + res = this.dialect.inline.__oneElement__.call(this, text, patterns, out ); + text = text.substr( res.shift() ); + forEach(res, add ) + } + + return out; + }, + + // These characters are intersting elsewhere, so have rules for them so that + // chunks of plain text blocks don't include them + "]": function () {}, + "}": function () {}, + + "\\": 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 ) { + + // Unlike images, alt text is plain text only. no other elements are + // allowed in there + + // ![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(), original: m[0] } ] ]; + } + + // Just consume the '![' + return [ 2, "![" ]; + }, + + "[": function link( text ) { + + var orig = String(text); + // Inline content is possible inside `link text` + var res = Markdown.DialectHelpers.inline_until_char.call( this, text.substr(1), ']' ); + + // No closing ']' found. Just consume the [ + if ( !res ) return [ 1, '[' ]; + + var consumed = 1 + res[ 0 ], + children = res[ 1 ], + link, + attrs; + + // At this point the first [...] has been parsed. See what follows to find + // out which kind of link we are (reference or direct url) + text = text.substr( consumed ); + + // [link text](/path/to/img.jpg "Optional title") + // 1 2 3 <--- captures + // This will capture up to the last paren in the block. We then pull + // back based on if there a matching ones in the url + // ([here](/url/(test)) + // The parens have to be balanced + var m = text.match( /^\s*\([ \t]*(\S+)(?:[ \t]+(["'])(.*?)\2)?[ \t]*\)/ ); + if ( m ) { + var url = m[1]; + consumed += m[0].length; + + if ( url && url[0] == '<' && url[url.length-1] == '>' ) + url = url.substring( 1, url.length - 1 ); + + // If there is a title we don't have to worry about parens in the url + if ( !m[3] ) { + var open_parens = 1; // One open that isn't in the capture + for (var len = 0; len < url.length; len++) { + switch ( url[len] ) { + case '(': + open_parens++; + break; + case ')': + if ( --open_parens == 0) { + consumed -= url.length - len; + url = url.substring(0, len); + } + break; + } + } + } + + // Process escapes only + url = this.dialect.inline.__call__.call( this, url, /\\/ )[0]; + + attrs = { href: url || "" }; + if ( m[3] !== undefined) + attrs.title = m[3]; + + link = [ "link", attrs ].concat( children ); + return [ consumed, link ]; + } + + // [Alt text][id] + // [Alt text] [id] + m = text.match( /^\s*\[(.*?)\]/ ); + + if ( m ) { + + consumed += m[ 0 ].length; + + // [links][] uses links as its reference + attrs = { ref: ( m[ 1 ] || String(children) ).toLowerCase(), original: orig.substr( 0, consumed ) }; + + link = [ "link_ref", attrs ].concat( children ); + + // 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 [ consumed, link ]; + } + + // [id] + // Only if id is plain (no formatting.) + if ( children.length == 1 && typeof children[0] == "string" ) { + + attrs = { ref: children[0].toLowerCase(), original: orig.substr( 0, consumed ) }; + link = [ "link_ref", attrs, children[0] ]; + return [ consumed, link ]; + } + + // 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 ) { + // __foo__ is reserved and not a pattern + if ( i.match( /^__.*__$/) ) continue; + var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" ) + .replace( /\n/, "\\n" ); + patterns.push( i.length == 1 ? l : "(?:" + l + ")" ); + } + + patterns = patterns.join("|"); + d.__patterns__ = patterns; + //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); + } + }; +}; + +Markdown.DialectHelpers = {}; +Markdown.DialectHelpers.inline_until_char = function( text, want ) { + var consumed = 0, + nodes = []; + + while ( true ) { + if ( text[ consumed ] == want ) { + // Found the character we were looking for + consumed++; + return [ consumed, nodes ]; + } + + if ( consumed >= text.length ) { + // No closing char found. Abort. + return null; + } + + var res = this.dialect.inline.__oneElement__.call(this, text.substr( consumed ) ); + consumed += res[ 0 ]; + // Add any returned nodes. + nodes.push.apply( nodes, res.slice( 1 ) ); + } +} + +// 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.processMetaHash = function processMetaHash( 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; +} + +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 = this.dialect.processMetaHash( m[ 2 ] ); + + var hash; + + // 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 + 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+([\s\S]+)$/, + list = [ "dl" ], + i; + + // 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 ( i = 0; i < terms.length; ++i ) { + list.push( [ "dt", terms[ i ] ] ); + } + + for ( 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 = this.dialect.processMetaHash( 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 ); + +var isArray = Array.isArray || function(obj) { + return Object.prototype.toString.call(obj) == '[object Array]'; +}; + +var forEach; +// Don't mess with Array.prototype. Its not friendly +if ( Array.prototype.forEach ) { + forEach = function( arr, cb, thisp ) { + return arr.forEach( cb, thisp ); + }; +} +else { + forEach = function(arr, cb, thisp) { + for (var i = 0; i < arr.length; i++) { + cb.call(thisp || arr, arr[i], i, arr); + } + } +} + +function extract_attr( jsonml ) { + return isArray(jsonml) + && jsonml.length > 1 + && typeof jsonml[ 1 ] === "object" + && !( isArray(jsonml[ 1 ]) ) + ? jsonml[ 1 ] + : undefined; +} + + + +/** + * 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 escapeHTML( text ) { + return text.replace( /&/g, "&" ) + .replace( //g, ">" ) + .replace( /"/g, """ ) + .replace( /'/g, "'" ); +} + +function render_tree( jsonml ) { + // basic case + if ( typeof jsonml === "string" ) { + return escapeHTML( jsonml ); + } + + 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 + '="' + escapeHTML( attributes[ a ] ) + '"'; + } + + // be careful about adding whitespace here for inline elements + if ( tag == "img" || tag == "br" || tag == "hr" ) { + return "<"+ tag + tag_attrs + "/>"; + } + else { + return "<"+ tag + tag_attrs + ">" + content.join( "" ) + ""; + } +} + +function convert_tree_to_html( tree, references, options ) { + var i; + options = options || {}; + + // shallow clone + var jsonml = tree.slice( 0 ); + + if (typeof options.preprocessTreeNode === "function") { + jsonml = options.preprocessTreeNode(jsonml, references); + } + + // Clone attributes if they exist + var attrs = extract_attr( jsonml ); + if ( attrs ) { + jsonml[ 1 ] = {}; + for ( 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"; + 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 = Object.prototype.hasOwnProperty.call(references, attrs.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; + case "img_ref": + jsonml[ 0 ] = "img"; + + // grab this ref and clean up the attribute node + var ref = Object.prototype.hasOwnProperty.call(references, attrs.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.src = 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 + 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, options ); + } + + 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/data/scripts/jsdoc/node_modules/markdown/package.json b/data/scripts/jsdoc/node_modules/markdown/package.json new file mode 100644 index 00000000..0db4dd0a --- /dev/null +++ b/data/scripts/jsdoc/node_modules/markdown/package.json @@ -0,0 +1,68 @@ +{ + "name": "markdown", + "version": "0.4.0", + "description": "A sensible Markdown parser for javascript", + "keywords": [ + "markdown", + "text processing", + "ast" + ], + "maintainers": [ + { + "name": "Dominic Baggott", + "email": "dominic.baggott@gmail.com", + "url": "http://evilstreak.co.uk" + }, + { + "name": "Ash Berlin", + "email": "ash_markdownjs@firemirror.com", + "url": "http://ashberlin.com" + } + ], + "contributors": [ + { + "name": "Dominic Baggott", + "email": "dominic.baggott@gmail.com", + "url": "http://evilstreak.co.uk" + }, + { + "name": "Ash Berlin", + "email": "ash_markdownjs@firemirror.com", + "url": "http://ashberlin.com" + } + ], + "bugs": { + "url": "http://github.com/evilstreak/markdown-js/issues" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/mit-license.php" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/evilstreak/markdown-js.git" + }, + "main": "./lib/index.js", + "bin": { + "md2html": "./bin/md2html.js" + }, + "dependencies": { + "nopt": "1" + }, + "devDependencies": { + "tap": "0" + }, + "scripts": { + "test": "tap test/*.t.js" + }, + "readme": "markdown-js\n===========\n\nYet another markdown parser, this time for JavaScript. There's a few\noptions that precede this project but they all treat markdown to HTML\nconversion as a single step process. You pass markdown in and get HTML\nout, end of story. We had some pretty particular views on how the\nprocess should actually look, which include:\n\n * producing well-formed HTML. This means that em and strong nesting is\n important, as is the ability to output as both HTML and XHTML\n\n * having an intermediate representation to allow processing of parsed\n data (we in fact have two, both [JsonML]: a markdown tree and an\n HTML tree)\n\n * being easily extensible to add new dialects without having to\n rewrite the entire parsing mechanics\n\n * having a good test suite. The only test suites we could find tested\n massive blocks of input, and passing depended on outputting the HTML\n with exactly the same whitespace as the original implementation\n\n[JsonML]: http://jsonml.org/ \"JSON Markup Language\"\n\n## Installation\n\nJust the `markdown` library:\n\n npm install markdown\n\nAlso install `md2html` to `/usr/local/bin` (or wherever)\n\n npm install -g markdown\n\n## Usage\n\nThe simple way to use it with CommonJS is:\n\n var input = \"# Heading\\n\\nParagraph\";\n var output = require( \"markdown\" ).markdown.toHTML( input );\n print( output );\n\nIf you want more control check out the documentation in\n[lib/markdown.js] which details all the methods and parameters\navailable (including examples!). One day we'll get the docs generated\nand hosted somewhere for nicer browsing.\n\nIt also works in a browser; here is a complete example:\n\n \n \n \n \n
    \n \n \n \n \n\n### md2html\n\n md2html /path/to/doc.md > /path/to/doc.html\n\n[lib/markdown.js]: http://github.com/evilstreak/markdown-js/blob/master/lib/markdown.js\n\n## Intermediate Representation\n\nInternally the process to convert a chunk of markdown into a chunk of\nHTML has three steps:\n\n 1. Parse the markdown into a JsonML tree. Any references found in the\n parsing are stored in the attribute hash of the root node under the\n key `references`.\n\n 2. Convert the markdown tree into an HTML tree. Rename any nodes that\n need it (`bulletlist` to `ul` for example) and lookup any references\n used by links or images. Remove the references attribute once done.\n\n 3. Stringify the HTML tree being careful not to wreck whitespace where\n whitespace is important (surrounding inline elements for example).\n\nEach step of this process can be called individually if you need to do\nsome processing or modification of the data at an intermediate stage.\nFor example, you may want to grab a list of all URLs linked to in the\ndocument before rendering it to HTML which you could do by recursing\nthrough the HTML tree looking for `a` nodes.\n\n## Running tests\n\nTo run the tests under node you will need tap installed (it's listed as a\ndevDependencies so `npm install` from the checkout should be enough), then do\n\n $ ./node_modules/.bin/tap test/*.t.js\n\n## Contributing\n\nDo the usual github fork and pull request dance. Add yourself to the\ncontributors section of package.json too if you want to.\n\n## License\n\nReleased under the MIT license.\n", + "readmeFilename": "README.markdown", + "_id": "markdown@0.4.0", + "dist": { + "shasum": "6a5d7cb751c1d1e6adc374d40e9d358474b59a8e" + }, + "_resolved": "git://github.com/jsdoc3/markdown-js.git#fd27f4c979f3f71e82e1fe76ffe6415980b31f00", + "_from": "git://github.com/jsdoc3/markdown-js.git" +} diff --git a/data/scripts/jsdoc/node_modules/marked/LICENSE b/data/scripts/jsdoc/node_modules/marked/LICENSE new file mode 100644 index 00000000..11a61898 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/marked/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2013, Christopher Jeffrey (https://github.com/chjj/) + +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/data/scripts/jsdoc/node_modules/marked/index.js b/data/scripts/jsdoc/node_modules/marked/index.js new file mode 100644 index 00000000..a12f9056 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/marked/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/marked'); diff --git a/data/scripts/jsdoc/node_modules/marked/lib/marked.js b/data/scripts/jsdoc/node_modules/marked/lib/marked.js new file mode 100644 index 00000000..1b76ab03 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/marked/lib/marked.js @@ -0,0 +1,1076 @@ +/** + * marked - a markdown parser + * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed) + * https://github.com/chjj/marked + */ + +;(function() { + +/** + * Block-Level Grammar + */ + +var block = { + newline: /^\n+/, + code: /^( {4}[^\n]+\n*)+/, + fences: noop, + hr: /^( *[-*_]){3,} *(?:\n+|$)/, + heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, + nptable: noop, + lheading: /^([^\n]+)\n *(=|-){3,} *\n*/, + blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/, + list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, + html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/, + def: /^ *\[([^\]]+)\]: *]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, + table: noop, + paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, + text: /^[^\n]+/ +}; + +block.bullet = /(?:[*+-]|\d+\.)/; +block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; +block.item = replace(block.item, 'gm') + (/bull/g, block.bullet) + (); + +block.list = replace(block.list) + (/bull/g, block.bullet) + ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/) + (); + +block._tag = '(?!(?:' + + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code' + + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo' + + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b'; + +block.html = replace(block.html) + ('comment', //) + ('closed', /<(tag)[\s\S]+?<\/\1>/) + ('closing', /])*?>/) + (/tag/g, block._tag) + (); + +block.paragraph = replace(block.paragraph) + ('hr', block.hr) + ('heading', block.heading) + ('lheading', block.lheading) + ('blockquote', block.blockquote) + ('tag', '<' + block._tag) + ('def', block.def) + (); + +/** + * Normal Block Grammar + */ + +block.normal = merge({}, block); + +/** + * GFM Block Grammar + */ + +block.gfm = merge({}, block.normal, { + fences: /^ *(`{3,}|~{3,}) *(\w+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/, + paragraph: /^/ +}); + +block.gfm.paragraph = replace(block.paragraph) + ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|') + (); + +/** + * GFM + Tables Block Grammar + */ + +block.tables = merge({}, block.gfm, { + nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, + table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/ +}); + +/** + * Block Lexer + */ + +function Lexer(options) { + this.tokens = []; + this.tokens.links = {}; + this.options = options || marked.defaults; + this.rules = block.normal; + + if (this.options.gfm) { + if (this.options.tables) { + this.rules = block.tables; + } else { + this.rules = block.gfm; + } + } +} + +/** + * Expose Block Rules + */ + +Lexer.rules = block; + +/** + * Static Lex Method + */ + +Lexer.lex = function(src, options) { + var lexer = new Lexer(options); + return lexer.lex(src); +}; + +/** + * Preprocessing + */ + +Lexer.prototype.lex = function(src) { + src = src + .replace(/\r\n|\r/g, '\n') + .replace(/\t/g, ' ') + .replace(/\u00a0/g, ' ') + .replace(/\u2424/g, '\n'); + + return this.token(src, true); +}; + +/** + * Lexing + */ + +Lexer.prototype.token = function(src, top) { + var src = src.replace(/^ +$/gm, '') + , next + , loose + , cap + , bull + , b + , item + , space + , i + , l; + + while (src) { + // newline + if (cap = this.rules.newline.exec(src)) { + src = src.substring(cap[0].length); + if (cap[0].length > 1) { + this.tokens.push({ + type: 'space' + }); + } + } + + // code + if (cap = this.rules.code.exec(src)) { + src = src.substring(cap[0].length); + cap = cap[0].replace(/^ {4}/gm, ''); + this.tokens.push({ + type: 'code', + text: !this.options.pedantic + ? cap.replace(/\n+$/, '') + : cap + }); + continue; + } + + // fences (gfm) + if (cap = this.rules.fences.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'code', + lang: cap[2], + text: cap[3] + }); + continue; + } + + // heading + if (cap = this.rules.heading.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'heading', + depth: cap[1].length, + text: cap[2] + }); + continue; + } + + // table no leading pipe (gfm) + if (top && (cap = this.rules.nptable.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: 'table', + header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3].replace(/\n$/, '').split('\n') + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i].split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + + // lheading + if (cap = this.rules.lheading.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'heading', + depth: cap[2] === '=' ? 1 : 2, + text: cap[1] + }); + continue; + } + + // hr + if (cap = this.rules.hr.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'hr' + }); + continue; + } + + // blockquote + if (cap = this.rules.blockquote.exec(src)) { + src = src.substring(cap[0].length); + + this.tokens.push({ + type: 'blockquote_start' + }); + + cap = cap[0].replace(/^ *> ?/gm, ''); + + // Pass `top` to keep the current + // "toplevel" state. This is exactly + // how markdown.pl works. + this.token(cap, top); + + this.tokens.push({ + type: 'blockquote_end' + }); + + continue; + } + + // list + if (cap = this.rules.list.exec(src)) { + src = src.substring(cap[0].length); + + this.tokens.push({ + type: 'list_start', + ordered: isFinite(cap[2]) + }); + + // Get each top-level item. + cap = cap[0].match(this.rules.item); + + // Get bullet. + if (this.options.smartLists) { + bull = block.bullet.exec(cap[0])[0]; + } + + next = false; + l = cap.length; + i = 0; + + for (; i < l; i++) { + item = cap[i]; + + // Remove the list item's bullet + // so it is seen as the next token. + space = item.length; + item = item.replace(/^ *([*+-]|\d+\.) +/, ''); + + // Outdent whatever the + // list item contains. Hacky. + if (~item.indexOf('\n ')) { + space -= item.length; + item = !this.options.pedantic + ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') + : item.replace(/^ {1,4}/gm, ''); + } + + // Determine whether the next list item belongs here. + // Backpedal if it does not belong in this list. + if (this.options.smartLists && i !== l - 1) { + b = block.bullet.exec(cap[i+1])[0]; + if (bull !== b && !(bull[1] === '.' && b[1] === '.')) { + src = cap.slice(i + 1).join('\n') + src; + i = l - 1; + } + } + + // Determine whether item is loose or not. + // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ + // for discount behavior. + loose = next || /\n\n(?!\s*$)/.test(item); + if (i !== l - 1) { + next = item[item.length-1] === '\n'; + if (!loose) loose = next; + } + + this.tokens.push({ + type: loose + ? 'loose_item_start' + : 'list_item_start' + }); + + // Recurse. + this.token(item, false); + + this.tokens.push({ + type: 'list_item_end' + }); + } + + this.tokens.push({ + type: 'list_end' + }); + + continue; + } + + // html + if (cap = this.rules.html.exec(src)) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: this.options.sanitize + ? 'paragraph' + : 'html', + pre: cap[1] === 'pre', + text: cap[0] + }); + continue; + } + + // def + if (top && (cap = this.rules.def.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.links[cap[1].toLowerCase()] = { + href: cap[2], + title: cap[3] + }; + continue; + } + + // table (gfm) + if (top && (cap = this.rules.table.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: 'table', + header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i] + .replace(/^ *\| *| *\| *$/g, '') + .split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + + // top-level paragraph + if (top && (cap = this.rules.paragraph.exec(src))) { + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'paragraph', + text: cap[1][cap[1].length-1] === '\n' + ? cap[1].slice(0, -1) + : cap[1] + }); + continue; + } + + // text + if (cap = this.rules.text.exec(src)) { + // Top-level should never reach here. + src = src.substring(cap[0].length); + this.tokens.push({ + type: 'text', + text: cap[0] + }); + continue; + } + + if (src) { + throw new + Error('Infinite loop on byte: ' + src.charCodeAt(0)); + } + } + + return this.tokens; +}; + +/** + * Inline-Level Grammar + */ + +var inline = { + escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, + autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, + url: noop, + tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, + link: /^!?\[(inside)\]\(href\)/, + reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, + nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, + strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, + em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, + code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, + br: /^ {2,}\n(?!\s*$)/, + del: noop, + text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/; + +inline.link = replace(inline.link) + ('inside', inline._inside) + ('href', inline._href) + (); + +inline.reflink = replace(inline.reflink) + ('inside', inline._inside) + (); + +/** + * Normal Inline Grammar + */ + +inline.normal = merge({}, inline); + +/** + * Pedantic Inline Grammar + */ + +inline.pedantic = merge({}, inline.normal, { + strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, + em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/ +}); + +/** + * GFM Inline Grammar + */ + +inline.gfm = merge({}, inline.normal, { + escape: replace(inline.escape)('])', '~|])')(), + url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/, + del: /^~~(?=\S)([\s\S]*?\S)~~/, + text: replace(inline.text) + (']|', '~]|') + ('|', '|https?://|') + () +}); + +/** + * GFM + Line Breaks Inline Grammar + */ + +inline.breaks = merge({}, inline.gfm, { + br: replace(inline.br)('{2,}', '*')(), + text: replace(inline.gfm.text)('{2,}', '*')() +}); + +/** + * Inline Lexer & Compiler + */ + +function InlineLexer(links, options) { + this.options = options || marked.defaults; + this.links = links; + this.rules = inline.normal; + + if (!this.links) { + throw new + Error('Tokens array requires a `links` property.'); + } + + if (this.options.gfm) { + if (this.options.breaks) { + this.rules = inline.breaks; + } else { + this.rules = inline.gfm; + } + } else if (this.options.pedantic) { + this.rules = inline.pedantic; + } +} + +/** + * Expose Inline Rules + */ + +InlineLexer.rules = inline; + +/** + * Static Lexing/Compiling Method + */ + +InlineLexer.output = function(src, links, options) { + var inline = new InlineLexer(links, options); + return inline.output(src); +}; + +/** + * Lexing/Compiling + */ + +InlineLexer.prototype.output = function(src) { + var out = '' + , link + , text + , href + , cap; + + while (src) { + // escape + if (cap = this.rules.escape.exec(src)) { + src = src.substring(cap[0].length); + out += cap[1]; + continue; + } + + // autolink + if (cap = this.rules.autolink.exec(src)) { + src = src.substring(cap[0].length); + if (cap[2] === '@') { + text = cap[1][6] === ':' + ? this.mangle(cap[1].substring(7)) + : this.mangle(cap[1]); + href = this.mangle('mailto:') + text; + } else { + text = escape(cap[1]); + href = text; + } + out += '
    ' + + text + + ''; + continue; + } + + // url (gfm) + if (cap = this.rules.url.exec(src)) { + src = src.substring(cap[0].length); + text = escape(cap[1]); + href = text; + out += '' + + text + + ''; + continue; + } + + // tag + if (cap = this.rules.tag.exec(src)) { + src = src.substring(cap[0].length); + out += this.options.sanitize + ? escape(cap[0]) + : cap[0]; + continue; + } + + // link + if (cap = this.rules.link.exec(src)) { + src = src.substring(cap[0].length); + out += this.outputLink(cap, { + href: cap[2], + title: cap[3] + }); + continue; + } + + // reflink, nolink + if ((cap = this.rules.reflink.exec(src)) + || (cap = this.rules.nolink.exec(src))) { + src = src.substring(cap[0].length); + link = (cap[2] || cap[1]).replace(/\s+/g, ' '); + link = this.links[link.toLowerCase()]; + if (!link || !link.href) { + out += cap[0][0]; + src = cap[0].substring(1) + src; + continue; + } + out += this.outputLink(cap, link); + continue; + } + + // strong + if (cap = this.rules.strong.exec(src)) { + src = src.substring(cap[0].length); + out += '' + + this.output(cap[2] || cap[1]) + + ''; + continue; + } + + // em + if (cap = this.rules.em.exec(src)) { + src = src.substring(cap[0].length); + out += '' + + this.output(cap[2] || cap[1]) + + ''; + continue; + } + + // code + if (cap = this.rules.code.exec(src)) { + src = src.substring(cap[0].length); + out += '' + + escape(cap[2], true) + + ''; + continue; + } + + // br + if (cap = this.rules.br.exec(src)) { + src = src.substring(cap[0].length); + out += '
    '; + continue; + } + + // del (gfm) + if (cap = this.rules.del.exec(src)) { + src = src.substring(cap[0].length); + out += '' + + this.output(cap[1]) + + ''; + continue; + } + + // text + if (cap = this.rules.text.exec(src)) { + src = src.substring(cap[0].length); + out += escape(cap[0]); + continue; + } + + if (src) { + throw new + Error('Infinite loop on byte: ' + src.charCodeAt(0)); + } + } + + return out; +}; + +/** + * Compile Link + */ + +InlineLexer.prototype.outputLink = function(cap, link) { + if (cap[0][0] !== '!') { + return '' + + this.output(cap[1]) + + ''; + } else { + return ''
+      + escape(cap[1])
+      + ''; + } +}; + +/** + * Mangle Links + */ + +InlineLexer.prototype.mangle = function(text) { + var out = '' + , l = text.length + , i = 0 + , ch; + + for (; i < l; i++) { + ch = text.charCodeAt(i); + if (Math.random() > 0.5) { + ch = 'x' + ch.toString(16); + } + out += '&#' + ch + ';'; + } + + return out; +}; + +/** + * Parsing & Compiling + */ + +function Parser(options) { + this.tokens = []; + this.token = null; + this.options = options || marked.defaults; +} + +/** + * Static Parse Method + */ + +Parser.parse = function(src, options) { + var parser = new Parser(options); + return parser.parse(src); +}; + +/** + * Parse Loop + */ + +Parser.prototype.parse = function(src) { + this.inline = new InlineLexer(src.links, this.options); + this.tokens = src.reverse(); + + var out = ''; + while (this.next()) { + out += this.tok(); + } + + return out; +}; + +/** + * Next Token + */ + +Parser.prototype.next = function() { + return this.token = this.tokens.pop(); +}; + +/** + * Preview Next Token + */ + +Parser.prototype.peek = function() { + return this.tokens[this.tokens.length-1] || 0; +}; + +/** + * Parse Text Tokens + */ + +Parser.prototype.parseText = function() { + var body = this.token.text; + + while (this.peek().type === 'text') { + body += '\n' + this.next().text; + } + + return this.inline.output(body); +}; + +/** + * Parse Current Token + */ + +Parser.prototype.tok = function() { + switch (this.token.type) { + case 'space': { + return ''; + } + case 'hr': { + return '
    \n'; + } + case 'heading': { + return '' + + this.inline.output(this.token.text) + + '\n'; + } + case 'code': { + if (this.options.highlight) { + var code = this.options.highlight(this.token.text, this.token.lang); + if (code != null && code !== this.token.text) { + this.token.escaped = true; + this.token.text = code; + } + } + + if (!this.token.escaped) { + this.token.text = escape(this.token.text, true); + } + + return '
    '
    +        + this.token.text
    +        + '
    \n'; + } + case 'table': { + var body = '' + , heading + , i + , row + , cell + , j; + + // header + body += '\n\n'; + for (i = 0; i < this.token.header.length; i++) { + heading = this.inline.output(this.token.header[i]); + body += this.token.align[i] + ? '' + heading + '\n' + : '' + heading + '\n'; + } + body += '\n\n'; + + // body + body += '\n' + for (i = 0; i < this.token.cells.length; i++) { + row = this.token.cells[i]; + body += '\n'; + for (j = 0; j < row.length; j++) { + cell = this.inline.output(row[j]); + body += this.token.align[j] + ? '' + cell + '\n' + : '' + cell + '\n'; + } + body += '\n'; + } + body += '\n'; + + return '\n' + + body + + '
    \n'; + } + case 'blockquote_start': { + var body = ''; + + while (this.next().type !== 'blockquote_end') { + body += this.tok(); + } + + return '
    \n' + + body + + '
    \n'; + } + case 'list_start': { + var type = this.token.ordered ? 'ol' : 'ul' + , body = ''; + + while (this.next().type !== 'list_end') { + body += this.tok(); + } + + return '<' + + type + + '>\n' + + body + + '\n'; + } + case 'list_item_start': { + var body = ''; + + while (this.next().type !== 'list_item_end') { + body += this.token.type === 'text' + ? this.parseText() + : this.tok(); + } + + return '
  • ' + + body + + '
  • \n'; + } + case 'loose_item_start': { + var body = ''; + + while (this.next().type !== 'list_item_end') { + body += this.tok(); + } + + return '
  • ' + + body + + '
  • \n'; + } + case 'html': { + return !this.token.pre && !this.options.pedantic + ? this.inline.output(this.token.text) + : this.token.text; + } + case 'paragraph': { + return '

    ' + + this.inline.output(this.token.text) + + '

    \n'; + } + case 'text': { + return '

    ' + + this.parseText() + + '

    \n'; + } + } +}; + +/** + * Helpers + */ + +function escape(html, encode) { + return html + .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function replace(regex, opt) { + regex = regex.source; + opt = opt || ''; + return function self(name, val) { + if (!name) return new RegExp(regex, opt); + val = val.source || val; + val = val.replace(/(^|[^\[])\^/g, '$1'); + regex = regex.replace(name, val); + return self; + }; +} + +function noop() {} +noop.exec = noop; + +function merge(obj) { + var i = 1 + , target + , key; + + for (; i < arguments.length; i++) { + target = arguments[i]; + for (key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + obj[key] = target[key]; + } + } + } + + return obj; +} + +/** + * Marked + */ + +function marked(src, opt) { + try { + if (opt) opt = merge({}, marked.defaults, opt); + return Parser.parse(Lexer.lex(src, opt), opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/chjj/marked.'; + if ((opt || marked.defaults).silent) { + return '

    An error occured:

    '
    +        + escape(e.message + '', true)
    +        + '
    '; + } + throw e; + } +} + +/** + * Options + */ + +marked.options = +marked.setOptions = function(opt) { + merge(marked.defaults, opt); + return marked; +}; + +marked.defaults = { + gfm: true, + tables: true, + breaks: false, + pedantic: false, + sanitize: false, + smartLists: false, + silent: false, + highlight: null, + langPrefix: 'lang-' +}; + +/** + * Expose + */ + +marked.Parser = Parser; +marked.parser = Parser.parse; + +marked.Lexer = Lexer; +marked.lexer = Lexer.lex; + +marked.InlineLexer = InlineLexer; +marked.inlineLexer = InlineLexer.output; + +marked.parse = marked; + +if (typeof exports === 'object') { + module.exports = marked; +} else if (typeof define === 'function' && define.amd) { + define(function() { return marked; }); +} else { + this.marked = marked; +} + +}).call(function() { + return this || (typeof window !== 'undefined' ? window : global); +}()); diff --git a/data/scripts/jsdoc/node_modules/marked/package.json b/data/scripts/jsdoc/node_modules/marked/package.json new file mode 100644 index 00000000..fce5b5f9 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/marked/package.json @@ -0,0 +1,46 @@ +{ + "name": "marked", + "description": "A markdown parser built for speed", + "author": { + "name": "Christopher Jeffrey" + }, + "version": "0.2.8", + "main": "./lib/marked.js", + "bin": { + "marked": "./bin/marked" + }, + "man": [ + "./man/marked.1" + ], + "preferGlobal": true, + "repository": { + "type": "git", + "url": "git://github.com/chjj/marked.git" + }, + "homepage": "https://github.com/chjj/marked", + "bugs": { + "url": "http://github.com/chjj/marked/issues" + }, + "keywords": [ + "markdown", + "markup", + "html" + ], + "tags": [ + "markdown", + "markup", + "html" + ], + "scripts": { + "test": "node test", + "bench": "node test --bench" + }, + "readme": "# marked\n\nA full-featured markdown parser and compiler, written in javascript.\nBuilt for speed.\n\n## Benchmarks\n\nnode v0.4.x\n\n``` bash\n$ node test --bench\nmarked completed in 12071ms.\nshowdown (reuse converter) completed in 27387ms.\nshowdown (new converter) completed in 75617ms.\nmarkdown-js completed in 70069ms.\n```\n\nnode v0.6.x\n\n``` bash\n$ node test --bench\nmarked completed in 6448ms.\nmarked (gfm) completed in 7357ms.\nmarked (pedantic) completed in 6092ms.\ndiscount completed in 7314ms.\nshowdown (reuse converter) completed in 16018ms.\nshowdown (new converter) completed in 18234ms.\nmarkdown-js completed in 24270ms.\n```\n\n__Marked is now faster than Discount, which is written in C.__\n\nFor those feeling skeptical: These benchmarks run the entire markdown test suite\n1000 times. The test suite tests every feature. It doesn't cater to specific\naspects.\n\n## Install\n\n``` bash\n$ npm install marked\n```\n\n## Another Javascript Markdown Parser\n\nThe point of marked was to create a markdown compiler where it was possible to\nfrequently parse huge chunks of markdown without having to worry about\ncaching the compiled output somehow...or blocking for an unnecesarily long time.\n\nmarked is very concise and still implements all markdown features. It is also\nnow fully compatible with the client-side.\n\nmarked more or less passes the official markdown test suite in its\nentirety. This is important because a surprising number of markdown compilers\ncannot pass more than a few tests. It was very difficult to get marked as\ncompliant as it is. It could have cut corners in several areas for the sake\nof performance, but did not in order to be exactly what you expect in terms\nof a markdown rendering. In fact, this is why marked could be considered at a\ndisadvantage in the benchmarks above.\n\nAlong with implementing every markdown feature, marked also implements\n[GFM features](http://github.github.com/github-flavored-markdown/).\n\n## Options\n\nmarked has a few different switches which change behavior.\n\n- __pedantic__: Conform to obscure parts of `markdown.pl` as much as possible.\n Don't fix any of the original markdown bugs or poor behavior.\n- __gfm__: Enable github flavored markdown (enabled by default).\n- __sanitize__: Sanitize the output. Ignore any HTML that has been input.\n- __highlight__: A callback to highlight code blocks.\n- __tables__: Enable GFM tables. This is enabled by default. (Requires the\n `gfm` option in order to be enabled).\n- __breaks__: Enable GFM line breaks. Disabled by default.\n- __smartLists__: Use smarter list behavior than the original markdown.\n Disabled by default. May eventually be default with the old behavior\n moved into `pedantic`.\n- __langPrefix__: Set the prefix for code block classes. Defaults to `lang-`.\n\n## Usage\n\n``` js\n// Set default options\nmarked.setOptions({\n gfm: true,\n tables: true,\n breaks: false,\n pedantic: false,\n sanitize: true,\n smartLists: true,\n langPrefix: 'language-',\n highlight: function(code, lang) {\n if (lang === 'js') {\n return highlighter.javascript(code);\n }\n return code;\n }\n});\nconsole.log(marked('i am using __markdown__.'));\n```\n\nYou also have direct access to the lexer and parser if you so desire.\n\n``` js\nvar tokens = marked.lexer(text, options);\nconsole.log(marked.parser(tokens));\n```\n\n``` js\nvar lexer = new marked.Lexer(options);\nvar tokens = lexer.lex(text);\nconsole.log(tokens);\nconsole.log(lexer.rules);\n```\n\n``` bash\n$ node\n> require('marked').lexer('> i am using marked.')\n[ { type: 'blockquote_start' },\n { type: 'paragraph',\n text: 'i am using marked.' },\n { type: 'blockquote_end' },\n links: {} ]\n```\n\n## CLI\n\n``` bash\n$ marked -o hello.html\nhello world\n^D\n$ cat hello.html\n

    hello world

    \n```\n\n## License\n\nCopyright (c) 2011-2013, Christopher Jeffrey. (MIT License)\n\nSee LICENSE for more info.\n", + "readmeFilename": "README.md", + "_id": "marked@0.2.8", + "dist": { + "shasum": "740103e3cd0e98050c99cd1cc2b8d823bf640aee" + }, + "_from": "marked@0.2.8", + "_resolved": "https://registry.npmjs.org/marked/-/marked-0.2.8.tgz" +} diff --git a/data/scripts/jsdoc/node_modules/taffydb/README.md b/data/scripts/jsdoc/node_modules/taffydb/README.md new file mode 100644 index 00000000..52d14a3a --- /dev/null +++ b/data/scripts/jsdoc/node_modules/taffydb/README.md @@ -0,0 +1 @@ +See [http://taffydb.com](http://taffydb.com). \ No newline at end of file diff --git a/data/scripts/jsdoc/node_modules/taffydb/package.json b/data/scripts/jsdoc/node_modules/taffydb/package.json new file mode 100644 index 00000000..3cb561cf --- /dev/null +++ b/data/scripts/jsdoc/node_modules/taffydb/package.json @@ -0,0 +1,17 @@ +{ + "name": "taffydb", + "version": "2.6.2", + "description": "An open-source library that brings database features into your JavaScript applications.", + "main": "taffy.js", + "repository": { + "type": "git", + "url": "git://github.com/hegemonic/taffydb.git" + }, + "license": "BSD", + "readme": "See [http://taffydb.com](http://taffydb.com).", + "_id": "taffydb@2.6.2", + "dist": { + "shasum": "4ffeb4b49fd82c123fb5804606b0928f3e9e30b1" + }, + "_from": "taffydb@git://github.com/hegemonic/taffydb.git" +} diff --git a/data/scripts/jsdoc/node_modules/taffydb/taffy-test.html b/data/scripts/jsdoc/node_modules/taffydb/taffy-test.html new file mode 100644 index 00000000..c4df78b4 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/taffydb/taffy-test.html @@ -0,0 +1,84 @@ + + + + + taffy test + + + + + + +
    +Please open your javascript console to see test results +
    + + + + diff --git a/data/scripts/jsdoc/node_modules/taffydb/taffy.js b/data/scripts/jsdoc/node_modules/taffydb/taffy.js new file mode 100644 index 00000000..b7ad88cd --- /dev/null +++ b/data/scripts/jsdoc/node_modules/taffydb/taffy.js @@ -0,0 +1,1973 @@ +/* + + Software License Agreement (BSD License) + http://taffydb.com + Copyright (c) + All rights reserved. + + + 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. + + */ + +/*jslint browser : true, continue : true, + devel : true, indent : 2, maxerr : 500, + newcap : true, nomen : true, plusplus : true, + regexp : true, sloppy : true, vars : false, + white : true +*/ + +// BUILD 193d48d, modified by mmikowski to pass jslint + +// Setup TAFFY name space to return an object with methods +var TAFFY, exports, T; +(function () { + 'use strict'; + var + typeList, makeTest, idx, typeKey, + version, TC, idpad, cmax, + API, protectJSON, each, eachin, + isIndexable, returnFilter, runFilters, + numcharsplit, orderByCol, run + ; + + + if ( ! TAFFY ){ + // TC = Counter for Taffy DBs on page, used for unique IDs + // cmax = size of charnumarray conversion cache + // idpad = zeros to pad record IDs with + version = '2.6.2'; // proposed mmikowski 2012-08-06 + TC = 1; + idpad = '000000'; + cmax = 1000; + API = {}; + + protectJSON = function ( t ) { + // **************************************** + // * + // * Takes: a variable + // * Returns: the variable if object/array or the parsed variable if JSON + // * + // **************************************** + if ( TAFFY.isArray( t ) || TAFFY.isObject( t ) ){ + return t; + } + else { + return JSON.parse( t ); + } + }; + + each = function ( a, fun, u ) { + var r, i, x, y; + // **************************************** + // * + // * Takes: + // * a = an object/value or an array of objects/values + // * f = a function + // * u = optional flag to describe how to handle undefined values + // in array of values. True: pass them to the functions, + // False: skip. Default False; + // * Purpose: Used to loop over arrays + // * + // **************************************** + if ( a && ((T.isArray( a ) && a.length === 1) || (!T.isArray( a ))) ){ + fun( (T.isArray( a )) ? a[0] : a, 0 ); + } + else { + for ( r, i, x = 0, a = (T.isArray( a )) ? a : [a], y = a.length; + x < y; x++ ) + { + i = a[x]; + if ( !T.isUndefined( i ) || (u || false) ){ + r = fun( i, x ); + if ( r === T.EXIT ){ + break; + } + + } + } + } + }; + + eachin = function ( o, fun ) { + // **************************************** + // * + // * Takes: + // * o = an object + // * f = a function + // * Purpose: Used to loop over objects + // * + // **************************************** + var x = 0, r, i; + + for ( i in o ){ + if ( o.hasOwnProperty( i ) ){ + r = fun( o[i], i, x++ ); + if ( r === T.EXIT ){ + break; + } + } + } + + }; + + API.extend = function ( m, f ) { + // **************************************** + // * + // * Takes: method name, function + // * Purpose: Add a custom method to the API + // * + // **************************************** + API[m] = function () { + return f.apply( this, arguments ); + }; + }; + + isIndexable = function ( f ) { + var i; + // Check to see if record ID + if ( T.isString( f ) && /[t][0-9]*[r][0-9]*/i.test( f ) ){ + return true; + } + // Check to see if record + if ( T.isObject( f ) && f.___id && f.___s ){ + return true; + } + + // Check to see if array of indexes + if ( T.isArray( f ) ){ + i = true; + each( f, function ( r ) { + if ( !isIndexable( r ) ){ + i = false; + + return TAFFY.EXIT; + } + }); + return i; + } + + return false; + }; + + runFilters = function ( r, filter ) { + // **************************************** + // * + // * Takes: takes a record and a collection of filters + // * Returns: true if the record matches, false otherwise + // **************************************** + var match = true; + + + each( filter, function ( mf ) { + switch ( T.typeOf( mf ) ){ + case 'function': + // run function + if ( !mf.apply( r ) ){ + match = false; + return TAFFY.EXIT; + } + break; + case 'array': + // loop array and treat like a SQL or + match = (mf.length === 1) ? (runFilters( r, mf[0] )) : + (mf.length === 2) ? (runFilters( r, mf[0] ) || + runFilters( r, mf[1] )) : + (mf.length === 3) ? (runFilters( r, mf[0] ) || + runFilters( r, mf[1] ) || runFilters( r, mf[2] )) : + (mf.length === 4) ? (runFilters( r, mf[0] ) || + runFilters( r, mf[1] ) || runFilters( r, mf[2] ) || + runFilters( r, mf[3] )) : false; + if ( mf.length > 4 ){ + each( mf, function ( f ) { + if ( runFilters( r, f ) ){ + match = true; + } + }); + } + break; + } + }); + + return match; + }; + + returnFilter = function ( f ) { + // **************************************** + // * + // * Takes: filter object + // * Returns: a filter function + // * Purpose: Take a filter object and return a function that can be used to compare + // * a TaffyDB record to see if the record matches a query + // **************************************** + var nf = []; + if ( T.isString( f ) && /[t][0-9]*[r][0-9]*/i.test( f ) ){ + f = { ___id : f }; + } + if ( T.isArray( f ) ){ + // if we are working with an array + + each( f, function ( r ) { + // loop the array and return a filter func for each value + nf.push( returnFilter( r ) ); + }); + // now build a func to loop over the filters and return true if ANY of the filters match + // This handles logical OR expressions + f = function () { + var that = this, match = false; + each( nf, function ( f ) { + if ( runFilters( that, f ) ){ + match = true; + } + }); + return match; + }; + return f; + + } + // if we are dealing with an Object + if ( T.isObject( f ) ){ + if ( T.isObject( f ) && f.___id && f.___s ){ + f = { ___id : f.___id }; + } + + // Loop over each value on the object to prep match type and match value + eachin( f, function ( v, i ) { + + // default match type to IS/Equals + if ( !T.isObject( v ) ){ + v = { + 'is' : v + }; + } + // loop over each value on the value object - if any + eachin( v, function ( mtest, s ) { + // s = match type, e.g. is, hasAll, like, etc + var c = [], looper; + + // function to loop and apply filter + looper = (s === 'hasAll') ? + function ( mtest, func ) { + func( mtest ); + } : each; + + // loop over each test + looper( mtest, function ( mtest ) { + + // su = match success + // f = match false + var su = true, f = false, matchFunc; + + + // push a function onto the filter collection to do the matching + matchFunc = function () { + + // get the value from the record + var + mvalue = this[i], + eqeq = '==', + bangeq = '!=', + eqeqeq = '===', + lt = '<', + gt = '>', + lteq = '<=', + gteq = '>=', + bangeqeq = '!==', + r + ; + + + if ( (s.indexOf( '!' ) === 0) && s !== bangeq && + s !== bangeqeq ) + { + // if the filter name starts with ! as in '!is' then reverse the match logic and remove the ! + su = false; + s = s.substring( 1, s.length ); + } + // get the match results based on the s/match type + /*jslint eqeq : true */ + r = ( + (s === 'regex') ? (mtest.test( mvalue )) : (s === 'lt' || s === lt) + ? (mvalue < mtest) : (s === 'gt' || s === gt) + ? (mvalue > mtest) : (s === 'lte' || s === lteq) + ? (mvalue <= mtest) : (s === 'gte' || s === gteq) + ? (mvalue >= mtest) : (s === 'left') + ? (mvalue.indexOf( mtest ) === 0) : (s === 'leftnocase') + ? (mvalue.toLowerCase().indexOf( mtest.toLowerCase() ) + === 0) : (s === 'right') + ? (mvalue.substring( (mvalue.length - mtest.length) ) + === mtest) : (s === 'rightnocase') + ? (mvalue.toLowerCase().substring( + (mvalue.length - mtest.length) ) === mtest.toLowerCase()) + : (s === 'like') + ? (mvalue.indexOf( mtest ) >= 0) : (s === 'likenocase') + ? (mvalue.toLowerCase().indexOf(mtest.toLowerCase()) >= 0) + : (s === eqeqeq || s === 'is') + ? (mvalue === mtest) : (s === eqeq) + ? (mvalue == mtest) : (s === bangeqeq) + ? (mvalue !== mtest) : (s === bangeq) + ? (mvalue != mtest) : (s === 'isnocase') + ? (mvalue.toLowerCase + ? mvalue.toLowerCase() === mtest.toLowerCase() + : mvalue === mtest) : (s === 'has') + ? (T.has( mvalue, mtest )) : (s === 'hasall') + ? (T.hasAll( mvalue, mtest )) : ( + s.indexOf( 'is' ) === -1 + && !TAFFY.isNull( mvalue ) + && !TAFFY.isUndefined( mvalue ) + && !TAFFY.isObject( mtest ) + && !TAFFY.isArray( mtest ) + ) + ? (mtest === mvalue[s]) + : (T[s] && T.isFunction( T[s] ) + && s.indexOf( 'is' ) === 0) + ? T[s]( mvalue ) === mtest + : (T[s] && T.isFunction( T[s] )) + ? T[s]( mvalue, mtest ) : (false) + ); + /*jslint eqeq : false */ + r = (r && !su) ? false : (!r && !su) ? true : r; + + return r; + }; + c.push( matchFunc ); + + }); + // if only one filter in the collection push it onto the filter list without the array + if ( c.length === 1 ){ + + nf.push( c[0] ); + } + else { + // else build a function to loop over all the filters and return true only if ALL match + // this is a logical AND + nf.push( function () { + var that = this, match = false; + each( c, function ( f ) { + if ( f.apply( that ) ){ + match = true; + } + }); + return match; + }); + } + }); + }); + // finally return a single function that wraps all the other functions and will run a query + // where all functions have to return true for a record to appear in a query result + f = function () { + var that = this, match = true; + // faster if less than 4 functions + match = (nf.length === 1 && !nf[0].apply( that )) ? false : + (nf.length === 2 && + (!nf[0].apply( that ) || !nf[1].apply( that ))) ? false : + (nf.length === 3 && + (!nf[0].apply( that ) || !nf[1].apply( that ) || + !nf[2].apply( that ))) ? false : + (nf.length === 4 && + (!nf[0].apply( that ) || !nf[1].apply( that ) || + !nf[2].apply( that ) || !nf[3].apply( that ))) ? false + : true; + if ( nf.length > 4 ){ + each( nf, function ( f ) { + if ( !runFilters( that, f ) ){ + match = false; + } + }); + } + return match; + }; + return f; + } + + // if function + if ( T.isFunction( f ) ){ + return f; + } + }; + + orderByCol = function ( ar, o ) { + // **************************************** + // * + // * Takes: takes an array and a sort object + // * Returns: the array sorted + // * Purpose: Accept filters such as "[col], [col2]" or "[col] desc" and sort on those columns + // * + // **************************************** + + var sortFunc = function ( a, b ) { + // function to pass to the native array.sort to sort an array + var r = 0; + + T.each( o, function ( sd ) { + // loop over the sort instructions + // get the column name + var o, col, dir, c, d; + o = sd.split( ' ' ); + col = o[0]; + + // get the direction + dir = (o.length === 1) ? "logical" : o[1]; + + + if ( dir === 'logical' ){ + // if dir is logical than grab the charnum arrays for the two values we are looking at + c = numcharsplit( a[col] ); + d = numcharsplit( b[col] ); + // loop over the charnumarrays until one value is higher than the other + T.each( (c.length <= d.length) ? c : d, function ( x, i ) { + if ( c[i] < d[i] ){ + r = -1; + return TAFFY.EXIT; + } + else if ( c[i] > d[i] ){ + r = 1; + return TAFFY.EXIT; + } + } ); + } + else if ( dir === 'logicaldesc' ){ + // if logicaldesc than grab the charnum arrays for the two values we are looking at + c = numcharsplit( a[col] ); + d = numcharsplit( b[col] ); + // loop over the charnumarrays until one value is lower than the other + T.each( (c.length <= d.length) ? c : d, function ( x, i ) { + if ( c[i] > d[i] ){ + r = -1; + return TAFFY.EXIT; + } + else if ( c[i] < d[i] ){ + r = 1; + return TAFFY.EXIT; + } + } ); + } + else if ( dir === 'asec' && a[col] < b[col] ){ + // if asec - default - check to see which is higher + r = -1; + return T.EXIT; + } + else if ( dir === 'asec' && a[col] > b[col] ){ + // if asec - default - check to see which is higher + r = 1; + return T.EXIT; + } + else if ( dir === 'desc' && a[col] > b[col] ){ + // if desc check to see which is lower + r = -1; + return T.EXIT; + + } + else if ( dir === 'desc' && a[col] < b[col] ){ + // if desc check to see which is lower + r = 1; + return T.EXIT; + + } + // if r is still 0 and we are doing a logical sort than look to see if one array is longer than the other + if ( r === 0 && dir === 'logical' && c.length < d.length ){ + r = -1; + } + else if ( r === 0 && dir === 'logical' && c.length > d.length ){ + r = 1; + } + else if ( r === 0 && dir === 'logicaldesc' && c.length > d.length ){ + r = -1; + } + else if ( r === 0 && dir === 'logicaldesc' && c.length < d.length ){ + r = 1; + } + + if ( r !== 0 ){ + return T.EXIT; + } + + + } ); + return r; + }; + // call the sort function and return the newly sorted array + return (ar && ar.push) ? ar.sort( sortFunc ) : ar; + + + }; + + // **************************************** + // * + // * Takes: a string containing numbers and letters and turn it into an array + // * Returns: return an array of numbers and letters + // * Purpose: Used for logical sorting. String Example: 12ABC results: [12,'ABC'] + // **************************************** + (function () { + // creates a cache for numchar conversions + var cache = {}, cachcounter = 0; + // creates the numcharsplit function + numcharsplit = function ( thing ) { + // if over 1000 items exist in the cache, clear it and start over + if ( cachcounter > cmax ){ + cache = {}; + cachcounter = 0; + } + + // if a cache can be found for a numchar then return its array value + return cache['_' + thing] || (function () { + // otherwise do the conversion + // make sure it is a string and setup so other variables + var nthing = String( thing ), + na = [], + rv = '_', + rt = '', + x, xx, c; + + // loop over the string char by char + for ( x = 0, xx = nthing.length; x < xx; x++ ){ + // take the char at each location + c = nthing.charCodeAt( x ); + // check to see if it is a valid number char and append it to the array. + // if last char was a string push the string to the charnum array + if ( ( c >= 48 && c <= 57 ) || c === 46 ){ + if ( rt !== 'n' ){ + rt = 'n'; + na.push( rv.toLowerCase() ); + rv = ''; + } + rv = rv + nthing.charAt( x ); + } + else { + // check to see if it is a valid string char and append to string + // if last char was a number push the whole number to the charnum array + if ( rt !== 's' ){ + rt = 's'; + na.push( parseFloat( rv ) ); + rv = ''; + } + rv = rv + nthing.charAt( x ); + } + } + // once done, push the last value to the charnum array and remove the first uneeded item + na.push( (rt === 'n') ? parseFloat( rv ) : rv.toLowerCase() ); + na.shift(); + // add to cache + cache['_' + thing] = na; + cachcounter++; + // return charnum array + return na; + }()); + }; + }()); + + // **************************************** + // * + // * Runs a query + // **************************************** + + + run = function () { + this.context( { + results : this.getDBI().query( this.context() ) + }); + + }; + + API.extend( 'filter', function () { + // **************************************** + // * + // * Takes: takes unlimited filter objects as arguments + // * Returns: method collection + // * Purpose: Take filters as objects and cache functions for later lookup when a query is run + // **************************************** + var + nc = TAFFY.mergeObj( this.context(), { run : null } ), + nq = [] + ; + each( nc.q, function ( v ) { + nq.push( v ); + }); + nc.q = nq; + // Hadnle passing of ___ID or a record on lookup. + each( arguments, function ( f ) { + nc.q.push( returnFilter( f ) ); + nc.filterRaw.push( f ); + }); + + return this.getroot( nc ); + }); + + API.extend( 'order', function ( o ) { + // **************************************** + // * + // * Purpose: takes a string and creates an array of order instructions to be used with a query + // **************************************** + + o = o.split( ',' ); + var x = [], nc; + + each( o, function ( r ) { + x.push( r.replace( /^\s*/, '' ).replace( /\s*$/, '' ) ); + }); + + nc = TAFFY.mergeObj( this.context(), {sort : null} ); + nc.order = x; + + return this.getroot( nc ); + }); + + API.extend( 'limit', function ( n ) { + // **************************************** + // * + // * Purpose: takes a limit number to limit the number of rows returned by a query. Will update the results + // * of a query + // **************************************** + var nc = TAFFY.mergeObj( this.context(), {}), + limitedresults + ; + + nc.limit = n; + + if ( nc.run && nc.sort ){ + limitedresults = []; + each( nc.results, function ( i, x ) { + if ( (x + 1) > n ){ + return TAFFY.EXIT; + } + limitedresults.push( i ); + }); + nc.results = limitedresults; + } + + return this.getroot( nc ); + }); + + API.extend( 'start', function ( n ) { + // **************************************** + // * + // * Purpose: takes a limit number to limit the number of rows returned by a query. Will update the results + // * of a query + // **************************************** + var nc = TAFFY.mergeObj( this.context(), {} ), + limitedresults + ; + + nc.start = n; + + if ( nc.run && nc.sort && !nc.limit ){ + limitedresults = []; + each( nc.results, function ( i, x ) { + if ( (x + 1) > n ){ + limitedresults.push( i ); + } + }); + nc.results = limitedresults; + } + else { + nc = TAFFY.mergeObj( this.context(), {run : null, start : n} ); + } + + return this.getroot( nc ); + }); + + API.extend( 'update', function ( arg0, arg1, arg2 ) { + // **************************************** + // * + // * Takes: a object and passes it off DBI update method for all matched records + // **************************************** + var runEvent = true, o = {}, args = arguments, that; + if ( TAFFY.isString( arg0 ) && + (arguments.length === 2 || arguments.length === 3) ) + { + o[arg0] = arg1; + if ( arguments.length === 3 ){ + runEvent = arg2; + } + } + else { + o = arg0; + if ( args.length === 2 ){ + runEvent = arg1; + } + } + + that = this; + run.call( this ); + each( this.context().results, function ( r ) { + var c = o; + if ( TAFFY.isFunction( c ) ){ + c = c.apply( TAFFY.mergeObj( r, {} ) ); + } + else { + if ( T.isFunction( c ) ){ + c = c( TAFFY.mergeObj( r, {} ) ); + } + } + if ( TAFFY.isObject( c ) ){ + that.getDBI().update( r.___id, c, runEvent ); + } + }); + if ( this.context().results.length ){ + this.context( { run : null }); + } + return this; + }); + API.extend( 'remove', function ( runEvent ) { + // **************************************** + // * + // * Purpose: removes records from the DB via the remove and removeCommit DBI methods + // **************************************** + var that = this, c = 0; + run.call( this ); + each( this.context().results, function ( r ) { + that.getDBI().remove( r.___id ); + c++; + }); + if ( this.context().results.length ){ + this.context( { + run : null + }); + that.getDBI().removeCommit( runEvent ); + } + + return c; + }); + + + API.extend( 'count', function () { + // **************************************** + // * + // * Returns: The length of a query result + // **************************************** + run.call( this ); + return this.context().results.length; + }); + + API.extend( 'callback', function ( f, delay ) { + // **************************************** + // * + // * Returns null; + // * Runs a function on return of run.call + // **************************************** + if ( f ){ + var that = this; + setTimeout( function () { + run.call( that ); + f.call( that.getroot( that.context() ) ); + }, delay || 0 ); + } + + + return null; + }); + + API.extend( 'get', function () { + // **************************************** + // * + // * Returns: An array of all matching records + // **************************************** + run.call( this ); + return this.context().results; + }); + + API.extend( 'stringify', function () { + // **************************************** + // * + // * Returns: An JSON string of all matching records + // **************************************** + return JSON.stringify( this.get() ); + }); + API.extend( 'first', function () { + // **************************************** + // * + // * Returns: The first matching record + // **************************************** + run.call( this ); + return this.context().results[0] || false; + }); + API.extend( 'last', function () { + // **************************************** + // * + // * Returns: The last matching record + // **************************************** + run.call( this ); + return this.context().results[this.context().results.length - 1] || + false; + }); + + + API.extend( 'sum', function () { + // **************************************** + // * + // * Takes: column to sum up + // * Returns: Sums the values of a column + // **************************************** + var total = 0, that = this; + run.call( that ); + each( arguments, function ( c ) { + each( that.context().results, function ( r ) { + total = total + r[c]; + }); + }); + return total; + }); + + API.extend( 'min', function ( c ) { + // **************************************** + // * + // * Takes: column to find min + // * Returns: the lowest value + // **************************************** + var lowest = null; + run.call( this ); + each( this.context().results, function ( r ) { + if ( lowest === null || r[c] < lowest ){ + lowest = r[c]; + } + }); + return lowest; + }); + + // Taffy innerJoin Extension (OCD edition) + // ======================================= + // + // How to Use + // ********** + // + // left_table.innerJoin( right_table, condition1 <,... conditionN> ) + // + // A condition can take one of 2 forms: + // + // 1. An ARRAY with 2 or 3 values: + // A column name from the left table, an optional comparison string, + // and column name from the right table. The condition passes if the test + // indicated is true. If the condition string is omitted, '===' is assumed. + // EXAMPLES: [ 'last_used_time', '>=', 'current_use_time' ], [ 'user_id','id' ] + // + // 2. A FUNCTION: + // The function receives a left table row and right table row during the + // cartesian join. If the function returns true for the rows considered, + // the merged row is included in the result set. + // EXAMPLE: function (l,r){ return l.name === r.label; } + // + // Conditions are considered in the order they are presented. Therefore the best + // performance is realized when the least expensive and highest prune-rate + // conditions are placed first, since if they return false Taffy skips any + // further condition tests. + // + // Other notes + // *********** + // + // This code passes jslint with the exception of 2 warnings about + // the '==' and '!=' lines. We can't do anything about that short of + // deleting the lines. + // + // Credits + // ******* + // + // Heavily based upon the work of Ian Toltz. + // Revisions to API by Michael Mikowski. + // Code convention per standards in http://manning.com/mikowski + (function () { + var innerJoinFunction = (function () { + var fnCompareList, fnCombineRow, fnMain; + + fnCompareList = function ( left_row, right_row, arg_list ) { + var data_lt, data_rt, op_code, error; + + if ( arg_list.length === 2 ){ + data_lt = left_row[arg_list[0]]; + op_code = '==='; + data_rt = right_row[arg_list[1]]; + } + else { + data_lt = left_row[arg_list[0]]; + op_code = arg_list[1]; + data_rt = right_row[arg_list[2]]; + } + + /*jslint eqeq : true */ + switch ( op_code ){ + case '===' : + return data_lt === data_rt; + case '!==' : + return data_lt !== data_rt; + case '<' : + return data_lt < data_rt; + case '>' : + return data_lt > data_rt; + case '<=' : + return data_lt <= data_rt; + case '>=' : + return data_lt >= data_rt; + case '==' : + return data_lt == data_rt; + case '!=' : + return data_lt != data_rt; + default : + throw String( op_code ) + ' is not supported'; + } + // 'jslint eqeq : false' here results in + // "Unreachable '/*jslint' after 'return'". + // We don't need it though, as the rule exception + // is discarded at the end of this functional scope + }; + + fnCombineRow = function ( left_row, right_row ) { + var out_map = {}, i, prefix; + + for ( i in left_row ){ + if ( left_row.hasOwnProperty( i ) ){ + out_map[i] = left_row[i]; + } + } + for ( i in right_row ){ + if ( right_row.hasOwnProperty( i ) && i !== '___id' && + i !== '___s' ) + { + prefix = !TAFFY.isUndefined( out_map[i] ) ? 'right_' : ''; + out_map[prefix + String( i ) ] = right_row[i]; + } + } + return out_map; + }; + + fnMain = function ( table ) { + var + right_table, i, + arg_list = arguments, + arg_length = arg_list.length, + result_list = [] + ; + + if ( typeof table.filter !== 'function' ){ + if ( table.TAFFY ){ right_table = table(); } + else { + throw 'TAFFY DB or result not supplied'; + } + } + else { right_table = table; } + + this.context( { + results : this.getDBI().query( this.context() ) + } ); + + TAFFY.each( this.context().results, function ( left_row ) { + right_table.each( function ( right_row ) { + var arg_data, is_ok = true; + CONDITION: + for ( i = 1; i < arg_length; i++ ){ + arg_data = arg_list[i]; + if ( typeof arg_data === 'function' ){ + is_ok = arg_data( left_row, right_row ); + } + else if ( typeof arg_data === 'object' && arg_data.length ){ + is_ok = fnCompareList( left_row, right_row, arg_data ); + } + else { + is_ok = false; + } + + if ( !is_ok ){ break CONDITION; } // short circuit + } + + if ( is_ok ){ + result_list.push( fnCombineRow( left_row, right_row ) ); + } + } ); + } ); + return TAFFY( result_list )(); + }; + + return fnMain; + }()); + + API.extend( 'join', innerJoinFunction ); + }()); + + API.extend( 'max', function ( c ) { + // **************************************** + // * + // * Takes: column to find max + // * Returns: the highest value + // **************************************** + var highest = null; + run.call( this ); + each( this.context().results, function ( r ) { + if ( highest === null || r[c] > highest ){ + highest = r[c]; + } + }); + return highest; + }); + + API.extend( 'select', function () { + // **************************************** + // * + // * Takes: columns to select values into an array + // * Returns: array of values + // * Note if more than one column is given an array of arrays is returned + // **************************************** + + var ra = [], args = arguments; + run.call( this ); + if ( arguments.length === 1 ){ + + each( this.context().results, function ( r ) { + + ra.push( r[args[0]] ); + }); + } + else { + each( this.context().results, function ( r ) { + var row = []; + each( args, function ( c ) { + row.push( r[c] ); + }); + ra.push( row ); + }); + } + return ra; + }); + API.extend( 'distinct', function () { + // **************************************** + // * + // * Takes: columns to select unique alues into an array + // * Returns: array of values + // * Note if more than one column is given an array of arrays is returned + // **************************************** + var ra = [], args = arguments; + run.call( this ); + if ( arguments.length === 1 ){ + + each( this.context().results, function ( r ) { + var v = r[args[0]], dup = false; + each( ra, function ( d ) { + if ( v === d ){ + dup = true; + return TAFFY.EXIT; + } + }); + if ( !dup ){ + ra.push( v ); + } + }); + } + else { + each( this.context().results, function ( r ) { + var row = [], dup = false; + each( args, function ( c ) { + row.push( r[c] ); + }); + each( ra, function ( d ) { + var ldup = true; + each( args, function ( c, i ) { + if ( row[i] !== d[i] ){ + ldup = false; + return TAFFY.EXIT; + } + }); + if ( ldup ){ + dup = true; + return TAFFY.EXIT; + } + }); + if ( !dup ){ + ra.push( row ); + } + }); + } + return ra; + }); + API.extend( 'supplant', function ( template, returnarray ) { + // **************************************** + // * + // * Takes: a string template formated with key to be replaced with values from the rows, flag to determine if we want array of strings + // * Returns: array of values or a string + // **************************************** + var ra = []; + run.call( this ); + each( this.context().results, function ( r ) { + // TODO: The curly braces used to be unescaped + ra.push( template.replace( /\{([^\{\}]*)\}/g, function ( a, b ) { + var v = r[b]; + return typeof v === 'string' || typeof v === 'number' ? v : a; + } ) ); + }); + return (!returnarray) ? ra.join( "" ) : ra; + }); + + + API.extend( 'each', function ( m ) { + // **************************************** + // * + // * Takes: a function + // * Purpose: loops over every matching record and applies the function + // **************************************** + run.call( this ); + each( this.context().results, m ); + return this; + }); + API.extend( 'map', function ( m ) { + // **************************************** + // * + // * Takes: a function + // * Purpose: loops over every matching record and applies the function, returing the results in an array + // **************************************** + var ra = []; + run.call( this ); + each( this.context().results, function ( r ) { + ra.push( m( r ) ); + }); + return ra; + }); + + + + T = function ( d ) { + // **************************************** + // * + // * T is the main TAFFY object + // * Takes: an array of objects or JSON + // * Returns a new TAFFYDB + // **************************************** + var TOb = [], + ID = {}, + RC = 1, + settings = { + template : false, + onInsert : false, + onUpdate : false, + onRemove : false, + onDBChange : false, + storageName : false, + forcePropertyCase : null, + cacheSize : 100, + name : '' + }, + dm = new Date(), + CacheCount = 0, + CacheClear = 0, + Cache = {}, + DBI, runIndexes, root + ; + // **************************************** + // * + // * TOb = this database + // * ID = collection of the record IDs and locations within the DB, used for fast lookups + // * RC = record counter, used for creating IDs + // * settings.template = the template to merge all new records with + // * settings.onInsert = event given a copy of the newly inserted record + // * settings.onUpdate = event given the original record, the changes, and the new record + // * settings.onRemove = event given the removed record + // * settings.forcePropertyCase = on insert force the proprty case to be lower or upper. default lower, null/undefined will leave case as is + // * dm = the modify date of the database, used for query caching + // **************************************** + + + runIndexes = function ( indexes ) { + // **************************************** + // * + // * Takes: a collection of indexes + // * Returns: collection with records matching indexed filters + // **************************************** + + var records = [], UniqueEnforce = false; + + if ( indexes.length === 0 ){ + return TOb; + } + + each( indexes, function ( f ) { + // Check to see if record ID + if ( T.isString( f ) && /[t][0-9]*[r][0-9]*/i.test( f ) && + TOb[ID[f]] ) + { + records.push( TOb[ID[f]] ); + UniqueEnforce = true; + } + // Check to see if record + if ( T.isObject( f ) && f.___id && f.___s && + TOb[ID[f.___id]] ) + { + records.push( TOb[ID[f.___id]] ); + UniqueEnforce = true; + } + // Check to see if array of indexes + if ( T.isArray( f ) ){ + each( f, function ( r ) { + each( runIndexes( r ), function ( rr ) { + records.push( rr ); + }); + + }); + } + }); + if ( UniqueEnforce && records.length > 1 ){ + records = []; + } + + return records; + }; + + DBI = { + // **************************************** + // * + // * The DBI is the internal DataBase Interface that interacts with the data + // **************************************** + dm : function ( nd ) { + // **************************************** + // * + // * Takes: an optional new modify date + // * Purpose: used to get and set the DB modify date + // **************************************** + if ( nd ){ + dm = nd; + Cache = {}; + CacheCount = 0; + CacheClear = 0; + } + if ( settings.onDBChange ){ + setTimeout( function () { + settings.onDBChange.call( TOb ); + }, 0 ); + } + if ( settings.storageName ){ + setTimeout( function () { + localStorage.setItem( 'taffy_' + settings.storageName, + JSON.stringify( TOb ) ); + }); + } + return dm; + }, + insert : function ( i, runEvent ) { + // **************************************** + // * + // * Takes: a new record to insert + // * Purpose: merge the object with the template, add an ID, insert into DB, call insert event + // **************************************** + var columns = [], + records = [], + input = protectJSON( i ) + ; + each( input, function ( v, i ) { + var nv, o; + if ( T.isArray( v ) && i === 0 ){ + each( v, function ( av ) { + + columns.push( (settings.forcePropertyCase === 'lower') + ? av.toLowerCase() + : (settings.forcePropertyCase === 'upper') + ? av.toUpperCase() : av ); + }); + return true; + } + else if ( T.isArray( v ) ){ + nv = {}; + each( v, function ( av, ai ) { + nv[columns[ai]] = av; + }); + v = nv; + + } + else if ( T.isObject( v ) && settings.forcePropertyCase ){ + o = {}; + + eachin( v, function ( av, ai ) { + o[(settings.forcePropertyCase === 'lower') ? ai.toLowerCase() + : (settings.forcePropertyCase === 'upper') + ? ai.toUpperCase() : ai] = v[ai]; + }); + v = o; + } + + RC++; + v.___id = 'T' + String( idpad + TC ).slice( -6 ) + 'R' + + String( idpad + RC ).slice( -6 ); + v.___s = true; + records.push( v.___id ); + if ( settings.template ){ + v = T.mergeObj( settings.template, v ); + } + TOb.push( v ); + + ID[v.___id] = TOb.length - 1; + if ( settings.onInsert && + (runEvent || TAFFY.isUndefined( runEvent )) ) + { + settings.onInsert.call( v ); + } + DBI.dm( new Date() ); + }); + return root( records ); + }, + sort : function ( o ) { + // **************************************** + // * + // * Purpose: Change the sort order of the DB itself and reset the ID bucket + // **************************************** + TOb = orderByCol( TOb, o.split( ',' ) ); + ID = {}; + each( TOb, function ( r, i ) { + ID[r.___id] = i; + }); + DBI.dm( new Date() ); + return true; + }, + update : function ( id, changes, runEvent ) { + // **************************************** + // * + // * Takes: the ID of record being changed and the changes + // * Purpose: Update a record and change some or all values, call the on update method + // **************************************** + + var nc = {}, or, nr, tc, hasChange; + if ( settings.forcePropertyCase ){ + eachin( changes, function ( v, p ) { + nc[(settings.forcePropertyCase === 'lower') ? p.toLowerCase() + : (settings.forcePropertyCase === 'upper') ? p.toUpperCase() + : p] = v; + }); + changes = nc; + } + + or = TOb[ID[id]]; + nr = T.mergeObj( or, changes ); + + tc = {}; + hasChange = false; + eachin( nr, function ( v, i ) { + if ( TAFFY.isUndefined( or[i] ) || or[i] !== v ){ + tc[i] = v; + hasChange = true; + } + }); + if ( hasChange ){ + if ( settings.onUpdate && + (runEvent || TAFFY.isUndefined( runEvent )) ) + { + settings.onUpdate.call( nr, TOb[ID[id]], tc ); + } + TOb[ID[id]] = nr; + DBI.dm( new Date() ); + } + }, + remove : function ( id ) { + // **************************************** + // * + // * Takes: the ID of record to be removed + // * Purpose: remove a record, changes its ___s value to false + // **************************************** + TOb[ID[id]].___s = false; + }, + removeCommit : function ( runEvent ) { + var x; + // **************************************** + // * + // * + // * Purpose: loop over all records and remove records with ___s = false, call onRemove event, clear ID + // **************************************** + for ( x = TOb.length - 1; x > -1; x-- ){ + + if ( !TOb[x].___s ){ + if ( settings.onRemove && + (runEvent || TAFFY.isUndefined( runEvent )) ) + { + settings.onRemove.call( TOb[x] ); + } + ID[TOb[x].___id] = undefined; + TOb.splice( x, 1 ); + } + } + ID = {}; + each( TOb, function ( r, i ) { + ID[r.___id] = i; + }); + DBI.dm( new Date() ); + }, + query : function ( context ) { + // **************************************** + // * + // * Takes: the context object for a query and either returns a cache result or a new query result + // **************************************** + var returnq, cid, results, indexed, limitq, ni; + + if ( settings.cacheSize ) { + cid = ''; + each( context.filterRaw, function ( r ) { + if ( T.isFunction( r ) ){ + cid = 'nocache'; + return TAFFY.EXIT; + } + }); + if ( cid === '' ){ + cid = JSON.stringify( T.mergeObj( context, + {q : false, run : false, sort : false} ) ); + } + } + // Run a new query if there are no results or the run date has been cleared + if ( !context.results || !context.run || + (context.run && DBI.dm() > context.run) ) + { + results = []; + + // check Cache + + if ( settings.cacheSize && Cache[cid] ){ + + Cache[cid].i = CacheCount++; + return Cache[cid].results; + } + else { + // if no filter, return DB + if ( context.q.length === 0 && context.index.length === 0 ){ + each( TOb, function ( r ) { + results.push( r ); + }); + returnq = results; + } + else { + // use indexes + + indexed = runIndexes( context.index ); + + // run filters + each( indexed, function ( r ) { + // Run filter to see if record matches query + if ( context.q.length === 0 || runFilters( r, context.q ) ){ + results.push( r ); + } + }); + + returnq = results; + } + } + + + } + else { + // If query exists and run has not been cleared return the cache results + returnq = context.results; + } + // If a custom order array exists and the run has been clear or the sort has been cleared + if ( context.order.length > 0 && (!context.run || !context.sort) ){ + // order the results + returnq = orderByCol( returnq, context.order ); + } + + // If a limit on the number of results exists and it is less than the returned results, limit results + if ( returnq.length && + ((context.limit && context.limit < returnq.length) || + context.start) + ) { + limitq = []; + each( returnq, function ( r, i ) { + if ( !context.start || + (context.start && (i + 1) >= context.start) ) + { + if ( context.limit ){ + ni = (context.start) ? (i + 1) - context.start : i; + if ( ni < context.limit ){ + limitq.push( r ); + } + else if ( ni > context.limit ){ + return TAFFY.EXIT; + } + } + else { + limitq.push( r ); + } + } + }); + returnq = limitq; + } + + // update cache + if ( settings.cacheSize && cid !== 'nocache' ){ + CacheClear++; + + setTimeout( function () { + var bCounter, nc; + if ( CacheClear >= settings.cacheSize * 2 ){ + CacheClear = 0; + bCounter = CacheCount - settings.cacheSize; + nc = {}; + eachin( function ( r, k ) { + if ( r.i >= bCounter ){ + nc[k] = r; + } + }); + Cache = nc; + } + }, 0 ); + + Cache[cid] = { i : CacheCount++, results : returnq }; + } + return returnq; + } + }; + + + root = function () { + var iAPI, context; + // **************************************** + // * + // * The root function that gets returned when a new DB is created + // * Takes: unlimited filter arguments and creates filters to be run when a query is called + // **************************************** + // **************************************** + // * + // * iAPI is the the method collection valiable when a query has been started by calling dbname + // * Certain methods are or are not avaliable once you have started a query such as insert -- you can only insert into root + // **************************************** + iAPI = TAFFY.mergeObj( TAFFY.mergeObj( API, { insert : undefined } ), + { getDBI : function () { return DBI; }, + getroot : function ( c ) { return root.call( c ); }, + context : function ( n ) { + // **************************************** + // * + // * The context contains all the information to manage a query including filters, limits, and sorts + // **************************************** + if ( n ){ + context = TAFFY.mergeObj( context, + n.hasOwnProperty('results') + ? TAFFY.mergeObj( n, { run : new Date(), sort: new Date() }) + : n + ); + } + return context; + }, + extend : undefined + }); + + context = (this && this.q) ? this : { + limit : false, + start : false, + q : [], + filterRaw : [], + index : [], + order : [], + results : false, + run : null, + sort : null, + settings : settings + }; + // **************************************** + // * + // * Call the query method to setup a new query + // **************************************** + each( arguments, function ( f ) { + + if ( isIndexable( f ) ){ + context.index.push( f ); + } + else { + context.q.push( returnFilter( f ) ); + } + context.filterRaw.push( f ); + }); + + + return iAPI; + }; + + // **************************************** + // * + // * If new records have been passed on creation of the DB either as JSON or as an array/object, insert them + // **************************************** + TC++; + if ( d ){ + DBI.insert( d ); + } + + + root.insert = DBI.insert; + + root.merge = function ( i, key, runEvent ) { + var + search = {}, + finalSearch = [], + obj = {} + ; + + runEvent = runEvent || false; + key = key || 'id'; + + each( i, function ( o ) { + var existingObject; + search[key] = o[key]; + finalSearch.push( o[key] ); + existingObject = root( search ).first(); + if ( existingObject ){ + DBI.update( existingObject.___id, o, runEvent ); + } + else { + DBI.insert( o, runEvent ); + } + }); + + obj[key] = finalSearch; + return root( obj ); + }; + + root.TAFFY = true; + root.sort = DBI.sort; + // **************************************** + // * + // * These are the methods that can be accessed on off the root DB function. Example dbname.insert; + // **************************************** + root.settings = function ( n ) { + // **************************************** + // * + // * Getting and setting for this DB's settings/events + // **************************************** + if ( n ){ + settings = TAFFY.mergeObj( settings, n ); + if ( n.template ){ + + root().update( n.template ); + } + } + return settings; + }; + + // **************************************** + // * + // * These are the methods that can be accessed on off the root DB function. Example dbname.insert; + // **************************************** + root.store = function ( n ) { + // **************************************** + // * + // * Setup localstorage for this DB on a given name + // * Pull data into the DB as needed + // **************************************** + var r = false, i; + if ( localStorage ){ + if ( n ){ + i = localStorage.getItem( 'taffy_' + n ); + if ( i && i.length > 0 ){ + root.insert( i ); + r = true; + } + if ( TOb.length > 0 ){ + setTimeout( function () { + localStorage.setItem( 'taffy_' + settings.storageName, + JSON.stringify( TOb ) ); + }); + } + } + root.settings( {storageName : n} ); + } + return root; + }; + + // **************************************** + // * + // * Return root on DB creation and start having fun + // **************************************** + return root; + }; + // **************************************** + // * + // * Sets the global TAFFY object + // **************************************** + TAFFY = T; + + + // **************************************** + // * + // * Create public each method + // * + // **************************************** + T.each = each; + + // **************************************** + // * + // * Create public eachin method + // * + // **************************************** + T.eachin = eachin; + // **************************************** + // * + // * Create public extend method + // * Add a custom method to the API + // * + // **************************************** + T.extend = API.extend; + + + // **************************************** + // * + // * Creates TAFFY.EXIT value that can be returned to stop an each loop + // * + // **************************************** + TAFFY.EXIT = 'TAFFYEXIT'; + + // **************************************** + // * + // * Create public utility mergeObj method + // * Return a new object where items from obj2 + // * have replaced or been added to the items in + // * obj1 + // * Purpose: Used to combine objs + // * + // **************************************** + TAFFY.mergeObj = function ( ob1, ob2 ) { + var c = {}; + eachin( ob1, function ( v, n ) { c[n] = ob1[n]; }); + eachin( ob2, function ( v, n ) { c[n] = ob2[n]; }); + return c; + }; + + + // **************************************** + // * + // * Create public utility has method + // * Returns true if a complex object, array + // * or taffy collection contains the material + // * provided in the second argument + // * Purpose: Used to comare objects + // * + // **************************************** + TAFFY.has = function ( var1, var2 ) { + + var re = true, n; + + if ( (var1.TAFFY) ){ + re = var1( var2 ); + if ( re.length > 0 ){ + return true; + } + else { + return false; + } + } + else { + + switch ( T.typeOf( var1 ) ){ + case 'object': + if ( T.isObject( var2 ) ){ + eachin( var2, function ( v, n ) { + if ( re === true && !T.isUndefined( var1[n] ) && + var1.hasOwnProperty( n ) ) + { + re = T.has( var1[n], var2[n] ); + } + else { + re = false; + return TAFFY.EXIT; + } + }); + } + else if ( T.isArray( var2 ) ){ + each( var2, function ( v, n ) { + re = T.has( var1, var2[n] ); + if ( re ){ + return TAFFY.EXIT; + } + }); + } + else if ( T.isString( var2 ) ){ + if ( !TAFFY.isUndefined( var1[var2] ) ){ + return true; + } + else { + return false; + } + } + return re; + case 'array': + if ( T.isObject( var2 ) ){ + each( var1, function ( v, i ) { + re = T.has( var1[i], var2 ); + if ( re === true ){ + return TAFFY.EXIT; + } + }); + } + else if ( T.isArray( var2 ) ){ + each( var2, function ( v2, i2 ) { + each( var1, function ( v1, i1 ) { + re = T.has( var1[i1], var2[i2] ); + if ( re === true ){ + return TAFFY.EXIT; + } + }); + if ( re === true ){ + return TAFFY.EXIT; + } + }); + } + else if ( T.isString( var2 ) || T.isNumber( var2 ) ){ + for ( n = 0; n < var1.length; n++ ){ + re = T.has( var1[n], var2 ); + if ( re ){ + return true; + } + } + } + return re; + case 'string': + if ( T.isString( var2 ) && var2 === var1 ){ + return true; + } + break; + default: + if ( T.typeOf( var1 ) === T.typeOf( var2 ) && var1 === var2 ){ + return true; + } + break; + } + } + return false; + }; + + // **************************************** + // * + // * Create public utility hasAll method + // * Returns true if a complex object, array + // * or taffy collection contains the material + // * provided in the call - for arrays it must + // * contain all the material in each array item + // * Purpose: Used to comare objects + // * + // **************************************** + TAFFY.hasAll = function ( var1, var2 ) { + + var T = TAFFY, ar; + if ( T.isArray( var2 ) ){ + ar = true; + each( var2, function ( v ) { + ar = T.has( var1, v ); + if ( ar === false ){ + return TAFFY.EXIT; + } + }); + return ar; + } + else { + return T.has( var1, var2 ); + } + }; + + + // **************************************** + // * + // * typeOf Fixed in JavaScript as public utility + // * + // **************************************** + TAFFY.typeOf = function ( v ) { + var s = typeof v; + if ( s === 'object' ){ + if ( v ){ + if ( typeof v.length === 'number' && + !(v.propertyIsEnumerable( 'length' )) ) + { + s = 'array'; + } + } + else { + s = 'null'; + } + } + return s; + }; + + // **************************************** + // * + // * Create public utility getObjectKeys method + // * Returns an array of an objects keys + // * Purpose: Used to get the keys for an object + // * + // **************************************** + TAFFY.getObjectKeys = function ( ob ) { + var kA = []; + eachin( ob, function ( n, h ) { + kA.push( h ); + }); + kA.sort(); + return kA; + }; + + // **************************************** + // * + // * Create public utility isSameArray + // * Returns an array of an objects keys + // * Purpose: Used to get the keys for an object + // * + // **************************************** + TAFFY.isSameArray = function ( ar1, ar2 ) { + return (TAFFY.isArray( ar1 ) && TAFFY.isArray( ar2 ) && + ar1.join( ',' ) === ar2.join( ',' )) ? true : false; + }; + + // **************************************** + // * + // * Create public utility isSameObject method + // * Returns true if objects contain the same + // * material or false if they do not + // * Purpose: Used to comare objects + // * + // **************************************** + TAFFY.isSameObject = function ( ob1, ob2 ) { + var T = TAFFY, rv = true; + + if ( T.isObject( ob1 ) && T.isObject( ob2 ) ){ + if ( T.isSameArray( T.getObjectKeys( ob1 ), + T.getObjectKeys( ob2 ) ) ) + { + eachin( ob1, function ( v, n ) { + if ( ! ( (T.isObject( ob1[n] ) && T.isObject( ob2[n] ) && + T.isSameObject( ob1[n], ob2[n] )) || + (T.isArray( ob1[n] ) && T.isArray( ob2[n] ) && + T.isSameArray( ob1[n], ob2[n] )) || (ob1[n] === ob2[n]) ) + ) { + rv = false; + return TAFFY.EXIT; + } + }); + } + else { + rv = false; + } + } + else { + rv = false; + } + return rv; + }; + + // **************************************** + // * + // * Create public utility is[DataType] methods + // * Return true if obj is datatype, false otherwise + // * Purpose: Used to determine if arguments are of certain data type + // * + // * mmikowski 2012-08-06 refactored to make much less "magical": + // * fewer closures and passes jslint + // * + // **************************************** + + typeList = [ + 'String', 'Number', 'Object', 'Array', + 'Boolean', 'Null', 'Function', 'Undefined' + ]; + + makeTest = function ( thisKey ) { + return function ( data ) { + return TAFFY.typeOf( data ) === thisKey.toLowerCase() ? true : false; + }; + }; + + for ( idx = 0; idx < typeList.length; idx++ ){ + typeKey = typeList[idx]; + TAFFY['is' + typeKey] = makeTest( typeKey ); + } + } +}()); + +if ( typeof(exports) === 'object' ){ + exports.taffy = TAFFY; +} + diff --git a/data/scripts/jsdoc/node_modules/underscore/LICENSE b/data/scripts/jsdoc/node_modules/underscore/LICENSE new file mode 100644 index 00000000..61d28c08 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/underscore/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2009-2012 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/data/scripts/jsdoc/node_modules/underscore/package.json b/data/scripts/jsdoc/node_modules/underscore/package.json new file mode 100644 index 00000000..4507c010 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/underscore/package.json @@ -0,0 +1,25 @@ +{ + "name": "underscore", + "description": "JavaScript's functional programming helper library.", + "homepage": "http://underscorejs.org", + "keywords": [ + "util", + "functional", + "server", + "client", + "browser" + ], + "author": { + "name": "Jeremy Ashkenas", + "email": "jeremy@documentcloud.org" + }, + "repository": { + "type": "git", + "url": "git://github.com/documentcloud/underscore.git" + }, + "main": "underscore.js", + "version": "1.4.2", + "readme": " __\n /\\ \\ __\n __ __ ___ \\_\\ \\ __ _ __ ____ ___ ___ _ __ __ /\\_\\ ____\n /\\ \\/\\ \\ /' _ `\\ /'_ \\ /'__`\\/\\ __\\/ ,__\\ / ___\\ / __`\\/\\ __\\/'__`\\ \\/\\ \\ /',__\\\n \\ \\ \\_\\ \\/\\ \\/\\ \\/\\ \\ \\ \\/\\ __/\\ \\ \\//\\__, `\\/\\ \\__//\\ \\ \\ \\ \\ \\//\\ __/ __ \\ \\ \\/\\__, `\\\n \\ \\____/\\ \\_\\ \\_\\ \\___,_\\ \\____\\\\ \\_\\\\/\\____/\\ \\____\\ \\____/\\ \\_\\\\ \\____\\/\\_\\ _\\ \\ \\/\\____/\n \\/___/ \\/_/\\/_/\\/__,_ /\\/____/ \\/_/ \\/___/ \\/____/\\/___/ \\/_/ \\/____/\\/_//\\ \\_\\ \\/___/\n \\ \\____/\n \\/___/\n\nUnderscore.js is a utility-belt library for JavaScript that provides\nsupport for the usual functional suspects (each, map, reduce, filter...)\nwithout extending any core JavaScript objects.\n\nFor Docs, License, Tests, and pre-packed downloads, see:\nhttp://underscorejs.org\n\nMany thanks to our contributors:\nhttps://github.com/documentcloud/underscore/contributors\n", + "_id": "underscore@1.4.2", + "_from": "underscore" +} diff --git a/data/scripts/jsdoc/node_modules/underscore/underscore.js b/data/scripts/jsdoc/node_modules/underscore/underscore.js new file mode 100644 index 00000000..1ebe2671 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/underscore/underscore.js @@ -0,0 +1,1200 @@ +// Underscore.js 1.4.2 +// http://underscorejs.org +// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. + +(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 push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + 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) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root['_'] = _; + } + + // Current version. + _.VERSION = '1.4.2'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `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 (obj.length === +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 (_.has(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 = _.collect = 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 = arguments.length > 2; + 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) { + 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) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // 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) { + iterator || (iterator = _.identity); + 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 || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + var found = false; + if (obj == null) return found; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + found = any(obj, function(value) { + return value === target; + }); + 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 (_.isFunction(method) ? method : 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]; }); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // with specific `key:value` pairs. + _.where = function(obj, attrs) { + if (_.isEmpty(attrs)) return []; + return _.filter(obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + 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) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + 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; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (obj.length === +obj.length) return slice.call(obj); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + return (obj.length === +obj.length) ? obj.length : _.keys(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` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, function(value){ return !!value; }); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // 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, iterator, context) { + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // 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; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // 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 = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : 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 + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // 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 bind(func, context) { + var bound, args; + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // 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 _.has(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(null, 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))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = _.debounce(function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) { + result = func.apply(context, args); + } + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + throttling = true; + result = func.apply(context, args); + } + whenDone(); + return result; + }; + }; + + // 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. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + + // 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; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // 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]; + push.apply(args, 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 = arguments; + return function() { + var args = 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) { + if (times <= 0) return 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 (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.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) { + obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // 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) { + if (!_.isObject(obj)) return 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; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(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 object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return _.isNumber(obj) && isFinite(obj); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // 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; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // 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); + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + (0 | Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // 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, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/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(text, data, settings) { + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + source += + escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" : + interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" : + evaluate ? "';\n" + evaluate + "\n__p+='" : ''; + index = offset + match.length; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // 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. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // 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]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + +}).call(this); diff --git a/data/scripts/jsdoc/node_modules/wrench/LICENSE b/data/scripts/jsdoc/node_modules/wrench/LICENSE new file mode 100644 index 00000000..a85a94a6 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/wrench/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2010 Ryan McGrath + +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/data/scripts/jsdoc/node_modules/wrench/lib/wrench.js b/data/scripts/jsdoc/node_modules/wrench/lib/wrench.js new file mode 100644 index 00000000..00f4166b --- /dev/null +++ b/data/scripts/jsdoc/node_modules/wrench/lib/wrench.js @@ -0,0 +1,399 @@ +/* wrench.js + * + * A collection of various utility functions I've found myself in need of + * for use with Node.js (http://nodejs.org/). This includes things like: + * + * - Recursively deleting directories in Node.js (Sync, not Async) + * - Recursively copying directories in Node.js (Sync, not Async) + * - Recursively chmoding a directory structure from Node.js (Sync, not Async) + * - Other things that I'll add here as time goes on. Shhhh... + * + * ~ Ryan McGrath (ryan [at] venodesigns.net) + */ + +var fs = require("fs"), + _path = require("path"); + + +/* wrench.readdirSyncRecursive("directory_path"); + * + * Recursively dives through directories and read the contents of all the + * children directories. + */ +exports.readdirSyncRecursive = function(baseDir) { + baseDir = baseDir.replace(/\/$/, ''); + + var readdirSyncRecursive = function(baseDir) { + var files = [], + curFiles, + nextDirs, + isDir = function(fname){ + return fs.statSync( _path.join(baseDir, fname) ).isDirectory(); + }, + prependBaseDir = function(fname){ + return _path.join(baseDir, fname); + }; + + curFiles = fs.readdirSync(baseDir); + nextDirs = curFiles.filter(isDir); + curFiles = curFiles.map(prependBaseDir); + + files = files.concat( curFiles ); + + while (nextDirs.length) { + files = files.concat( readdirSyncRecursive( _path.join(baseDir, nextDirs.shift()) ) ); + } + + return files; + }; + + // convert absolute paths to relative + var fileList = readdirSyncRecursive(baseDir).map(function(val){ + return _path.relative(baseDir, val); + }); + + return fileList; +}; + +/* wrench.readdirRecursive("directory_path", function(error, files) {}); + * + * Recursively dives through directories and read the contents of all the + * children directories. + * + * Asynchronous, so returns results/error in callback. + * Callback receives the of files in currently recursed directory. + * When no more directories are left, callback is called with null for all arguments. + * + */ +exports.readdirRecursive = function(baseDir, fn) { + baseDir = baseDir.replace(/\/$/, ''); + + var waitCount = 0; + + function readdirRecursive(curDir) { + var files = [], + curFiles, + nextDirs, + prependcurDir = function(fname){ + return _path.join(curDir, fname); + }; + + waitCount++; + fs.readdir(curDir, function(e, curFiles) { + waitCount--; + + curFiles = curFiles.map(prependcurDir); + + curFiles.forEach(function(it) { + waitCount++; + + fs.stat(it, function(e, stat) { + waitCount--; + + if (e) { + fn(e); + } else { + if (stat.isDirectory()) { + readdirRecursive(it); + } + } + + if (waitCount == 0) { + fn(null, null); + } + }); + }); + + fn(null, curFiles.map(function(val) { + // convert absolute paths to relative + return _path.relative(baseDir, val); + })); + + if (waitCount == 0) { + fn(null, null); + } + }); + }; + + readdirRecursive(baseDir); +}; + + + +/* wrench.rmdirSyncRecursive("directory_path", forceDelete, failSilent); + * + * Recursively dives through directories and obliterates everything about it. This is a + * Sync-function, which blocks things until it's done. No idea why anybody would want an + * Asynchronous version. :\ + */ +exports.rmdirSyncRecursive = function(path, failSilent) { + var files; + + try { + files = fs.readdirSync(path); + } catch (err) { + if(failSilent) return; + throw new Error(err.message); + } + + /* Loop through and delete everything in the sub-tree after checking it */ + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(path + "/" + files[i]); + + if(currFile.isDirectory()) // Recursive function back to the beginning + exports.rmdirSyncRecursive(path + "/" + files[i]); + + else if(currFile.isSymbolicLink()) // Unlink symlinks + fs.unlinkSync(path + "/" + files[i]); + + else // Assume it's a file - perhaps a try/catch belongs here? + fs.unlinkSync(path + "/" + files[i]); + } + + /* Now that we know everything in the sub-tree has been deleted, we can delete the main + directory. Huzzah for the shopkeep. */ + return fs.rmdirSync(path); +}; + +/* wrench.copyDirSyncRecursive("directory_to_copy", "new_directory_location", opts); + * + * Recursively dives through a directory and moves all its files to a new location. This is a + * Synchronous function, which blocks things until it's done. If you need/want to do this in + * an Asynchronous manner, look at wrench.copyDirRecursively() below. + * + * Note: Directories should be passed to this function without a trailing slash. + */ +exports.copyDirSyncRecursive = function(sourceDir, newDirLocation, opts) { + + if (!opts || !opts.preserve) { + try { + if(fs.statSync(newDirLocation).isDirectory()) exports.rmdirSyncRecursive(newDirLocation); + } catch(e) { } + } + + /* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */ + var checkDir = fs.statSync(sourceDir); + try { + fs.mkdirSync(newDirLocation, checkDir.mode); + } catch (e) { + //if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(sourceDir + "/" + files[i]); + + if(currFile.isDirectory()) { + /* recursion this thing right on back. */ + exports.copyDirSyncRecursive(sourceDir + "/" + files[i], newDirLocation + "/" + files[i], opts); + } else if(currFile.isSymbolicLink()) { + var symlinkFull = fs.readlinkSync(sourceDir + "/" + files[i]); + fs.symlinkSync(symlinkFull, newDirLocation + "/" + files[i]); + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + var contents = fs.readFileSync(sourceDir + "/" + files[i]); + fs.writeFileSync(newDirLocation + "/" + files[i], contents); + } + } +}; + +/* wrench.chmodSyncRecursive("directory", filemode); + * + * Recursively dives through a directory and chmods everything to the desired mode. This is a + * Synchronous function, which blocks things until it's done. + * + * Note: Directories should be passed to this function without a trailing slash. + */ +exports.chmodSyncRecursive = function(sourceDir, filemode) { + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(sourceDir + "/" + files[i]); + + if(currFile.isDirectory()) { + /* ...and recursion this thing right on back. */ + exports.chmodSyncRecursive(sourceDir + "/" + files[i], filemode); + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + fs.chmod(sourceDir + "/" + files[i], filemode); + } + } + + /* Finally, chmod the parent directory */ + fs.chmod(sourceDir, filemode); +}; + + +/* wrench.chownSyncRecursive("directory", uid, gid); + * + * Recursively dives through a directory and chowns everything to the desired user and group. This is a + * Synchronous function, which blocks things until it's done. + * + * Note: Directories should be passed to this function without a trailing slash. + */ +exports.chownSyncRecursive = function(sourceDir, uid, gid) { + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(sourceDir + "/" + files[i]); + + if(currFile.isDirectory()) { + /* ...and recursion this thing right on back. */ + exports.chownSyncRecursive(sourceDir + "/" + files[i], uid, gid); + } else { + /* At this point, we've hit a file actually worth chowning... so own it. */ + fs.chownSync(sourceDir + "/" + files[i], uid, gid); + } + } + + /* Finally, chown the parent directory */ + fs.chownSync(sourceDir, uid, gid); +}; + + + +/* wrench.rmdirRecursive("directory_path", callback); + * + * Recursively dives through directories and obliterates everything about it. + */ +exports.rmdirRecursive = function rmdirRecursive(dir, clbk){ + fs.readdir(dir, function(err, files){ + if (err) return clbk(err); + (function rmFile(err){ + if (err) return clbk(err); + + var filename = files.shift(); + if (filename === null || typeof filename == 'undefined') + return fs.rmdir(dir, clbk); + + var file = dir+'/'+filename; + fs.stat(file, function(err, stat){ + if (err) return clbk(err); + if (stat.isDirectory()) + rmdirRecursive(file, rmFile); + else + fs.unlink(file, rmFile); + }); + })(); + }); +}; + +/* wrench.copyDirRecursive("directory_to_copy", "new_location", callback); + * + * Recursively dives through a directory and moves all its files to a new + * location. + * + * Note: Directories should be passed to this function without a trailing slash. + */ +exports.copyDirRecursive = function copyDirRecursive(srcDir, newDir, clbk) { + fs.stat(newDir, function(err, newDirStat){ + if (!err) return exports.rmdirRecursive(newDir, function(err){ + copyDirRecursive(srcDir, newDir, clbk); + }); + + fs.stat(srcDir, function(err, srcDirStat){ + if (err) return clbk(err); + fs.mkdir(newDir, srcDirStat.mode, function(err){ + if (err) return clbk(err); + fs.readdir(srcDir, function(err, files){ + if (err) return clbk(err); + (function copyFiles(err){ + if (err) return clbk(err); + + var filename = files.shift(); + if (filename === null || typeof filename == 'undefined') + return clbk(); + + var file = srcDir+'/'+filename, + newFile = newDir+'/'+filename; + + fs.stat(file, function(err, fileStat){ + if (fileStat.isDirectory()) + copyDirRecursive(file, newFile, copyFiles); + else if (fileStat.isSymbolicLink()) + fs.readlink(file, function(err, link){ + fs.symlink(link, newFile, copyFiles); + }); + else + fs.readFile(file, function(err, data){ + fs.writeFile(newFile, data, copyFiles); + }); + }); + })(); + }); + }); + }); + }); +}; + +var mkdirSyncRecursive = function(path, mode) { + var self = this; + + try { + fs.mkdirSync(path, mode); + } catch(err) { + if(err.code == "ENOENT") { + var slashIdx = path.lastIndexOf("/"); + if(slashIdx < 0) { + slashIdx = path.lastIndexOf("\\"); + } + + if(slashIdx > 0) { + var parentPath = path.substring(0, slashIdx); + mkdirSyncRecursive(parentPath, mode); + mkdirSyncRecursive(path, mode); + } else { + throw err; + } + } else if(err.code == "EEXIST") { + return; + } else { + throw err; + } + } +}; +exports.mkdirSyncRecursive = mkdirSyncRecursive; + +exports.LineReader = function(filename, bufferSize) { + this.bufferSize = bufferSize || 8192; + this.buffer = ""; + this.fd = fs.openSync(filename, "r"); + this.currentPosition = 0; +}; + +exports.LineReader.prototype = { + getBufferAndSetCurrentPosition: function(position) { + var res = fs.readSync(this.fd, this.bufferSize, position, "ascii"); + + this.buffer += res[0]; + if(res[1] === 0) { + this.currentPosition = -1; + } else { + this.currentPosition = position + res[1]; + } + + return this.currentPosition; + }, + + hasNextLine: function() { + while(this.buffer.indexOf('\n') === -1) { + this.getBufferAndSetCurrentPosition(this.currentPosition); + if(this.currentPosition === -1) return false; + } + + if(this.buffer.indexOf("\n") > -1) return true; + return false; + }, + + getNextLine: function() { + var lineEnd = this.buffer.indexOf("\n"), + result = this.buffer.substring(0, lineEnd); + + this.buffer = this.buffer.substring(result.length + 1, this.buffer.length); + return result; + } +}; + +// vim: et ts=4 sw=4 diff --git a/data/scripts/jsdoc/node_modules/wrench/package.json b/data/scripts/jsdoc/node_modules/wrench/package.json new file mode 100644 index 00000000..7c426e76 --- /dev/null +++ b/data/scripts/jsdoc/node_modules/wrench/package.json @@ -0,0 +1,39 @@ +{ + "name": "wrench", + "description": "Recursive filesystem (and other) operations that Node *should* have.", + "version": "1.3.9", + "author": { + "name": "Ryan McGrath", + "email": "ryan@venodesigns.net" + }, + "repository": { + "type": "git", + "url": "https://ryanmcgrath@github.com/ryanmcgrath/wrench-js.git" + }, + "bugs": { + "url": "http://github.com/ryanmcgrath/wrench-js/issues" + }, + "directories": { + "lib": "./lib/" + }, + "dependencies": {}, + "devDependencies": { + "nodeunit": ">= 0.6.4" + }, + "main": "./lib/wrench", + "engines": { + "node": ">=0.1.97" + }, + "scripts": { + "test": "nodeunit tests/runner.js" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/ryanmcgrath/wrench-js/raw/master/LICENSE" + } + ], + "readme": "wrench.js - Recursive file operations in Node.js\n----------------------------------------------------------------------------\nWhile I love Node.js, I've found myself missing some functions. Things like\nrecursively deleting/chmodding a directory (or even deep copying a directory),\nor even a basic line reader, shouldn't need to be re-invented time and time again.\n\nThat said, here's my attempt at a re-usable solution, at least until something\nmore formalized gets integrated into Node.js (*hint hint*). wrench.js is fairly simple\nto use - check out the documentation/examples below:\n\nInstallation\n-----------------------------------------------------------------------------\n\n npm install wrench\n\nUsage\n-----------------------------------------------------------------------------\n``` javascript\nvar wrench = require('wrench'),\n\tutil = require('util');\n```\n\n### Synchronous operations\n``` javascript\n// Recursively create directories, sub-trees and all.\nwrench.mkdirSyncRecursive(dir, 0777);\n\n// Recursively delete the entire sub-tree of a directory, then kill the directory\nwrench.rmdirSyncRecursive('my_directory_name', failSilently);\n\n// Recursively read directories contents.\nwrench.readdirSyncRecursive('my_directory_name');\n\n// Recursively chmod the entire sub-tree of a directory\nwrench.chmodSyncRecursive('my_directory_name', 0755);\n\n// Recursively chown the entire sub-tree of a directory\nwrench.chownSyncRecursive(\"directory\", uid, gid);\n\n// Deep-copy an existing directory\nwrench.copyDirSyncRecursive('directory_to_copy', 'location_where_copy_should_end_up');\n\n// Read lines in from a file until you hit the end\nvar f = new wrench.LineReader('x.txt');\nwhile(f.hasNextLine()) {\n\tutil.puts(x.getNextLine());\n}\n```\n\n### Asynchronous operations\n``` javascript\n// Recursively read directories contents\nvar files = [];\nwrench.readdirRecursive('my_directory_name', function(error, curFiles) {\n // curFiles is what you want\n});\n\n```\n\nQuestions, comments? Hit me up. (ryan [at] venodesigns.net | http://twitter.com/ryanmcgrath)\n", + "_id": "wrench@1.3.9", + "_from": "wrench" +} diff --git a/data/scripts/jsdoc/nodejs/bin/jsdoc b/data/scripts/jsdoc/nodejs/bin/jsdoc new file mode 100644 index 00000000..c53efc4d --- /dev/null +++ b/data/scripts/jsdoc/nodejs/bin/jsdoc @@ -0,0 +1,26 @@ +#!/usr/bin/env node + +// This wrapper script allows JSDoc to be installed with 'npm install -g'. +// Note that JSDoc will still run on Mozilla Rhino, NOT Node.js. + + +var fs = require('fs'); +var os = require('os'); +var path = require('path'); +var spawnProc = require('child_process').spawn; + +var args = process.argv.slice(2); +var script = path.normalize( path.join( path.dirname(fs.realpathSync(process.argv[1])), '..', '..', + 'jsdoc' ) ); +var jsdoc; + +if (process.platform === 'win32') { + jsdoc = spawnProc(script + '.cmd', args, {stdio: 'inherit'}); +} +else { + jsdoc = spawnProc(script, args, {stdio: 'inherit'}); +} + +jsdoc.on('close', function(code) { + process.exit(code); +}); diff --git a/data/scripts/jsdoc/nodejs/jsdoc/fs.js b/data/scripts/jsdoc/nodejs/jsdoc/fs.js new file mode 100644 index 00000000..7115ff0c --- /dev/null +++ b/data/scripts/jsdoc/nodejs/jsdoc/fs.js @@ -0,0 +1,17 @@ +var e = ' is not implemented for Node.js!'; + +exports.ls = function() { + throw new Error('fs.ls' + e); +}; + +exports.toDir = function() { + throw new Error('fs.toDir' + e); +}; + +exports.mkPath = function() { + throw new Error('fs.mkpath' + e); +}; + +exports.copyFileSync = function() { + throw new Error('fs.copyFileSync' + e); +}; diff --git a/data/scripts/jsdoc/nodejs/jsdoc/util/include.js b/data/scripts/jsdoc/nodejs/jsdoc/util/include.js new file mode 100644 index 00000000..c5c0d4de --- /dev/null +++ b/data/scripts/jsdoc/nodejs/jsdoc/util/include.js @@ -0,0 +1,8 @@ +// TODO: not tested +module.exports = function(filepath) { + var fs = require('jsdoc/fs'); + var vm = require('vm'); + + var script = fs.readFileSync(filepath, 'utf8'); + vm.runInNewContext(script, global, filepath); +}; diff --git a/data/scripts/jsdoc/out/app.html b/data/scripts/jsdoc/out/app.html new file mode 100644 index 00000000..70224fc1 --- /dev/null +++ b/data/scripts/jsdoc/out/app.html @@ -0,0 +1,122 @@ + + + + + JSDoc: Namespace: app + + + + + + + + + + +
    + +

    Namespace: app

    + + + + + +
    + +
    +

    + app +

    + +
    + +
    +
    + + + + +
    Data that must be shared across the entire application.
    + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + +
    + + + + + + + + + + + + + + + + + + +
    + +
    + + + + +
    + + + +
    + +
    + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:10 GMT+0300 (EEST) +
    + + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/out/env.html b/data/scripts/jsdoc/out/env.html new file mode 100644 index 00000000..49789891 --- /dev/null +++ b/data/scripts/jsdoc/out/env.html @@ -0,0 +1,441 @@ + + + + + JSDoc: Namespace: env + + + + + + + + + + +
    + +

    Namespace: env

    + + + + + +
    + +
    +

    + env +

    + +
    + +
    +
    + + + + +
    Data representing the environment in which this app is running.
    + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + +
    + + + + + + + + + + + + +

    Members

    + +
    + +
    +

    <static> args :Array

    + + +
    +
    + +
    + The command-line arguments passed into JSDoc. +
    + + + +
    Type:
    +
      +
    • + +Array + + +
    • +
    + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + +
    + + + +
    +

    <static> conf :Object

    + + +
    +
    + +
    + The parsed JSON data from the configuration file. +
    + + + +
    Type:
    +
      +
    • + +Object + + +
    • +
    + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + +
    + + + +
    +

    <static> opts :Object

    + + +
    +
    + +
    + The command-line arguments, parsed into a key/value hash. +
    + + + +
    Type:
    +
      +
    • + +Object + + +
    • +
    + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + +
    Example
    + +
     if (env.opts.help) { console.log('Helpful message.'); }
    + + +
    + + + +
    +

    <static> run

    + + +
    +
    + +
    + Running start and finish times. +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + +
    + + + +
    +

    <static> version :Object

    + + +
    +
    + +
    + The JSDoc version number and revision date. +
    + + + +
    Type:
    +
      +
    • + +Object + + +
    • +
    + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + +
    + +
    + + + + + + + +
    + +
    + + + + +
    + + + +
    + +
    + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:10 GMT+0300 (EEST) +
    + + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/out/functions..html b/data/scripts/jsdoc/out/functions..html new file mode 100644 index 00000000..57c46574 --- /dev/null +++ b/data/scripts/jsdoc/out/functions..html @@ -0,0 +1,124 @@ + + + + + JSDoc: Namespace: General javascript functions. + + + + + + + + + + +
    + +

    Namespace: General javascript functions.

    + + + + + +
    + +
    +

    + General javascript functions. +

    + +
    + +
    +
    + + + + +
    This file contains the Genral Functions javascript namespace. +It contains functions that apply both on the front and back +end of the application.
    + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    + + + + +
    + + + + + + + + + + + + + + + + + + +
    + +
    + + + + +
    + + + +
    + +
    + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:22:10 GMT+0300 (EEST) +
    + + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/out/general_functions.js.html b/data/scripts/jsdoc/out/general_functions.js.html new file mode 100644 index 00000000..f52c1b0a --- /dev/null +++ b/data/scripts/jsdoc/out/general_functions.js.html @@ -0,0 +1,134 @@ + + + + + JSDoc: Source: general_functions.js + + + + + + + + + + +
    + +

    Source: general_functions.js

    + + + + + +
    +
    +
    /**
    + * This file contains the Genral Functions javascript namespace.
    + * It contains functions that apply both on the front and back 
    + * end of the application.
    + * 
    + * @namespace General javascript functions.
    + */
    +var GeneralFunctions = {};
    +
    +/**
    + * This functions displays a message box in
    + * the admin array. It is usefull when user
    + * decisions or verifications are needed.
    + *  
    + * @param {string} title The title of the message box.
    + * @param {string} message The message of the dialog.
    + * @param {array} messageButtons Contains the dialog  
    + * buttons along with their functions.
    + */
    +GeneralFunctions.displayMessageBox = function(title, message, messageButtons) {
    +    // Check arguments integrity.
    +    if (title == undefined || title == "") {
    +        title = "<No Title Given>";
    +    }   
    +        
    +    if (message == undefined || message == "") {
    +        message = "<No Message Given>";
    +    } 
    +    
    +    if (messageButtons == undefined) {
    +        messageButtons = {
    +            Close: function() {
    +                jQuery("#message_box").dialog("close");
    +            }
    +        };
    +    }
    +    
    +    // Destroy previous dialog instances.
    +    jQuery("#message_box").dialog("destroy");
    +    jQuery("#message_box").remove();
    +    
    +    // Create the html of the message box.
    +    jQuery("body").append(
    +        "<div id='message_box' title='" + title + "'>" +
    +        "<p>" + message + "</p>" +
    +        "</div>"
    +    );    
    +
    +    jQuery("#message_box").dialog({
    +        autoOpen        : false,
    +        modal           : true,
    +        resize          : "auto",
    +        width           : 400,
    +        height          : "auto",
    +        resizable       : false,
    +        buttons         : messageButtons,
    +        closeOnEscape   : false
    +    });
    +    
    +    jQuery("#message_box").dialog("open"); 
    +    jQuery("#message_box .ui-dialog-titlebar-close").hide();
    +}
    +
    +/**
    + * This method centers a DOM element vertically and horizontally
    + * on the page.
    + * 
    + * @param {object} elementHandle The object that is going to be 
    + * centered.
    + */
    +GeneralFunctions.centerElementOnPage = function(elementHandle) {
    +    // Center main frame vertical middle
    +    $(window).resize(function() {
    +        var elementLeft = ($(window).width() - elementHandle.outerWidth()) / 2;
    +        var elementTop = ($(window).height() - elementHandle.outerHeight()) / 2;
    +        elementTop = (elementTop > 0 ) ? elementTop : 20;
    +        
    +        elementHandle.css({
    +            position    : 'absolute',
    +            left        : elementLeft,
    +            top         : elementTop
    +        }); 
    +    });
    +    $(window).resize();
    +}
    +
    +
    + + + + +
    + + + +
    + +
    + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:22:10 GMT+0300 (EEST) +
    + + + + + diff --git a/data/scripts/jsdoc/out/global.html b/data/scripts/jsdoc/out/global.html new file mode 100644 index 00000000..874eef5c --- /dev/null +++ b/data/scripts/jsdoc/out/global.html @@ -0,0 +1,195 @@ + + + + + JSDoc: Global + + + + + + + + + + +
    + +

    Global

    + + + + + +
    + +
    +

    + +

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

    Methods

    + +
    + +
    +

    main()

    + + +
    +
    + + +
    + Run the jsdoc application. +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + +
    Source:
    +
    + + + + + + + +
    To Do:
    +
    +
      +
    • Refactor function (and require statements) into smaller functions
    • +
    +
    + +
    + + + + + + + + + + + + + +
    + +
    + + + + + +
    + +
    + + + + +
    + + + +
    + +
    + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:10 GMT+0300 (EEST) +
    + + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/out/index.html b/data/scripts/jsdoc/out/index.html new file mode 100644 index 00000000..af89af85 --- /dev/null +++ b/data/scripts/jsdoc/out/index.html @@ -0,0 +1,63 @@ + + + + + JSDoc: Index + + + + + + + + + + +
    + +

    Index

    + + + + + + + +

    + + + + + + + + + + + + + + + + + + + +
    + + + +
    + +
    + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:22:10 GMT+0300 (EEST) +
    + + + + + \ No newline at end of file diff --git a/data/scripts/jsdoc/out/jsdoc.js.html b/data/scripts/jsdoc/out/jsdoc.js.html new file mode 100644 index 00000000..c8b3fb7d --- /dev/null +++ b/data/scripts/jsdoc/out/jsdoc.js.html @@ -0,0 +1,358 @@ + + + + + JSDoc: Source: jsdoc.js + + + + + + + + + + +
    + +

    Source: jsdoc.js

    + + + + + +
    +
    +
    /*global app: true, args: true, env: true, publish: true */
    +/**
    + * @project jsdoc
    + * @author Michael Mathews <micmath@gmail.com>
    + * @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
    +
    +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
    +
    +/**
    + * Data representing the environment in which this app is running.
    + *
    + * @namespace
    + * @name env
    + */
    +require('lib/jsdoc/util/global').env = {
    +    /**
    +     * Running start and finish times.
    +     * 
    +     * @memberof env
    +     */
    +    run: {
    +        start: new Date(),
    +        finish: null
    +    },
    +
    +    /**
    +     * The command-line arguments passed into JSDoc.
    +     *
    +     * @type Array
    +     * @memberof env
    +     */
    +    args: [],
    +
    +    /**
    +     * The parsed JSON data from the configuration file.
    +     * 
    +     * @type Object
    +     * @memberof env
    +     */
    +    conf: {},
    +
    +    /**
    +     * The absolute path to the base directory of the JSDoc application.
    +     * 
    +     * @private
    +     * @deprecated Use `__dirname` instead.
    +     * @type string
    +     * @memberof env
    +     */
    +    dirname: '.',
    +
    +    /**
    +     * The command-line arguments, parsed into a key/value hash.
    +     * 
    +     * @type Object
    +     * @memberof env
    +     * @example if (env.opts.help) { console.log('Helpful message.'); }
    +    */
    +    opts: {},
    +
    +    /**
    +     * The JSDoc version number and revision date.
    +     * 
    +     * @type Object
    +     * @memberof env
    +     */
    +    version: {}
    +};
    +
    +// initialize the environment for the current JavaScript VM
    +(function(args) {
    +    var vm = require('jsdoc/util/vm').vm;
    +    // TODO: may need to move this file to support Node.js
    +    require('initialize')[vm](args);
    +})( Array.prototype.slice.call(arguments, 0) );
    +
    +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
    +
    +/**
    + * Data that must be shared across the entire application.
    + * @namespace
    + * @name app
    + */
    +require('lib/jsdoc/util/global').app = {
    +    jsdoc: {
    +        scanner: new (require('jsdoc/src/scanner').Scanner)(),
    +        parser: new (require('jsdoc/src/parser').Parser)(),
    +        name: require('jsdoc/name')
    +    }
    +};
    +
    +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
    +
    +/**
    +    Try to recursively print out all key/values in an object.
    +    @global
    +    @private
    +    @param {Object} ... Object/s to dump out to console.
    + */
    +function dump() {
    +    var doop = require('jsdoc/util/doop').doop;
    +    var _dump = require('jsdoc/util/dumper').dump;
    +    for (var i = 0, l = arguments.length; i < l; i++) {
    +        console.log( _dump(doop(arguments[i])) );
    +    }
    +}
    +
    +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
    +
    +/**
    + * Run the jsdoc application.
    + * @todo Refactor function (and require statements) into smaller functions
    + */
    +function main() {
    +    var _ = require('underscore');
    +    var fs = require('jsdoc/fs');
    +    var path = require('jsdoc/path');
    +    var taffy = require('taffydb').taffy;
    +
    +    var jsdoc = {
    +        augment: require('jsdoc/augment'),
    +        borrow: require('jsdoc/borrow'),
    +        Config: require('jsdoc/config'),
    +        opts: {
    +            args: require('jsdoc/opts/args')
    +        },
    +        'package': require('jsdoc/package'),
    +        plugins: require('jsdoc/plugins'),
    +        Readme: require('jsdoc/readme'),
    +        src: {
    +            filter: require('jsdoc/src/filter'),
    +            handlers: require('jsdoc/src/handlers')
    +        },
    +        tutorial: {
    +            resolver: require('jsdoc/tutorial/resolver')
    +        },
    +        util: {
    +            include: require('jsdoc/util/include')
    +        }
    +    };
    +
    +    var confPath;
    +    var defaultOpts;
    +    var docs;
    +    var filter;
    +    var i;
    +    var info;
    +    var l;
    +    var packageDocs;
    +    var packageJson;
    +    var sourceFiles;
    +    var template;
    +
    +
    +    defaultOpts = {
    +        destination: './out/',
    +        encoding: 'utf8'
    +    };
    +
    +    // get JSDoc version number
    +    info = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
    +    env.version = {
    +        number: info.version,
    +        revision: new Date(parseInt(info.revision, 10)).toUTCString()
    +    };
    +
    +    env.opts = jsdoc.opts.args.parse(env.args);
    +
    +    confPath = env.opts.configure || path.join(__dirname, 'conf.json');
    +    if ( !fs.statSync(confPath).isFile() ) {
    +        confPath = path.join(__dirname, 'conf.json.EXAMPLE');
    +    }
    +
    +    try {
    +        env.conf = new jsdoc.Config( fs.readFileSync(confPath, 'utf8') )
    +            .get();
    +    }
    +    catch (e) {
    +        throw new Error('Cannot parse the config file ' + confPath + ': ' + e);
    +    }
    +
    +    // look for options on the command line, in the config file, and in the defaults, in that order
    +    env.opts = _.defaults(env.opts, env.conf.opts, defaultOpts);
    +
    +    if (env.opts.help) {
    +        console.log( jsdoc.opts.args.help() );
    +        process.exit(0);
    +    } else if (env.opts.test) {
    +        jsdoc.util.include('test/runner.js');
    +        process.exit(0);
    +    } else if (env.opts.version) {
    +        console.log('JSDoc ' + env.version.number + ' (' + env.version.revision + ')');
    +        process.exit(0);
    +    }
    +
    +    if (env.conf.plugins) {
    +        jsdoc.plugins.installPlugins(env.conf.plugins, app.jsdoc.parser);
    +    }
    +    
    +    if (env.conf.source && env.conf.source.include) {
    +        env.opts._ = (env.opts._ || []).concat(env.conf.source.include);
    +    }
    +
    +    // any source file named package.json or README.md is treated special
    +    for (i = 0, l = env.opts._.length; i < l; i++ ) {
    +        if (/\bpackage\.json$/i.test(env.opts._[i])) {
    +            packageJson = fs.readFileSync( env.opts._[i], 'utf8' );
    +            env.opts._.splice(i--, 1);
    +        }
    +        
    +        if (/(\bREADME|\.md)$/i.test(env.opts._[i])) {
    +            env.opts.readme = new jsdoc.Readme(env.opts._[i]).html;
    +            env.opts._.splice(i--, 1);
    +        }
    +    }
    +    
    +    if (env.conf.source && env.opts._.length > 0) { // are there any files to scan and parse?
    +        filter = new jsdoc.src.filter.Filter(env.conf.source);
    +
    +        sourceFiles = app.jsdoc.scanner.scan(env.opts._, (env.opts.recurse? 10 : undefined), filter);
    +
    +        jsdoc.src.handlers.attachTo(app.jsdoc.parser);
    +
    +        docs = app.jsdoc.parser.parse(sourceFiles, env.opts.encoding);
    +
    +        //The files are ALWAYS useful for the templates to have
    +        //If there is no package.json, just create an empty package
    +        packageDocs = new jsdoc.package.Package(packageJson);
    +        packageDocs.files = sourceFiles || [];
    +        docs.push(packageDocs);
    +
    +        jsdoc.borrow.indexAll(docs);
    +
    +        jsdoc.augment.addInherited(docs);
    +        jsdoc.borrow.resolveBorrows(docs);
    +
    +        if (env.opts.explain) {
    +            dump(docs);
    +            process.exit(0);
    +        }
    +
    +        if (env.opts.tutorials) {
    +            jsdoc.tutorial.resolver.load(env.opts.tutorials);
    +            jsdoc.tutorial.resolver.resolve();
    +        }
    +
    +        env.opts.template = (function() {
    +            var publish = env.opts.template || 'templates/default';
    +            // if we don't find it, keep the user-specified value so the error message is useful
    +            return path.getResourcePath(publish) || env.opts.template;
    +        })();
    +
    +        try {
    +            template = require(env.opts.template + '/publish');
    +        }
    +        catch(e) {
    +            throw new Error('Unable to load template: ' + e.message || e);
    +        }
    +
    +        // templates should include a publish.js file that exports a "publish" function
    +        if (template.publish && typeof template.publish === 'function') {
    +            // convert this from a URI back to a path if necessary
    +            env.opts.template = path._uriToPath(env.opts.template);
    +            template.publish(
    +                taffy(docs),
    +                env.opts,
    +                jsdoc.tutorial.resolver.root
    +            );
    +        }
    +        else {
    +            // old templates define a global "publish" function, which is deprecated
    +            jsdoc.util.include(env.opts.template + '/publish.js');
    +            if (publish && typeof publish === 'function') {
    +                console.log( env.opts.template + ' uses a global "publish" function, which is ' +
    +                    'deprecated and may not be supported in future versions. ' +
    +                    'Please update the template to use "exports.publish" instead.' );
    +                // convert this from a URI back to a path if necessary
    +                env.opts.template = path._uriToPath(env.opts.template);
    +                publish(
    +                    taffy(docs),
    +                    env.opts,
    +                    jsdoc.tutorial.resolver.root
    +                );
    +            }
    +            else {
    +                throw new Error( env.opts.template + ' does not export a "publish" function.' );
    +            }
    +        }
    +    }
    +}
    +
    +try {
    +    main();
    +    env.run.finish = new Date();
    +    process.exit(0);
    +}
    +catch(e) {
    +    env.run.finish = new Date();
    +    if (e.rhinoException != null) {
    +        e.rhinoException.printStackTrace();
    +        process.exit(1);
    +    } else {
    +        throw e;
    +    }
    +}
    +
    +
    +
    + + + + +
    + + + +
    + +
    + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 17:20:10 GMT+0300 (EEST) +
    + + + + + diff --git a/data/scripts/jsdoc/out/scripts/linenumber.js b/data/scripts/jsdoc/out/scripts/linenumber.js new file mode 100644 index 00000000..a0c570d5 --- /dev/null +++ b/data/scripts/jsdoc/out/scripts/linenumber.js @@ -0,0 +1,17 @@ +(function() { + var counter = 0; + var numbered; + var source = document.getElementsByClassName('prettyprint source'); + + if (source && source[0]) { + source = source[0].getElementsByTagName('code')[0]; + + numbered = source.innerHTML.split('\n'); + numbered = numbered.map(function(item) { + counter++; + return '' + item; + }); + + source.innerHTML = numbered.join('\n'); + } +})(); diff --git a/data/scripts/jsdoc/out/scripts/prettify/Apache-License-2.0.txt b/data/scripts/jsdoc/out/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/data/scripts/jsdoc/out/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/data/scripts/jsdoc/out/scripts/prettify/lang-css.js b/data/scripts/jsdoc/out/scripts/prettify/lang-css.js new file mode 100644 index 00000000..041e1f59 --- /dev/null +++ b/data/scripts/jsdoc/out/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/data/scripts/jsdoc/out/scripts/prettify/prettify.js b/data/scripts/jsdoc/out/scripts/prettify/prettify.js new file mode 100644 index 00000000..eef5ad7e --- /dev/null +++ b/data/scripts/jsdoc/out/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + }) + .synonym('throws'); +2. lookUp(title) + Used to lookup a tag. Returns either the tag or false if it's not defined +3. isNamespace(kind) + Used to determine if a particular doclet type represents a namespace +4. normalise(title) + Used to find the canonical name of a tag. The name passed in might be that + name or a synonym + +### Node Visitors + +At the lowest level, plugin authors can process each node in the parse tree by +defining a node visitor that will visit each node, creating an opportunity to +do things like modify comments and trigger parser events for any arbitrary piece +of code. + +Plugins can define a node visitor by exporting a ```nodeVisitor``` object that +contains a ```visitNode``` function, like so: + + exports.nodeVisitor = { + visitNode: function(node, e, parser, currentSourceName) { + //do all sorts of crazy things here + } + } + +The function is called on each node with the following parameters: + +- node: the node of the parse tree +- e: the event. If the node is one that the parser handles, this will already + be populated with the same things described in the _symbolFound_ event above. + Otherwise, it will be an empty object on which to set various properties. +- parser: the parser +- currentSourceName: the name of the file being parsed + +#### Making things happen + +The primary reasons to implement a node visitor are to be able to document +things that aren't normally documented (like function calls that create classes) +or to auto generate documentation for code that isn't documented. For instance, +a plugin might look for calls to a "_trigger" method since it knows that means +an event is fired and then generate documentation for the event. + +To make things happen, the ```visitNode``` function should modify properties +of the event parameter. In general the goal is to construct a comment and then +get an event to fire. After the parser lets all of the node visitors have a +look at the node, it looks to see if the event object has a ```comment``` +property and an ```event``` property. If it has both, the event named in the event +property is fired. The event is usually "symbolFound" or "jsdocCommentFound", +but theoretically, a plugin could define its own events and handle them. + +#### Example + +Below is an example of what a plugin for documenting jQuery UI widgets might do. +jQuery UI uses a factory function call to create widget classes. The plugin +looks for that function call and creates a symbol with documentation. It also +looks for any "this._trigger" function calls and automatically creates +documentation for the events that are triggered: + + exports.nodeVisitor = { + visitNode: function(node, e, parser, currentSourceName) { + if (node.type === Token.OBJECTLIT && node.parent && node.parent.type === Token.CALL && isInWidgetFactory(node, 1)) { + var widgetName = node.parent.arguments.get(0).toSource(); + e.id = 'astnode' + node.hashCode(); // the id of the object literal node + e.comment = String(node.parent.jsDoc||''); + e.lineno = node.parent.getLineno(); + e.filename = currentSourceName; + e.astnode = node; + e.code = { + name: "" + widgetName.substring(1, widgetName.length() - 1), + type: "class", + node: node + }; + e.event = "symbolFound"; + e.finishers = [parser.addDocletRef]; + + addCommentTag(e, "param", "{Object=} options A set of configuration options"); + } + else if(isTriggerCall(node)) { + var nameNode = node.arguments.get(0); + eventName = String((nameNode.type == Token.STRING) ? nameNode.value : nameNode.toSource()), + func = {}, + comment = "@event\n", + eventKey = ""; + + if (node.enclosingFunction) { + func.id = 'astnode'+node.enclosingFunction.hashCode(); + func.doclet = parser.refs[func.id]; + } + if(func.doclet) { + func.doclet.addTag("fires", eventName); + if (func.doclet.memberof) { + eventKey = func.doclet.memberof + "#event:" + eventName; + comment += "@name " + func.doclet.memberof + "#" + eventName; + } + } + e.comment = comment; + e.lineno = node.getLineno(); + e.filename = currentSourceName; + e.event = "jsdocCommentFound"; + } + } + }; + function isTriggerCall(node) { + if(node.type != Token.CALL) { return false; } + var target = node.getTarget(), + left = target && target.left && String(target.left.toSource()), + right = target && target.right && String(target.right.toSource()); + return (left === "this" && right === "_trigger"); + } + + function isInWidgetFactory(node, depth) { + var parent = node.parent, + d = 0; + while(parent && (!depth || d < depth)) { + if (parent.type === Token.CALL) { + var target = parent.getTarget(), + left = target && target.left && String(target.left.toSource()), + right = target && target.right && String(target.right.toSource()); + return ((left === "$" || left === "jQuery") && right === "widget"); + } else { + parent = parent.parent; + d++; + } + } + return false; + } + +You'll notice a "finishers" property set. The finishers property should contain +an array of functions to be called after the event is fired and all the handlers +have processed it. The parser provides an ```addDocletRef``` function that adds the +doclet to the map (keyed off of the id property) of doclets it knows about. + +Lastly, the visitors are executed in the order the plugins are listed in the +conf.json file. A plugin can stop later plugins from visiting a node by +setting a ```stopPropagation``` property on the event object (e.stopPropagation = true). +A plugin can stop the event from firing setting a ```preventDefault``` property. + +### Throwing Errors + +If you wish your plugin to throw an error, do it using the `handle` function in +the `jsdoc/util/error` module: + + require('jsdoc/util/error').handle( new Error('I do not like green eggs and ham!') ); + +By default, this will throw the error, halting the execution of JSDoc. However, +if the user enabled JSDoc's `--lenient` switch, JSDoc will simply log the error +to the console and continue. + +Packaging JSDoc 3 Plugins +---- + +The JSDoc 3 Jakefile has an ```install``` task that can be used to install a +plugin into the JSDoc directory. So running the following will install the +plugin: + + $>jake install[path/to/YourPluginFolder] + +**Note**: On some operating systems, including OS X, you may need to quote the +target name and parameters: + + $>jake 'install[path/to/YourPluginFolder]' + +The task is passed a directory that should look something like the following: + + YourPluginFolder + |- plugins + | |- YourPlugin.js + | \- test + | |- fixtures + | | \- YourFixtures.js + | \- specs + | \- YourTests.js + \- templates + \- YourTemplate + \- publish.js diff --git a/data/scripts/jsdoc/plugins/commentConvert.js b/data/scripts/jsdoc/plugins/commentConvert.js new file mode 100644 index 00000000..244c3313 --- /dev/null +++ b/data/scripts/jsdoc/plugins/commentConvert.js @@ -0,0 +1,21 @@ +/** + @overview Demonstrate how to modify the source code before the parser sees it. + @module plugins/commentConvert + @author Michael Mathews + */ + + +exports.handlers = { + /// + /// Convert ///-style comments into jsdoc comments. + /// @param e + /// @param e.filename + /// @param e.source + /// + beforeParse: function(e) { + e.source = e.source.replace(/(\n[ \t]*\/\/\/[^\n]*)+/g, function($) { + var replacement = '\n/**' + $.replace(/^[ \t]*\/\/\//mg, '').replace(/(\n$|$)/, '*/$1'); + return replacement; + }); + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/plugins/commentsOnly.js b/data/scripts/jsdoc/plugins/commentsOnly.js new file mode 100644 index 00000000..e5106e9f --- /dev/null +++ b/data/scripts/jsdoc/plugins/commentsOnly.js @@ -0,0 +1,17 @@ +/** + * @overview Remove everything in a file except JSDoc-style comments. By enabling this plugin, you + * can document source files that are not valid JavaScript (including source files for other + * languages). + * @module plugins/commentsOnly + * @author Jeff Williams + */ + +exports.handlers = { + beforeParse: function(e) { + // a JSDoc comment looks like: /**[one or more chars]*/ + var comments = e.source.match(/\/\*\*[\s\S]+?\*\//g); + if (comments) { + e.source = comments.join('\n\n'); + } + } +}; diff --git a/data/scripts/jsdoc/plugins/escapeHtml.js b/data/scripts/jsdoc/plugins/escapeHtml.js new file mode 100644 index 00000000..d77c2629 --- /dev/null +++ b/data/scripts/jsdoc/plugins/escapeHtml.js @@ -0,0 +1,21 @@ +/** + @overview Escape HTML tags in descriptions. + @module plugins/escapeHtml + @author Michael Mathews + */ + + +exports.handlers = { + /** + Translate HTML tags in descriptions into safe entities. + Replaces <, & and newlines + */ + newDoclet: function(e) { + if (e.doclet.description) { + e.doclet.description = e.doclet.description + .replace(/&/g,'&') + .replace(/'); + } + } +}; diff --git a/data/scripts/jsdoc/plugins/eventDumper.js b/data/scripts/jsdoc/plugins/eventDumper.js new file mode 100644 index 00000000..70eff028 --- /dev/null +++ b/data/scripts/jsdoc/plugins/eventDumper.js @@ -0,0 +1,69 @@ +/*global env: true */ +/** + * @overview Dump information about parser events to the console. + * @module plugins/eventDumper + * @author Jeff Williams + */ + +var _ = require('underscore'); +var util = require('util'); + +var conf = env.conf.eventDumper || {}; + +// Dump the included parser events (defaults to all events) +var events = conf.include || [ + 'parseBegin', + 'fileBegin', + 'beforeParse', + 'jsdocCommentFound', + 'symbolFound', + 'newDoclet', + 'fileComplete', + 'parseComplete' +]; +// Don't dump the excluded parser events +if (conf.exclude) { + events = _.difference(events, conf.exclude); +} + +/** + * Get rid of native Java crud in an event object so that JSON.stringify() works. + * @param {object} e The event object. + * @return {object} The fixed-up object. + */ +function cleanse(e) { + var result = {}; + + Object.keys(e).forEach(function(prop) { + // by default, don't stringify properties that contain an array of functions + if (!conf.includeFunctions && util.isArray(e[prop]) && e[prop][0] && + String(typeof e[prop][0]) === 'function') { + result[prop] = 'function[' + e[prop].length + ']'; + } + // never include functions that belong to the object + else if (typeof e[prop] === 'function') { + // do nothing + } + // go down an extra level for these + else if (['code', 'doclet', 'meta'].indexOf(prop) !== -1) { + result[prop] = cleanse(e[prop]); + } + else { + result[prop] = String(e[prop]); + } + }); + + return result; +} + + +exports.handlers = {}; + +events.forEach(function(eventType) { + exports.handlers[eventType] = function(e) { + console.log( JSON.stringify({ + type: eventType, + content: cleanse(e) + }, null, 4) ); + }; +}); diff --git a/data/scripts/jsdoc/plugins/markdown.js b/data/scripts/jsdoc/plugins/markdown.js new file mode 100644 index 00000000..0ec29ae5 --- /dev/null +++ b/data/scripts/jsdoc/plugins/markdown.js @@ -0,0 +1,67 @@ +/*global env: true */ +/** + * @overview Translate doclet descriptions from MarkDown into HTML. + * @module plugins/markdown + * @author Michael Mathews + * @author Ben Blank + */ +var conf = env.conf.markdown; +var defaultTags = [ "classdesc", "description", "params", "properties", "returns", "see"]; +var hasOwnProp = Object.prototype.hasOwnProperty; +var parse = require('jsdoc/util/markdown').getParser(); +var tags = []; +var excludeTags = []; + +/** + * Process the markdown source in a doclet. The properties that should be + * processed are configurable, but always include "classdesc", "description", + * "params", "properties", and "returns". Handled properties can be bare + * strings, objects, or arrays of objects. + */ +function process(doclet) { + tags.forEach(function(tag) { + if ( !hasOwnProp.call(doclet, tag) ) { + return; + } + + if (typeof doclet[tag] === "string" && + (tag != 'see' || + // treat '@see' specially, since we only want to process @see text that contains links + (tag == 'see' && doclet[tag].indexOf('[') != -1))) { + doclet[tag] = parse(doclet[tag]); + } else if (doclet[tag] instanceof Array) { + doclet[tag].forEach(function(value, index, original){ + var inner = {}; + inner[tag] = value; + process(inner); + original[index] = inner[tag]; + }); + } else if (doclet[tag]) { + process(doclet[tag]); + } + }); +} + +// set up the list of "tags" (properties) to process +if (conf && conf.tags) { + tags = conf.tags.slice(); +} +// set up the list of default tags to exclude from processing +if (conf && conf.excludeTags) { + excludeTags = conf.excludeTags.slice(); +} +defaultTags.forEach(function(tag) { + if (excludeTags.indexOf(tag) === -1 && tags.indexOf(tag) === -1) { + tags.push(tag); + } +}); + +exports.handlers = { + /** + * Translate markdown syntax in a new doclet's description into HTML. Is run + * by JSDoc 3 whenever a "newDoclet" event fires. + */ + newDoclet: function(e) { + process(e.doclet); + } +}; diff --git a/data/scripts/jsdoc/plugins/markdown.md b/data/scripts/jsdoc/plugins/markdown.md new file mode 100644 index 00000000..578d751e --- /dev/null +++ b/data/scripts/jsdoc/plugins/markdown.md @@ -0,0 +1,86 @@ +# How to use the Markdown plugin + +For most users, all you need to do is add the plugin to your JSDoc configuration (`conf.json`) as you would any other, by adding a reference to the plugin in the `"plugins"` entry: + + ... + "plugins": [ "plugins/markdown" ] + ... + +This will cause Markdown in `@description` tags (including implicit descriptions without tags), `@classdesc` tags, `@param` tags, `@property` tags, and `@returns` tags to be parsed. + +Also, be sure to use leading asterisks in your doc comments! If you omit the leading asterisks, JSDoc's code parser may remove other asterisks that are used for Markdown formatting. + +# Configuring the Markdown plugin + +The plugin also offers several configuration options for advanced users who want GitHub integration, extended tag support, etc. All configuration for the Markdown plugin should be added to a `"markdown"` property in your JSDoc configuration: + + ... + "plugins": [ "plugins/markdown" ], + + "markdown": { + "opt1": "value", + "opt2": [ "foo", "bar", "baz" ] + } + ... + +## Choosing a parser + +The plugin currently supports two Markdown parsers. You can select which parser to use by adding a `"parser"` property to your Markdown configuration: + + ... + "plugins": [ "plugins/markdown" ], + + "markdown": { + "parser": "gfm" + } + ... + +### Dominic "evilstreak" Baggott's markdown-js + +The default parser is Dominic Baggott's excellent [markdown-js](https://github.com/evilstreak/markdown-js). It can be explicitly selected by setting the `parser` to `evilstreak` and has one additional (and optional) configuration option, `dialect`, which can be used to select which of markdown-js' built-in dialects to use. If omitted, markdown-js' default dialect will be used. + + ... + "plugins": [ "plugins/markdown" ], + + "markdown": { + "parser": "evilstreak", + "dialect": "Maruku" + } + ... + +### GitHib Flavored Markdown + +The alternative parser is the modified Showdown parser supplied by GitHub for their [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/). GFM provides several enhancements to standard Markdown syntax (see its documentation) intended to be useful to developers. It *also* has the ability to quickly link to GitHub repositories, files, and issues. It can be selected by setting the `parser` to `gfm` and supports three additional (and optional) configuration options. + +The `hardwrap` option controls the hard wrapping of line ends. Unlike standard Markdown, GFM considers a single newline to indicate a "hard break" in the paragraph, but this doesn't work well with the line length limitations commonly used with comment documentation, so is disabled by default. If you want to turn hard wrapping back on, set `hardwrap` to `true` (or any non-falsy value). + +The `githubRepoName` and `githubRepoOwner` indicate which GitHub repo should be used for GitHub links that do not fully specify a repo. These options have no effect unless used together, and if they are omitted, several of GFM's default link types will be unavailable. Conversely, if you supply both `github*` options but do not explicitly select `gfm` as your parser, it will be automatically selected for you. + + ... + "plugins": [ "plugins/markdown" ], + + "markdown": { + "parser": "gfm", + "hardwrap": true + } + ... + +### Why two parsers? + +The "evilstreak" parser is flexible, extensible, currently maintained, and was the only parser available in earlier versions of the Markdown plugin, but doesn't support the useful GFM extensions. The "gfm" parser is based on the no-longer-maintained Showdown parser, but it provides GFM extensions. + +In the future, if GFM support is made available for the "evilstreak" parser, this plugin will drop the "gfm" parser in favor of that support. + +## Extended tag support + +While the Markdown plugin already supports JSDoc's default tags, if you're using other plugins, you may well have extra tags available. You can tell the Markdown plugin to handle those extra tags as well using the `tags` property, which is an array of the tags* it should check in addition to the default set. + + ... + "plugins": [ "plugins/markdown" ], + + "markdown": { + "tags": [ "foo", "bars", "bazzes" ] + } + ... + +\* Because the Markdown plugin works with JSDoc's internal representation rather than with the source comments, the names you need to enter in the `tags` property aren't necessarily the same as the actual tag names. For example, in the default set of tags, `@param` is stored under `params`. If you are having trouble getting the Markdown plugin to work with your extra tags, either take a peek at the output of JSDoc's `--explain` command-line parameter (which displays the internal state that plugins work with) or ask the plugin author which "doclet properties" their plugin uses. The Markdown plugin supports strings, arrays, and objects/subdoclets. diff --git a/data/scripts/jsdoc/plugins/overloadHelper.js b/data/scripts/jsdoc/plugins/overloadHelper.js new file mode 100644 index 00000000..35fe3213 --- /dev/null +++ b/data/scripts/jsdoc/plugins/overloadHelper.js @@ -0,0 +1,184 @@ +/** + * The Overload Helper plugin automatically adds a signature-like string to the longnames of + * overloaded functions and methods. In JSDoc, this string is known as a _variation_. (The longnames + * of overloaded constructor functions are _not_ updated, so that JSDoc can identify the class' + * members correctly.) + * + * Using this plugin allows you to link to overloaded functions without manually adding `@variation` + * tags to your documentation. + * + * For example, suppose your code includes a function named `foo` that you can call in the + * following ways: + * + * + `foo()` + * + `foo(bar)` + * + `foo(bar, baz)` (where `baz` is repeatable) + * + * This plugin assigns the following variations and longnames to each version of `foo`: + * + * + `foo()` gets the variation `()` and the longname `foo()`. + * + `foo(bar)` gets the variation `(bar)` and the longname `foo(bar)`. + * + `foo(bar, baz)` (where `baz` is repeatable) gets the variation `(bar, ...baz)` and the longname + * `foo(bar, ...baz)`. + * + * You can then link to these functions with `{@link foo()}`, `{@link foo(bar)}`, and + * `{@link foo(bar, ...baz)`. Note that the variation is based on the names of the function + * parameters, _not_ their types. + * + * If you prefer to manually assign variations to certain functions, you can still do so with the + * `@variation` tag. This plugin will not change these variations or add more variations for that + * function, as long as the variations you've defined result in unique longnames. + * + * If an overloaded function includes multiple signatures with the same parameter names, the plugin + * will assign numeric variations instead, starting at `(1)` and counting upwards. + * + * @module plugins/overloadHelper + * @author Jeff Williams + * @license Apache License 2.0 + */ + +// lookup table of function doclets by longname +var functionDoclets; + +function hasUniqueValues(obj) { + var isUnique = true; + var seen = []; + Object.keys(obj).forEach(function(key) { + if (seen.indexOf(obj[key]) !== -1) { + isUnique = false; + } + + seen.push(obj[key]); + }); + + return isUnique; +} + +function getParamNames(params) { + var names = []; + + params.forEach(function(param) { + var name = param.name || ''; + if (param.variable) { + name = '...' + name; + } + if (name !== '') { + names.push(name); + } + }); + + return names.length ? names.join(', ') : ''; +} + +function getParamVariation(doclet) { + return getParamNames(doclet.params || []); +} + +function getUniqueVariations(doclets) { + var counter = 0; + var variations = {}; + var docletKeys = Object.keys(doclets); + + function getUniqueNumbers() { + var format = require('util').format; + + docletKeys.forEach(function(doclet) { + var newLongname; + + while (true) { + counter++; + variations[doclet] = String(counter); + + // is this longname + variation unique? + newLongname = format('%s(%s)', doclets[doclet].longname, variations[doclet]); + if ( !functionDoclets[newLongname] ) { + break; + } + } + }); + } + + function getUniqueNames() { + // start by trying to preserve existing variations + docletKeys.forEach(function(doclet) { + variations[doclet] = doclets[doclet].variation || getParamVariation(doclets[doclet]); + }); + + // if they're identical, try again, without preserving existing variations + if ( !hasUniqueValues(variations) ) { + docletKeys.forEach(function(doclet) { + variations[doclet] = getParamVariation(doclets[doclet]); + }); + + // if they're STILL identical, switch to numeric variations + if ( !hasUniqueValues(variations) ) { + getUniqueNumbers(); + } + } + } + + // are we already using numeric variations? if so, keep doing that + if (functionDoclets[doclets.newDoclet.longname + '(1)']) { + getUniqueNumbers(); + } + else { + getUniqueNames(); + } + + return variations; +} + +function ensureUniqueLongname(newDoclet) { + var doclets = { + oldDoclet: functionDoclets[newDoclet.longname], + newDoclet: newDoclet + }; + var docletKeys = Object.keys(doclets); + var oldDocletLongname; + var variations = {}; + + if (doclets.oldDoclet) { + oldDocletLongname = doclets.oldDoclet.longname; + // if the shared longname has a variation, like MyClass#myLongname(variation), + // remove the variation + if (doclets.oldDoclet.variation || doclets.oldDoclet.variation === '') { + docletKeys.forEach(function(doclet) { + doclets[doclet].longname = doclets[doclet].longname.replace(/\([\s\S]*\)$/, ''); + doclets[doclet].variation = null; + }); + } + + variations = getUniqueVariations(doclets); + + // update the longnames/variations + docletKeys.forEach(function(doclet) { + doclets[doclet].longname += '(' + variations[doclet] + ')'; + doclets[doclet].variation = variations[doclet]; + }); + + // update the old doclet in the lookup table + functionDoclets[oldDocletLongname] = null; + functionDoclets[doclets.oldDoclet.longname] = doclets.oldDoclet; + } + + // always store the new doclet in the lookup table + functionDoclets[doclets.newDoclet.longname] = doclets.newDoclet; + + return doclets.newDoclet; +} + +exports.handlers = { + parseBegin: function() { + functionDoclets = {}; + }, + + newDoclet: function(e) { + if (e.doclet.kind === 'function') { + e.doclet = ensureUniqueLongname(e.doclet); + } + }, + + parseComplete: function() { + functionDoclets = null; + } +}; diff --git a/data/scripts/jsdoc/plugins/partial.js b/data/scripts/jsdoc/plugins/partial.js new file mode 100644 index 00000000..61d20d26 --- /dev/null +++ b/data/scripts/jsdoc/plugins/partial.js @@ -0,0 +1,31 @@ +/*global env: true */ +/** + @overview Adds support for reusable partial jsdoc files. + @module plugins/partial + @author Ludo Antonov + */ + +var fs = require('jsdoc/fs'); +var path = require('path'); + +exports.handlers = { + /// + /// Include a partial jsdoc + /// @param e + /// @param e.filename + /// @param e.source + /// + /// @example + /// @partial "partial_doc.jsdoc" + /// + beforeParse: function(e) { + e.source = e.source.replace(/(@partial \".*\")+/g, function($) { + var pathArg = $.match(/\".*\"/)[0].replace(/"/g,''); + var fullPath = path.join(e.filename , '..', pathArg); + + var partialData = fs.readFileSync(fullPath, env.opts.encoding); + + return partialData; + }); + } +}; diff --git a/data/scripts/jsdoc/plugins/railsTemplate.js b/data/scripts/jsdoc/plugins/railsTemplate.js new file mode 100644 index 00000000..9ca63098 --- /dev/null +++ b/data/scripts/jsdoc/plugins/railsTemplate.js @@ -0,0 +1,20 @@ +/** + @overview Strips the rails template tags from a js.erb file + @module plugins/railsTemplate + @author Jannon Frank + */ + + +exports.handlers = { + /** + * Remove rails tags from the source input (e.g. <% foo bar %>) + * @param e + * @param e.filename + * @param e.source + */ + beforeParse: function(e) { + if (e.filename.match(/\.erb$/)) { + e.source = e.source.replace(/<%.*%>/g, ""); + } + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/plugins/shout.js b/data/scripts/jsdoc/plugins/shout.js new file mode 100644 index 00000000..bb9c2c5d --- /dev/null +++ b/data/scripts/jsdoc/plugins/shout.js @@ -0,0 +1,16 @@ +/** + @overview This is just an example. + @module plugins/shout + @author Michael Mathews + */ + +exports.handlers = { + /** + Make your descriptions more shoutier. + */ + newDoclet: function(e) { + if (typeof e.doclet.description === 'string') { + e.doclet.description = e.doclet.description.toUpperCase(); + } + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/plugins/sourcetag.js b/data/scripts/jsdoc/plugins/sourcetag.js new file mode 100644 index 00000000..efde8e0a --- /dev/null +++ b/data/scripts/jsdoc/plugins/sourcetag.js @@ -0,0 +1,43 @@ +/** + @module plugins/sourcetag + @author Michael Mathews + */ + +exports.handlers = { + /** + Support @source tag. Expected value like: + { "filename": "myfile.js", "lineno": 123 } + Modifies the corresponding meta values on the given doclet. + @source { "filename": "sourcetag.js", "lineno": 13 } + */ + newDoclet: function(e) { + var tags = e.doclet.tags, + tag, + value; + + //console.log(e.doclet); + // any user-defined tags in this doclet? + if (typeof tags !== 'undefined') { + // only interested in the @source tags + tags = tags.filter(function($) { + return $.title === 'source'; + }); + + if (tags.length) { + // take the first one + tag = tags[0]; + + try { + value = JSON.parse(tag.value); + } + catch(e) { + throw new Error('@source tag expects a valid JSON value, like { "filename": "myfile.js", "lineno": 123 }.'); + } + + e.doclet.meta = e.doclet.meta || {}; + e.doclet.meta.filename = value.filename || ''; + e.doclet.meta.lineno = value.lineno || ''; + } + } + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/plugins/test/fixtures/overloadHelper.js b/data/scripts/jsdoc/plugins/test/fixtures/overloadHelper.js new file mode 100644 index 00000000..12c298c6 --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/fixtures/overloadHelper.js @@ -0,0 +1,50 @@ +/** + * A bowl of non-spicy soup. + * @class + *//** + * A bowl of spicy soup. + * @class + * @param {number} spiciness - The spiciness of the soup, in Scoville heat units (SHU). + */ +function Soup(spiciness) {} + +/** + * Slurp the soup. + *//** + * Slurp the soup loudly. + * @param {number} dBA - The slurping volume, in A-weighted decibels. + */ +Soup.prototype.slurp = function(dBA) {}; + +/** + * Salt the soup as needed, using a highly optimized soup-salting heuristic. + *//** + * Salt the soup, specifying the amount of salt to add. + * @variation mg + * @param {number} amount - The amount of salt to add, in milligrams. + */ +Soup.prototype.salt = function(amount) {}; + +/** + * Heat the soup by the specified number of degrees. + * @param {number} degrees - The number of degrees, in Fahrenheit, by which to heat the soup. + *//** + * Heat the soup by the specified number of degrees. + * @variation 1 + * @param {string} degrees - The number of degrees, in Fahrenheit, by which to heat the soup, but + * as a string for some reason. + *//** + * Heat the soup by the specified number of degrees. + * @param {boolean} degrees - The number of degrees, as a boolean. Wait, what? + */ +Soup.prototype.heat = function(degrees) {}; + +/** + * Discard the soup. + * @variation discardSoup + *//** + * Discard the soup by pouring it into the specified container. + * @variation discardSoup + * @param {Object} container - The container in which to discard the soup. + */ +Soup.prototype.discard = function(container) {}; diff --git a/data/scripts/jsdoc/plugins/test/fixtures/railsTemplate.js.erb b/data/scripts/jsdoc/plugins/test/fixtures/railsTemplate.js.erb new file mode 100644 index 00000000..446aa409 --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/fixtures/railsTemplate.js.erb @@ -0,0 +1,20 @@ +/** + @overview Strips the rails template tags from a js.erb file + @module plugins/railsTemplate + @author Jannon Frank + */ + + +exports.handlers = { + /** + * Remove rails tags from the source input (e.g. <% foo bar %>) + * @param e + * @param e.filename + * @param e.source + */ + beforeParse: function(e) { + if (e.filename.match(/\.erb$/)) { + e.source = e.source.replace(/<%.*%> /g, ""); + } + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/plugins/test/fixtures/seetag-markdown.js b/data/scripts/jsdoc/plugins/test/fixtures/seetag-markdown.js new file mode 100644 index 00000000..57137616 --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/fixtures/seetag-markdown.js @@ -0,0 +1,12 @@ +/** +* @see [Nowhere](http://nowhere.com) +*/ +function foo() { +} + +/** +* @see AnObject#myProperty +*/ +function bar() { +} + diff --git a/data/scripts/jsdoc/plugins/test/specs/commentConvert.js b/data/scripts/jsdoc/plugins/test/specs/commentConvert.js new file mode 100644 index 00000000..3f1dc08c --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/commentConvert.js @@ -0,0 +1,14 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe("commentConvert plugin", function() { + var parser = new (require("jsdoc/src/parser")).Parser(), + plugin = require('plugins/commentConvert'), + docSet; + + require('jsdoc/plugins').installPlugins(['plugins/commentConvert'], parser); + docSet = jasmine.getDocSetFromFile("plugins/commentConvert.js", parser); + + it("should convert '///-style comments into jsdoc comments", function() { + var doclet = docSet.getByLongname("module:plugins/commentConvert.handlers.beforeParse"); + expect(doclet.length).toEqual(1); + }); +}); diff --git a/data/scripts/jsdoc/plugins/test/specs/escapeHtml.js b/data/scripts/jsdoc/plugins/test/specs/escapeHtml.js new file mode 100644 index 00000000..8bd88210 --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/escapeHtml.js @@ -0,0 +1,14 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe("escapeHtml plugin", function() { + var parser = new (require("jsdoc/src/parser")).Parser(), + plugin = require('plugins/escapeHtml'), + docSet; + + require('jsdoc/plugins').installPlugins(['plugins/escapeHtml'], parser); + docSet = jasmine.getDocSetFromFile("plugins/escapeHtml.js", parser); + + it("should escape '&', '<' and newlines in doclet descriptions", function() { + var doclet = docSet.getByLongname("module:plugins/escapeHtml.handlers.newDoclet"); + expect(doclet[0].description).toEqual("Translate HTML tags in descriptions into safe entities.
    Replaces <, & and newlines"); + }); +}); diff --git a/data/scripts/jsdoc/plugins/test/specs/markdown.js b/data/scripts/jsdoc/plugins/test/specs/markdown.js new file mode 100644 index 00000000..ce08324f --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/markdown.js @@ -0,0 +1,22 @@ +describe("markdown plugin", function() { + //TODO +}); + +describe("markdown see tag support", function() { + var plugin = require('plugins/markdown'), + docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/seetag-markdown.js'), + foo = docSet.getByLongname('foo')[0], + bar = docSet.getByLongname('bar')[0]; + + it ('should parse @see tags containing links', function() { + plugin.handlers.newDoclet({doclet:foo}); + expect(typeof foo).toEqual('object'); + expect(foo.see[0]).toEqual('

    Nowhere

    '); + }) + + it ('should not parse @see tags that do not contain links', function() { + plugin.handlers.newDoclet({doclet:bar}); + expect(typeof bar).toEqual('object'); + expect(bar.see[0]).toEqual('AnObject#myProperty'); + }) +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/plugins/test/specs/overloadHelper.js b/data/scripts/jsdoc/plugins/test/specs/overloadHelper.js new file mode 100644 index 00000000..c57d2130 --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/overloadHelper.js @@ -0,0 +1,96 @@ +/*global describe: true, expect: true, it: true, jasmine: true, xit: true */ +describe('plugins/overloadHelper', function() { + var parser = new (require('jsdoc/src/parser')).Parser(); + var plugin = require('plugins/overloadHelper'); + var docSet; + + require('jsdoc/plugins').installPlugins(['plugins/overloadHelper'], parser); + docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/overloadHelper.js', parser); + + it('should exist', function() { + expect(plugin).toBeDefined(); + expect(typeof plugin).toBe('object'); + }); + + it('should export handlers', function() { + expect(plugin.handlers).toBeDefined(); + expect(typeof plugin.handlers).toBe('object'); + }); + + it('should export a "newDoclet" handler', function() { + expect(plugin.handlers.newDoclet).toBeDefined(); + expect(typeof plugin.handlers.newDoclet).toBe('function'); + }); + + it('should export a "parseComplete" handler', function() { + expect(plugin.handlers.parseComplete).toBeDefined(); + expect(typeof plugin.handlers.parseComplete).toBe('function'); + }); + + describe('newDoclet handler', function() { + it('should not add unique longnames to constructors', function() { + var soup = docSet.getByLongname('Soup'); + var soup1 = docSet.getByLongname('Soup()'); + var soup2 = docSet.getByLongname('Soup(spiciness)'); + + expect(soup.length).toBe(2); + expect(soup1.length).toBe(0); + expect(soup2.length).toBe(0); + }); + + it('should add unique longnames to methods', function() { + var slurp = docSet.getByLongname('Soup#slurp'); + var slurp1 = docSet.getByLongname('Soup#slurp()'); + var slurp2 = docSet.getByLongname('Soup#slurp(dBA)'); + + expect(slurp.length).toBe(0); + expect(slurp1.length).toBe(1); + expect(slurp2.length).toBe(1); + }); + + it('should update the "variation" property of the method', function() { + var slurp1 = docSet.getByLongname('Soup#slurp()')[0]; + var slurp2 = docSet.getByLongname('Soup#slurp(dBA)')[0]; + + expect(slurp1.variation).toBe(''); + expect(slurp2.variation).toBe('dBA'); + }); + + it('should not add to or change existing variations that are unique', function() { + var salt1 = docSet.getByLongname('Soup#salt'); + var salt2 = docSet.getByLongname('Soup#salt(mg)'); + + expect(salt1.length).toBe(1); + expect(salt2.length).toBe(1); + }); + + it('should not duplicate the names of existing numeric variations', function() { + var heat1 = docSet.getByLongname('Soup#heat(1)'); + var heat2 = docSet.getByLongname('Soup#heat(2)'); + var heat3 = docSet.getByLongname('Soup#heat(3)'); + + expect(heat1.length).toBe(1); + expect(heat2.length).toBe(1); + expect(heat3.length).toBe(1); + }); + + it('should replace identical variations with new, unique variations', function() { + var discard1 = docSet.getByLongname('Soup#discard()'); + var discard2 = docSet.getByLongname('Soup#discard(container)'); + + expect(discard1.length).toBe(1); + expect(discard2.length).toBe(1); + }); + }); + + describe('parseComplete handler', function() { + // disabled because on the second run, each comment is being parsed twice; who knows why... + xit('should not retain parse results between parser runs', function() { + parser.clear(); + docSet = jasmine.getDocSetFromFile('plugins/test/fixtures/overloadHelper.js', parser); + var heat = docSet.getByLongname('Soup#heat(4)'); + + expect(heat.length).toBe(0); + }); + }); +}); diff --git a/data/scripts/jsdoc/plugins/test/specs/railsTemplate.js b/data/scripts/jsdoc/plugins/test/specs/railsTemplate.js new file mode 100644 index 00000000..2f89ee5a --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/railsTemplate.js @@ -0,0 +1,16 @@ +/*global describe: true, expect: true, it: true */ +describe("railsTemplate plugin", function() { + var parser = new (require("jsdoc/src/parser")).Parser(), + plugin = require('plugins/railsTemplate'); + + + require('jsdoc/plugins').installPlugins(['plugins/railsTemplate'], parser); + require('jsdoc/src/handlers').attachTo(parser); + + it("should remove <% %> rails template tags from the source of *.erb files", function() { + var path = require("path"), + docSet = parser.parse([path.join(__dirname, "plugins/test/fixtures/railsTemplate.js.erb")]); + + expect(docSet[2].description).toEqual("Remove rails tags from the source input (e.g. )"); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/plugins/test/specs/shout.js b/data/scripts/jsdoc/plugins/test/specs/shout.js new file mode 100644 index 00000000..75e70b64 --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/shout.js @@ -0,0 +1,14 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe("shout plugin", function() { + var parser = new (require("jsdoc/src/parser")).Parser(), + plugin = require('plugins/shout'), + docSet; + + require('jsdoc/plugins').installPlugins(['plugins/shout'], parser); + docSet = jasmine.getDocSetFromFile("plugins/shout.js", parser); + + it("should make the description uppercase", function() { + var doclet = docSet.getByLongname("module:plugins/shout.handlers.newDoclet"); + expect(doclet[0].description).toEqual("MAKE YOUR DESCRIPTIONS MORE SHOUTIER."); + }); +}); diff --git a/data/scripts/jsdoc/plugins/test/specs/sourcetag.js b/data/scripts/jsdoc/plugins/test/specs/sourcetag.js new file mode 100644 index 00000000..84d65337 --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/sourcetag.js @@ -0,0 +1,16 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe("sourcetag plugin", function() { + var parser = new (require("jsdoc/src/parser")).Parser(), + plugin = require('plugins/sourcetag'), + docSet; + + require('jsdoc/plugins').installPlugins(['plugins/sourcetag'], parser); + docSet = jasmine.getDocSetFromFile("plugins/sourcetag.js", parser); + + it("should set the lineno and filename of the doclet's meta property", function() { + var doclet = docSet.getByLongname("module:plugins/sourcetag.handlers.newDoclet"); + expect(doclet[0].meta).toBeDefined(); + expect(doclet[0].meta.filename).toEqual("sourcetag.js"); + expect(doclet[0].meta.lineno).toEqual(13); + }); +}); diff --git a/data/scripts/jsdoc/plugins/test/specs/verboseOutput.js b/data/scripts/jsdoc/plugins/test/specs/verboseOutput.js new file mode 100644 index 00000000..e8ac43bd --- /dev/null +++ b/data/scripts/jsdoc/plugins/test/specs/verboseOutput.js @@ -0,0 +1,19 @@ +/*global describe: true, expect: true, it: true, jasmine: true, xit: true */ +/** + * @author Rob Taylor [manix84@gmail.com] + */ + +describe("verbose output plugin", function () { + var parser = new (require("jsdoc/src/parser")).Parser(), + plugin = require('plugins/verboseOutput'), + docSet; + + //require('jsdoc/plugins').installPlugins(['plugins/verboseOutput'], parser); + docSet = jasmine.getDocSetFromFile("plugins/verboseOutput.js", parser); + + xit("should log file names to console", function() { + // TODO: this doesn't actually test the plugin... + var fileBegin = docSet.getByLongname("module:plugins/verboseOutput.handlers.fileBegin"); + expect(fileBegin[0].description).toEqual("Logging the file name to the console."); + }); +}); diff --git a/data/scripts/jsdoc/plugins/verboseOutput.js b/data/scripts/jsdoc/plugins/verboseOutput.js new file mode 100644 index 00000000..454a4a05 --- /dev/null +++ b/data/scripts/jsdoc/plugins/verboseOutput.js @@ -0,0 +1,15 @@ +/** + * Adds a verbose output to the console, so that you can see what's happening in your process. + * @module plugins/verboseOutput + * @author Rob Taylor - The basic idea + * @author Michael Mathews - Wrote the first itteration with me :) + */ + +exports.handlers = { + /** + * Logging the file name to the console. + */ + fileBegin: function (data) { + console.log(data.filename); + } +}; diff --git a/data/scripts/jsdoc/rhino/MPL_1.1.txt b/data/scripts/jsdoc/rhino/MPL_1.1.txt new file mode 100644 index 00000000..7714141d --- /dev/null +++ b/data/scripts/jsdoc/rhino/MPL_1.1.txt @@ -0,0 +1,470 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``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 Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + diff --git a/data/scripts/jsdoc/rhino/crypto.js b/data/scripts/jsdoc/rhino/crypto.js new file mode 100644 index 00000000..487e55cf --- /dev/null +++ b/data/scripts/jsdoc/rhino/crypto.js @@ -0,0 +1 @@ +module.exports = require('crypto-browserify'); diff --git a/data/scripts/jsdoc/rhino/events.js b/data/scripts/jsdoc/rhino/events.js new file mode 100644 index 00000000..4546adab --- /dev/null +++ b/data/scripts/jsdoc/rhino/events.js @@ -0,0 +1,200 @@ +/** + * Shim for Node.js `events` module. Adapted from `node-browserify`. + * @see https://github.com/substack/node-browserify + * @license MIT + */ + +if (!process.EventEmitter) { + process.EventEmitter = function () {}; +} + +var EventEmitter = exports.EventEmitter = process.EventEmitter; +var isArray = typeof Array.isArray === 'function' ? + Array.isArray : + function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; + } +; + +// By default EventEmitters will print a warning if more than +// 10 listeners are added to it. This is a useful default which +// helps finding memory leaks. +// +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +var defaultMaxListeners = 10; +EventEmitter.prototype.setMaxListeners = function(n) { + if (!this._events) { + this._events = {}; + } + this._events.maxListeners = n; +}; + + +EventEmitter.prototype.emit = function(type) { + var args; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events || !this._events.error || + (isArray(this._events.error) && !this._events.error.length)) + { + if (arguments[1] instanceof Error) { + throw arguments[1]; // Unhandled 'error' event + } else { + throw new Error("Uncaught, unspecified 'error' event."); + } + return false; + } + } + + if (!this._events) { + return false; + } + var handler = this._events[type]; + if (!handler) { + return false; + } + + if (typeof handler == 'function') { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + args = Array.prototype.slice.call(arguments, 1); + handler.apply(this, args); + } + return true; + + } else if (isArray(handler)) { + args = Array.prototype.slice.call(arguments, 1); + + var listeners = handler.slice(); + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + return true; + + } else { + return false; + } +}; + +// EventEmitter is defined in src/node_events.cc +// EventEmitter.prototype.emit() is also defined there. +EventEmitter.prototype.addListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('addListener only takes instances of Function'); + } + + if (!this._events) { + this._events = {}; + } + + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + this.emit('newListener', type, listener); + + if (!this._events[type]) { + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + } else if (isArray(this._events[type])) { + + // Check for listener leak + if (!this._events[type].warned) { + var m; + if (this._events.maxListeners !== undefined) { + m = this._events.maxListeners; + } else { + m = defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + console.trace(); + } + } + + // If we've already got an array, just append. + this._events[type].push(listener); + } else { + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + var self = this; + self.on(type, function g() { + self.removeListener(type, g); + listener.apply(this, arguments); + }); + + return this; +}; + +EventEmitter.prototype.removeListener = function(type, listener) { + if ('function' !== typeof listener) { + throw new Error('removeListener only takes instances of Function'); + } + + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events || !this._events[type]) { + return this; + } + + var list = this._events[type]; + + if (isArray(list)) { + var i = list.indexOf(listener); + if (i < 0) { + return this; + } + list.splice(i, 1); + if (list.length === 0) { + delete this._events[type]; + } + } else if (this._events[type] === listener) { + delete this._events[type]; + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + // does not use listeners(), so no side effect of creating _events[type] + if (type && this._events && this._events[type]) { + this._events[type] = null; + } + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + if (!this._events) { + this._events = {}; + } + if (!this._events[type]) { + this._events[type] = []; + } + if (!isArray(this._events[type])) { + this._events[type] = [this._events[type]]; + } + return this._events[type]; +}; diff --git a/data/scripts/jsdoc/rhino/fs.js b/data/scripts/jsdoc/rhino/fs.js new file mode 100644 index 00000000..eeb29c6f --- /dev/null +++ b/data/scripts/jsdoc/rhino/fs.js @@ -0,0 +1,176 @@ +/*global Packages: true */ + +/** + * Partial Rhino shim for Node.js' `fs` module. + * @see http://nodejs.org/api/fs.html + */ + +var path = require('path'); + +var asyncify = path._asyncify; + +function checkEncoding(enc, name) { + // we require the `encoding` parameter for Node.js compatibility; on Node.js, if you omit the + // encoding, you get a stream instead of a string + if (!enc || typeof enc === 'function') { + throw new Error(name + ' requires an encoding on Rhino!'); + } + + // Node.js wants 'utf8', but Java wants 'utf-8' + if (enc === 'utf8') { + enc = 'utf-8'; + } + + return enc; +} + +exports.readFileSync = function(filename, encoding) { + encoding = checkEncoding(encoding, 'fs.readFile[Sync]'); + + return readFile(filename, encoding); +}; +exports.readFile = asyncify(exports.readFileSync); + +// in node 0.8, path.exists() and path.existsSync() moved to the "fs" module +exports.existsSync = path.existsSync; +exports.exists = path.exists; + +var statSync = exports.statSync = function(_path) { + var f = new java.io.File(_path); + return { + isFile: function() { + return f.isFile(); + }, + isDirectory: function() { + return f.isDirectory(); + } + }; +}; +exports.stat = asyncify(statSync); + +var readdirSync = exports.readdirSync = function(_path) { + var dir; + var files; + + dir = new java.io.File(_path); + if (!dir.directory) { + throw new Error("ENOENT, no such file or directory '" + _path + "'"); + } + + files = dir.list(); + + // Convert files to Javascript strings so they play nice with node modules + files = files.map(function(fileName) { + return String(fileName); + }); + + return files; +}; +exports.readdir = asyncify(readdirSync); + +// JSDoc extension to `fs` module +var ls = 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; } + + if ( statSync(dir).isFile(dir) ) { + files = [dir]; + } + else { + files = 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) { + ls(_path.join('/'), recurse, _allFiles, _path); + } + _path.pop(); + } + else { // it's a file + _allFiles.push( + path.normalize(_path.join('/') + '/' + file) + ); + } + } + + return _allFiles; +}; + +// JSDoc extension to `fs` module +var toDir = exports.toDir = function(_path) { + var f = new java.io.File(_path); + + if (f.isDirectory()){ + return _path; + } else { + return path.dirname(_path); + } +}; + +var mkdirSync = exports.mkdirSync = function(_path) { + var dir_path = toDir(_path); + (new java.io.File(dir_path)).mkdir(); +}; +exports.mkdir = asyncify(mkdirSync); + +// JSDoc extension to `fs` module +exports.mkPath = function(/**Array*/ _path) { + if (_path.constructor == Array) { _path = _path.join(''); } + + (new java.io.File(_path)).mkdirs(); +}; + +// JSDoc extension to `fs` module +exports.copyFileSync = function(inFile, outDir, fileName) { + if (fileName == null){fileName = path.basename(inFile);} + + outDir = toDir(outDir); + + inFile = new java.io.File(inFile); + var outFile = new java.io.File(outDir+'/'+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.copyFile = asyncify(exports.copyFileSync); + +exports.writeFileSync = function(filename, data, encoding) { + encoding = checkEncoding(encoding, 'fs.writeFile[Sync]'); + + var out = new Packages.java.io.PrintWriter( + new Packages.java.io.OutputStreamWriter( + new Packages.java.io.FileOutputStream(filename), + encoding + ) + ); + + try { + out.write(data); + } + finally { + out.flush(); + out.close(); + } +}; +exports.writeFile = asyncify(exports.writeFileSync); diff --git a/data/scripts/jsdoc/rhino/js.jar b/data/scripts/jsdoc/rhino/js.jar new file mode 100644 index 00000000..a6a87eba Binary files /dev/null and b/data/scripts/jsdoc/rhino/js.jar differ diff --git a/data/scripts/jsdoc/rhino/jsdoc.js b/data/scripts/jsdoc/rhino/jsdoc.js new file mode 100644 index 00000000..f3de7b89 --- /dev/null +++ b/data/scripts/jsdoc/rhino/jsdoc.js @@ -0,0 +1,9 @@ +// Platform-specific functions to support jsdoc.js + +exports.pathToUri = function(_path) { + return String( new java.io.File(_path).toURI() ); +}; + +exports.uriToPath = function(uri) { + return String( new java.io.File(new java.net.URI(uri)) ); +}; diff --git a/data/scripts/jsdoc/rhino/jsdoc/util/include.js b/data/scripts/jsdoc/rhino/jsdoc/util/include.js new file mode 100644 index 00000000..db0ef197 --- /dev/null +++ b/data/scripts/jsdoc/rhino/jsdoc/util/include.js @@ -0,0 +1,7 @@ +/*global Packages: true */ +module.exports = function(filepath) { + var myGlobal = require('jsdoc/util/global'); + + var cx = Packages.org.mozilla.javascript.Context.getCurrentContext(); + Packages.org.mozilla.javascript.tools.shell.Main.processFile(cx, myGlobal, filepath); +}; diff --git a/data/scripts/jsdoc/rhino/os.js b/data/scripts/jsdoc/rhino/os.js new file mode 100644 index 00000000..ea9553c3 --- /dev/null +++ b/data/scripts/jsdoc/rhino/os.js @@ -0,0 +1,18 @@ +/** + * Partial Rhino implementation of Node.js' `os` module. + * @module os + * @author Jeff Williams + * @see http://nodejs.org/api/os.html + */ + +exports.EOL = String( java.lang.System.getProperty('line.separator') ); + +// clearly not accurate, but probably good enough +exports.platform = function() { + if ( String(java.lang.System.getProperty('os.name')).match(/^[Ww]in/) ) { + return 'win32'; + } + else { + return 'linux'; + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/rhino/path.js b/data/scripts/jsdoc/rhino/path.js new file mode 100644 index 00000000..73df076e --- /dev/null +++ b/data/scripts/jsdoc/rhino/path.js @@ -0,0 +1,399 @@ +var isWindows = java.lang.System.getProperty("os.name").toLowerCase().contains("windows"); +var fileSeparator = exports.sep = String( java.lang.System.getProperty("file.separator") ); + +function noOp() {} + +// exported for the benefit of our `fs` shim +var asyncify = exports._asyncify = function(func) { + return function() { + var args = Array.prototype.slice.call(arguments); + var callback = args.pop(); + var data; + + callback = typeof callback === 'function' ? callback : noOp; + + try { + data = func.apply(this, args); + process.nextTick(function() { + callback(null, data); + }); + } + catch (e) { + process.nextTick(function() { + callback(e); + }); + } + }; +}; + +/** + * Returns everything on a path except for the last item + * e.g. if the path was 'path/to/something', the return value would be 'path/to' + */ +exports.dirname = function(_path) { + var f = new java.io.File(_path); + return String(f.getParent()); +}; + +/** + * Returns the last item on a path + */ +exports.basename = function(_path, ext) { + var f = new java.io.File(_path); + var p = f.getParentFile(); + var base = String(f.getName()); + if (p != null) { + var idx = ext ? base.indexOf(ext) : -1; + if (idx !== -1) { + base = base.substring(0, base.length - ext.length); + } + } + return base; +}; + +exports.existsSync = function(_path) { + var f = new java.io.File(_path); + + if (f.isDirectory()){ + return true; + } + if (!f.exists()){ + return false; + } + if (!f.canRead()){ + return false; + } + return true; +}; + +exports.exists = asyncify(exports.existsSync); + +//Code below taken from node + +//resolves . and .. elements in a path array with directory names there +//must be no slashes, empty elements, or device names (c:\) in the array +//(so also no leading and trailing slashes - it does not distinguish +//relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for ( var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last == '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +if (isWindows) { + // Regex to split a windows path into three parts: [*, device, slash, + // tail] windows-only + var splitDeviceRe = + /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/; + + // path.resolve([from ...], to) + // windows version + exports.resolve = function() { + var resolvedDevice = '', + resolvedTail = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1; i--) { + var path; + if (i >= 0) { + path = arguments[i]; + } else if (!resolvedDevice) { + path = process.cwd(); + } else { + // Windows has the concept of drive-specific current working + // directories. If we've resolved a drive letter but not yet an + // absolute path, get cwd for that drive. We're sure the device is not + // an unc path at this points, because unc paths are always absolute. + path = process.env['=' + resolvedDevice]; + // Verify that a drive-local cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + if (!path || path.substr(0, 3).toLowerCase() !== + resolvedDevice.toLowerCase() + '\\') { + path = resolvedDevice + '\\'; + } + } + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; + } + + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute + tail = result[3]; + + if (device && + resolvedDevice && + device.toLowerCase() !== resolvedDevice.toLowerCase()) { + // This path points to another device so it is not applicable + continue; + } + + if (!resolvedDevice) { + resolvedDevice = device; + } + if (!resolvedAbsolute) { + resolvedTail = tail + '\\' + resolvedTail; + resolvedAbsolute = isAbsolute; + } + + if (resolvedDevice && resolvedAbsolute) { + break; + } + } + + // Replace slashes (in UNC share name) by backslashes + resolvedDevice = resolvedDevice.replace(/\//g, '\\'); + + // At this point the path should be resolved to a full absolute path, + // but handle relative paths to be safe (might happen when process.cwd() + // fails) + + // Normalize the tail path + + function f(p) { + return !!p; + } + + resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f), + !resolvedAbsolute).join('\\'); + + return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || + '.'; + }; + + // windows version + exports.normalize = function(_path) { + var result = splitDeviceRe.exec(_path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute + tail = result[3], + trailingSlash = /[\\\/]$/.test(tail); + + // Normalize the tail path + tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) { + return !!p; + }), !isAbsolute).join('\\'); + + if (!tail && !isAbsolute) { + tail = '.'; + } + if (tail && trailingSlash) { + tail += '\\'; + } + + return device + (isAbsolute ? '\\' : '') + tail; + }; + + //windows version + exports.join = function() { + function f(p) { + return p && typeof p === 'string'; + } + + var _paths = Array.prototype.slice.call(arguments, 0).filter(f); + var joined = _paths.join('\\'); + + // Make sure that the joined path doesn't start with two slashes + // - it will be mistaken for an unc path by normalize() - + // unless the _paths[0] also starts with two slashes + if (/^[\\\/]{2}/.test(joined) && !/^[\\\/]{2}/.test(_paths[0])) { + joined = joined.slice(1); + } + + return exports.normalize(joined); + }; + + // path.relative(from, to) + // it will solve the relative path from 'from' to 'to', for instance: + // from = 'C:\\orandea\\test\\aaa' + // to = 'C:\\orandea\\impl\\bbb' + // The output of the function should be: '..\\..\\impl\\bbb' + // windows version + exports.relative = function(from, to) { + from = exports.resolve(from); + to = exports.resolve(to); + + // windows is not case sensitive + var lowerFrom = from.toLowerCase(); + var lowerTo = to.toLowerCase(); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') { + break; + } + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') { + break; + } + } + + if (start > end) { + return []; + } + return arr.slice(start, end - start + 1); + } + + var toParts = trim(to.split('\\')); + + var lowerFromParts = trim(lowerFrom.split('\\')); + var lowerToParts = trim(lowerTo.split('\\')); + + var length = Math.min(lowerFromParts.length, lowerToParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (lowerFromParts[i] !== lowerToParts[i]) { + samePartsLength = i; + break; + } + } + + if (samePartsLength === 0) { + return to; + } + + var outputParts = []; + for (i = samePartsLength; i < lowerFromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('\\'); + }; +} else { + // path.resolve([from ...], to) + // posix version + exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + }; + + // path.normalize(_path) + // posix version + exports.normalize = function(_path) { + var isAbsolute = _path.charAt(0) === '/', + trailingSlash = _path.slice(-1) === '/'; + + // Normalize the path + _path = normalizeArray(_path.split('/').filter(function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!_path && !isAbsolute) { + _path = '.'; + } + if (_path && trailingSlash) { + _path += '/'; + } + + return (isAbsolute ? '/' : '') + _path; + }; + + // posix version + exports.join = function() { + var _paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(_paths.filter(function(p, index) { + return p && typeof p === 'string'; + }).join('/')); + }; + + // path.relative(from, to) + // posix version + exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') { + break; + } + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') { + break; + } + } + + if (start > end) { + return []; + } + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); + }; +} \ No newline at end of file diff --git a/data/scripts/jsdoc/rhino/querystring.js b/data/scripts/jsdoc/rhino/querystring.js new file mode 100644 index 00000000..b38921a4 --- /dev/null +++ b/data/scripts/jsdoc/rhino/querystring.js @@ -0,0 +1,122 @@ +/** + * Adapted version of Node.js' `querystring` module. + * @module querystring + * @see http://nodejs.org/api/querystring.html + * @see https://github.com/joyent/node/blob/f105f2f2/lib/querystring.js + * @license MIT + */ +var QueryString = exports; + +// If obj.hasOwnProperty has been overridden, then calling +// obj.hasOwnProperty(prop) will break. +// See: https://github.com/joyent/node/issues/1707 +function hasOwnProp(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + + +QueryString.unescape = function(s) { + return decodeURIComponent(s); +}; + + +QueryString.escape = function(str) { + return encodeURIComponent(str); +}; + +var stringifyPrimitive = function(v) { + switch (typeof v) { + case 'string': + return v; + + case 'boolean': + return v ? 'true' : 'false'; + + case 'number': + return isFinite(v) ? v : ''; + + default: + return ''; + } +}; + + +QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) { + sep = sep || '&'; + eq = eq || '='; + if (obj === null) { + obj = undefined; + } + + if (typeof obj === 'object') { + return Object.keys(obj).map(function(k) { + var ks = QueryString.escape(stringifyPrimitive(k)) + eq; + if (Array.isArray(obj[k])) { + return obj[k].map(function(v) { + return ks + QueryString.escape(stringifyPrimitive(v)); + }).join(sep); + } else { + return ks + QueryString.escape(stringifyPrimitive(obj[k])); + } + }).join(sep); + + } + + if (!name) { + return ''; + } + return QueryString.escape(stringifyPrimitive(name)) + eq + + QueryString.escape(stringifyPrimitive(obj)); +}; + +// Parse a key=val string. +QueryString.parse = QueryString.decode = function(qs, sep, eq, options) { + sep = sep || '&'; + eq = eq || '='; + var obj = {}; + + if (typeof qs !== 'string' || qs.length === 0) { + return obj; + } + + var regexp = /\+/g; + qs = qs.split(sep); + + var maxKeys = 1000; + if (options && typeof options.maxKeys === 'number') { + maxKeys = options.maxKeys; + } + + var len = qs.length; + // maxKeys <= 0 means that we should not limit keys count + if (maxKeys > 0 && len > maxKeys) { + len = maxKeys; + } + + for (var i = 0; i < len; ++i) { + var x = qs[i].replace(regexp, '%20'), + idx = x.indexOf(eq), + kstr, vstr, k, v; + + if (idx >= 0) { + kstr = x.substr(0, idx); + vstr = x.substr(idx + 1); + } else { + kstr = x; + vstr = ''; + } + + k = QueryString.unescape(kstr); + v = QueryString.unescape(vstr); + + if (!hasOwnProp(obj, k)) { + obj[k] = v; + } else if (Array.isArray(obj[k])) { + obj[k].push(v); + } else { + obj[k] = [obj[k], v]; + } + } + + return obj; +}; diff --git a/data/scripts/jsdoc/rhino/rhino-shim.js b/data/scripts/jsdoc/rhino/rhino-shim.js new file mode 100644 index 00000000..16c126f6 --- /dev/null +++ b/data/scripts/jsdoc/rhino/rhino-shim.js @@ -0,0 +1,123 @@ +/*global env: true, Packages: true */ +/** + * @overview A minimal emulation of the standard features of Node.js necessary + * to get JSDoc to run. + */ + +/** + * Emulate DOM timeout/interval functions. + * @see https://developer.mozilla.org/en-US/docs/DOM/window#Methods + */ + +setTimeout = null, +clearTimeout = null, +setInterval = null, +clearInterval = null; + +(function() { + // TODO: tune number of threads if necessary + var timerPool = new java.util.concurrent.ScheduledThreadPoolExecutor(10); + var timers = {}; + var timerCount = 1; + var timerUnits = java.util.concurrent.TimeUnit.MILLISECONDS; + + function getCallback(fn) { + return new java.lang.Runnable({ + run: Packages.org.mozilla.javascript.Context.call(fn) + }); + } + + setTimeout = function(fn, delay) { + var timerId = timerCount++; + var callback = getCallback(fn); + timers[timerId] = timerPool.schedule(callback, delay, timerUnits); + return timerId; + }; + + clearTimeout = function(timerId) { + if (timers[timerId]) { + timerPool.remove(timers[timerId]); + delete timers[timerId]; + } + }; + + setInterval = function(fn, delay) { + var timerId = timerCount++; + var callback = getCallback(fn); + timers[timerId] = timerPool.scheduleAtFixedRate(callback, delay, delay, timerUnits); + return timerId; + }; + + clearInterval = clearTimeout; +})(); + +/** + * Emulate Node.js console functions. + * @see http://nodejs.org/api/stdio.html + */ +console = (function() { + function println(stream, args) { + java.lang.System[stream].println( require('util').format.apply(this, args) ); + } + + return { + error: function() { + println('err', arguments); + }, + info: function() { + println('out', arguments); + }, + log: function() { + println('out', arguments); + }, + trace: function(label) { + // this puts some extra junk at the top of the stack trace, but it's close enough + var e = new java.lang.Exception(label || 'Trace'); + e.printStackTrace(); + }, + warn: function() { + println('err', arguments); + } + }; +})(); + +/** + * Emulate Node.js process functions. + * @see http://nodejs.org/api/process.html + */ +process = { + argv: [__dirname + '/jsdoc.js'].concat(Array.prototype.slice.call(arguments, 0)), + cwd: function() { + return new Packages.java.io.File('.').getCanonicalPath() + ''; + }, + env: (function() { + var result = {}; + + var env = java.lang.System.getenv(); + var keys = env.keySet().toArray(); + var key; + for (var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + result[key + ''] = env.get(key) + ''; + } + + return result; + })(), + exit: function(n) { + n = n || 0; + java.lang.System.exit(n); + }, + nextTick: function(callback) { + setTimeout(callback, 0); + }, + stderr: { + write: function(str) { + java.lang.System.err.print(str); + } + }, + stdout: { + write: function(str) { + java.lang.System.out.print(str); + } + } +}; diff --git a/data/scripts/jsdoc/rhino/util.js b/data/scripts/jsdoc/rhino/util.js new file mode 100644 index 00000000..bd696834 --- /dev/null +++ b/data/scripts/jsdoc/rhino/util.js @@ -0,0 +1,561 @@ +/** + * Adapted version of Node.js' `util` module. + * @module util + * @see http://nodejs.org/api/util.html + * @see https://github.com/joyent/node/blob/85090734/lib/util.js + * @license MIT + */ + +function hasOwnProp(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + + +// placate JSHint +var stylizeNoColor, stylizeWithColor, formatValue, formatPrimitive; + +/** + * Echoes the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) { + ctx.depth = arguments[2]; + } + if (arguments.length >= 4) { + ctx.colors = arguments[3]; + } + if (typeof opts === 'boolean') { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (typeof ctx.showHidden === 'undefined') { + ctx.showHidden = false; + } + if (typeof ctx.depth === 'undefined') { + ctx.depth = 2; + } + if (typeof ctx.colors === 'undefined') { + ctx.colors = false; + } + if (typeof ctx.customInspect === 'undefined') { + ctx.customInspect = true; + } + if (ctx.colors) { + ctx.stylize = stylizeWithColor; + } + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +stylizeWithColor = function(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +}; + + +stylizeNoColor = function(str, styleType) { + return str; +}; + + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + var i, len; + + if (typeof f !== 'string') { + var objects = []; + for (i = 0, len = arguments.length; i < len; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + i = 1; + var args = arguments; + len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') { + return '%'; + } + if (i >= len) { + return x; + } + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': return require('jsdoc/util/dumper').dump(args[i++]); + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (x === null || typeof x !== 'object') { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +exports.print = function() { + var args = Array.prototype.slice.call(arguments, 0); + for (var i = 0, len = args.length; i < len; ++i) { + process.stdout.write(String(args[i])); + } +}; + + +exports.puts = function() { + var args = Array.prototype.slice.call(arguments, 0); + for (var i = 0, len = args.length; i < len; ++i) { + process.stdout.write(args[i] + '\n'); + } +}; + + +exports.debug = function(x) { + process.stderr.write('DEBUG: ' + x + '\n'); +}; + + +var error = exports.error = function(x) { + var args = Array.prototype.slice.call(arguments, 0); + for (var i = 0, len = args.length; i < len; ++i) { + process.stderr.write(args[i] + '\n'); + } +}; + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProp(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (recurseTimes === null) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProp(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) { + numLinesEst++; + } + return prev + cur.length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar) || + (typeof ar === 'object' && objectToString(ar) === '[object Array]'); +} +exports.isArray = isArray; + + +function isRegExp(re) { + return typeof re === 'object' && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + + +function isDate(d) { + return typeof d === 'object' && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + + +function isError(e) { + return typeof e === 'object' && objectToString(e) === '[object Error]'; +} +exports.isError = isError; + + +formatValue = function(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + return String(value.inspect(recurseTimes)); + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (typeof value === 'function') { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length === 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +}; + + +formatPrimitive = function(ctx, value) { + switch (typeof value) { + case 'undefined': + return ctx.stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + + case 'number': + return ctx.stylize('' + value, 'number'); + + case 'boolean': + return ctx.stylize('' + value, 'boolean'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return ctx.stylize('null', 'null'); + } +}; + + +exports.p = exports.deprecate(function() { + var args = Array.prototype.slice.call(arguments, 0); + for (var i = 0, len = args.length; i < len; ++i) { + error(exports.inspect(args[i])); + } +}, 'util.p: Use console.error() instead.'); + + +function pad(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']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +exports.log = function(msg) { + exports.puts(timestamp() + ' - ' + msg.toString()); +}; + + +exports.exec = function() { + throw new Error('util.exec() is not implemented on Rhino (and was deprecated in Node.js 0.2)'); +}; + + +exports.pump = function() { + throw new Error('util.pump() is not implemented on Rhino (and was deprecated in Node.js 0.8'); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @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, + writable: true, + configurable: true + } + }); +}; + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || typeof add !== 'object') { + return origin; + } + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; diff --git a/data/scripts/jsdoc/templates/README.md b/data/scripts/jsdoc/templates/README.md new file mode 100644 index 00000000..fbdd7dbf --- /dev/null +++ b/data/scripts/jsdoc/templates/README.md @@ -0,0 +1,20 @@ +To create or use your own template, create a folder, and give it the name of your template, for example "mycooltemplate". Within this folder create a file named "publish.js". That file must define a global method named "publish". For example: + +````javascript +/** + * Turn the data about your docs into file output. + * @global + * @param {TAFFY} data - A TaffyDB collection representing + * all the symbols documented in your code. + * @param {object} opts - An object with options information. + */ +function publish(data, opts) { + // do stuff here to generate your output files +} +```` + +To invoke JSDoc 3 with your own template, use the `-t` command line option, giving it the path to your template folder. + +```` +./jsdoc mycode.js -t /path/to/mycooltemplate +```` \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/default/README.md b/data/scripts/jsdoc/templates/default/README.md new file mode 100644 index 00000000..a7bd96bf --- /dev/null +++ b/data/scripts/jsdoc/templates/default/README.md @@ -0,0 +1 @@ +The default template for JSDoc 3 uses: [the Taffy Database library](http://taffydb.com/) and the [Underscore Template library](http://documentcloud.github.com/underscore/#template). diff --git a/data/scripts/jsdoc/templates/default/publish.js b/data/scripts/jsdoc/templates/default/publish.js new file mode 100644 index 00000000..b79200a9 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/publish.js @@ -0,0 +1,559 @@ +/*global env: true */ +var template = require('jsdoc/template'), + fs = require('jsdoc/fs'), + path = require('jsdoc/path'), + taffy = require('taffydb').taffy, + handle = require('jsdoc/util/error').handle, + helper = require('jsdoc/util/templateHelper'), + htmlsafe = helper.htmlsafe, + linkto = helper.linkto, + resolveAuthorLinks = helper.resolveAuthorLinks, + scopeToPunc = helper.scopeToPunc, + hasOwnProp = Object.prototype.hasOwnProperty, + data, + view, + outdir = env.opts.destination; + + +function find(spec) { + return helper.find(data, spec); +} + +function tutoriallink(tutorial) { + return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' }); +} + +function getAncestorLinks(doclet) { + return helper.getAncestorLinks(data, doclet); +} + +function hashToLink(doclet, hash) { + if ( !/^(#.+)/.test(hash) ) { return hash; } + + var url = helper.createLink(doclet); + + url = url.replace(/(#.+|$)/, hash); + return '' + hash + ''; +} + +function needsSignature(doclet) { + var needsSig = false; + + // function and class definitions always get a signature + if (doclet.kind === 'function' || doclet.kind === 'class') { + needsSig = true; + } + // typedefs that contain functions get a signature, too + else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names && + doclet.type.names.length) { + for (var i = 0, l = doclet.type.names.length; i < l; i++) { + if (doclet.type.names[i].toLowerCase() === 'function') { + needsSig = true; + break; + } + } + } + + return needsSig; +} + +function addSignatureParams(f) { + var params = helper.getSignatureParams(f, 'optional'); + + f.signature = (f.signature || '') + '('+params.join(', ')+')'; +} + +function addSignatureReturns(f) { + var returnTypes = helper.getSignatureReturns(f); + + f.signature = ''+(f.signature || '') + '' + ''+(returnTypes.length? ' → {'+returnTypes.join('|')+'}' : '')+''; +} + +function addSignatureTypes(f) { + var types = helper.getSignatureTypes(f); + + f.signature = (f.signature || '') + ''+(types.length? ' :'+types.join('|') : '')+''; +} + +function addAttribs(f) { + var attribs = helper.getAttribs(f); + + f.attribs = ''+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+''; +} + +function shortenPaths(files, commonPrefix) { + // always use forward slashes + var regexp = new RegExp('\\\\', 'g'); + + Object.keys(files).forEach(function(file) { + files[file].shortened = files[file].resolved.replace(commonPrefix, '') + .replace(regexp, '/'); + }); + + return files; +} + +function resolveSourcePath(filepath) { + return path.resolve(process.cwd(), filepath); +} + +function getPathFromDoclet(doclet) { + if (!doclet.meta) { + return; + } + + var filepath = doclet.meta.path && doclet.meta.path !== 'null' ? + doclet.meta.path + '/' + doclet.meta.filename : + doclet.meta.filename; + + return filepath; +} + +function generate(title, docs, filename, resolveLinks) { + resolveLinks = resolveLinks === false ? false : true; + + var docData = { + title: title, + docs: docs + }; + + var outpath = path.join(outdir, filename), + html = view.render('container.tmpl', docData); + + if (resolveLinks) { + html = helper.resolveLinks(html); // turn {@link foo} into foo + } + + fs.writeFileSync(outpath, html, 'utf8'); +} + +function generateSourceFiles(sourceFiles) { + Object.keys(sourceFiles).forEach(function(file) { + var source; + // links are keyed to the shortened path in each doclet's `meta.filename` property + var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); + helper.registerLink(sourceFiles[file].shortened, sourceOutfile); + + try { + source = { + kind: 'source', + code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, 'utf8') ) + }; + } + catch(e) { + handle(e); + } + + generate('Source: ' + sourceFiles[file].shortened, [source], sourceOutfile, + false); + }); +} + +/** + * Look for classes or functions with the same name as modules (which indicates that the module + * exports only that class or function), then attach the classes or functions to the `module` + * property of the appropriate module doclets. The name of each class or function is also updated + * for display purposes. This function mutates the original arrays. + * + * @private + * @param {Array.} doclets - The array of classes and functions to + * check. + * @param {Array.} modules - The array of module doclets to search. + */ +function attachModuleSymbols(doclets, modules) { + var symbols = {}; + + // build a lookup table + doclets.forEach(function(symbol) { + symbols[symbol.longname] = symbol; + }); + + return modules.map(function(module) { + if (symbols[module.longname]) { + module.module = symbols[module.longname]; + module.module.name = module.module.name.replace('module:', 'require("') + '")'; + } + }); +} + +/** + * Create the navigation sidebar. + * @param {object} members The members that will be used to create the sidebar. + * @param {array} members.classes + * @param {array} members.externals + * @param {array} members.globals + * @param {array} members.mixins + * @param {array} members.modules + * @param {array} members.namespaces + * @param {array} members.tutorials + * @param {array} members.events + * @return {string} The HTML for the navigation sidebar. + */ +function buildNav(members) { + var nav = '

    Index

    ', + seen = {}, + hasClassList = false, + classNav = '', + globalNav = ''; + + if (members.modules.length) { + nav += '

    Modules

      '; + members.modules.forEach(function(m) { + if ( !hasOwnProp.call(seen, m.longname) ) { + nav += '
    • '+linkto(m.longname, m.name)+'
    • '; + } + seen[m.longname] = true; + }); + + nav += '
    '; + } + + if (members.externals.length) { + nav += '

    Externals

      '; + members.externals.forEach(function(e) { + if ( !hasOwnProp.call(seen, e.longname) ) { + nav += '
    • '+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'
    • '; + } + seen[e.longname] = true; + }); + + nav += '
    '; + } + + if (members.classes.length) { + members.classes.forEach(function(c) { + if ( !hasOwnProp.call(seen, c.longname) ) { + classNav += '
  • '+linkto(c.longname, c.name)+'
  • '; + } + seen[c.longname] = true; + }); + + if (classNav !== '') { + nav += '

    Classes

      '; + nav += classNav; + nav += '
    '; + } + } + + if (members.events.length) { + nav += '

    Events

      '; + members.events.forEach(function(e) { + if ( !hasOwnProp.call(seen, e.longname) ) { + nav += '
    • '+linkto(e.longname, e.name)+'
    • '; + } + seen[e.longname] = true; + }); + + nav += '
    '; + } + + if (members.namespaces.length) { + nav += '

    Namespaces

      '; + members.namespaces.forEach(function(n) { + if ( !hasOwnProp.call(seen, n.longname) ) { + nav += '
    • '+linkto(n.longname, n.name)+'
    • '; + } + seen[n.longname] = true; + }); + + nav += '
    '; + } + + if (members.mixins.length) { + nav += '

    Mixins

      '; + members.mixins.forEach(function(m) { + if ( !hasOwnProp.call(seen, m.longname) ) { + nav += '
    • '+linkto(m.longname, m.name)+'
    • '; + } + seen[m.longname] = true; + }); + + nav += '
    '; + } + + if (members.tutorials.length) { + nav += '

    Tutorials

      '; + members.tutorials.forEach(function(t) { + nav += '
    • '+tutoriallink(t.name)+'
    • '; + }); + + nav += '
    '; + } + + if (members.globals.length) { + members.globals.forEach(function(g) { + if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { + globalNav += '
  • ' + linkto(g.longname, g.name) + '
  • '; + } + seen[g.longname] = true; + }); + + if (!globalNav) { + // turn the heading into a link so you can actually get to the global page + nav += '

    ' + linkto('global', 'Global') + '

    '; + } + else { + nav += '

    Global

      ' + globalNav + '
    '; + } + } + + return nav; +} + + +/** + @param {TAFFY} taffyData See . + @param {object} opts + @param {Tutorial} tutorials + */ +exports.publish = function(taffyData, opts, tutorials) { + data = taffyData; + + var conf = env.conf.templates || {}; + conf['default'] = conf['default'] || {}; + + var templatePath = opts.template; + view = new template.Template(templatePath + '/tmpl'); + + // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness + // doesn't try to hand them out later + var indexUrl = helper.getUniqueFilename('index'); + // don't call registerLink() on this one! 'index' is also a valid longname + + var globalUrl = helper.getUniqueFilename('global'); + helper.registerLink('global', globalUrl); + + // set up templating + view.layout = 'layout.tmpl'; + + // set up tutorials for helper + helper.setTutorials(tutorials); + + data = helper.prune(data); + data.sort('longname, version, since'); + helper.addEventListeners(data); + + var sourceFiles = {}; + var sourceFilePaths = []; + data().each(function(doclet) { + doclet.attribs = ''; + + 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 + }; + }); + } + if (doclet.see) { + doclet.see.forEach(function(seeItem, i) { + doclet.see[i] = hashToLink(doclet, seeItem); + }); + } + + // build a list of source files + var sourcePath; + var resolvedSourcePath; + if (doclet.meta) { + sourcePath = getPathFromDoclet(doclet); + resolvedSourcePath = resolveSourcePath(sourcePath); + sourceFiles[sourcePath] = { + resolved: resolvedSourcePath, + shortened: null + }; + sourceFilePaths.push(resolvedSourcePath); + } + }); + + // update outdir if necessary, then create outdir + var packageInfo = ( find({kind: 'package'}) || [] ) [0]; + if (packageInfo && packageInfo.name) { + outdir = path.join(outdir, packageInfo.name, packageInfo.version); + } + fs.mkPath(outdir); + + // copy the template's static files to outdir + var fromDir = path.join(templatePath, 'static'); + var staticFiles = fs.ls(fromDir, 3); + + staticFiles.forEach(function(fileName) { + var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); + fs.mkPath(toDir); + fs.copyFileSync(fileName, toDir); + }); + + // copy user-specified static files to outdir + var staticFilePaths; + var staticFileFilter; + var staticFileScanner; + if (conf['default'].staticFiles) { + staticFilePaths = conf['default'].staticFiles.paths || []; + staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf['default'].staticFiles); + staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); + + staticFilePaths.forEach(function(filePath) { + var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); + + extraStaticFiles.forEach(function(fileName) { + var sourcePath = fs.statSync(filePath).isDirectory() ? filePath : + path.dirname(filePath); + var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); + fs.mkPath(toDir); + fs.copyFileSync(fileName, toDir); + }); + }); + } + + if (sourceFilePaths.length) { + sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); + } + data().each(function(doclet) { + var url = helper.createLink(doclet); + helper.registerLink(doclet.longname, url); + + // replace the filename with a shortened version of the full path + var docletPath; + if (doclet.meta) { + docletPath = getPathFromDoclet(doclet); + docletPath = sourceFiles[docletPath].shortened; + if (docletPath) { + doclet.meta.filename = docletPath; + } + } + }); + + data().each(function(doclet) { + var url = helper.longnameToUrl[doclet.longname]; + + if (url.indexOf('#') > -1) { + doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); + } + else { + doclet.id = doclet.name; + } + + if ( needsSignature(doclet) ) { + addSignatureParams(doclet); + addSignatureReturns(doclet); + addAttribs(doclet); + } + }); + + // do this after the urls have all been generated + data().each(function(doclet) { + doclet.ancestors = getAncestorLinks(doclet); + + if (doclet.kind === 'member') { + addSignatureTypes(doclet); + addAttribs(doclet); + } + + if (doclet.kind === 'constant') { + addSignatureTypes(doclet); + addAttribs(doclet); + doclet.kind = 'member'; + } + }); + + var members = helper.getMembers(data); + members.tutorials = tutorials.children; + + // add template helpers + view.find = find; + view.linkto = linkto; + view.resolveAuthorLinks = resolveAuthorLinks; + view.tutoriallink = tutoriallink; + view.htmlsafe = htmlsafe; + + // once for all + view.nav = buildNav(members); + attachModuleSymbols( find({ kind: ['class', 'function'], longname: {left: 'module:'} }), + members.modules ); + + // only output pretty-printed source files if requested; do this before generating any other + // pages, so the other pages can link to the source files + if (conf['default'].outputSourceFiles) { + generateSourceFiles(sourceFiles); + } + + if (members.globals.length) { generate('Global', [{kind: 'globalobj'}], globalUrl); } + + // index page displays information from package.json and lists files + var files = find({kind: 'file'}), + packages = find({kind: 'package'}); + + generate('Index', + packages.concat( + [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] + ).concat(files), + indexUrl); + + // set up the lists that we'll use to generate pages + var classes = taffy(members.classes); + var modules = taffy(members.modules); + var namespaces = taffy(members.namespaces); + var mixins = taffy(members.mixins); + var externals = taffy(members.externals); + + Object.keys(helper.longnameToUrl).forEach(function(longname) { + var myClasses = helper.find(classes, {longname: longname}); + if (myClasses.length) { + generate('Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); + } + + var myModules = helper.find(modules, {longname: longname}); + if (myModules.length) { + generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); + } + + var myNamespaces = helper.find(namespaces, {longname: longname}); + if (myNamespaces.length) { + generate('Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); + } + + var myMixins = helper.find(mixins, {longname: longname}); + if (myMixins.length) { + generate('Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); + } + + var myExternals = helper.find(externals, {longname: longname}); + if (myExternals.length) { + generate('External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname]); + } + }); + + // TODO: move the tutorial functions to templateHelper.js + function generateTutorial(title, tutorial, filename) { + var tutorialData = { + title: title, + header: tutorial.title, + content: tutorial.parse(), + children: tutorial.children + }; + + var tutorialPath = path.join(outdir, filename), + html = view.render('tutorial.tmpl', tutorialData); + + // yes, you can use {@link} in tutorials too! + html = helper.resolveLinks(html); // turn {@link foo} into foo + + fs.writeFileSync(tutorialPath, html, 'utf8'); + } + + // tutorials can have only one parent so there is no risk for loops + function saveChildren(node) { + node.children.forEach(function(child) { + generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); + saveChildren(child); + }); + } + saveChildren(tutorials); +}; diff --git a/data/scripts/jsdoc/templates/default/static/scripts/linenumber.js b/data/scripts/jsdoc/templates/default/static/scripts/linenumber.js new file mode 100644 index 00000000..a0c570d5 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/static/scripts/linenumber.js @@ -0,0 +1,17 @@ +(function() { + var counter = 0; + var numbered; + var source = document.getElementsByClassName('prettyprint source'); + + if (source && source[0]) { + source = source[0].getElementsByTagName('code')[0]; + + numbered = source.innerHTML.split('\n'); + numbered = numbered.map(function(item) { + counter++; + return '' + item; + }); + + source.innerHTML = numbered.join('\n'); + } +})(); diff --git a/data/scripts/jsdoc/templates/default/static/scripts/prettify/Apache-License-2.0.txt b/data/scripts/jsdoc/templates/default/static/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/static/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/data/scripts/jsdoc/templates/default/static/scripts/prettify/lang-css.js b/data/scripts/jsdoc/templates/default/static/scripts/prettify/lang-css.js new file mode 100644 index 00000000..041e1f59 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/static/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/data/scripts/jsdoc/templates/default/static/scripts/prettify/prettify.js b/data/scripts/jsdoc/templates/default/static/scripts/prettify/prettify.js new file mode 100644 index 00000000..eef5ad7e --- /dev/null +++ b/data/scripts/jsdoc/templates/default/static/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + + + + + +
    + +
    +

    + + + + + +

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

    Example 1? 's':'' ?>

    + + + +
    + + +

    Extends

    + +
      +
    • +
    + + + +

    Mixes In

    + +
      +
    • +
    + + + +

    Requires

    + +
      +
    • +
    + + + +

    Classes

    + +
    +
    +
    +
    + + + +

    Namespaces

    + +
    +
    +
    +
    + + + +

    Members

    + +
    + +
    + + + +

    Methods

    + +
    + +
    + + + +

    Type Definitions

    + +
    + + + +
    + + + +

    Events

    + +
    + +
    + +
    + +
    + + + diff --git a/data/scripts/jsdoc/templates/default/tmpl/details.tmpl b/data/scripts/jsdoc/templates/default/tmpl/details.tmpl new file mode 100644 index 00000000..d3b35522 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/details.tmpl @@ -0,0 +1,98 @@ + +
    + + +
    Properties:
    + +
    + + + + +
    Version:
    +
    + + + +
    Since:
    +
    + + + +
    Inherited From:
    +
    • + +
    + + + +
    Deprecated:
    • Yes
      + + + +
      Author:
      +
      +
        +
      • +
      +
      + + + + + + + + +
      License:
      +
      + + + +
      Default Value:
      +
      + + + +
      Source:
      +
      • + , +
      + + + +
      Tutorials:
      +
      +
        +
      • +
      +
      + + + +
      See:
      +
      +
        +
      • +
      +
      + + + +
      To Do:
      +
      +
        +
      • +
      +
      + +
      diff --git a/data/scripts/jsdoc/templates/default/tmpl/example.tmpl b/data/scripts/jsdoc/templates/default/tmpl/example.tmpl new file mode 100644 index 00000000..e87caa5b --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/example.tmpl @@ -0,0 +1,2 @@ + +
      diff --git a/data/scripts/jsdoc/templates/default/tmpl/examples.tmpl b/data/scripts/jsdoc/templates/default/tmpl/examples.tmpl new file mode 100644 index 00000000..23384c9d --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/examples.tmpl @@ -0,0 +1,11 @@ + +

      + +
      + \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/default/tmpl/exceptions.tmpl b/data/scripts/jsdoc/templates/default/tmpl/exceptions.tmpl new file mode 100644 index 00000000..78c4e250 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/exceptions.tmpl @@ -0,0 +1,30 @@ + + +
      +
      +
      + +
      +
      +
      +
      +
      + Type +
      +
      + +
      +
      +
      +
      + +
      + + + + + +
      + diff --git a/data/scripts/jsdoc/templates/default/tmpl/layout.tmpl b/data/scripts/jsdoc/templates/default/tmpl/layout.tmpl new file mode 100644 index 00000000..827b5acb --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/layout.tmpl @@ -0,0 +1,38 @@ + + + + + JSDoc: <?js= title ?> + + + + + + + + + + +
      + +

      + + +
      + + + +
      + +
      + Documentation generated by JSDoc on +
      + + + + + diff --git a/data/scripts/jsdoc/templates/default/tmpl/mainpage.tmpl b/data/scripts/jsdoc/templates/default/tmpl/mainpage.tmpl new file mode 100644 index 00000000..64e9e594 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/mainpage.tmpl @@ -0,0 +1,14 @@ + + + +

      + + + +
      +
      +
      + diff --git a/data/scripts/jsdoc/templates/default/tmpl/members.tmpl b/data/scripts/jsdoc/templates/default/tmpl/members.tmpl new file mode 100644 index 00000000..3fded716 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/members.tmpl @@ -0,0 +1,34 @@ + +
      +

      + + +

      + +
      +
      + +
      + +
      + + + +
      Type:
      +
        +
      • + +
      • +
      + + + + + +
      Example 1? 's':'' ?>
      + + +
      diff --git a/data/scripts/jsdoc/templates/default/tmpl/method.tmpl b/data/scripts/jsdoc/templates/default/tmpl/method.tmpl new file mode 100644 index 00000000..1fff9b86 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/method.tmpl @@ -0,0 +1,90 @@ + +
      +

      + + +

      + +
      +
      + + +
      + +
      + + + +
      Type:
      +
        +
      • + +
      • +
      + + + +
      This:
      +
      + + + +
      Parameters:
      + + + + + + +
      Fires:
      +
        +
      • +
      + + + +
      Listens to Events:
      +
        +
      • +
      + + + +
      Listeners of This Event:
      +
        +
      • +
      + + + +
      Throws:
      + 1) { ?>
        +
      • +
      + + + + +
      Returns:
      + 1) { ?>
        +
      • +
      + + + + +
      Example 1? 's':'' ?>
      + + +
      diff --git a/data/scripts/jsdoc/templates/default/tmpl/params.tmpl b/data/scripts/jsdoc/templates/default/tmpl/params.tmpl new file mode 100644 index 00000000..7b951149 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/params.tmpl @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeArgumentDefaultDescription
      + + + + + + <optional>
      + + + + <nullable>
      + + + + <repeatable>
      + +
      + + + + +
      Properties
      + +
      \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/default/tmpl/properties.tmpl b/data/scripts/jsdoc/templates/default/tmpl/properties.tmpl new file mode 100644 index 00000000..97a3d23a --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/properties.tmpl @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeArgumentDefaultDescription
      + + + + + + <optional>
      + + + + <nullable>
      + +
      + + + + +
      Properties
      +
      \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/default/tmpl/returns.tmpl b/data/scripts/jsdoc/templates/default/tmpl/returns.tmpl new file mode 100644 index 00000000..32e059ed --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/returns.tmpl @@ -0,0 +1,19 @@ + +
      + +
      + + + +
      +
      + Type +
      +
      + +
      +
      + \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/default/tmpl/source.tmpl b/data/scripts/jsdoc/templates/default/tmpl/source.tmpl new file mode 100644 index 00000000..e1092ef2 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/source.tmpl @@ -0,0 +1,8 @@ + +
      +
      +
      +
      +
      \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/default/tmpl/tutorial.tmpl b/data/scripts/jsdoc/templates/default/tmpl/tutorial.tmpl new file mode 100644 index 00000000..b0c79c1d --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/tutorial.tmpl @@ -0,0 +1,19 @@ +
      + +
      + 0) { ?> +
        +
      • +
      + + +

      +
      + +
      + +
      + +
      diff --git a/data/scripts/jsdoc/templates/default/tmpl/type.tmpl b/data/scripts/jsdoc/templates/default/tmpl/type.tmpl new file mode 100644 index 00000000..db8194d6 --- /dev/null +++ b/data/scripts/jsdoc/templates/default/tmpl/type.tmpl @@ -0,0 +1,7 @@ + + +| + \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/haruki/README.md b/data/scripts/jsdoc/templates/haruki/README.md new file mode 100644 index 00000000..ee6d36f1 --- /dev/null +++ b/data/scripts/jsdoc/templates/haruki/README.md @@ -0,0 +1,39 @@ +OVERVIEW +======== + +JSDoc 3 Haruki is an experimental template optimised for use with publishing processes that consume either JSON or XML. Whereas the default JSDoc template outputs an HTML representation of your API, Haruki will output a JSON, or optionally an XML, representation. + +Currently Haruki only supports a subset of the tags supported by the default template. Those are: + + * @name + * @desc + * @type + * @namespace + * @method (or @function) + * @member (or @var) + * @class + * @mixin + * @event + * @param + * @returns + * @throws + * @example + * @access (like @private or @public) + +This limited support set is intentional, as it is meant to be a usable set that could be shared with either JavaScript or PHP documentation -- another experimental tool, named "Vonnegut", can produce Haruki compatible JSON from PHPDoc tags. + +Note: `@link`s will appear in the output untransformed, there is no way to know at this stage what the file layout of your output will eventually be. It is assumed that whatever process emits the final output file/s will transform `@link` tags at that point. + +USAGE +===== + + ./jsdoc myscript.js -t templates/haruki -d console -q format=xml + +The results of this command will appear in `stdout` and can be piped into other tools for further processing. + +MORE +===== + +If you are interested in Haruki, you are encouraged to discuss your questions or ideas on the JSDoc-Users mailing list and fork/contribute to this project. + +For more information contact Michael Mathews at . \ No newline at end of file diff --git a/data/scripts/jsdoc/templates/haruki/publish.js b/data/scripts/jsdoc/templates/haruki/publish.js new file mode 100644 index 00000000..45c79876 --- /dev/null +++ b/data/scripts/jsdoc/templates/haruki/publish.js @@ -0,0 +1,220 @@ +/** + @overview Builds a tree-like JSON string from the doclet data. + @version 0.0.3 + @example + ./jsdoc scratch/jsdoc_test.js -t templates/haruki -d console -q format=xml + */ + +function graft(parentNode, childNodes, parentLongname, parentName) { + childNodes + .filter(function (element) { + return (element.memberof === parentLongname); + }) + .forEach(function (element, index) { + var i, + len; + + if (element.kind === 'namespace') { + if (! parentNode.namespaces) { + parentNode.namespaces = []; + } + + var thisNamespace = { + 'name': element.name, + 'description': element.description || '', + 'access': element.access || '', + 'virtual': !!element.virtual + }; + + parentNode.namespaces.push(thisNamespace); + + graft(thisNamespace, childNodes, element.longname, element.name); + } + else if (element.kind === 'mixin') { + if (! parentNode.mixins) { + parentNode.mixins = []; + } + + var thisMixin = { + 'name': element.name, + 'description': element.description || '', + 'access': element.access || '', + 'virtual': !!element.virtual + }; + + parentNode.mixins.push(thisMixin); + + graft(thisMixin, childNodes, element.longname, element.name); + } + else if (element.kind === 'function') { + if (! parentNode.functions) { + parentNode.functions = []; + } + + var thisFunction = { + 'name': element.name, + 'access': element.access || '', + 'virtual': !!element.virtual, + 'description': element.description || '', + 'parameters': [ ], + 'examples': [] + }; + + parentNode.functions.push(thisFunction); + + if (element.returns) { + thisFunction.returns = { + 'type': element.returns[0].type? (element.returns[0].type.names.length === 1? element.returns[0].type.names[0] : element.returns[0].type.names) : '', + 'description': element.returns[0].description || '' + }; + } + + if (element.examples) { + for (i = 0, len = element.examples.length; i < len; i++) { + thisFunction.examples.push(element.examples[i]); + } + } + + if (element.params) { + for (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 === 'member') { + if (! parentNode.properties) { + parentNode.properties = []; + } + parentNode.properties.push({ + 'name': element.name, + 'access': element.access || '', + 'virtual': !!element.virtual, + '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 = { + 'name': element.name, + 'access': element.access || '', + 'virtual': !!element.virtual, + 'description': element.description || '', + 'parameters': [], + 'examples': [] + }; + + parentNode.events.push(thisEvent); + + if (element.returns) { + thisEvent.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.examples) { + for (i = 0, len = element.examples.length; i < len; i++) { + thisEvent.examples.push(element.examples[i]); + } + } + + if (element.params) { + for (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 = { + 'name': element.name, + 'description': element.classdesc || '', + 'extends': element.augments || [], + 'access': element.access || '', + 'virtual': !!element.virtual, + 'fires': element.fires || '', + 'constructor': { + 'name': element.name, + 'description': element.description || '', + 'parameters': [ + ], + 'examples': [] + } + }; + + parentNode.classes.push(thisClass); + + if (element.examples) { + for (i = 0, len = element.examples.length; i < len; i++) { + thisClass.constructor.examples.push(element.examples[i]); + } + } + + if (element.params) { + for (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); + } + }); +} + +/** + @param {TAFFY} data + @param {object} opts + */ +exports.publish = function(data, opts) { + + var root = {}, + docs; + + data({undocumented: true}).remove(); + 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('js2xmlparser'); + console.log( xml('jsdoc', root) ); + } + else { + dump(root); + } + } + else { + console.log('This template only supports output to the console. Use the option "-d console" when you run JSDoc.'); + } + +}; diff --git a/data/scripts/jsdoc/test/README.md b/data/scripts/jsdoc/test/README.md new file mode 100644 index 00000000..dc8f9d96 --- /dev/null +++ b/data/scripts/jsdoc/test/README.md @@ -0,0 +1,45 @@ +Testing JSDoc 3 +=============== + +Running Tests +------------- + +Running tests is easy. Just change your working directory to the jsdoc folder +and run the following command on Windows: + + jsdoc -T + +Or on OS X, Linux, and other POSIX-compliant platforms: + + ./jsdoc -T + +If you can't get the short-form commands to work, try invoking Java directly: + + java -cp lib/js.jar org.mozilla.javascript.tools.shell.Main \ + -modules node_modules -modules rhino -modules lib -modules . \ + jsdoc.js -T + +Writing Tests +------------- + +Adding tests is pretty easy, too. You can write tests for JSDoc itself (to +make sure tags and the parser, etc. are working properly), tests for plugins, and/or +tests for templates. + +JSDoc 3 uses Jasmine (https://github.com/pivotal/jasmine) as its testing framework. +Take a look at that project's wiki for documentation on writing tests in general. + +### Tests for JSDoc + +Take a look at the files in the ```test``` directory for many examples of +writing tests for JSDoc itself. The ```test\fixtures``` directory hold fixtures +for use in the tests, and the ```test\specs``` directory holds the tests themselves. + +### Tests for plugins + +Tests for plugins are found in the ```plugins\test``` directory. Plugins containing +tests that were installed with the Jakefile install task will be run automatically. + +### Tests for templates + +TODO \ No newline at end of file diff --git a/data/scripts/jsdoc/test/async-callback.js b/data/scripts/jsdoc/test/async-callback.js new file mode 100644 index 00000000..d2388fa4 --- /dev/null +++ b/data/scripts/jsdoc/test/async-callback.js @@ -0,0 +1,57 @@ +/*global jasmine: true */ +(function() { + var withoutAsync = {}; + + ["it", "beforeEach", "afterEach"].forEach(function(jasmineFunction) { + withoutAsync[jasmineFunction] = jasmine.Env.prototype[jasmineFunction]; + return jasmine.Env.prototype[jasmineFunction] = function() { + var args = Array.prototype.slice.call(arguments, 0); + var timeout = null; + if (isLastArgumentATimeout(args)) { + timeout = args.pop(); + } + if (isLastArgumentAnAsyncSpecFunction(args)) + { + var specFunction = args.pop(); + args.push(function() { + return asyncSpec(specFunction, this, timeout); + }); + } + return withoutAsync[jasmineFunction].apply(this, args); + }; + }); + + function isLastArgumentATimeout(args) + { + return args.length > 0 && (typeof args[args.length-1]) === "number"; + } + + function isLastArgumentAnAsyncSpecFunction(args) + { + return args.length > 0 && (typeof args[args.length-1]) === "function" && args[args.length-1].length > 0; + } + + function asyncSpec(specFunction, spec, timeout) { + if (timeout == null){timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL || 1000;} + var done = false; + spec.runs(function() { + try { + return specFunction(function(error) { + done = true; + if (error != null) { + return spec.fail(error); + } + }); + } catch (e) { + done = true; + throw e; + } + }); + return spec.waitsFor(function() { + if (done === true) { + return true; + } + }, "spec to complete", timeout); + } + +}).call(this); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/abstracttag.js b/data/scripts/jsdoc/test/fixtures/abstracttag.js new file mode 100644 index 00000000..07aeb9b7 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/abstracttag.js @@ -0,0 +1,17 @@ +/** @constructor */ +function Thingy() { + + /** @abstract */ + this.pez = 2; + +} + +// same as... + +/** @constructor */ +function OtherThingy() { + + /** @virtual */ + this.pez = 2; + +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/accesstag.js b/data/scripts/jsdoc/test/fixtures/accesstag.js new file mode 100644 index 00000000..497c20dd --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/accesstag.js @@ -0,0 +1,29 @@ +/** @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/data/scripts/jsdoc/test/fixtures/alias.js b/data/scripts/jsdoc/test/fixtures/alias.js new file mode 100644 index 00000000..bbb7305c --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/alias.js @@ -0,0 +1,13 @@ +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/data/scripts/jsdoc/test/fixtures/alias2.js b/data/scripts/jsdoc/test/fixtures/alias2.js new file mode 100644 index 00000000..e1d8e5a5 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/alias2.js @@ -0,0 +1,10 @@ +(function() { + + /** @alias ns.Myclass# */ + var x = { + /** document me */ + myProperty: 'foo' + } + + return x; +})(); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/alias3.js b/data/scripts/jsdoc/test/fixtures/alias3.js new file mode 100644 index 00000000..3cea9aba --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/alias3.js @@ -0,0 +1,12 @@ +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/data/scripts/jsdoc/test/fixtures/aliasglobal.js b/data/scripts/jsdoc/test/fixtures/aliasglobal.js new file mode 100644 index 00000000..376a43a1 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/aliasglobal.js @@ -0,0 +1,7 @@ +(function() { + + /** @alias .log */ + var log = function() { + } + +})(); diff --git a/data/scripts/jsdoc/test/fixtures/aliasglobal2.js b/data/scripts/jsdoc/test/fixtures/aliasglobal2.js new file mode 100644 index 00000000..61f8082d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/aliasglobal2.js @@ -0,0 +1,18 @@ +(function () { + /** + * Creates a new test object. + * @alias Test + * @constructor + */ + var Test = function(testName) { + /** Document me. */ + this.name = testName; + } + + /** Document me. */ + Test.prototype.run = function(message) { + }; + + /** Document me. */ + Test.counter = 1; +})(); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/aliasresolve.js b/data/scripts/jsdoc/test/fixtures/aliasresolve.js new file mode 100644 index 00000000..8e9ec186 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/aliasresolve.js @@ -0,0 +1,19 @@ +/** + * @namespace + */ +var A = {}; + +(function(ns) { + /** + * @namespace + * @alias A.F + */ + var f = {}; + + /** + * @return {String} + */ + f.method = function(){}; + + ns.F = f; +})(A); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/aliasresolve2.js b/data/scripts/jsdoc/test/fixtures/aliasresolve2.js new file mode 100644 index 00000000..4ff8b282 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/aliasresolve2.js @@ -0,0 +1,19 @@ +/** + * @namespace + */ +var A = {}; + +/** + * @namespace + * @alias A.F + */ +var f = {}; + +(function(ns) { + /** + * @return {String} + */ + f.method = function(){}; + + ns.F = f; +})(A); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/also.js b/data/scripts/jsdoc/test/fixtures/also.js new file mode 100644 index 00000000..96a109ef --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/also.js @@ -0,0 +1,46 @@ +/** @class */ +function Asset() { + this._name = ''; + this._shape = ''; + this._shhhhKeepThisSecret = ''; +} + +/** + * + * 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; } +}; + +/** + * Set the value of the shape property. + * @param {string} newShape + *//** + * Set the value of the shape property, plus some other property. + * @param {string} newShape + * @param {string} mysteryProperty + *//** + * Get the value of the shape property. + * @returns {string} + */ +Asset.prototype.shape = function(newShape, mysteryProperty) { + if (newShape && mysteryProperty) { + this._shape = newShape; + this._shhhhKeepThisSecret = mysteryProperty; + } + else if (newShape) { + this._shape = newShape; + } + else { + return this._shape; + } +}; diff --git a/data/scripts/jsdoc/test/fixtures/augmentstag.js b/data/scripts/jsdoc/test/fixtures/augmentstag.js new file mode 100644 index 00000000..a30a1e22 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/augmentstag.js @@ -0,0 +1,53 @@ +/** + * @constructor + */ +function Foo() { + /** First property */ + this.prop1 = true; +} + +/** + * Second property + * @type {String} + */ +Foo.prototype.prop2 = "parent prop2"; + +/** + * First parent method. + */ +Foo.prototype.method1 = function() {}; + +/** + * Second parent method. + */ +Foo.prototype.method2 = function() {}; + + +/** + * @constructor + * @extends Foo + */ +function Bar() { + /** Thrid prop **/ + this.prop3 = true; +} + +/** + * Second child method. + */ +Bar.prototype.method2 = function() {}; + +/** + * @constructor + * @extends {Bar} + */ +function Baz() { + /** Override prop1 */ + this.prop1 = "new"; +} + +/** + * Third grandchild method. + */ +Baz.prototype.method3 = function() {}; + diff --git a/data/scripts/jsdoc/test/fixtures/augmentstag2.js b/data/scripts/jsdoc/test/fixtures/augmentstag2.js new file mode 100644 index 00000000..8def33a9 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/augmentstag2.js @@ -0,0 +1,6 @@ +// Test for @augments/@extends tags that refer to undefined symbols +/** + * @constructor + * @extends UndocumentedThing + */ +function Qux() {} diff --git a/data/scripts/jsdoc/test/fixtures/augmentstag3.js b/data/scripts/jsdoc/test/fixtures/augmentstag3.js new file mode 100644 index 00000000..80851817 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/augmentstag3.js @@ -0,0 +1,18 @@ +// test to see that we can @augment multiple things (code allows for it) +/** @class */ +function Foo() { +} +/** A method. */ +Foo.prototype.method1 = function () {}; + +/** @class */ +function Bar() { +} +/** Another method. */ +Bar.prototype.method2 = function () {} + +/** @class + * @augments Foo + * @augments Bar */ +function FooBar() { +} diff --git a/data/scripts/jsdoc/test/fixtures/authortag.js b/data/scripts/jsdoc/test/fixtures/authortag.js new file mode 100644 index 00000000..3bba0f0f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/authortag.js @@ -0,0 +1,10 @@ +/** @constructor + @author Michael Mathews +*/ +function Thingy() { +} + +/** @author John Doe + * @author Jane Doe */ +function Thingy2() { +} diff --git a/data/scripts/jsdoc/test/fixtures/borrowstag.js b/data/scripts/jsdoc/test/fixtures/borrowstag.js new file mode 100644 index 00000000..b6d065dd --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/borrowstag.js @@ -0,0 +1,14 @@ +/** @namespace + @borrows trstr as trim +*/ +var util = { + "trim": trstr +}; + +/** + Remove whitespace from around a string. + @param {string} str + */ +function trstr(str) { +} + diff --git a/data/scripts/jsdoc/test/fixtures/borrowstag2.js b/data/scripts/jsdoc/test/fixtures/borrowstag2.js new file mode 100644 index 00000000..9e80dd0d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/borrowstag2.js @@ -0,0 +1,21 @@ +/** @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/data/scripts/jsdoc/test/fixtures/callbacktag.js b/data/scripts/jsdoc/test/fixtures/callbacktag.js new file mode 100644 index 00000000..5dd468d5 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/callbacktag.js @@ -0,0 +1,21 @@ +/** + * @param {requestResponseCallback} cb + */ +function makeSpecialRequest(cb) { +} + +/** + * @param {wrongTypeCallback} cb + */ +function makeExtraSpecialRequest(cb) { +} + +/** + * @callback requestResponseCallback + * @param {number} responseCode + * @param {string} responseText + */ + +/** + * @callback {(object|string)} wrongTypeCallback + */ \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/classdesctag.js b/data/scripts/jsdoc/test/fixtures/classdesctag.js new file mode 100644 index 00000000..707a79e6 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/classdesctag.js @@ -0,0 +1,7 @@ +/** + * Asdf. + * @class + * @classdesc A description of the class. + */ +function Foo () { +} diff --git a/data/scripts/jsdoc/test/fixtures/classtag.js b/data/scripts/jsdoc/test/fixtures/classtag.js new file mode 100644 index 00000000..ea4584b1 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/classtag.js @@ -0,0 +1,12 @@ +/** + 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/data/scripts/jsdoc/test/fixtures/constanttag.js b/data/scripts/jsdoc/test/fixtures/constanttag.js new file mode 100644 index 00000000..a9f78058 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/constanttag.js @@ -0,0 +1,6 @@ +/** @constant */ +var FOO = 1; + +/** @const BAR */ + +/** @const {string} BAZ */ diff --git a/data/scripts/jsdoc/test/fixtures/constructortag.js b/data/scripts/jsdoc/test/fixtures/constructortag.js new file mode 100644 index 00000000..275ef3c6 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/constructortag.js @@ -0,0 +1,15 @@ +/** + 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/data/scripts/jsdoc/test/fixtures/constructstag.js b/data/scripts/jsdoc/test/fixtures/constructstag.js new file mode 100644 index 00000000..a31a1cd0 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/constructstag.js @@ -0,0 +1,19 @@ +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/data/scripts/jsdoc/test/fixtures/constructstag2.js b/data/scripts/jsdoc/test/fixtures/constructstag2.js new file mode 100644 index 00000000..604d4bad --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/constructstag2.js @@ -0,0 +1,16 @@ +Classify('Menu', + /** + @constructs Menu + @param items + */ + function (items) { + + }, + { + /** + @memberof Menu# + */ + show: function(){ + } + } +); diff --git a/data/scripts/jsdoc/test/fixtures/constructstag3.js b/data/scripts/jsdoc/test/fixtures/constructstag3.js new file mode 100644 index 00000000..3b4e410f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/constructstag3.js @@ -0,0 +1,26 @@ +/** + 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/data/scripts/jsdoc/test/fixtures/constructstag4.js b/data/scripts/jsdoc/test/fixtures/constructstag4.js new file mode 100644 index 00000000..f00cbf35 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/constructstag4.js @@ -0,0 +1,24 @@ +var Person = Class.create(/** @lends Person# */{ + + /** + Describe the constructor. + @classdesc A class that represents a person. + @constructs + @param {string} name + */ + initialize: function(name) { + + /** The name of the person. */ + this.name = name; + }, + + /** + @param {string} message + */ + say: function(message) { + + /** The person's message. */ + this.message = message; + } +}); + diff --git a/data/scripts/jsdoc/test/fixtures/constructstag5.js b/data/scripts/jsdoc/test/fixtures/constructstag5.js new file mode 100644 index 00000000..93063a5a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/constructstag5.js @@ -0,0 +1,14 @@ +Duck = (function() { + return /** @lends Duck# */ { + /** + Constructs a duck. + @constructs + @param tog + */ + constructor: function(tog) { + }, + /** Say hello. */ + quack: function() { + } + } +})(); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/copyrighttag.js b/data/scripts/jsdoc/test/fixtures/copyrighttag.js new file mode 100644 index 00000000..68a09f34 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/copyrighttag.js @@ -0,0 +1,6 @@ +/** @constructor + @copyright (c) 2011 Michael Mathews +*/ +function Thingy() { + +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/defaulttag.js b/data/scripts/jsdoc/test/fixtures/defaulttag.js new file mode 100644 index 00000000..bd8461aa --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/defaulttag.js @@ -0,0 +1,34 @@ +/** + @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/data/scripts/jsdoc/test/fixtures/deprecatedtag.js b/data/scripts/jsdoc/test/fixtures/deprecatedtag.js new file mode 100644 index 00000000..6b6269cb --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/deprecatedtag.js @@ -0,0 +1,11 @@ +/** @deprecated +*/ +function foo() { + +} + +/** @deprecated since version 2.0 +*/ +function bar() { + +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/descriptiontag.js b/data/scripts/jsdoc/test/fixtures/descriptiontag.js new file mode 100644 index 00000000..78d9d05a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/descriptiontag.js @@ -0,0 +1,7 @@ +/** Blah Blah Blah + * @desc halb halb halb + */ +var x; + +/** @description lkjasdf */ +var y; diff --git a/data/scripts/jsdoc/test/fixtures/destructuring.js b/data/scripts/jsdoc/test/fixtures/destructuring.js new file mode 100644 index 00000000..2b13f928 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/destructuring.js @@ -0,0 +1,5 @@ +/** + A builder function for the Stick application; + @var {function} Application + */ +var {Application} = require("stick"); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/doclet.js b/data/scripts/jsdoc/test/fixtures/doclet.js new file mode 100644 index 00000000..abba2473 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/doclet.js @@ -0,0 +1,23 @@ +/** + Markdown asterisks in a doclet that does not use leading asterisks. + **Strong** is strong. + + * List item 1. + * List item 2. + @param {string} thingy - The thingy. + */ +function test1(thingy) { + +} + +/** + * Markdown asterisks in a doclet that uses leading asterisks. + * **Strong** is strong. + * + * * List item 1. + * * List item 2. + * @param {string} thingy - The thingy. + */ +function test2(thingy) { + +} diff --git a/data/scripts/jsdoc/test/fixtures/enumtag.js b/data/scripts/jsdoc/test/fixtures/enumtag.js new file mode 100644 index 00000000..2374f09e --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/enumtag.js @@ -0,0 +1,11 @@ +/** + * Enum for tri-state values. + * @enum {number} + */ +var TriState = { + /** true */ + TRUE: 1, + FALSE: -1, + /** @type {boolean} */ + MAYBE: true +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/eventfirestag.js b/data/scripts/jsdoc/test/fixtures/eventfirestag.js new file mode 100644 index 00000000..331eaf8c --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/eventfirestag.js @@ -0,0 +1,30 @@ +/** + * @class + */ +var Hurl = function () { +}; + +/** + * Throw a snowball. + * + * @fires Hurl#snowball + * @fires Hurl#event:brick + */ +Hurl.prototype.snowball = function () { + /** + * @event Hurl#snowball + */ + this.emit('snowball', {}); +}; + +/** + * Throw a football match. + * + * @emits Hurl#footballMatch + */ +Hurl.prototype.footballMatch = function () { + /** + * @event Hurl#footballMatch + */ + this.emit('footballMatch', {}); +}; diff --git a/data/scripts/jsdoc/test/fixtures/exampletag.js b/data/scripts/jsdoc/test/fixtures/exampletag.js new file mode 100644 index 00000000..b20f7530 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/exampletag.js @@ -0,0 +1,14 @@ +/** @example + * console.log("foo"); + * console.log("bar"); + */ +var x; + +/** @example + * console.log("foo"); + * console.log("bar"); + * @example + * Example 2 + * 1 + 2; + */ +var y; diff --git a/data/scripts/jsdoc/test/fixtures/exceptiontag.js b/data/scripts/jsdoc/test/fixtures/exceptiontag.js new file mode 100644 index 00000000..14d7d9a7 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/exceptiontag.js @@ -0,0 +1,20 @@ +/** + @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/data/scripts/jsdoc/test/fixtures/exports.js b/data/scripts/jsdoc/test/fixtures/exports.js new file mode 100644 index 00000000..9c132478 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/exports.js @@ -0,0 +1,15 @@ +/** + * 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/data/scripts/jsdoc/test/fixtures/exportstag.js b/data/scripts/jsdoc/test/fixtures/exportstag.js new file mode 100644 index 00000000..26f787ea --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/exportstag.js @@ -0,0 +1,20 @@ +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/data/scripts/jsdoc/test/fixtures/exportstag2.js b/data/scripts/jsdoc/test/fixtures/exportstag2.js new file mode 100644 index 00000000..5e00268a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/exportstag2.js @@ -0,0 +1,18 @@ +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/data/scripts/jsdoc/test/fixtures/exportstag3.js b/data/scripts/jsdoc/test/fixtures/exportstag3.js new file mode 100644 index 00000000..d48e94e2 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/exportstag3.js @@ -0,0 +1,22 @@ +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/data/scripts/jsdoc/test/fixtures/exportstag4.js b/data/scripts/jsdoc/test/fixtures/exportstag4.js new file mode 100644 index 00000000..3097a565 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/exportstag4.js @@ -0,0 +1,12 @@ +define( + /** @exports some/module */ + function () { + /** @class */ + function myClass() {} + + /** Some method */ + myClass.prototype.myMethod = function () {}; + + return new myClass(); + } +); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/externals.js b/data/scripts/jsdoc/test/fixtures/externals.js new file mode 100644 index 00000000..6a8dd030 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/externals.js @@ -0,0 +1,24 @@ +/** + * The built in string object. + * @external String + * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String String} + */ + +/** + * Adds a new method to the built-in string. + * @function external:String#rot13 + * @example + * var greeting = new String('hello world'); + * console.log( greeting.rot13() ); // uryyb jbeyq + */ + +/** + * The jQuery plugin namespace. + * @external "jQuery.fn" + * @see {@link http://docs.jquery.com/Plugins/Authoring The jQuery Plugin Guide} + */ + +/** + * A jQuery plugin to make stars fly around your home page. + * @function external:"jQuery.fn".starfairy + */ diff --git a/data/scripts/jsdoc/test/fixtures/externals2.js b/data/scripts/jsdoc/test/fixtures/externals2.js new file mode 100644 index 00000000..0099f0c1 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/externals2.js @@ -0,0 +1,11 @@ +/** + Namespace provided by the browser. + @external XMLHttpRequest + @see https://developer.mozilla.org/en/xmlhttprequest + */ + +/** + Extends the built in XMLHttpRequest to send data encoded with a secret key. + @class EncryptedRequest + @extends external:XMLHttpRequest +*/ \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/file.js b/data/scripts/jsdoc/test/fixtures/file.js new file mode 100644 index 00000000..e6d62cdc --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/file.js @@ -0,0 +1,7 @@ +/** + * @overview This is a file doclet. + * @copyright Michael Mathews 2011 + */ + +function ignoreMe() { +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/functiontag.js b/data/scripts/jsdoc/test/fixtures/functiontag.js new file mode 100644 index 00000000..9783268f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/functiontag.js @@ -0,0 +1,7 @@ +/** @func Foo */ +function Foo() { +} + +/** @method */ +function Bar() { +} diff --git a/data/scripts/jsdoc/test/fixtures/getset.js b/data/scripts/jsdoc/test/fixtures/getset.js new file mode 100644 index 00000000..34a362f3 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/getset.js @@ -0,0 +1,37 @@ +/** @class */ +var Person = makeClass( + /** @lends Person# */ + { + /** Set up initial values. */ + initialize: function(name) { + }, + + /** Speak a message. */ + say: function(message) { + return this.name + " says: " + message; + }, + + /** + * The name of the person. + * @type {string} + */ + get name() { + return this._name; + }, + + /** + * @type {string} + * @param val + */ + set name(val) { + this._name = name; + }, + + /** + * @type {number} + */ + get age() { + return 25; + } + } +); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/globaltag.js b/data/scripts/jsdoc/test/fixtures/globaltag.js new file mode 100644 index 00000000..44f1d186 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/globaltag.js @@ -0,0 +1,16 @@ +/** + @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/data/scripts/jsdoc/test/fixtures/ignoretag.js b/data/scripts/jsdoc/test/fixtures/ignoretag.js new file mode 100644 index 00000000..4e8c2121 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/ignoretag.js @@ -0,0 +1,6 @@ +/** + @ignore +*/ +function foo(x) { + +} diff --git a/data/scripts/jsdoc/test/fixtures/ignoretag2.js b/data/scripts/jsdoc/test/fixtures/ignoretag2.js new file mode 100644 index 00000000..43db1b49 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/ignoretag2.js @@ -0,0 +1,6 @@ +/** + @ignore value that shouldn't be here +*/ +function foo(x) { + +} diff --git a/data/scripts/jsdoc/test/fixtures/include.js b/data/scripts/jsdoc/test/fixtures/include.js new file mode 100644 index 00000000..1e76887f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/include.js @@ -0,0 +1,3 @@ +// Used to test jsdoc/util/include +var myGlobal = require('jsdoc/util/global'); +myGlobal.__globalForIncludeTest__++; diff --git a/data/scripts/jsdoc/test/fixtures/inlinecomment.js b/data/scripts/jsdoc/test/fixtures/inlinecomment.js new file mode 100644 index 00000000..883d5f0b --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/inlinecomment.js @@ -0,0 +1,2 @@ +/** Inline Comment 1 */ this.test = function(){} +/** Inline Comment 2 */ this.test2 = function(){}; diff --git a/data/scripts/jsdoc/test/fixtures/inner.js b/data/scripts/jsdoc/test/fixtures/inner.js new file mode 100644 index 00000000..4c07b97a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/inner.js @@ -0,0 +1,7 @@ +function sendMessage(text) { + /** document me */ + var encoding = 'utf8'; + + /** document me */ + function encrypt(){} +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/innerscope.js b/data/scripts/jsdoc/test/fixtures/innerscope.js new file mode 100644 index 00000000..5d2d5f5c --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/innerscope.js @@ -0,0 +1,18 @@ +/** @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/data/scripts/jsdoc/test/fixtures/innerscope2.js b/data/scripts/jsdoc/test/fixtures/innerscope2.js new file mode 100644 index 00000000..68f30cba --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/innerscope2.js @@ -0,0 +1,19 @@ +/** @constructor */ +function Message(to) { + + var headers = {}; + + /** document me */ + headers.to = to; + + (function() { + var headers = { + /** document me */ + cache: {} + }; + + /** document me */ + headers.from = ''; + })() +} + diff --git a/data/scripts/jsdoc/test/fixtures/jshint/badfile.js b/data/scripts/jsdoc/test/fixtures/jshint/badfile.js new file mode 100644 index 00000000..1f1042b3 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/jshint/badfile.js @@ -0,0 +1,3 @@ +foo(); + +function foo() {} diff --git a/data/scripts/jsdoc/test/fixtures/jshint/goodfile.js b/data/scripts/jsdoc/test/fixtures/jshint/goodfile.js new file mode 100644 index 00000000..6c42f3b5 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/jshint/goodfile.js @@ -0,0 +1 @@ +var foo = 0; diff --git a/data/scripts/jsdoc/test/fixtures/jslangnames.js b/data/scripts/jsdoc/test/fixtures/jslangnames.js new file mode 100644 index 00000000..65f8404a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/jslangnames.js @@ -0,0 +1,24 @@ + +/** @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/data/scripts/jsdoc/test/fixtures/kindtag.js b/data/scripts/jsdoc/test/fixtures/kindtag.js new file mode 100644 index 00000000..72c5682f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/kindtag.js @@ -0,0 +1,2 @@ +/** @kind function */ +var x; diff --git a/data/scripts/jsdoc/test/fixtures/lends.js b/data/scripts/jsdoc/test/fixtures/lends.js new file mode 100644 index 00000000..6d86ae6d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/lends.js @@ -0,0 +1,16 @@ +/** @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/data/scripts/jsdoc/test/fixtures/lends2.js b/data/scripts/jsdoc/test/fixtures/lends2.js new file mode 100644 index 00000000..57c4fa38 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/lends2.js @@ -0,0 +1,18 @@ + +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/data/scripts/jsdoc/test/fixtures/lends3.js b/data/scripts/jsdoc/test/fixtures/lends3.js new file mode 100644 index 00000000..13c11a31 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/lends3.js @@ -0,0 +1,18 @@ +/** @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/data/scripts/jsdoc/test/fixtures/lendsglobal.js b/data/scripts/jsdoc/test/fixtures/lendsglobal.js new file mode 100644 index 00000000..13803be1 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/lendsglobal.js @@ -0,0 +1,14 @@ +declare({ + globals: /** @lends */ { + + /** document me */ + 'test': function() { }, + + /** @namespace */ + 'test1': { + + /** document me */ + 'test2': function() { } + } + } +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/licensetag.js b/data/scripts/jsdoc/test/fixtures/licensetag.js new file mode 100644 index 00000000..e9d852f5 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/licensetag.js @@ -0,0 +1,2 @@ +/** @license GPL v2 */ +var x; diff --git a/data/scripts/jsdoc/test/fixtures/linktag.js b/data/scripts/jsdoc/test/fixtures/linktag.js new file mode 100644 index 00000000..6f23ff1f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/linktag.js @@ -0,0 +1,15 @@ +/** @namespace ns */ +var ns = {}; + +/** +* Similar to [the bar function]{@link bar}. +* @see {@link bar} +*/ +ns.foo = function () { +} + +/** +* @see {@link ns.foo} +*/ +function bar() { +} diff --git a/data/scripts/jsdoc/test/fixtures/listenstag.js b/data/scripts/jsdoc/test/fixtures/listenstag.js new file mode 100644 index 00000000..50f82da3 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/listenstag.js @@ -0,0 +1,34 @@ +/** @module myModule */ + +/** An event (has listeners). + * @event MyEvent + * @memberof module:myModule + * @param {number} foo - asdf. */ + +/** A handler. + * @listens module:myModule.MyEvent + * @listens module:myModule~Events.event:Event2 + * @listens fakeEvent + */ +function MyHandler() { +} + +/** Another handler. + * @listens module:myModule.MyEvent + */ +function AnotherHandler() { +} + +/** a namespace. + * @namespace */ +var Events = { +}; + +/** Another event (has listeners). + * @event Event2 + * @memberof module:myModule~Events + */ + +/** An event with no listeners. + * @event module:myModule#Event3 */ + diff --git a/data/scripts/jsdoc/test/fixtures/markdowntest.md b/data/scripts/jsdoc/test/fixtures/markdowntest.md new file mode 100644 index 00000000..276aada8 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/markdowntest.md @@ -0,0 +1,10 @@ +This is a header +---- + +This is some text. + + this is some code + +* this +* a +* list \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/memberoftag.js b/data/scripts/jsdoc/test/fixtures/memberoftag.js new file mode 100644 index 00000000..27993933 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/memberoftag.js @@ -0,0 +1,11 @@ +/** @constructor + @memberof mathlib + */ +function Data() { + + /** @member */ + this.point = {}; +} + +/** @namespace */ +mathlib = {Data: Data}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/memberoftag2.js b/data/scripts/jsdoc/test/fixtures/memberoftag2.js new file mode 100644 index 00000000..b926dc6a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/memberoftag2.js @@ -0,0 +1,10 @@ +create( + 'Observable', + { + /** @memberof Observable */ + cache: [], + + /** @memberof Observable.prototype */ + publish: function(msg) {} + } +); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/memberoftag3.js b/data/scripts/jsdoc/test/fixtures/memberoftag3.js new file mode 100644 index 00000000..a19f1357 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/memberoftag3.js @@ -0,0 +1,19 @@ +/** @module terrain + @example + var terrain = require('terrain'), + forest = new terrain.Forest(), + tree = new forest.Tree(); +*/ + +/** @class */ +exports.Forest = function(){} +var Forest = exports.Forest; + +/** + @class + @memberof module:terrain +*/ +Forest.prototype.Tree = function() { + /** A leaf */ + this.leaf = 1; +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/memberoftag4.js b/data/scripts/jsdoc/test/fixtures/memberoftag4.js new file mode 100644 index 00000000..521343d8 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/memberoftag4.js @@ -0,0 +1,16 @@ +/** + * Namespace doStuff. + * @namespace doStuff + */ + +/** + * Function with the same name as its namespace. + * @memberof doStuff + */ +function doStuff() {} + +/** + * Function with a different name than the namespace. + * @memberof doStuff + */ +function doOtherStuff() {} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/memberoftagforced.js b/data/scripts/jsdoc/test/fixtures/memberoftagforced.js new file mode 100644 index 00000000..c1b495de --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/memberoftagforced.js @@ -0,0 +1,44 @@ + + /** @constructor + */ + function Data() { + + /** + The current position. + @type {object} + @property {boolean} needsRevalidate Does this point need to be revalidated? + */ + this.point = { + /** + The x coordinate of the point. + @type {number} + @name point.x + @memberof! Data# + */ + x: 0, + + /** + The y coordinate of the point. + @type {number} + @name point.y + @memberof! Data# + @see {@link Data#point.x} + */ + y: 0, + + needsRevalidate: false + }; + } + +var map = { + /** + @type {Array} + @name map.routes + @memberof! + @property {Data#point} point + */ + routes: [] +} + +/** The current cursor. */ +var cursor = {}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/membertag.js b/data/scripts/jsdoc/test/fixtures/membertag.js new file mode 100644 index 00000000..66aee08d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/membertag.js @@ -0,0 +1,5 @@ +/** @member */ +var x; + +/** @var foobar */ +/** @var {string} baz */ diff --git a/data/scripts/jsdoc/test/fixtures/mixintag.js b/data/scripts/jsdoc/test/fixtures/mixintag.js new file mode 100644 index 00000000..07dc085d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/mixintag.js @@ -0,0 +1,27 @@ +/** + * This provides methods used for event handling. It's not meant to + * be used directly, except as a provider of related methods. + * + * @mixin + */ +var Eventful = { + /** fires something. */ + fires: function () {}, + /** handles a signal. */ + on: function () {} +}; + +/** + * @constructor + * @mixes Eventful + */ +var FormButton = function() { +}; + +/** @mixin AnotherMixin*/ + +/** I mix in multiple things + * @constructor MyClass + * @mixes Eventful + * @mixes AnotherMixin */ + diff --git a/data/scripts/jsdoc/test/fixtures/moduleinner.js b/data/scripts/jsdoc/test/fixtures/moduleinner.js new file mode 100644 index 00000000..7f2d0a3f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/moduleinner.js @@ -0,0 +1,28 @@ +/** +* @module my/module +*/ +(function() { + +/** document fooIn */ +fooIn = function() { +}; + +/** @namespace */ +bar = { + /** document bar.Zop */ + zop: function() { + } +} + +/** @constructor */ +exports.Frotz = function() { + /** document exports.Frotz#quaz */ + this.quaz = 1; +} + +}) (); + +/** document fooOut +*/ +fooOut = function() { +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/moduleisconstructor.js b/data/scripts/jsdoc/test/fixtures/moduleisconstructor.js new file mode 100644 index 00000000..1990d7f7 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/moduleisconstructor.js @@ -0,0 +1,17 @@ +/** + Describe the module here. + @module mymodule/config +*/ + +/** + Create a new configuration + @param {string} id + @constructor + @alias module:mymodule/config +*/ +function Config(id) { + /** Document me. */ + this.id = id; +} + +module.exports = Config; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/moduleisfunction.js b/data/scripts/jsdoc/test/fixtures/moduleisfunction.js new file mode 100644 index 00000000..21c29bda --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/moduleisfunction.js @@ -0,0 +1,10 @@ +/** + * This is a module called foo. + * @module foo + */ + +/** + * The module exports a single function. + * @param {string} bar + */ +module.exports = function(bar) {}; diff --git a/data/scripts/jsdoc/test/fixtures/modules/data/mod-1.js b/data/scripts/jsdoc/test/fixtures/modules/data/mod-1.js new file mode 100644 index 00000000..a5de108f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/modules/data/mod-1.js @@ -0,0 +1,5 @@ +/** @module */ +define({ + property: "foo", + method: function() {} +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/modules/data/mod-2.js b/data/scripts/jsdoc/test/fixtures/modules/data/mod-2.js new file mode 100644 index 00000000..1027fd9d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/modules/data/mod-2.js @@ -0,0 +1,5 @@ +/** @module my/module/name */ +define({ + property: "foo", + method: function() {} +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/modules/data/mod-3.js b/data/scripts/jsdoc/test/fixtures/modules/data/mod-3.js new file mode 100644 index 00000000..91a3251a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/modules/data/mod-3.js @@ -0,0 +1,21 @@ +/** + 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/data/scripts/jsdoc/test/fixtures/moduletag.js b/data/scripts/jsdoc/test/fixtures/moduletag.js new file mode 100644 index 00000000..fb4c4665 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/moduletag.js @@ -0,0 +1,11 @@ +/** + * @module bookshelf + */ + +/** + * @class + */ +this.Book = function(title) { + /** document me */ + this.title = title; +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/moduletag2.js b/data/scripts/jsdoc/test/fixtures/moduletag2.js new file mode 100644 index 00000000..0dd0bc3e --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/moduletag2.js @@ -0,0 +1,9 @@ +/** @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/data/scripts/jsdoc/test/fixtures/moduletag3.js b/data/scripts/jsdoc/test/fixtures/moduletag3.js new file mode 100644 index 00000000..d3b635b0 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/moduletag3.js @@ -0,0 +1,20 @@ +/** +@module foo/Photo/manager +@desc Manage a collection of photos. +*/ + +/** +Construct a new Photo manager +@constructor module:foo/Photo/manager +@param {String} collectionId The identifier of the managed collection. +*/ +module.exports = function(collectionId) { + + /** + @function module:foo/Photo/manager#getPhoto + @param {String} photoName + */ + this.getPhoto = function() {} + +} + diff --git a/data/scripts/jsdoc/test/fixtures/namedFuncStatement.js b/data/scripts/jsdoc/test/fixtures/namedFuncStatement.js new file mode 100644 index 00000000..924a659f --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/namedFuncStatement.js @@ -0,0 +1,7 @@ +/** @class */ +var Foo = function Bar(a) { + /** document me */ + var var1 = 1; + /** document me */ + this.member1 = 2; +}; diff --git a/data/scripts/jsdoc/test/fixtures/namedFuncStatement2.js b/data/scripts/jsdoc/test/fixtures/namedFuncStatement2.js new file mode 100644 index 00000000..0223092a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/namedFuncStatement2.js @@ -0,0 +1,7 @@ +/** @class */ +Foo = function Bar(a) { + /** document me */ + var var1 = 1; + /** document me */ + this.member1 = 2; +}; diff --git a/data/scripts/jsdoc/test/fixtures/namedFuncStatement3.js b/data/scripts/jsdoc/test/fixtures/namedFuncStatement3.js new file mode 100644 index 00000000..8f185816 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/namedFuncStatement3.js @@ -0,0 +1,9 @@ +ns = { + /** @class */ + Foo: function Bar(a) { + /** document me */ + var var1 = 1; + /** document me */ + this.member1 = 2; + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/namespacetag.js b/data/scripts/jsdoc/test/fixtures/namespacetag.js new file mode 100644 index 00000000..82dc1081 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/namespacetag.js @@ -0,0 +1,5 @@ +/** @namespace */ +var x = { +}; +/** @namespace Foo */ +/** @namespace {function} Bar */ diff --git a/data/scripts/jsdoc/test/fixtures/objectlit.js b/data/scripts/jsdoc/test/fixtures/objectlit.js new file mode 100644 index 00000000..f976cfb4 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/objectlit.js @@ -0,0 +1,8 @@ +/** document me */ +var tools = { + /** document me */ + serialiser: { + /** document me */ + value: '' + } +}; diff --git a/data/scripts/jsdoc/test/fixtures/objectlit2.js b/data/scripts/jsdoc/test/fixtures/objectlit2.js new file mode 100644 index 00000000..072d4eda --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/objectlit2.js @@ -0,0 +1,8 @@ +/** document me */ +var position = { + axis: { + /** document me */ + x: 0, + y: 0 + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/objectpropertykeys.js b/data/scripts/jsdoc/test/fixtures/objectpropertykeys.js new file mode 100644 index 00000000..abc611f2 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/objectpropertykeys.js @@ -0,0 +1,22 @@ +Call( +{ + methodA: function() + { + this.id = this.createUUID(); + }, + + valueOf: function() + { + return this.id; + }, + + toString: function() + { + return this.id; + } +}); + +//Simple inheritance model with correct constructor +function Test() {} +function Test2() { Test.call(this); } +Test2.prototype = Object.create(Test.prototype, {constructor: {value: Test2}}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/paramtag.js b/data/scripts/jsdoc/test/fixtures/paramtag.js new file mode 100644 index 00000000..c6fd831c --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/paramtag.js @@ -0,0 +1,47 @@ +/** +* @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) { +} + +/** + * @param [async=true] - whether to be asynchronous + */ +function request(async) { +} diff --git a/data/scripts/jsdoc/test/fixtures/plugins.js b/data/scripts/jsdoc/test/fixtures/plugins.js new file mode 100644 index 00000000..d71e6e9d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/plugins.js @@ -0,0 +1,10 @@ +/** + * @name virtual + */ + +var foo = "bar"; + +/** + * @foo bar + */ +var test = "tada"; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/privatetag.js b/data/scripts/jsdoc/test/fixtures/privatetag.js new file mode 100644 index 00000000..71e0d971 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/privatetag.js @@ -0,0 +1,11 @@ +/** +* @constructor +* @private +*/ +function Foo() { + + /** document me */ + this.bar = 1; +} + + diff --git a/data/scripts/jsdoc/test/fixtures/projecttag.js b/data/scripts/jsdoc/test/fixtures/projecttag.js new file mode 100644 index 00000000..b00c8e2c --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/projecttag.js @@ -0,0 +1,9 @@ +/** + 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/data/scripts/jsdoc/test/fixtures/propertytag.js b/data/scripts/jsdoc/test/fixtures/propertytag.js new file mode 100644 index 00000000..475fe31b --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/propertytag.js @@ -0,0 +1,13 @@ +/** + * @namespace + * @property {Object} defaults The default values. + * @property {Number} defaults.a The a property of the defaults. + * @property {String} defaults.b The b property of the defaults. + */ +myobject = { + defaults: { + a: 1, + b: "Hit the light", + c: true + } +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/quotename.js b/data/scripts/jsdoc/test/fixtures/quotename.js new file mode 100644 index 00000000..aa7577c1 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/quotename.js @@ -0,0 +1,19 @@ +/** @namespace */ +var chat = {}; + +/** + @namespace + */ +chat["#channel"] = {}; + + +/** + @member + @type {boolean} + @defaultvalue + */ +chat["#channel"].open = true; + +/** + @event chat."#channel"."op:announce-motd" + */ \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/quotename2.js b/data/scripts/jsdoc/test/fixtures/quotename2.js new file mode 100644 index 00000000..b05c8136 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/quotename2.js @@ -0,0 +1,10 @@ +/** @namespace */ +var contacts = { + + /** @namespace */ + 'say-"hello"@example.com': { + + /** document me */ + "username": 'Sue Smart' + } +} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/readonlytag.js b/data/scripts/jsdoc/test/fixtures/readonlytag.js new file mode 100644 index 00000000..30e826ce --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/readonlytag.js @@ -0,0 +1,10 @@ +/** +* @constructor +*/ +function Collection() { + + /** @readonly */ + this.length = 0; +} + + diff --git a/data/scripts/jsdoc/test/fixtures/requirestag.js b/data/scripts/jsdoc/test/fixtures/requirestag.js new file mode 100644 index 00000000..3fe27229 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/requirestag.js @@ -0,0 +1,12 @@ +/** +* @requires module:foo/helper +*/ +function foo() { +} + +/** +* @requires foo +* @requires Pez#blat this text is ignored +*/ +function bar() { +} diff --git a/data/scripts/jsdoc/test/fixtures/returnstag.js b/data/scripts/jsdoc/test/fixtures/returnstag.js new file mode 100644 index 00000000..d3cf4de4 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/returnstag.js @@ -0,0 +1,11 @@ +/** +* @returns { String | Array} The names of the found item(s). +*/ +function find(targetName) { +} + +/** +* @return The binding id. +*/ +function bind(callback) { +} diff --git a/data/scripts/jsdoc/test/fixtures/scopetags.js b/data/scripts/jsdoc/test/fixtures/scopetags.js new file mode 100644 index 00000000..1eeeb204 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/scopetags.js @@ -0,0 +1,10 @@ +/** (scope tags for global objects do not override globalness hence need a container class) + * @module scopetags */ +/** @inner */ +var myInner; + +/** @instance */ +var myInstance; + +/** @static */ +var myStatic; diff --git a/data/scripts/jsdoc/test/fixtures/seetag.js b/data/scripts/jsdoc/test/fixtures/seetag.js new file mode 100644 index 00000000..b439c261 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/seetag.js @@ -0,0 +1,11 @@ +/** +* @see {@link bar} +*/ +function foo() { +} + +/** +* @see http://example.com/someref +*/ +function bar() { +} diff --git a/data/scripts/jsdoc/test/fixtures/sincetag.js b/data/scripts/jsdoc/test/fixtures/sincetag.js new file mode 100644 index 00000000..527b2a39 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/sincetag.js @@ -0,0 +1,6 @@ +/** + @since 1.2.3 +*/ +function foo(x) { + +} diff --git a/data/scripts/jsdoc/test/fixtures/specialnames.js b/data/scripts/jsdoc/test/fixtures/specialnames.js new file mode 100644 index 00000000..d775a0b1 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/specialnames.js @@ -0,0 +1,2 @@ +/** document me */ +var hasOwnProperty = Object.prototype.hasOwnProperty; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/src/_dir2/four.js b/data/scripts/jsdoc/test/fixtures/src/_dir2/four.js new file mode 100644 index 00000000..e69de29b diff --git a/data/scripts/jsdoc/test/fixtures/src/_ignored.js b/data/scripts/jsdoc/test/fixtures/src/_ignored.js new file mode 100644 index 00000000..e69de29b diff --git a/data/scripts/jsdoc/test/fixtures/src/dir1/three.js b/data/scripts/jsdoc/test/fixtures/src/dir1/three.js new file mode 100644 index 00000000..e69de29b diff --git a/data/scripts/jsdoc/test/fixtures/src/ignored.txt b/data/scripts/jsdoc/test/fixtures/src/ignored.txt new file mode 100644 index 00000000..e69de29b diff --git a/data/scripts/jsdoc/test/fixtures/src/one.js b/data/scripts/jsdoc/test/fixtures/src/one.js new file mode 100644 index 00000000..e69de29b diff --git a/data/scripts/jsdoc/test/fixtures/src/two.js b/data/scripts/jsdoc/test/fixtures/src/two.js new file mode 100644 index 00000000..e69de29b diff --git a/data/scripts/jsdoc/test/fixtures/starbangstar.js b/data/scripts/jsdoc/test/fixtures/starbangstar.js new file mode 100644 index 00000000..262da969 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/starbangstar.js @@ -0,0 +1,12 @@ +/*!* +* Script that does something awesome +* +* @copyright (c) 2011 Rotorz Limited. All rights reserved. +* @author Lea Hayes +* @module myscript/core +*/ + +/*!********************************* + * This should be ignored by JSDoc + * @var x + */ \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/summarytag.js b/data/scripts/jsdoc/test/fixtures/summarytag.js new file mode 100644 index 00000000..b1f4c496 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/summarytag.js @@ -0,0 +1,3 @@ +/** @summary I do not like green eggs and ham! */ +function Sam() { +} diff --git a/data/scripts/jsdoc/test/fixtures/testPlugin1.js b/data/scripts/jsdoc/test/fixtures/testPlugin1.js new file mode 100644 index 00000000..3b3e26b7 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/testPlugin1.js @@ -0,0 +1,39 @@ +var myGlobal = require('jsdoc/util/global'); +myGlobal.jsdocPluginsTest.plugin1 = {}; + +exports.handlers = { + fileBegin: function() { + myGlobal.jsdocPluginsTest.plugin1.fileBegin = true; + }, + beforeParse: function() { + myGlobal.jsdocPluginsTest.plugin1.beforeParse = true; + }, + jsdocCommentFound: function() { + myGlobal.jsdocPluginsTest.plugin1.jsdocCommentFound = true; + }, + symbolFound: function() { + myGlobal.jsdocPluginsTest.plugin1.symbolFound = true; + }, + newDoclet: function() { + myGlobal.jsdocPluginsTest.plugin1.newDoclet = true; + }, + fileComplete: function() { + myGlobal.jsdocPluginsTest.plugin1.fileComplete = true; + } +}; + +exports.defineTags = function(dictionary) { + dictionary.defineTag("foo", { + canHaveName: true, + onTagged: function(doclet, tag) { + doclet.foo = true; + } + }); +}; + +exports.nodeVisitor = { + visitNode: function(node, e, parser, currentSourceName) { + myGlobal.jsdocPluginsTest.plugin1.visitNode = true; + e.stopPropagation = true; + } +}; diff --git a/data/scripts/jsdoc/test/fixtures/testPlugin2.js b/data/scripts/jsdoc/test/fixtures/testPlugin2.js new file mode 100644 index 00000000..bd7189f4 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/testPlugin2.js @@ -0,0 +1,29 @@ +var myGlobal = require('jsdoc/util/global'); +myGlobal.jsdocPluginsTest.plugin2 = {}; + +exports.handlers = { + fileBegin: function() { + myGlobal.jsdocPluginsTest.plugin2.fileBegin = true; + }, + beforeParse: function() { + myGlobal.jsdocPluginsTest.plugin2.beforeParse = true; + }, + jsdocCommentFound: function() { + myGlobal.jsdocPluginsTest.plugin2.jsdocCommentFound = true; + }, + symbolFound: function() { + myGlobal.jsdocPluginsTest.plugin2.symbolFound = true; + }, + newDoclet: function() { + myGlobal.jsdocPluginsTest.plugin2.newDoclet = true; + }, + fileComplete: function() { + myGlobal.jsdocPluginsTest.plugin2.fileComplete = true; + } +}; + +exports.nodeVisitor = { + visitNode: function() { + myGlobal.jsdocPluginsTest.plugin2.visitNode = true; + } +}; diff --git a/data/scripts/jsdoc/test/fixtures/this-and-objectlit.js b/data/scripts/jsdoc/test/fixtures/this-and-objectlit.js new file mode 100644 index 00000000..37b3c94a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/this-and-objectlit.js @@ -0,0 +1,12 @@ +/** @constructor */ +function Page(title) { + this.parts = { + title: title, + body: { + /** document me */ + heading: '', + main: '' + } + }; +} + diff --git a/data/scripts/jsdoc/test/fixtures/this.js b/data/scripts/jsdoc/test/fixtures/this.js new file mode 100644 index 00000000..7c167606 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/this.js @@ -0,0 +1,10 @@ +/** + @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/data/scripts/jsdoc/test/fixtures/this2.js b/data/scripts/jsdoc/test/fixtures/this2.js new file mode 100644 index 00000000..88fd9fa6 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/this2.js @@ -0,0 +1,15 @@ +/** @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/data/scripts/jsdoc/test/fixtures/this3.js b/data/scripts/jsdoc/test/fixtures/this3.js new file mode 100644 index 00000000..7e4e0a77 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/this3.js @@ -0,0 +1,5 @@ +function setPosition(newP) { + /** document me */ + this.position = newP; // sets global property +} + diff --git a/data/scripts/jsdoc/test/fixtures/thistag.js b/data/scripts/jsdoc/test/fixtures/thistag.js new file mode 100644 index 00000000..1ea79253 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/thistag.js @@ -0,0 +1,10 @@ +/** @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/data/scripts/jsdoc/test/fixtures/todotag.js b/data/scripts/jsdoc/test/fixtures/todotag.js new file mode 100644 index 00000000..5ecfac4b --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/todotag.js @@ -0,0 +1,6 @@ +/** A function. + * @todo something + * @todo something else + */ +function x() { +} diff --git a/data/scripts/jsdoc/test/fixtures/tutorialtag.js b/data/scripts/jsdoc/test/fixtures/tutorialtag.js new file mode 100644 index 00000000..b0124dbe --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/tutorialtag.js @@ -0,0 +1,5 @@ +/** Some documentation. + * @tutorial tute1 + * @tutorial tute2 + */ +var x; diff --git a/data/scripts/jsdoc/test/fixtures/typedeftag.js b/data/scripts/jsdoc/test/fixtures/typedeftag.js new file mode 100644 index 00000000..fa2b0890 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/typedeftag.js @@ -0,0 +1,11 @@ +/** @typedef {(string|number)} calc.NumberLike */ + +/** @typedef {string} */ +calc.Operator; + +/** @typedef {calc.NumberLike} calc.Result */ +calc.Outcome; + +/** @param {calc.NumberLike} x A number or a string. */ +calc.readNumber = function(x) { +}; diff --git a/data/scripts/jsdoc/test/fixtures/typekind.js b/data/scripts/jsdoc/test/fixtures/typekind.js new file mode 100644 index 00000000..46d6c1eb --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/typekind.js @@ -0,0 +1,17 @@ +/** + @module {ConnectServer} blog/server +*/ + +module.exports = require('connect').createServer( + Connect.logger(), + Connect.conditionalGet(), + Connect.favicon(), + Connect.cache(), + Connect.gzip(), + require('wheat')(__dirname) +); + +/** + @member {number} module:blog/server.port + @default 8080 +*/ \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/typetag.js b/data/scripts/jsdoc/test/fixtures/typetag.js new file mode 100644 index 00000000..acaa7cac --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/typetag.js @@ -0,0 +1,10 @@ +/** + @type {string|Array} +*/ +var foo; + + +/** + @type integer +*/ +var bar = +(new Date()).getTime(); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/undocumentedtag.js b/data/scripts/jsdoc/test/fixtures/undocumentedtag.js new file mode 100644 index 00000000..01ab4fc3 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/undocumentedtag.js @@ -0,0 +1,3 @@ +/** Undocumented doclet. + * @undocumented */ +var x; diff --git a/data/scripts/jsdoc/test/fixtures/utf8.js b/data/scripts/jsdoc/test/fixtures/utf8.js new file mode 100644 index 00000000..ec6eea74 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/utf8.js @@ -0,0 +1,6 @@ +/** + * @constructor + * @desc Τεκμηρίωση είναι η επικοινωνία! + */ +Test = function() { +}; \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/var.js b/data/scripts/jsdoc/test/fixtures/var.js new file mode 100644 index 00000000..0daea9f5 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/var.js @@ -0,0 +1,10 @@ +/** 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/data/scripts/jsdoc/test/fixtures/variations.js b/data/scripts/jsdoc/test/fixtures/variations.js new file mode 100644 index 00000000..e292cae7 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/variations.js @@ -0,0 +1,26 @@ +/** + * @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/data/scripts/jsdoc/test/fixtures/variations2.js b/data/scripts/jsdoc/test/fixtures/variations2.js new file mode 100644 index 00000000..2105c463 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/variations2.js @@ -0,0 +1,36 @@ +/** +* Describe the namespace. +* @namespace dollar(1) +*/ + +/** +* Describe the constructor. +* @constructor dollar(2) +* @param {object} [options] +*/ + +/** +* Describe the global function. +* @function dollar(3) +* @param {string} selector - The selector string. +*/ +dollar = function(a, b) { +}; + +/** +* Describe the instance method. +* @instance +* @function empty +* @memberof dollar(2) +*/ +dollar.prototype.empty = function() { +} + +/** +* Describe the static method. +* @function klass +* @memberof dollar(1) +* @param {string} name +*/ +dollar.klass = function(name) { +}; diff --git a/data/scripts/jsdoc/test/fixtures/variations3.js b/data/scripts/jsdoc/test/fixtures/variations3.js new file mode 100644 index 00000000..0b3fdfc4 --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/variations3.js @@ -0,0 +1,15 @@ +/** +* @constructor +*/ +someObject = function() {} + +/** +* @constructor +* @variation 2 +*/ +someObject = function() {} + +/** +* @memberof someObject(2) +*/ +someMethod = function() {} \ No newline at end of file diff --git a/data/scripts/jsdoc/test/fixtures/versiontag.js b/data/scripts/jsdoc/test/fixtures/versiontag.js new file mode 100644 index 00000000..a482075a --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/versiontag.js @@ -0,0 +1,6 @@ +/** + @version 1.2.3 +*/ +function foo(x) { + +} diff --git a/data/scripts/jsdoc/test/fixtures/virtual.js b/data/scripts/jsdoc/test/fixtures/virtual.js new file mode 100644 index 00000000..4eb25c1d --- /dev/null +++ b/data/scripts/jsdoc/test/fixtures/virtual.js @@ -0,0 +1,3 @@ +/** @name dimensions */ + +var width = 12 \ No newline at end of file diff --git a/data/scripts/jsdoc/test/jasmine-jsdoc.js b/data/scripts/jsdoc/test/jasmine-jsdoc.js new file mode 100644 index 00000000..f77b0f5f --- /dev/null +++ b/data/scripts/jsdoc/test/jasmine-jsdoc.js @@ -0,0 +1,142 @@ +/*global env: true, expect: true, runs: true, waits: true */ +/*jshint evil: true */ +var fs = require('jsdoc/fs'); +var path = require('path'); +var util = require('util'); + +var hasOwnProp = Object.prototype.hasOwnProperty; + +var myGlobal = require('jsdoc/util/global'); + +var jasmineAll = myGlobal.jasmineAll = require('test/lib/jasmine'); +var jasmine = myGlobal.jasmine = jasmineAll.jasmine; + +// due to scoping issues, requiring this file doesn't work +eval( fs.readFileSync(__dirname + '/test/async-callback.js', 'utf8') ); + +var jasmineNode = require('test/reporter').jasmineNode; + +// set up jasmine's global functions +['spyOn', 'it', 'xit', 'expect', 'runs', 'waitsFor', 'beforeEach', 'afterEach', 'describe', + 'xdescribe'].forEach(function(item) { + myGlobal[item] = jasmineAll[item]; +}); + +var reporter = null; +jasmine.initialize = function(done, verbose) { + var jasmineEnv = jasmine.getEnv(); + + if (reporter !== null) { + // If we've run before, we need to reset the runner + jasmineEnv.currentRunner_ = new jasmine.Runner(jasmineEnv); + // And clear the reporter + jasmineEnv.reporter.subReporters_.splice(jasmineEnv.reporter.subReporters_.indexOf(reporter)); + } + + var reporterOpts = { + print: util.print, + color: env.opts.nocolor === true ? false : true, + onComplete: done + }; + + reporter = env.opts.verbose ? new jasmineNode.TerminalVerboseReporter(reporterOpts) : + new jasmineNode.TerminalReporter(reporterOpts); + jasmineEnv.addReporter(reporter); + + // updateInterval is set to 0 because there were not-fully-understood + // issues with asynchronous behavior in jasmine otherwise. + jasmineEnv.updateInterval = 0; + + return jasmineEnv; +}; + +/** + * Execute the specs in the specified folder. Helpers in each folder will be + * added to the environment. Helpers in parent directories will be available to child + * directories. + * @param {string} folder The folder in which the specs are to be found. + * @param {function?} done Callback function to execute when finished. + * @param {object} opts Options for executing the specs. + * @param {boolean} opts.verbose Whether or not to output verbose results. + * @param {RegExp} opts.matcher A regular expression to filter specs by. Only matching specs run. + */ +jasmine.executeSpecsInFolder = function(folder, done, opts) { + var fileMatcher = opts.matcher || new RegExp(".(js)$", "i"), + specs = require('./spec-collection'), + jasmineEnv = jasmine.initialize(done, opts.verbose); + + // Load the specs + specs.load(folder, fileMatcher, true); + + var specsList = specs.getSpecs(); + var filename; + + // Add the specs to the context + for (var i = 0, len = specsList.length; i < len; ++i) { + filename = specsList[i]; + require(filename.path().replace(/\\/g, '/'). + replace(new RegExp('^' + __dirname + '/'), ""). + replace(/\.\w+$/, "")); + } + + // Run Jasmine + jasmineEnv.execute(); +}; + +function now() { + return new Date().getTime(); +} + +jasmine.asyncSpecWait = function() { + var wait = this.asyncSpecWait; + wait.start = now(); + wait.done = false; + (function innerWait() { + waits(10); + runs(function() { + if (wait.start + wait.timeout < now()) { + expect('timeout waiting for spec').toBeNull(); + } else if (wait.done) { + wait.done = false; + } else { + innerWait(); + } + }); + })(); +}; +jasmine.asyncSpecWait.timeout = 4 * 1000; +jasmine.asyncSpecDone = function() { + jasmine.asyncSpecWait.done = true; +}; + +jasmine.getDocSetFromFile = function(filename, parser) { + var sourceCode = fs.readFileSync(__dirname + '/' + filename, 'utf8'), + testParser = parser || new (require('jsdoc/src/parser')).Parser(), + indexAll = require('jsdoc/borrow').indexAll, + doclets; + + require('jsdoc/src/handlers').attachTo(testParser); + + doclets = testParser.parse('javascript:' + sourceCode); + indexAll(doclets); + + require('jsdoc/augment').addInherited(doclets); + + // test assume borrows have not yet been resolved + // require('jsdoc/borrow').resolveBorrows(doclets); + + return { + doclets: doclets, + getByLongname: function(longname) { + return doclets.filter(function(doclet) { + return (doclet.longname || doclet.name) === longname; + }); + } + }; +}; + +for (var key in jasmine) { + if ( hasOwnProp.call(jasmine, key) ) { + exports[key] = jasmine[key]; + } +} diff --git a/data/scripts/jsdoc/test/lib/jasmine.js b/data/scripts/jsdoc/test/lib/jasmine.js new file mode 100644 index 00000000..4b4b2b27 --- /dev/null +++ b/data/scripts/jsdoc/test/lib/jasmine.js @@ -0,0 +1,2547 @@ +var isCommonJS = typeof window == "undefined"; + +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace + */ +var jasmine = {}; +if (isCommonJS){exports.jasmine = jasmine;} +/** + * @private + */ +jasmine.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); +}; + +/** + * Use jasmine.undefined instead of undefined, since undefined is just + * a plain old variable and may be redefined by somebody else. + * + * @private + */ +jasmine.undefined = jasmine.___undefined___; + +/** + * Show diagnostic messages in the console if set to true + * + */ +jasmine.VERBOSE = false; + +/** + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. + * + */ +jasmine.DEFAULT_UPDATE_INTERVAL = 250; + +/** + * Default timeout interval in milliseconds for waitsFor() blocks. + */ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + +jasmine.getGlobal = function() { + function getGlobal() { + return this; + } + + return getGlobal(); +}; + +/** + * Allows for bound functions to be compared. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ +jasmine.bindOriginal_ = function(base, name) { + var original = base[name]; + if (original.apply) { + return function() { + return original.apply(base, arguments); + }; + } else { + // IE support + return jasmine.getGlobal()[name]; + } +}; + +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); + +jasmine.MessageResult = function(values) { + this.type = 'log'; + this.values = values; + this.trace = new Error(); // todo: test better +}; + +jasmine.MessageResult.prototype.toString = function() { + var text = ""; + for (var i = 0; i < this.values.length; i++) { + if (i > 0){text += " ";} + if (jasmine.isString_(this.values[i])) { + text += this.values[i]; + } else { + text += jasmine.pp(this.values[i]); + } + } + return text; +}; + +jasmine.ExpectationResult = function(params) { + this.type = 'expect'; + this.matcherName = params.matcherName; + this.passed_ = params.passed; + this.expected = params.expected; + this.actual = params.actual; + this.message = this.passed_ ? 'Passed.' : params.message; + + var trace = (params.trace || new Error(this.message)); + this.trace = this.passed_ ? '' : trace; +}; + +jasmine.ExpectationResult.prototype.toString = function () { + return this.message; +}; + +jasmine.ExpectationResult.prototype.passed = function () { + return this.passed_; +}; + +/** + * Getter for the Jasmine environment. Ensures one gets created + */ +jasmine.getEnv = function() { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); + return env; +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isArray_ = function(value) { + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +}; + +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ +jasmine.pp = function(value) { + var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; +}; + +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ +jasmine.isDomNode = function(obj) { + return obj.nodeType > 0; +}; + +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ +jasmine.any = function(clazz) { + return new jasmine.Matchers.Any(clazz); +}; + +/** + * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the + * attributes on the object. + * + * @example + * // don't care about any other attributes than foo. + * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); + * + * @param sample {Object} sample + * @returns matchable object for the sample + */ +jasmine.objectContaining = function (sample) { + return new jasmine.Matchers.ObjectContaining(sample); +}; + +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). + * + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).toHaveBeenCalled(); + * expect(foo.not).toHaveBeenCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The actual function this spy stubs. + */ + this.plan = function() { + }; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; + this.calls = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.calls = []; + this.mostRecentCall = {}; +}; + +jasmine.createSpy = function(name) { + + var spyObj = function() { + spyObj.wasCalled = true; + spyObj.callCount++; + var args = jasmine.util.argsToArray(arguments); + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; + spyObj.argsForCall.push(args); + spyObj.calls.push({object: this, args: args}); + return spyObj.plan.apply(this, arguments); + }; + + var spy = new jasmine.Spy(name); + + for (var prop in spy) { + spyObj[prop] = spy[prop]; + } + + spyObj.reset(); + + return spyObj; +}; + +/** + * Determines whether an object is a spy. + * + * @param {jasmine.Spy|Object} putativeSpy + * @returns {Boolean} + */ +jasmine.isSpy = function(putativeSpy) { + return putativeSpy && putativeSpy.isSpy; +}; + +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ +jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); + } + return obj; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the current spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.log = function() { + var spec = jasmine.getEnv().currentSpec; + spec.log.apply(spec, arguments); +}; + +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @returns a Jasmine spy that can be chained with all spy methods + */ +var spyOn = function(obj, methodName) { + return jasmine.getEnv().currentSpec.spyOn(obj, methodName); +}; +if (isCommonJS){exports.spyOn = spyOn;} + +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var it = function(desc, func) { + return jasmine.getEnv().it(desc, func); +}; +if (isCommonJS){exports.it = it;} + +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var xit = function(desc, func) { + return jasmine.getEnv().xit(desc, func); +}; +if (isCommonJS){exports.xit = xit;} + +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + */ +var expect = function(actual) { + return jasmine.getEnv().currentSpec.expect(actual); +}; +if (isCommonJS){exports.expect = expect;} + +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ +var runs = function(func) { + jasmine.getEnv().currentSpec.runs(func); +}; +if (isCommonJS){exports.runs = runs;} + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +var waits = function(timeout) { + jasmine.getEnv().currentSpec.waits(timeout); +}; +if (isCommonJS){exports.waits = waits;} + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); +}; +if (isCommonJS){exports.waitsFor = waitsFor;} + +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ +var beforeEach = function(beforeEachFunction) { + jasmine.getEnv().beforeEach(beforeEachFunction); +}; +if (isCommonJS){exports.beforeEach = beforeEach;} + +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ +var afterEach = function(afterEachFunction) { + jasmine.getEnv().afterEach(afterEachFunction); +}; +if (isCommonJS){exports.afterEach = afterEach;} + +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var describe = function(description, specDefinitions) { + return jasmine.getEnv().describe(description, specDefinitions); +}; +if (isCommonJS){exports.describe = describe;} + +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var xdescribe = function(description, specDefinitions) { + return jasmine.getEnv().xdescribe(description, specDefinitions); +}; +if (isCommonJS){exports.xdescribe = xdescribe;} + + +// Provide the XMLHttpRequest class for IE 5.x-6.x: +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { + function tryIt(f) { + try { + return f(); + } catch(e) { + } + return null; + } + + var xhr = tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.6.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.3.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP"); + }) || + tryIt(function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }); + + if (!xhr) { + throw new Error("This browser does not support XMLHttpRequest."); +} + + return xhr; +} : XMLHttpRequest; +/** + * @namespace + */ +jasmine.util = {}; + +/** + * Declare that a child class inherit it's prototype from the parent class. + * + * @private + * @param {Function} childClass + * @param {Function} parentClass + */ +jasmine.util.inherit = function(childClass, parentClass) { + /** + * @private + */ + var subclass = function() { + }; + subclass.prototype = parentClass.prototype; + childClass.prototype = new subclass(); +}; + +jasmine.util.formatException = function(e) { + var lineNumber; + if (e.line) { + lineNumber = e.line; + } + else if (e.lineNumber) { + lineNumber = e.lineNumber; + } + + var file; + + if (e.sourceURL) { + file = e.sourceURL; + } + else if (e.fileName) { + file = e.fileName; + } + + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); + + if (file && lineNumber) { + message += ' in ' + file + ' (line ' + lineNumber + ')'; + } + + return message; +}; + +jasmine.util.htmlEscape = function(str) { + if (!str) { + return str; +} + return str.replace(/&/g, '&') + .replace(//g, '>'); +}; + +jasmine.util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) {arrayOfArgs.push(args[i]);} + return arrayOfArgs; +}; + +jasmine.util.extend = function(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; +}; + +/** + * Environment for Jasmine + * + * @constructor + */ +jasmine.Env = function() { + this.currentSpec = null; + this.currentSuite = null; + this.currentRunner_ = new jasmine.Runner(this); + + this.reporter = new jasmine.MultiReporter(); + + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); +}; + + +jasmine.Env.prototype.setTimeout = jasmine.setTimeout; +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; +jasmine.Env.prototype.setInterval = jasmine.setInterval; +jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + +/** + * @returns an object containing jasmine version build info, if set. + */ +jasmine.Env.prototype.version = function () { + if (jasmine.version_) { + return jasmine.version_; + } else { + throw new Error('Version not set'); + } +}; + +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (!jasmine.version_) { + return "version unknown"; + } + + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSpecId = function () { + return this.nextSpecId_++; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSuiteId = function () { + return this.nextSuiteId_++; +}; + +/** + * Register a reporter to receive status updates from Jasmine. + * @param {jasmine.Reporter} reporter An object which will receive status updates. + */ +jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); +}; + +jasmine.Env.prototype.execute = function() { + this.currentRunner_.execute(); +}; + +jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); + + var parentSuite = this.currentSuite; + if (parentSuite) { + parentSuite.add(suite); + } else { + this.currentRunner_.add(suite); + } + + this.currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch(e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; +}; + +jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + if (this.currentSuite) { + this.currentSuite.beforeEach(beforeEachFunction); + } else { + this.currentRunner_.beforeEach(beforeEachFunction); + } +}; + +jasmine.Env.prototype.currentRunner = function () { + return this.currentRunner_; +}; + +jasmine.Env.prototype.afterEach = function(afterEachFunction) { + if (this.currentSuite) { + this.currentSuite.afterEach(afterEachFunction); + } else { + this.currentRunner_.afterEach(afterEachFunction); + } + +}; + +jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { + return { + execute: function() { + } + }; +}; + +jasmine.Env.prototype.it = function(description, func) { + var spec = new jasmine.Spec(this, this.currentSuite, description); + this.currentSuite.add(spec); + this.currentSpec = spec; + + if (func) { + spec.runs(func); + } + + return spec; +}; + +jasmine.Env.prototype.xit = function(desc, func) { + return { + id: this.nextSpecId(), + runs: function() { + } + }; +}; + +jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') { + continue; + } + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) { + return result; + } + } + + if (a === b) { + return true; +} + + if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { + return (a == jasmine.undefined && b == jasmine.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a.jasmineMatches) { + return a.jasmineMatches(b); + } + + if (b.jasmineMatches) { + return b.jasmineMatches(a); + } + + if (a instanceof jasmine.Matchers.ObjectContaining) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.ObjectContaining) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); +}; + +jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) { + return true; + } + } + return false; + } + return haystack.indexOf(needle) >= 0; +}; + +jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); +}; +/** No-op base class for Jasmine reporters. + * + * @constructor + */ +jasmine.Reporter = function() { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerResults = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecStarting = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecResults = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.log = function(str) { +}; + +/** + * Blocks are functions with executable code that make up a spec. + * + * @constructor + * @param {jasmine.Env} env + * @param {Function} func + * @param {jasmine.Spec} spec + */ +jasmine.Block = function(env, func, spec) { + this.env = env; + this.func = func; + this.spec = spec; +}; + +jasmine.Block.prototype.execute = function(onComplete) { + try { + this.func.apply(this.spec); + } catch (e) { + this.spec.fail(e); + } + onComplete(); +}; +/** JavaScript API reporter. + * + * @constructor + */ +jasmine.JsApiReporter = function() { + this.started = false; + this.finished = false; + this.suites_ = []; + this.results_ = {}; +}; + +jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { + this.started = true; + var suites = runner.topLevelSuites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } +}; + +jasmine.JsApiReporter.prototype.suites = function() { + return this.suites_; +}; + +jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + var summary = { + id: suiteOrSpec.id, + name: suiteOrSpec.description, + type: isSuite ? 'suite' : 'spec', + children: [] + }; + + if (isSuite) { + var children = suiteOrSpec.children(); + for (var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + return summary; +}; + +jasmine.JsApiReporter.prototype.results = function() { + return this.results_; +}; + +jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { + return this.results_[specId]; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { + this.finished = true; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { + this.results_[spec.id] = { + messages: spec.results().getItems(), + result: spec.results().failedCount > 0 ? "failed" : "passed" + }; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.log = function(str) { +}; + +jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ + var results = {}; + for (var i = 0; i < specIds.length; i++) { + var specId = specIds[i]; + results[specId] = this.summarizeResult_(this.results_[specId]); + } + return results; +}; + +jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ + var summaryMessages = []; + var messagesLength = result.messages.length; + for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { + var resultMessage = result.messages[messageIndex]; + summaryMessages.push({ + text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, + passed: resultMessage.passed ? resultMessage.passed() : true, + type: resultMessage.type, + message: resultMessage.message, + trace: { + stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined + } + }); + } + + return { + result : result.result, + messages : summaryMessages + }; +}; + +/** + * @constructor + * @param {jasmine.Env} env + * @param actual + * @param {jasmine.Spec} spec + */ +jasmine.Matchers = function(env, actual, spec, opt_isNot) { + this.env = env; + this.actual = actual; + this.spec = spec; + this.isNot = opt_isNot || false; + this.reportWasCalled_ = false; +}; + +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] +jasmine.Matchers.pp = function(str) { + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); +}; + +// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] +jasmine.Matchers.prototype.report = function(result, failing_message, details) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); +}; + +jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { + for (var methodName in prototype) { + if (methodName == 'report') { + continue; + } + var orig = prototype[methodName]; + matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); + } +}; + +jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { + return function() { + var matcherArgs = jasmine.util.argsToArray(arguments); + var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + + if (this.reportWasCalled_) { + return result; + } + + var message; + if (!result) { + if (this.message) { + message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } + } else { + var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; + if (matcherArgs.length > 0) { + for (var i = 0; i < matcherArgs.length; i++) { + if (i > 0){message += ",";} + message += " " + jasmine.pp(matcherArgs[i]); + } + } + message += "."; + } + } + var expectationResult = new jasmine.ExpectationResult({ + matcherName: matcherName, + passed: result, + expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], + actual: this.actual, + message: message + }); + this.spec.addMatcherResult(expectationResult); + return jasmine.undefined; + }; +}; + + + + +/** + * toBe: compares the actual to the expected using === + * @param expected + */ +jasmine.Matchers.prototype.toBe = function(expected) { + return this.actual === expected; +}; + +/** + * toNotBe: compares the actual to the expected using !== + * @param expected + * @deprecated as of 1.0. Use not.toBe() instead. + */ +jasmine.Matchers.prototype.toNotBe = function(expected) { + return this.actual !== expected; +}; + +/** + * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. + * + * @param expected + */ +jasmine.Matchers.prototype.toEqual = function(expected) { + return this.env.equals_(this.actual, expected); +}; + +/** + * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual + * @param expected + * @deprecated as of 1.0. Use not.toEqual() instead. + */ +jasmine.Matchers.prototype.toNotEqual = function(expected) { + return !this.env.equals_(this.actual, expected); +}; + +/** + * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes + * a pattern or a String. + * + * @param expected + */ +jasmine.Matchers.prototype.toMatch = function(expected) { + return new RegExp(expected).test(this.actual); +}; + +/** + * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch + * @param expected + * @deprecated as of 1.0. Use not.toMatch() instead. + */ +jasmine.Matchers.prototype.toNotMatch = function(expected) { + return !(new RegExp(expected).test(this.actual)); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeDefined = function() { + return (this.actual !== jasmine.undefined); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeUndefined = function() { + return (this.actual === jasmine.undefined); +}; + +/** + * Matcher that compares the actual to null. + */ +jasmine.Matchers.prototype.toBeNull = function() { + return (this.actual === null); +}; + +/** + * Matcher that boolean not-nots the actual. + */ +jasmine.Matchers.prototype.toBeTruthy = function() { + return !!this.actual; +}; + + +/** + * Matcher that boolean nots the actual. + */ +jasmine.Matchers.prototype.toBeFalsy = function() { + return !this.actual; +}; + + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called. + */ +jasmine.Matchers.prototype.toHaveBeenCalled = function() { + if (arguments.length > 0) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to have been called.", + "Expected spy " + this.actual.identity + " not to have been called." + ]; + }; + + return this.actual.wasCalled; +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ +jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was not called. + * + * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead + */ +jasmine.Matchers.prototype.wasNotCalled = function() { + if (arguments.length > 0) { + throw new Error('wasNotCalled does not take arguments'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to not have been called.", + "Expected spy " + this.actual.identity + " to have been called." + ]; + }; + + return !this.actual.wasCalled; +}; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. + * + * @example + * + */ +jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + this.message = function() { + if (this.actual.callCount === 0) { + // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." + ]; + } else { + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) + ]; + } + }; + + return this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; + +/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", + "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" + ]; + }; + + return !this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** + * Matcher that checks that the expected item is an element in the actual Array. + * + * @param {Object} expected + */ +jasmine.Matchers.prototype.toContain = function(expected) { + return this.env.contains_(this.actual, expected); +}; + +/** + * Matcher that checks that the expected item is NOT an element in the actual Array. + * + * @param {Object} expected + * @deprecated as of 1.0. Use not.toContain() instead. + */ +jasmine.Matchers.prototype.toNotContain = function(expected) { + return !this.env.contains_(this.actual, expected); +}; + +jasmine.Matchers.prototype.toBeLessThan = function(expected) { + return this.actual < expected; +}; + +jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { + return this.actual > expected; +}; + +/** + * Matcher that checks that the expected item is equal to the actual item + * up to a given level of decimal precision (default 2). + * + * @param {Number} expected + * @param {Number} precision + */ +jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { + if (!(precision === 0)) { + precision = precision || 2; + } + var multiplier = Math.pow(10, precision); + var actual = Math.round(this.actual * multiplier); + expected = Math.round(expected * multiplier); + return expected == actual; +}; + +/** + * Matcher that checks that the expected exception was thrown by the actual. + * + * @param {String} expected + */ +jasmine.Matchers.prototype.toThrow = function(expected) { + var result = false; + var exception; + if (typeof this.actual != 'function') { + throw new Error('Actual is not a function'); + } + try { + this.actual(); + } catch (e) { + exception = e; + } + if (exception) { + result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); + } + + var not = this.isNot ? "not " : ""; + + this.message = function() { + if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { + return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); + } else { + return "Expected function to throw an exception."; + } + }; + + return result; +}; + +jasmine.Matchers.Any = function(expectedClass) { + this.expectedClass = expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { + if (this.expectedClass == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedClass == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedClass == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedClass == Object) { + return typeof other == 'object'; + } + + return other instanceof this.expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineToString = function() { + return ''; +}; + +jasmine.Matchers.ObjectContaining = function (sample) { + this.sample = sample; +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + var env = jasmine.getEnv(); + + var hasKey = function(obj, keyName) { + return obj != null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in this.sample) { + if (!hasKey(other, property) && hasKey(this.sample, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); + } + } + + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { + return ""; +}; +// Mock setTimeout, clearTimeout +// Contributed by Pivotal Computer Systems, www.pivotalsf.com + +jasmine.FakeTimer = function() { + this.reset(); + + var self = this; + self.setTimeout = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); + return self.timeoutsMade; + }; + + self.setInterval = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); + return self.timeoutsMade; + }; + + self.clearTimeout = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + + self.clearInterval = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + +}; + +jasmine.FakeTimer.prototype.reset = function() { + this.timeoutsMade = 0; + this.scheduledFunctions = {}; + this.nowMillis = 0; +}; + +jasmine.FakeTimer.prototype.tick = function(millis) { + var oldMillis = this.nowMillis; + var newMillis = oldMillis + millis; + this.runFunctionsWithinRange(oldMillis, newMillis); + this.nowMillis = newMillis; +}; + +jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { + var scheduledFunc; + var funcsToRun = []; + for (var timeoutKey in this.scheduledFunctions) { + scheduledFunc = this.scheduledFunctions[timeoutKey]; + if (scheduledFunc != jasmine.undefined && + scheduledFunc.runAtMillis >= oldMillis && + scheduledFunc.runAtMillis <= nowMillis) { + funcsToRun.push(scheduledFunc); + this.scheduledFunctions[timeoutKey] = jasmine.undefined; + } + } + + if (funcsToRun.length > 0) { + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + for (var i = 0; i < funcsToRun.length; ++i) { + try { + var funcToRun = funcsToRun[i]; + this.nowMillis = funcToRun.runAtMillis; + funcToRun.funcToCall(); + if (funcToRun.recurring) { + this.scheduleFunction(funcToRun.timeoutKey, + funcToRun.funcToCall, + funcToRun.millis, + true); + } + } catch(e) { + } + } + this.runFunctionsWithinRange(oldMillis, nowMillis); + } +}; + +jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { + this.scheduledFunctions[timeoutKey] = { + runAtMillis: this.nowMillis + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; +}; + +/** + * @namespace + */ +jasmine.Clock = { + defaultFakeTimer: new jasmine.FakeTimer(), + + reset: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.reset(); + }, + + tick: function(millis) { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.tick(millis); + }, + + runFunctionsWithinRange: function(oldMillis, nowMillis) { + jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); + }, + + scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { + jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); + }, + + useMock: function() { + if (!jasmine.Clock.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Clock.uninstallMock); + + jasmine.Clock.installMock(); + } + }, + + installMock: function() { + jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; + }, + + uninstallMock: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.installed = jasmine.Clock.real; + }, + + real: { + setTimeout: jasmine.getGlobal().setTimeout, + clearTimeout: jasmine.getGlobal().clearTimeout, + setInterval: jasmine.getGlobal().setInterval, + clearInterval: jasmine.getGlobal().clearInterval + }, + + assertInstalled: function() { + if (!jasmine.Clock.isInstalled()) { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }, + + isInstalled: function() { + return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; + }, + + installed: null +}; +jasmine.Clock.installed = jasmine.Clock.real; + +//else for IE support +jasmine.getGlobal().setTimeout = function(funcToCall, millis) { + if (jasmine.Clock.installed.setTimeout.apply) { + return jasmine.Clock.installed.setTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.setTimeout(funcToCall, millis); + } +}; + +jasmine.getGlobal().setInterval = function(funcToCall, millis) { + if (jasmine.Clock.installed.setInterval.apply) { + return jasmine.Clock.installed.setInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.setInterval(funcToCall, millis); + } +}; + +jasmine.getGlobal().clearTimeout = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearTimeout(timeoutKey); + } +}; + +jasmine.getGlobal().clearInterval = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearInterval(timeoutKey); + } +}; + +/** + * @constructor + */ +jasmine.MultiReporter = function() { + this.subReporters_ = []; +}; +jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); + +jasmine.MultiReporter.prototype.addReporter = function(reporter) { + this.subReporters_.push(reporter); +}; + +(function() { + var functionNames = [ + "reportRunnerStarting", + "reportRunnerResults", + "reportSuiteResults", + "reportSpecStarting", + "reportSpecResults", + "log" + ]; + for (var i = 0; i < functionNames.length; i++) { + var functionName = functionNames[i]; + jasmine.MultiReporter.prototype[functionName] = (function(functionName) { + return function() { + for (var j = 0; j < this.subReporters_.length; j++) { + var subReporter = this.subReporters_[j]; + if (subReporter[functionName]) { + subReporter[functionName].apply(subReporter, arguments); + } + } + }; + })(functionName); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Adds a log message. + * @param values Array of message parts which will be concatenated later. + */ +jasmine.NestedResults.prototype.log = function(values) { + this.items_.push(new jasmine.MessageResult(values)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'log') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed()) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + if (this.ppNestLevel_ > 40) { + throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); + } + + this.ppNestLevel_++; + try { + if (value === jasmine.undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === jasmine.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (jasmine.isSpy(value)) { + this.emitScalar("spy on " + value.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (property == '__Jasmine_been_here_before__') { + continue; + } + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + obj.__lookupGetter__(property) !== null) : false); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +jasmine.Queue = function(env) { + this.env = env; + this.blocks = []; + this.running = false; + this.index = 0; + this.offset = 0; + this.abort = false; +}; + +jasmine.Queue.prototype.addBefore = function(block) { + this.blocks.unshift(block); +}; + +jasmine.Queue.prototype.add = function(block) { + this.blocks.push(block); +}; + +jasmine.Queue.prototype.insertNext = function(block) { + this.blocks.splice((this.index + this.offset + 1), 0, block); + this.offset++; +}; + +jasmine.Queue.prototype.start = function(onComplete) { + this.running = true; + this.onComplete = onComplete; + this.next_(); +}; + +jasmine.Queue.prototype.isRunning = function() { + return this.running; +}; + +jasmine.Queue.LOOP_DONT_RECURSE = true; + +jasmine.Queue.prototype.next_ = function() { + var self = this; + var goAgain = true; + + while (goAgain) { + goAgain = false; + if (self.index < self.blocks.length && !this.abort) { + var calledSynchronously = true; + var completedSynchronously = false; + + var onComplete = function () { + if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { + completedSynchronously = true; + return; + } + + if (self.blocks[self.index].abort) { + self.abort = true; + } + + self.offset = 0; + self.index++; + + var now = new Date().getTime(); + if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { + self.env.lastUpdate = now; + self.env.setTimeout(function() { + self.next_(); + }, 0); + } else { + if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { + goAgain = true; + } else { + self.next_(); + } + } + }; + self.blocks[self.index].execute(onComplete); + + calledSynchronously = false; + if (completedSynchronously) { + onComplete(); + } + + } else { + self.running = false; + if (self.onComplete) { + self.onComplete(); + } + } + } +}; + +jasmine.Queue.prototype.results = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.blocks.length; i++) { + if (this.blocks[i].results) { + results.addResult(this.blocks[i].results()); + } + } + return results; +}; + + +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + var self = this; + self.env = env; + self.queue = new jasmine.Queue(env); + self.before_ = []; + self.after_ = []; + self.suites_ = []; +}; + +jasmine.Runner.prototype.execute = function() { + var self = this; + if (self.env.reporter.reportRunnerStarting) { + self.env.reporter.reportRunnerStarting(this); + } + self.queue.start(function () { + self.finishCallback(); + }); +}; + +jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.splice(0,0,beforeEachFunction); +}; + +jasmine.Runner.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.splice(0,0,afterEachFunction); +}; + + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.addSuite = function(suite) { + this.suites_.push(suite); +}; + +jasmine.Runner.prototype.add = function(block) { + if (block instanceof jasmine.Suite) { + this.addSuite(block); + } + this.queue.add(block); +}; + +jasmine.Runner.prototype.specs = function () { + var suites = this.suites(); + var specs = []; + for (var i = 0; i < suites.length; i++) { + specs = specs.concat(suites[i].specs()); + } + return specs; +}; + +jasmine.Runner.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Runner.prototype.topLevelSuites = function() { + var topLevelSuites = []; + for (var i = 0; i < this.suites_.length; i++) { + if (!this.suites_[i].parentSuite) { + topLevelSuites.push(this.suites_[i]); + } + } + return topLevelSuites; +}; + +jasmine.Runner.prototype.results = function() { + return this.queue.results(); +}; +/** + * Internal representation of a Jasmine specification, or test. + * + * @constructor + * @param {jasmine.Env} env + * @param {jasmine.Suite} suite + * @param {String} description + */ +jasmine.Spec = function(env, suite, description) { + if (!env) { + throw new Error('jasmine.Env() required'); + } + if (!suite) { + throw new Error('jasmine.Suite() required'); + } + var spec = this; + spec.id = env.nextSpecId ? env.nextSpecId() : null; + spec.env = env; + spec.suite = suite; + spec.description = description; + spec.queue = new jasmine.Queue(env); + + spec.afterCallbacks = []; + spec.spies_ = []; + + spec.results_ = new jasmine.NestedResults(); + spec.results_.description = description; + spec.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + + +jasmine.Spec.prototype.results = function() { + return this.results_; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.Spec.prototype.log = function() { + return this.results_.log(arguments); +}; + +jasmine.Spec.prototype.runs = function (func) { + var block = new jasmine.Block(this.env, func, this); + this.addToQueue(block); + return this; +}; + +jasmine.Spec.prototype.addToQueue = function (block) { + if (this.queue.isRunning()) { + this.queue.insertNext(block); + } else { + this.queue.add(block); + } +}; + +/** + * @param {jasmine.ExpectationResult} result + */ +jasmine.Spec.prototype.addMatcherResult = function(result) { + this.results_.addResult(result); +}; + +jasmine.Spec.prototype.expect = function(actual) { + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; +}; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.addToQueue(waitsFunc); + return this; +}; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + var latchFunction_ = null; + var optional_timeoutMessage_ = null; + var optional_timeout_ = null; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + switch (typeof arg) { + case 'function': + latchFunction_ = arg; + break; + case 'string': + optional_timeoutMessage_ = arg; + break; + case 'number': + optional_timeout_ = arg; + break; + } + } + + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); + this.addToQueue(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.fail = function (e) { + var expectationResult = new jasmine.ExpectationResult({ + passed: false, + message: e ? jasmine.util.formatException(e) : 'Exception', + trace: { stack: e.stack } + }); + this.results_.addResult(expectationResult); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || this.env.matchersClass; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function(onComplete) { + this.removeAllSpies(); + this.finishCallback(); + if (onComplete) { + onComplete(); + } +}; + +jasmine.Spec.prototype.after = function(doAfter) { + if (this.queue.isRunning()) { + this.queue.add(new jasmine.Block(this.env, doAfter, this)); + } else { + this.afterCallbacks.unshift(doAfter); + } +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var spec = this; + if (!spec.env.specFilter(spec)) { + spec.results_.skipped = true; + spec.finish(onComplete); + return; + } + + this.env.reporter.reportSpecStarting(this); + + spec.env.currentSpec = spec; + + spec.addBeforesAndAftersToQueue(); + + spec.queue.start(function () { + spec.finish(onComplete); + }); +}; + +jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { + var runner = this.env.currentRunner(); + var i; + + for (var suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); + } + } + for (i = 0; i < runner.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); + } + for (i = 0; i < this.afterCallbacks.length; i++) { + this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); + } + for (suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); + } + } + for (i = 0; i < runner.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == jasmine.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + throw new Error(methodName + ' has already been spied upon'); + } + + var spyObj = jasmine.createSpy(methodName); + + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; + + obj[methodName] = spyObj; + + return spyObj; +}; + +jasmine.Spec.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; +}; + +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var self = this; + self.id = env.nextSuiteId ? env.nextSuiteId() : null; + self.description = description; + self.queue = new jasmine.Queue(env); + self.parentSuite = parentSuite; + self.env = env; + self.before_ = []; + self.after_ = []; + self.children_ = []; + self.suites_ = []; + self.specs_ = []; +}; + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finish = function(onComplete) { + this.env.reporter.reportSuiteResults(this); + this.finished = true; + if (typeof(onComplete) == 'function') { + onComplete(); + } +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.unshift(beforeEachFunction); +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.unshift(afterEachFunction); +}; + +jasmine.Suite.prototype.results = function() { + return this.queue.results(); +}; + +jasmine.Suite.prototype.add = function(suiteOrSpec) { + this.children_.push(suiteOrSpec); + if (suiteOrSpec instanceof jasmine.Suite) { + this.suites_.push(suiteOrSpec); + this.env.currentRunner().addSuite(suiteOrSpec); + } else { + this.specs_.push(suiteOrSpec); + } + this.queue.add(suiteOrSpec); +}; + +jasmine.Suite.prototype.specs = function() { + return this.specs_; +}; + +jasmine.Suite.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Suite.prototype.children = function() { + return this.children_; +}; + +jasmine.Suite.prototype.execute = function(onComplete) { + var self = this; + this.queue.start(function () { + self.finish(onComplete); + }); +}; +jasmine.WaitsBlock = function(env, timeout, spec) { + this.timeout = timeout; + jasmine.Block.call(this, env, null, spec); +}; + +jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); + +jasmine.WaitsBlock.prototype.execute = function (onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + } + this.env.setTimeout(function () { + onComplete(); + }, this.timeout); +}; +/** + * A block which waits for some condition to become true, with timeout. + * + * @constructor + * @extends jasmine.Block + * @param {jasmine.Env} env The Jasmine environment. + * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. + * @param {Function} latchFunction A function which returns true when the desired condition has been met. + * @param {String} message The message to display if the desired condition hasn't been met within the given time period. + * @param {jasmine.Spec} spec The Jasmine spec. + */ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout || env.defaultTimeoutInterval; + this.latchFunction = latchFunction; + this.message = message; + this.totalTimeSpentWaitingForLatch = 0; + jasmine.Block.call(this, env, null, spec); +}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); + +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; + +jasmine.WaitsForBlock.prototype.execute = function(onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); + } + var latchFunctionResult; + try { + latchFunctionResult = this.latchFunction.apply(this.spec); + } catch (e) { + this.spec.fail(e); + onComplete(); + return; + } + + if (latchFunctionResult) { + onComplete(); + } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { + var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); + this.spec.fail({ + name: 'timeout', + message: message + }); + + this.abort = true; + onComplete(); + } else { + this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + var self = this; + this.env.setTimeout(function() { + self.execute(onComplete); + }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; + +jasmine.version_= { + "major": 1, + "minor": 2, + "build": 0, + "revision": 1333557965, + "release_candidate": 3 +}; diff --git a/data/scripts/jsdoc/test/reporter.js b/data/scripts/jsdoc/test/reporter.js new file mode 100644 index 00000000..0678bd8f --- /dev/null +++ b/data/scripts/jsdoc/test/reporter.js @@ -0,0 +1,299 @@ +(function() { + // + // Imports + // + + if (!jasmineNode) { + var jasmineNode = {}; + } + + // + // Helpers + // + function noop() { + } + + jasmineNode.ANSIColors = { + pass : function() { + return '\033[32m'; + }, // Green + fail : function() { + return '\033[31m'; + }, // Red + neutral : function() { + return '\033[0m'; + } // Normal + }; + + jasmineNode.NoColors = { + pass : function() { + return ''; + }, + fail : function() { + return ''; + }, + neutral : function() { + return ''; + } + }; + + jasmineNode.TerminalReporter = function(config) { + this.print_ = config.print || print; + this.color_ = config.color ? jasmineNode.ANSIColors : jasmineNode.NoColors; + + this.started_ = false; + this.finished_ = false; + + this.callback_ = config.onComplete || false; + + this.suites_ = []; + this.specResults_ = {}; + this.failures_ = {}; + this.failures_.length = 0; + }; + + jasmineNode.TerminalReporter.prototype = { + reportRunnerStarting : function(runner) { + this.started_ = true; + this.startedAt = new Date(); + var suites = runner.topLevelSuites(); + for ( var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } + }, + + summarize_ : function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + + // We could use a separate object for suite and spec + var summary = { + id : suiteOrSpec.id, + name : suiteOrSpec.description, + type : isSuite ? 'suite' : 'spec', + suiteNestingLevel : 0, + children : [] + }; + + if (isSuite) { + var calculateNestingLevel = function(examinedSuite) { + var nestingLevel = 0; + while (examinedSuite.parentSuite !== null) { + nestingLevel += 1; + examinedSuite = examinedSuite.parentSuite; + } + return nestingLevel; + }; + + summary.suiteNestingLevel = calculateNestingLevel(suiteOrSpec); + + var children = suiteOrSpec.children(); + for ( var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + + return summary; + }, + + // This is heavily influenced by Jasmine's Html/Trivial Reporter + reportRunnerResults : function(runner) { + this.reportFailures_(); + + var results = runner.results(); + var resultColor = (results.failedCount > 0) ? this.color_.fail() : this.color_.pass(); + + var specs = runner.specs(); + var specCount = specs.length; + + var message = "\n\nFinished in " + + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + + " seconds"; + this.printLine_(message); + + // This is what jasmine-html.js has + // message = "" + specCount + " spec" + ( specCount === 1 ? "" : "s") + ", " + results.failedCount + " failure" + ((results.failedCount === 1) ? "" : "s"); + + this.printLine_(this.stringWithColor_(this.printRunnerResults_(runner), resultColor)); + + this.finished_ = true; + if (this.callback_) { + this.callback_(runner); + } + }, + + reportFailures_ : function() { + if (this.failures_.length === 0) { + return; + } + + var indent = ' ', failure; + this.printLine_('\n'); + + this.print_('Failures:'); + + for ( var suite in this.failures_) { + if (this.failures_.hasOwnProperty(suite) && suite !== "length") { + this.printLine_('\n'); + this.printLine_(suite); + failures = this.failures_[suite]; + for ( var i = 0; i < failures.length; i++) { + failure = failures[i]; + this.printLine_('\n'); + this.printLine_(indent + (i + 1) + ') ' + failure.spec); + this.printLine_(indent + 'Message:'); + this.printLine_(indent + indent + this.stringWithColor_(failure.message, this.color_.fail())); + this.printLine_(indent + 'Stacktrace:'); + this.print_(indent + indent + failure.stackTrace); + } + } + } + + }, + + reportSuiteResults : function(suite) { + // Not used in this context + }, + + reportSpecResults : function(spec) { + var result = spec.results(); + var msg = ''; + if (result.passed()) { + msg = this.stringWithColor_('.', this.color_.pass()); + // } else if (result.skipped) { TODO: Research why "result.skipped" returns false when "xit" is called on a spec? + // msg = (colors) ? (ansi.yellow + '*' + ansi.none) : '*'; + } else { + msg = this.stringWithColor_('F', this.color_.fail()); + this.addFailureToFailures_(spec); + } + this.spec_results += msg; + this.print_(msg); + }, + + addFailureToFailures_ : function(spec) { + var result = spec.results(); + var failureItem = null; + var suite = spec.suite.getFullName(); + var failures = null; + var items_length = result.items_.length; + for ( var i = 0; i < items_length; i++) { + if (result.items_[i].passed_ === false) { + failureItem = result.items_[i]; + var failure = { + spec : spec.description, + message : failureItem.message, + stackTrace : failureItem.trace.stack + }; + failures = this.failures_[suite]; + if (!failures) { + this.failures_[suite] = []; + } + this.failures_[suite].push(failure); + this.failures_.length++; + } + } + }, + + printRunnerResults_ : function(runner) { + var results = runner.results(); + var specs = runner.specs(); + var msg = ''; + msg += specs.length + ' test' + ((specs.length === 1) ? '' : 's') + ', '; + msg += results.totalCount + ' assertion' + ((results.totalCount === 1) ? '' : 's') + ', '; + msg += results.failedCount + ' failure' + ((results.failedCount === 1) ? '' : 's') + '\n'; + return msg; + }, + + // Helper Methods // + stringWithColor_ : function(stringValue, color) { + return (color || this.color_.neutral()) + stringValue + this.color_.neutral(); + }, + + printLine_ : function(stringValue) { + this.print_(stringValue); + this.print_('\n'); + } + }; + + // *************************************************************** + // TerminalVerboseReporter uses the TerminalReporter's constructor + // *************************************************************** + jasmineNode.TerminalVerboseReporter = function(config) { + jasmineNode.TerminalReporter.call(this, config); + // The extra field in this object + this.indent_ = 0; + }; + + jasmineNode.TerminalVerboseReporter.prototype = { + reportSpecResults : function(spec) { + if (spec.results().failedCount > 0) { + this.addFailureToFailures_(spec); + } + + this.specResults_[spec.id] = { + messages : spec.results().getItems(), + result : spec.results().failedCount > 0 ? 'failed' : 'passed' + }; + }, + + reportRunnerResults : function(runner) { + var messages = new Array(); + this.buildMessagesFromResults_(messages, this.suites_); + + var messages_length = messages.length; + for ( var i = 0; i < messages_length - 1; i++) { + this.printLine_(messages[i]); + } + + this.print_(messages[messages_length - 1]); + + // Call the parent object's method + jasmineNode.TerminalReporter.prototype.reportRunnerResults.call(this, runner); + }, + + buildMessagesFromResults_ : function(messages, results) { + var element, specResult, specIndentSpaces, msg = ''; + + var results_length = results.length; + for ( var i = 0; i < results_length; i++) { + element = results[i]; + + if (element.type === 'spec') { + specResult = this.specResults_[element.id.toString()]; + + specIndentSpaces = this.indent_ + 2; + if (specResult.result === 'passed') { + msg = this.stringWithColor_(this.indentMessage_(element.name, specIndentSpaces), this.color_.pass()); + } else { + msg = this.stringWithColor_(this.indentMessage_(element.name, specIndentSpaces), this.color_.fail()); + } + + messages.push(msg); + } else { + this.indent_ = element.suiteNestingLevel * 2; + + messages.push(''); + messages.push(this.indentMessage_(element.name,this.indent_)); + } + + this.buildMessagesFromResults_(messages, element.children); + } + }, + + indentMessage_ : function(message, indentCount) { + var _indent = ''; + for ( var i = 0; i < indentCount; i++) { + _indent += ' '; + } + return (_indent + message); + } + }; + + // Inherit from TerminalReporter + jasmineNode.TerminalVerboseReporter.prototype.__proto__ = jasmineNode.TerminalReporter.prototype; + + // + // Exports + // + exports.jasmineNode = jasmineNode; +})(); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/runner.js b/data/scripts/jsdoc/test/runner.js new file mode 100644 index 00000000..55f3a96f --- /dev/null +++ b/data/scripts/jsdoc/test/runner.js @@ -0,0 +1,57 @@ +/*global env: true */ +/* + * Test Steps: + * 1. Get Jasmine + * 2. Get the test options + * 3. Get the list of directories to run tests from + * 4. Run Jasmine on each directory + */ +var fs = require('jsdoc/fs'); +var jasmine = require('test/jasmine-jsdoc'); +var path = require('path'); + +fs.existsSync = fs.existsSync || path.existsSync; + +var hasOwnProp = Object.prototype.hasOwnProperty; + +for (var key in jasmine) { + if (hasOwnProp.call(jasmine, key)) { + this[key] = jasmine[key]; + } +} + +var opts = { + verbose: env.opts.verbose || false, + showColors: env.opts.nocolor === true ? false : true +}; + +var extensions = 'js'; +var match = env.opts.match || '.'; +if (match instanceof Array) { + match = match.join("|"); +} +opts.matcher = new RegExp("(" + match + ")\\.(" + extensions + ")$", 'i'); + +var specFolders = ['test/specs', 'plugins/test/specs']; + +var failedCount = 0; +var index = 0; + +var onComplete; + +function runNextFolder() { + if (index < specFolders.length) { + jasmine.executeSpecsInFolder(specFolders[index], onComplete, opts); + } +} + +onComplete = function(runner, log) { + if (runner.results().failedCount !== 0) { + failedCount += runner.results().failedCount; + } + index++; + runNextFolder(); +}; + +runNextFolder(); +process.exit(failedCount); diff --git a/data/scripts/jsdoc/test/spec-collection.js b/data/scripts/jsdoc/test/spec-collection.js new file mode 100644 index 00000000..24e23a06 --- /dev/null +++ b/data/scripts/jsdoc/test/spec-collection.js @@ -0,0 +1,55 @@ +/*global env: true */ +var wrench = require('wrench'); +var path = require('path'); +var fs = require('jsdoc/fs'); +var specs = []; + +var createSpecObj = function(_path, root) { + function relativePath() { + return _path.replace(root, '').replace(/^[\/\\]/, '').replace(/\\/g, '/'); + } + + return { + path: function() { + return _path; + }, + relativePath: relativePath, + directory: function() { + return _path.replace(/[\/\\][\s\w\.\-]*$/, "").replace(/\\/g, '/'); + }, + relativeDirectory: function() { + return relativePath().replace(/[\/\\][\s\w\.\-]*$/, "").replace(/\\/g, '/'); + }, + filename: function() { + return _path.replace(/^.*[\\\/]/, ''); + } + }; +}; + +var clearSpecs = exports.clearSpecs = function() { + specs.splice(0, specs.length); +}; + +exports.load = function(loadpath, matcher, clear) { + if (clear === true) { + clearSpecs(); + } + + var wannaBeSpecs = wrench.readdirSyncRecursive(loadpath); + for (var i = 0; i < wannaBeSpecs.length; i++) { + var file = path.join(__dirname, loadpath, wannaBeSpecs[i]); + try { + if (fs.statSync(file).isFile()) { + if (matcher.test(path.basename(file))) { + specs.push(createSpecObj(file)); + } + } + } catch(e) { + // nothing to do here + } + } +}; + +exports.getSpecs = function() { + return specs; +}; diff --git a/data/scripts/jsdoc/test/specs/documentation/alias.js b/data/scripts/jsdoc/test/specs/documentation/alias.js new file mode 100644 index 00000000..23508e1d --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/alias.js @@ -0,0 +1,67 @@ +describe("aliases", function() { + describe("standard", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/alias.js'), + found = docSet.getByLongname('myObject').filter(function($) { + return ! $.undocumented; + }), + foundMember = docSet.getByLongname('myObject.myProperty'); + + it('When a symbol is given an alias it is documented as if the name is the alias value.', function() { + expect(found[0].longname).toEqual('myObject'); + }); + + it('When a symbol is a member of an alias it is documented as if the memberof is the alias value.', function() { + expect(foundMember[0].longname).toEqual('myObject.myProperty'); + expect(foundMember[0].memberof).toEqual('myObject'); + }); + }); + + it('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() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/alias2.js'), + foundMember = docSet.getByLongname('ns.Myclass#myProperty'); + + expect(foundMember[0].longname).toEqual('ns.Myclass#myProperty'); + expect(foundMember[0].name).toEqual('myProperty'); + expect(foundMember[0].memberof).toEqual('ns.Myclass'); + expect(foundMember[0].scope).toEqual('instance'); + }); + + it('When a symbol is a member of an aliased class, a this-variables is documented as if it were a member that class.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/alias3.js'), + tcm = docSet.getByLongname('trackr.CookieManager')[0], + tcmValue = docSet.getByLongname('trackr.CookieManager#value')[0]; + + expect(tcmValue.memberof).toEqual('trackr.CookieManager'); + }); + + it('When a symbol is documented as a static member of it\'s scope is "global" and not "static".', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/aliasglobal.js'), + log = docSet.getByLongname('log')[0]; + + expect(log.scope).toEqual('global'); + }); + + it('When a symbol is documented as an instance member of class it\'s scope is "instance" and not "static".', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/aliasglobal2.js'), + run = docSet.getByLongname('Test#run')[0]; + + expect(run.scope).toEqual('instance'); + expect(run.memberof).toEqual('Test'); + }); + + describe("resolving", function() { + it('When a local reference has alias, put all members into aliased definition. Local modifications should be visible to outside.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/aliasresolve.js'), + method = docSet.getByLongname('A.F.method'); + + expect(method.length).toEqual(1); + }); + + it('When a reference in an outer scope has alias, put all members into aliased definition. Local modifications are visible to outside.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/aliasresolve2.js'), + method = docSet.getByLongname('A.F.method'); + + expect(method.length).toEqual(1); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/also.js b/data/scripts/jsdoc/test/specs/documentation/also.js new file mode 100644 index 00000000..ca71151a --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/also.js @@ -0,0 +1,47 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe("multiple doclets per symbol", function() { + function undocumented($) { + return ! $.undocumented; + } + + function checkInequality(doclets, property) { + for (var l = doclets.length - 1; l > 0; l--) { + if (doclets[l][property] !== undefined && doclets[l - 1][property] !== undefined) { + expect(doclets[l][property]).not.toBe(doclets[l - 1][property]); + } + } + } + + var docSet = jasmine.getDocSetFromFile('test/fixtures/also.js'); + var name = docSet.getByLongname('Asset#name').filter(undocumented); + var shape = docSet.getByLongname('Asset#shape').filter(undocumented); + + it('When a symbol has multiple adjacent JSDoc comments, both apply to the symbol.', function() { + expect(name.length).toBe(2); + expect(shape.length).toBe(3); + }); + + it('When a symbol has multiple adjacent JSDoc comments that are not identical, the doclets ' + + 'have different comments.', function() { + checkInequality(name, 'comment'); + checkInequality(shape, 'comment'); + }); + + it('When a symbol has multiple adjacent JSDoc comments with different descriptions, ' + + 'the doclets have different descriptions.', function() { + checkInequality(name, 'description'); + checkInequality(shape, 'description'); + }); + + it('When a symbol has multiple adjacent JSDoc comments with different numbers of ' + + '@param tags, the doclets have different parameter lists.', function() { + checkInequality(name, 'params.length'); + checkInequality(shape, 'params.length'); + }); + + it('When a symbol has multiple adjacent JSDoc comments with different numbers of ' + + '@returns tags, the doclets have different lists of return values.', function() { + checkInequality(name, 'returns.length'); + checkInequality(shape, 'returns.length'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/documentation/callback.js b/data/scripts/jsdoc/test/specs/documentation/callback.js new file mode 100644 index 00000000..d8e14412 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/callback.js @@ -0,0 +1,27 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe('callback tag', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/callbacktag.js'); + + function callbackTests(callback) { + expect(callback).toBeDefined(); + + expect(callback.type).toBeDefined(); + expect(typeof callback.type).toEqual('object'); + + expect(callback.type.names).toBeDefined(); + expect(callback.type.names instanceof Array).toEqual(true); + expect(callback.type.names.length).toEqual(1); + + expect(callback.type.names[0]).toEqual('function'); + } + + it('correctly handles callbacks that do not define a {type}', function() { + var callback = docSet.getByLongname('requestResponseCallback')[0]; + callbackTests(callback); + }); + + it('correctly handles callbacks that define an incorrect {type}', function() { + var callback = docSet.getByLongname('wrongTypeCallback')[0]; + callbackTests(callback); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/documentation/exports.js b/data/scripts/jsdoc/test/specs/documentation/exports.js new file mode 100644 index 00000000..39e7debc --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/exports.js @@ -0,0 +1,11 @@ +describe("'exports' symbol in modules", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/exports.js'), + helloworld = docSet.getByLongname('module:hello/world')[0], + sayhello = docSet.getByLongname('module:hello/world.sayHello')[0]; + + it('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() { + expect(typeof sayhello).toEqual('object'); + expect(sayhello.kind).toEqual('function'); + expect(sayhello.memberof).toEqual('module:hello/world'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/getset.js b/data/scripts/jsdoc/test/specs/documentation/getset.js new file mode 100644 index 00000000..d88ebe6f --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/getset.js @@ -0,0 +1,23 @@ +describe("When a getter or setter is the child of an object literal", function () { + var docSet = jasmine.getDocSetFromFile("test/fixtures/getset.js"), + foundName = docSet.getByLongname("Person#name"), + foundAge = docSet.getByLongname("Person#age"); + + it("should have a doclet with the correct longname", function () { + expect(foundName.length).toEqual(2); + expect(foundAge.length).toEqual(1); + }); + + it("should have a doclet with the correct name", function () { + expect(foundName[0].name).toEqual("name"); + expect(foundName[1].name).toEqual("name"); + expect(foundAge[0].name).toEqual("age"); + }); + + it("should have the correct memberof", function () { + expect(foundName[0].memberof).toEqual("Person"); + expect(foundName[1].memberof).toEqual("Person"); + expect(foundAge[0].memberof).toEqual("Person"); + }); + +}); diff --git a/data/scripts/jsdoc/test/specs/documentation/inlinecomment.js b/data/scripts/jsdoc/test/specs/documentation/inlinecomment.js new file mode 100644 index 00000000..d2692ec1 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/inlinecomment.js @@ -0,0 +1,12 @@ +describe("inline comments", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/inlinecomment.js'), + t = docSet.getByLongname('test'), + t2 = docSet.getByLongname('test2'); + + it('When there is an inline comment on a line ending with no semicolon, that comment and the next comment are still captured', function() { + //Inline comment on line without semicolon is captured + expect(t.length).toEqual(1); + //Inline comment on line after line without semicolon is captured + expect(t2.length).toEqual(1); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/inner.js b/data/scripts/jsdoc/test/specs/documentation/inner.js new file mode 100644 index 00000000..d69594ea --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/inner.js @@ -0,0 +1,24 @@ +describe("when a documented var memeber is inside a named function", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/inner.js'), + found1 = docSet.getByLongname('sendMessage~encoding'), + found2 = docSet.getByLongname('sendMessage~encrypt'); + + it("A Doclet with the correct longname should be found", function() { + expect(found1.length).toEqual(1); + expect(found2.length).toEqual(1); + }); + + it("The short name should be correct", function() { + expect(found1[0].name).toEqual('encoding'); + expect(found2[0].name).toEqual('encrypt'); + }); + + it("The member of should be correct", function() { + expect(found1[0].memberof).toEqual('sendMessage'); + expect(found2[0].memberof).toEqual('sendMessage'); + }); + it("The scope should default to 'inner'", function() { + expect(found1[0].scope).toEqual('inner'); + expect(found2[0].scope).toEqual('inner'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/innerscope.js b/data/scripts/jsdoc/test/specs/documentation/innerscope.js new file mode 100644 index 00000000..01cad5fc --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/innerscope.js @@ -0,0 +1,40 @@ +describe("inner scope", function() { + describe("Outer~inner.member cases", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/innerscope.js'), + to = docSet.getByLongname('Message~headers.to'), + from = docSet.getByLongname('Message~headers.from'), + response = docSet.getByLongname('Message~response.code'); + + it('should occur when a member of a var member is documented.', function() { + expect(to.length).toEqual(1); + }); + + it('should occur when a second member of a var member is documented.', function() { + expect(response.length).toEqual(1); + }); + + it('should occur when a deeply nested member of a var member is documented.', function() { + expect(from.length).toEqual(1); + }); + }); + + describe("other cases", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/innerscope2.js'), + to = docSet.getByLongname('Message~headers.to'), + from = docSet.getByLongname('~headers.from'), + cache = docSet.getByLongname('~headers.cache'); + + it('When a var is declared in a function, It is like Inner~member', function() { + expect(cache.length).toEqual(1); + }); + + it('When a var is masked by an inner var and a member of the inner is documented, it is like Inner~inner.member', function() { + expect(from.length).toEqual(1); + }); + + it('When a documented member is assigned to a var that masks an outer var.', function() { + expect(from[0].name).toEqual('from'); + expect(from[0].memberof).toEqual('~headers'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/lends.js b/data/scripts/jsdoc/test/specs/documentation/lends.js new file mode 100644 index 00000000..fe24a13c --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/lends.js @@ -0,0 +1,71 @@ +describe("lends", function() { + describe("when a documented member is inside an object literal associate with a @lends tag", function() { + describe("standard case", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/lends.js'), + init = docSet.getByLongname('Person#initialize'), + name = docSet.getByLongname('Person#name'); + + it("The member should be documented as a member of the lendee", function() { + expect(init.length, 1); + }); + + it("The this member should be documented as a member of the lendee", function() { + expect(name.length, 1); + }); + }); + + describe("case containg constructor", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/lends2.js'), + person = docSet.getByLongname('Person').filter(function($) { + return ! $.undocumented; + })[0], + name = docSet.getByLongname('Person#name'); + + it("A tag with a @constructs tag is documented as a constructor.", function() { + expect(person.description).toEqual('Construct a Person.'); + }); + + it("The member should be documented as a member of the lendee", function() { + expect(person.length, 1); + }); + + it("The this member should be documented as a member of the lendee", function() { + expect(name.length, 1); + }); + }); + + describe("case that uses @lends in a multiline doclet", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/lends3.js'), + init = docSet.getByLongname('Person#initialize'), + name = docSet.getByLongname('Person#name'); + + it("The member should be documented as a member of the lendee", function() { + expect(init.length, 1); + }); + + it("The this member should be documented as a member of the lendee", function() { + expect(name.length, 1); + }); + }); + + }); + + describe("when a documented member is inside an objlit associated with a @lends tag that has no value.", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/lendsglobal.js'), + testf = docSet.getByLongname('test')[0], + test1 = docSet.getByLongname('test1')[0], + test12 = docSet.getByLongname('test1.test2')[0]; + + it("The members of the objlit are not members of any symbol", function() { + expect(typeof testf.memberof).toEqual('undefined'); + }); + + it("The members of the objlit are documented as global.", function() { + expect(testf.longname).toEqual('test'); + }); + + it("The nested members of the objlit are members of a global symbol", function() { + expect(test12.memberof).toEqual('test1'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/moduleinner.js b/data/scripts/jsdoc/test/specs/documentation/moduleinner.js new file mode 100644 index 00000000..1a783c96 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/moduleinner.js @@ -0,0 +1,13 @@ +describe("inner scope for modules", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/moduleinner.js'), + fooIn = docSet.getByLongname('module:my/module~fooIn')[0], + fooOut = docSet.getByLongname('module:my/module~fooOut')[0]; + + it('When a function appears in the topscope of a module, the symbol is documented as an inner member of that module.', function() { + expect(typeof fooOut).toEqual('object'); + expect(fooOut.longname).toEqual('module:my/module~fooOut'); + + expect(typeof fooIn).toEqual('object'); + expect(fooIn.longname).toEqual('module:my/module~fooIn'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/moduleisconstructor.js b/data/scripts/jsdoc/test/specs/documentation/moduleisconstructor.js new file mode 100644 index 00000000..6b0cbe61 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/moduleisconstructor.js @@ -0,0 +1,5 @@ +/*global describe: true, expect: true, it: true, xdescribe: true */ + +xdescribe('module that exports a constructor', function() { + // TODO +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/moduleisfunction.js b/data/scripts/jsdoc/test/specs/documentation/moduleisfunction.js new file mode 100644 index 00000000..1aeb783d --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/moduleisfunction.js @@ -0,0 +1,23 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ + +describe('module that exports a function that is not a constructor', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/moduleisfunction.js'); + var functions = docSet.doclets.filter(function(doclet) { + return doclet.kind === 'function'; + }); + + it('should include one doclet whose kind is "function"', function() { + expect(functions.length).toBe(1); + expect(functions[0].kind).toBe('function'); + }); + + describe('function doclet', function() { + it('should not include a "scope" property', function() { + expect(functions[0].scope).not.toBeDefined(); + }); + + it('should not include a "memberof" property', function() { + expect(functions[0].memberof).not.toBeDefined(); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/documentation/modules.js b/data/scripts/jsdoc/test/specs/documentation/modules.js new file mode 100644 index 00000000..52481150 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/modules.js @@ -0,0 +1,23 @@ +/*global beforeEach: true, describe: true, env: true, expect: true, it: true */ +describe("module names", function() { + var parser = require('jsdoc/src/parser'), + srcParser = null, doclets; + + beforeEach(function() { + env.opts._ = [__dirname + '/test/fixtures/modules/']; + srcParser = new parser.Parser(); + require('jsdoc/src/handlers').attachTo(srcParser); + }); + + it("should create a name from the file path when no documented module name exists", function() { + doclets = srcParser.parse(__dirname + '/test/fixtures/modules/data/mod-1.js'); + expect(doclets.length).toBeGreaterThan(1); + expect(doclets[0].longname).toEqual('module:data/mod-1'); + }); + + it("should use the documented module name if available", function() { + doclets = srcParser.parse(__dirname + '/test/fixtures/modules/data/mod-2.js'); + expect(doclets.length).toBeGreaterThan(1); + expect(doclets[0].longname).toEqual('module:my/module/name'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/namedFuncStatement.js b/data/scripts/jsdoc/test/specs/documentation/namedFuncStatement.js new file mode 100644 index 00000000..5dd1de6f --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/namedFuncStatement.js @@ -0,0 +1,43 @@ +describe("named function statements", function() { + describe("standard", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/namedFuncStatement.js'), + fooMember = docSet.getByLongname('Foo#member1')[0], + fooVariable = docSet.getByLongname('Foo~var1')[0]; + + it('A symbol that is a member of a named function statement should documented as a member of the assigned name', function() { + expect(fooMember.longname).toEqual('Foo#member1'); + }); + + it('A symbol that is a variable of a named function statement should documented as a member of the assigned name', function() { + expect(fooVariable.longname).toEqual('Foo~var1'); + }); + }); + + describe("global", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/namedFuncStatement2.js'), + fooMember = docSet.getByLongname('Foo#member1')[0], + fooVariable = docSet.getByLongname('Foo~var1')[0]; + + it('A symbol that is a member of a named function statement should documented as a member of the assigned name', function() { + expect(fooMember.longname).toEqual('Foo#member1'); + }); + + it('A symbol that is a variable of a named function statement should documented as a member of the assigned name', function() { + expect(fooVariable.longname).toEqual('Foo~var1'); + }); + }); + + describe("as object literal property", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/namedFuncStatement3.js'), + fooMember = docSet.getByLongname('ns.Foo#member1')[0], + fooVariable = docSet.getByLongname('ns.Foo~var1')[0]; + + it('A symbol that is a member of a named function statement should documented as a member of the assigned name', function() { + expect(fooMember.longname).toEqual('ns.Foo#member1'); + }); + + it('A symbol that is a variable of a named function statement should documented as a member of the assigned name', function() { + expect(fooVariable.longname).toEqual('ns.Foo~var1'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/objectlit.js b/data/scripts/jsdoc/test/specs/documentation/objectlit.js new file mode 100644 index 00000000..1bc09d0b --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/objectlit.js @@ -0,0 +1,43 @@ +describe("object literals", function() { + describe("When a child of an objlit has no @name or @memberof tags", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/objectlit.js'), + found = docSet.getByLongname('tools.serialiser.value'); + + it("should have a doclet with the correct longname", function() { + expect(found.length).toEqual(1); + }); + + it("should have a doclet with the correct name", function() { + expect(found[0].name).toEqual('value'); + }); + + it("should have the correct memberof", function() { + expect(found[0].memberof).toEqual('tools.serialiser'); + }); + + it("should have a static scope", function() { + expect(found[0].scope).toEqual('static'); + }); + }); + + describe("When a parent of an objlit has no documentation", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/objectlit2.js'), + found = docSet.getByLongname('position.axis.x'); + + it("should have a doclet with the correct longname", function() { + expect(found.length).toEqual(1); + }); + + it("should have a doclet with the correct name", function() { + expect(found[0].name).toEqual('x'); + }); + + it("should have the correct memberof", function() { + expect(found[0].memberof).toEqual('position.axis'); + }); + + it("should have a static scope", function() { + expect(found[0].scope).toEqual('static'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/objectpropertykeys.js b/data/scripts/jsdoc/test/specs/documentation/objectpropertykeys.js new file mode 100644 index 00000000..0cb39e22 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/objectpropertykeys.js @@ -0,0 +1,6 @@ +describe("using existing Object properties as object literal keys", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/objectpropertykeys.js'); + it("should not crash", function() { + expect(true).toBeTruthy(); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/quotename.js b/data/scripts/jsdoc/test/specs/documentation/quotename.js new file mode 100644 index 00000000..41d30902 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/quotename.js @@ -0,0 +1,23 @@ +describe("quoted names", function() { + + describe("when found in square brackets", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/quotename.js'), + found1 = docSet.getByLongname('chat.\"#channel\".open')[0]; + + + it('should have correct name and memberof', function() { + expect(found1.name).toEqual('open'); + expect(found1.memberof).toEqual('chat.\"#channel\"'); + }); + }); + + describe("when found in an object literal", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/quotename2.js'), + found1 = docSet.getByLongname("contacts.\"say-\\\"hello\\\"@example.com\".username")[0]; + + it('should have correct name and memberof', function() { + expect(found1.name).toEqual('username'); + expect(found1.memberof).toEqual("contacts.\"say-\\\"hello\\\"@example.com\""); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/specialnames.js b/data/scripts/jsdoc/test/specs/documentation/specialnames.js new file mode 100644 index 00000000..f6d8b62b --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/specialnames.js @@ -0,0 +1,10 @@ +describe("documenting symbols with special names", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/specialnames.js'), + name = docSet.getByLongname('hasOwnProperty').filter(function($) { + return ! $.undocumented; + }); + + it('When a symbol has the documented name of "hasOwnProperty," JSDoc should correctly include it in the docs.', function() { + expect(name.length).toEqual(1); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/starbangstar.js b/data/scripts/jsdoc/test/specs/documentation/starbangstar.js new file mode 100644 index 00000000..0a875bb4 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/starbangstar.js @@ -0,0 +1,13 @@ +describe("starbangstar", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/starbangstar.js'), + mod = docSet.getByLongname('module:myscript/core')[0], + x = docSet.getByLongname('module:myscript/core.x')[0]; + + it('should not treat a doclet starting with /*!* as a JSDoc comment.', function() { + expect(mod.description).toEqual('Script that does something awesome'); + }); + + it('should not treat a doclet starting with /*!** as a JSDoc comment.', function() { + expect(x).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/this.js b/data/scripts/jsdoc/test/specs/documentation/this.js new file mode 100644 index 00000000..6393dba4 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/this.js @@ -0,0 +1,102 @@ +describe("this", function() { + describe("attaching members to 'this'", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/this.js'), + found1 = docSet.getByLongname('Singer#tralala'), + found2 = docSet.getByLongname('Singer#isSinging'); + + describe("in a contructor", function() { + it("should have a longname like Constructor#member", function() { + expect(found1.length).toEqual(1); + }); + + it("should havea correct short name", function() { + expect(found1[0].name).toEqual('tralala'); + }); + + it("should havea correct memberof", function() { + expect(found1[0].memberof).toEqual('Singer'); + }); + + it("should default to a 'instance' scope", function() { + expect(found1[0].scope).toEqual('instance'); + }); + }); + + describe("in a method of a constructor", function() { + it("should have a longname like Constructor#member", function() { + expect(found2.length).toEqual(1); + }); + + it("should havea correct short name", function() { + expect(found2[0].name).toEqual('isSinging'); + }); + + it("should havea correct memberof", function() { + expect(found2[0].memberof).toEqual('Singer'); + }); + + it("should default to a 'instance' scope", function() { + expect(found2[0].scope).toEqual('instance'); + }); + }); + }); + + describe("when a contructor is nested inside another constructor", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/this2.js'), + found = docSet.getByLongname('TemplateBuilder#Template#rendered'); + + it("should have a longname like Constructor#Constructor#member", function() { + expect(found.length).toEqual(1); + }); + + it("should havea correct short name", function() { + expect(found[0].name).toEqual('rendered'); + }); + + it("should havea correct memberof", function() { + expect(found[0].memberof).toEqual('TemplateBuilder#Template'); + }); + + it("should default to a 'instance' scope", function() { + expect(found[0].scope).toEqual('instance'); + }); + }); + + describe("When a this is assigned to inside a non-constructor function", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/this3.js'), + found = docSet.getByLongname('position'); + + it("should have a global member name like 'member'", function() { + expect(found.length).toEqual(1); + }); + + it("should havea correct short name", function() { + expect(found[0].name).toEqual('position'); + }); + + it("should havea correct memberof", function() { + expect(found[0].memberof).toBeUndefined(); + }); + }); + + describe("When a member is nested inside an objectlit 'this' property inside a constructor", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/this-and-objectlit.js'), + found = docSet.getByLongname('Page#parts.body.heading'); + + it("should have a longname like Constructor#objlit.member", function() { + expect(found.length).toEqual(1); + }); + + it("should havea correct short name", function() { + expect(found[0].name).toEqual('heading'); + }); + + it("should havea correct memberof", function() { + expect(found[0].memberof).toEqual('Page#parts.body'); + }); + + it("should default to a 'static' scope", function() { + expect(found[0].scope).toEqual('static'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/var.js b/data/scripts/jsdoc/test/specs/documentation/var.js new file mode 100644 index 00000000..a9b85fe5 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/var.js @@ -0,0 +1,58 @@ +describe("var statements", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/var.js'), + found = [ + docSet.getByLongname('GREEN'), + docSet.getByLongname('RED'), + docSet.getByLongname('validate'), + docSet.getByLongname('i'), + docSet.getByLongname('results') + ]; + + describe("when a series of constants are documented", function() { + it("should find the first constant", function() { + expect(found[0].length).toEqual(1); + }); + + it("attach the docs to the first constant", function() { + expect(found[0][0].comment).toEqual('/** document me */'); + }); + + it("should have a correct short name", function() { + expect(found[0][0].name).toEqual('GREEN'); + }); + + it("should have a correct memberof", function() { + expect(found[0][0].memberof).toBeUndefined(); + }); + + it("should give the constant a global scope", function() { + expect(found[0][0].scope).toEqual('global'); + }); + + it("should find the second constant", function() { + expect(found[1].length).toEqual(1); + }); + + it("should not attach the docs to the second constant", function() { + expect(found[1][0].undocumented).toEqual(true); + }); + }); + + describe('When a member of a series of vars are documented.', function() { + it("should attach the docs to the correct var", function() { + expect(found[4][0].comment).toEqual('/** document me */'); + }); + + it("should hav a correct short name", function() { + expect(found[4][0].name).toEqual('results'); + }); + + it("should leave memberof undefined", function() { + expect(found[4][0].memberof).toBeUndefined(); + }); + + it("should give the var a global scope", function() { + expect(found[4][0].scope).toEqual('global'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/variations.js b/data/scripts/jsdoc/test/specs/documentation/variations.js new file mode 100644 index 00000000..3bf133c8 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/variations.js @@ -0,0 +1,38 @@ +describe("variations", function() { + + describe("by name", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/variations.js'), + fadein1 = docSet.getByLongname('anim.fadein(1)')[0], + fadein2 = docSet.getByLongname('anim.fadein(2)')[0]; + + it('When a symbol has a name with a variation, the doclet has a variation property.', function() { + expect(fadein1.variation).toEqual('1'); + expect(fadein2.variation).toEqual('2'); + }); + + it('When a symbol has a name with a variation in the name, the doclet name has no variation in it.', function() { + expect(fadein1.name).toEqual('fadein'); + expect(fadein2.name).toEqual('fadein'); + }); + + it('When a symbol has a name with a variation in the name, the doclet longname has the variation in it.', function() { + expect(fadein1.longname).toEqual('anim.fadein(1)'); + expect(fadein2.longname).toEqual('anim.fadein(2)'); + }); + }); + + describe("by tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/variations3.js'), + someObject = docSet.getByLongname('someObject')[0], + someObject2 = docSet.getByLongname('someObject(2)')[0], + someObject2method = docSet.getByLongname('someObject(2).someMethod')[0]; + + it('When a symbol has a variation tag, the longname includes that variation.', function() { + expect(someObject2.longname).toEqual('someObject(2)'); + }); + + it('When a symbol is a member of a variation, the longname includes the variation.', function() { + expect(someObject2method.longname).toEqual('someObject(2).someMethod'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/documentation/virtual.js b/data/scripts/jsdoc/test/specs/documentation/virtual.js new file mode 100644 index 00000000..b6c89db6 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/documentation/virtual.js @@ -0,0 +1,15 @@ +describe("virtual symbols", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/virtual.js'), + found = [ + docSet.getByLongname('dimensions'), + docSet.getByLongname('width') + ]; + + it('should document virtual symbols', function() { + expect(found[0].length).toEqual(1); + }); + + it('should document an undocumented symbol found after a comment for a virtual symbol', function() { + expect(found[1].length).toEqual(1); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/augment.js b/data/scripts/jsdoc/test/specs/jsdoc/augment.js new file mode 100644 index 00000000..7b2daf92 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/augment.js @@ -0,0 +1,4 @@ +/*global describe: true, env: true, it: true */ +describe("jsdoc/augment", function() { + // TODO +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/borrow.js b/data/scripts/jsdoc/test/specs/jsdoc/borrow.js new file mode 100644 index 00000000..4085d9b1 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/borrow.js @@ -0,0 +1,3 @@ +describe("jsdoc/borrow", function() { + //TODO +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/config.js b/data/scripts/jsdoc/test/specs/jsdoc/config.js new file mode 100644 index 00000000..24e024fe --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/config.js @@ -0,0 +1,47 @@ +/*global describe: true, expect: true, it: true */ +describe("jsdoc/config", function() { + var jsdoc = {config: require('jsdoc/config') }; + + it("should exist", function() { + expect(jsdoc.config).toBeDefined(); + expect(typeof jsdoc.config).toEqual("function"); + }); + + it("should provide a 'get' instance function", function() { + var config = new jsdoc.config(); + expect(config.get).toBeDefined(); + expect(typeof config.get).toEqual("function"); + }); + + describe ("constructor with empty", function() { + it('should be possible to construct a Config with an empty arguments', function() { + var config = new jsdoc.config().get(); + + expect(config.plugins).toEqual([]); + }); + }); + + describe ("constructor with {}", function() { + it('should be possible to construct a Config with JSON of an object literal that is emptys', function() { + var config = new jsdoc.config('{}').get(); + + expect(config.plugins).toEqual([]); + }); + }); + + describe ("constructor with plugins value", function() { + it('should be possible to construct a Config with JSON of an object literal that has a plugin value', function() { + var config = new jsdoc.config('{"plugins":[42]}').get(); + + expect(config.plugins).toEqual([42]); + }); + }); + + describe ("constructor with source value", function() { + it('should be possible to construct a Config with JSON of an object literal that has a source value', function() { + var config = new jsdoc.config('{"source":{"includePattern":"hello"}}').get(); + + expect(config.source.includePattern).toEqual('hello'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/doclet.js b/data/scripts/jsdoc/test/specs/jsdoc/doclet.js new file mode 100644 index 00000000..efeaed25 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/doclet.js @@ -0,0 +1,16 @@ +/*global describe: true, env: true, expect: true, it: true, jasmine: true */ +describe("jsdoc/doclet", function() { + // TODO: more tests + + var docSet = jasmine.getDocSetFromFile('test/fixtures/doclet.js'), + test1 = docSet.getByLongname('test1')[0], + test2 = docSet.getByLongname('test2')[0]; + + var expectStrong = "**Strong** is strong"; + var expectList = "* List item 1"; + + it('does not mangle Markdown in a description that uses leading asterisks', function() { + expect(test2.description.indexOf(expectStrong)).toBeGreaterThan(-1); + expect(test2.description.indexOf(expectList)).toBeGreaterThan(-1); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/name.js b/data/scripts/jsdoc/test/specs/jsdoc/name.js new file mode 100644 index 00000000..f8ba382b --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/name.js @@ -0,0 +1,285 @@ +/*global describe: true, expect: true, it: true */ + +describe("jsdoc/name", function() { + var jsdoc = {name: require('jsdoc/name'), doclet: require('jsdoc/doclet') }; + + it("should exist", function() { + expect(jsdoc.name).toBeDefined(); + expect(typeof jsdoc.name).toEqual("object"); + }); + + it("should export an 'resolve' function", function() { + expect(jsdoc.name.resolve).toBeDefined(); + expect(typeof jsdoc.name.resolve).toEqual("function"); + }); + + it("should export an 'applyNamespace' function", function() { + expect(jsdoc.name.applyNamespace).toBeDefined(); + expect(typeof jsdoc.name.applyNamespace).toEqual("function"); + }); + + it("should export an 'shorten' function", function() { + expect(jsdoc.name.shorten).toBeDefined(); + expect(typeof jsdoc.name.shorten).toEqual("function"); + }); + + it("should export an 'splitName' function", function() { + expect(jsdoc.name.splitName).toBeDefined(); + expect(typeof jsdoc.name.splitName).toEqual("function"); + }); + + describe ("shorten", function() { + it('should break up a longname into the correct memberof, name and scope parts', function() { + var startName = 'lib.Panel#open', + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toEqual('open'); + expect(parts.memberof).toEqual('lib.Panel'); + expect(parts.scope).toEqual('#'); + }); + + it('should work on static names', function() { + var startName = 'elements.selected.getVisible', + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toEqual('getVisible'); + expect(parts.memberof).toEqual('elements.selected'); + expect(parts.scope).toEqual('.'); + }); + + it('should work on protoyped names', function() { + var startName = 'Validator.prototype.$element', + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toEqual('$element'); + expect(parts.memberof).toEqual('Validator'); + expect(parts.scope).toEqual('#'); + }); + + it('should work on inner names.', function() { + var startName = 'Button~_onclick', + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toEqual('_onclick'); + expect(parts.memberof).toEqual('Button'); + expect(parts.scope).toEqual('~'); + }); + + it('should work on global names.', function() { + var startName = 'close', + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toEqual('close'); + //'The memberof should be an empty string for global symbols.' + expect(parts.memberof).toEqual(''); + //'The scope should be an empty string for global symbols.' + expect(parts.scope).toEqual(''); + }); + + it('should work on bracketed stringy names', function() { + var startName = 'channels["#ops"]#open', + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toEqual('open'); + //'Bracketed stringy names should appear as quoted strings.' + expect(parts.memberof).toEqual('channels."#ops"'); + expect(parts.scope).toEqual('#'); + + startName = 'channels["#bots"]["log.max"]', + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toEqual('"log.max"'); + expect(parts.memberof).toEqual('channels."#bots"'); + expect(parts.scope).toEqual('.'); + }); + + it('should work on bracketed stringy names with single quotes', function() { + var startName = "channels['#ops']", + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toBe("'#ops'"); + expect(parts.memberof).toBe('channels'); + expect(parts.scope).toBe('.'); + }); + + it('should work on fully stringy names, like "foo.bar"', function() { + var startName = '"foo.bar"', + parts = jsdoc.name.shorten(startName); + + //'The name should be the full quoted string.' + expect(parts.name).toEqual('"foo.bar"'); + //'The longname should be the full quoted string.' + expect(parts.longname).toEqual('"foo.bar"'); + //'There should be no memberof, as it is global.' + expect(parts.memberof).toEqual(''); + //'The scope should be as global.' + expect(parts.scope).toEqual(''); + }); + + it('should work on fully stringy names in single quotes, like \'foo.bar\'', function() { + var startName = "'foo.bar'", + parts = jsdoc.name.shorten(startName); + + expect(parts.name).toBe("'foo.bar'"); + expect(parts.longname).toBe("'foo.bar'"); + expect(parts.memberof).toBe(''); + expect(parts.scope).toBe(''); + }); + + it('should find the variation', function() { + var startName = 'anim.fadein(2)', + parts = jsdoc.name.shorten(startName); + + expect(parts.variation).toEqual('2'); + expect(parts.name).toEqual('fadein'); + expect(parts.longname).toEqual('anim.fadein(2)'); + }); + }); + + describe("applyNamespace", function() { + it('should insert the namespace only before the name part of the longname', function() { + var startName = 'lib.Panel#open', + endName = jsdoc.name.applyNamespace(startName, 'event'); + + expect(endName, 'lib.Panel#event:open'); + }); + + it(" should insert the namespace before a global name", function() { + var startName = 'maths/bigint', + endName = jsdoc.name.applyNamespace(startName, 'module'); + + expect(endName, 'module:maths/bigint'); + }); + + it('should treat quoted parts of the name as atomic and insert namespace before a quoted shortname', function() { + var startName = 'foo."*dont\'t.look~in#here!"', + endName = jsdoc.name.applyNamespace(startName, 'event'); + + expect(endName, 'foo.event:"*dont\'t.look~in#here!"'); + }); + + it('should not add another namespace if one already exists.', function() { + var startName = 'lib.Panel#event:open', + endName = jsdoc.name.applyNamespace(startName, 'event'); + + expect(endName, 'lib.Panel#event:open'); + }); + }); + + describe("splitName", function() { + it('should find the name and description.', function() { + var startName = 'ns.Page#"last \\"sentence\\"".words~sort(2) - This is a description. ', + parts = jsdoc.name.splitName(startName); + + expect(parts.name, 'ns.Page#"last \\"sentence\\"".words~sort(2)'); + expect(parts.description, 'This is a description.'); + }); + }); + + describe("resolve", function() { + // TODO: further tests (namespaces, modules, ...) + + // @event testing. + var event = '@event'; + var memberOf = '@memberof MyClass'; + var name = '@name A'; + function makeDoclet(bits) { + var comment = '/**\n' + bits.join('\n') + '\n*/'; + return new jsdoc.doclet.Doclet(comment, {}); + } + + // Test the basic @event that is not nested. + it('unnested @event gets resolved correctly', function() { + var doclet = makeDoclet([event, name]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toBeUndefined(); + expect(doclet.longname).toEqual('event:A'); + }); + + // test all permutations of @event @name [name] @memberof. + it('@event @name @memberof resolves correctly', function() { + var doclet = makeDoclet([event, name, memberOf]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@event @memberof @name resolves correctly', function() { + var doclet = makeDoclet([event, memberOf, name]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@name @event @memberof resolves correctly', function() { + var doclet = makeDoclet([name, event, memberOf]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@name @memberof @event resolves correctly', function() { + var doclet = makeDoclet([name, memberOf, event]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@memberof @event @name resolves correctly', function() { + var doclet = makeDoclet([memberOf, event, name]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@memberof @name @event resolves correctly', function() { + var doclet = makeDoclet([memberOf, name, event]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + + // test all permutations of @event [name] @memberof + it('@event [name] @memberof resolves correctly', function() { + var doclet = makeDoclet(['@event A', memberOf]), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('@memberof @event [name] resolves correctly', function() { + var doclet = makeDoclet([memberOf, '@event A']), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + + // test full @event A.B + it('full @event definition works', function() { + var doclet = makeDoclet(['@event MyClass.A']), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + it('full @event definition with event: works', function() { + var doclet = makeDoclet(['@event MyClass.event:A']), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('event:A'); + expect(doclet.memberof).toEqual('MyClass'); + expect(doclet.longname).toEqual('MyClass.event:A'); + }); + + // a double-nested one just in case + it('@event @name MyClass.EventName @memberof somethingelse workse', function() { + var doclet = makeDoclet([event, '@name MyClass.A', '@memberof MyNamespace']), + out = jsdoc.name.resolve(doclet); + expect(doclet.name).toEqual('A'); + expect(doclet.memberof).toEqual('MyNamespace.MyClass'); + expect(doclet.longname).toEqual('MyNamespace.MyClass.event:A'); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/opts/argparser.js b/data/scripts/jsdoc/test/specs/jsdoc/opts/argparser.js new file mode 100644 index 00000000..5d4c1583 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/opts/argparser.js @@ -0,0 +1,32 @@ +/*global describe: true, expect: true, it: true */ +describe("jsdoc/opts/argparser", function() { + var ArgParser = require('jsdoc/opts/argparser'), + argParser = new ArgParser(), + ourOptions; + + function trueFalse(v) { + var r = false; + if (v) { + if (v === 'true') { r = true; } + else if (v === 'false') { r = false; } + else { v = !!r; } + } + + return r; + } + + argParser.addOption('s', 'strict', true, 'Throw error on invalid input.', false, trueFalse); + argParser.addOption('n', 'name', true, 'The name of the project.', false); + + ourOptions = argParser.parse(['-s', 'true', '-n', 'true']); + + it('should coerce a true value if a coercer is provided', function() { + expect(ourOptions.strict).toBeDefined(); + expect(ourOptions.strict).toEqual(true); + }); + + it('should coerce a string value if no coercer is provided', function() { + expect(ourOptions.name).toBeDefined(); + expect(ourOptions.name).toEqual('true'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/opts/args.js b/data/scripts/jsdoc/test/specs/jsdoc/opts/args.js new file mode 100644 index 00000000..bf924547 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/opts/args.js @@ -0,0 +1,259 @@ +/*global describe: true, expect: true, it: true */ +describe("jsdoc/opts/args", function() { + var args = require('jsdoc/opts/args'); + var querystring = require('querystring'); + + it("should exist", function() { + expect(args).toBeDefined(); + expect(typeof args).toEqual("object"); + }); + + it("should export a 'parse' function", function() { + expect(args.parse).toBeDefined(); + expect(typeof args.parse).toEqual("function"); + }); + + it("should export a 'help' function", function() { + expect(args.help).toBeDefined(); + expect(typeof args.help).toEqual("function"); + }); + + it("should export a 'get' function", function() { + expect(args.get).toBeDefined(); + expect(typeof args.get).toEqual("function"); + }); + + describe("parse", function() { + it("should accept a '-t' option and return an object with a 'template' property", function() { + args.parse(['-t', 'mytemplate']); + var r = args.get(); + + expect(r.template).toEqual('mytemplate'); + }); + + it("should accept a '--template' option and return an object with a 'template' property", function() { + args.parse(['--template', 'mytemplate']); + var r = args.get(); + + expect(r.template).toEqual('mytemplate'); + }); + + it("should accept a '-c' option and return an object with a 'configure' property", function() { + args.parse(['-c', 'myconf.json']); + var r = args.get(); + + expect(r.configure).toEqual('myconf.json'); + }); + + it("should accept a '--configure' option and return an object with a 'configure' property", function() { + args.parse(['--configure', 'myconf.json']); + var r = args.get(); + + expect(r.configure).toEqual('myconf.json'); + }); + + it("should accept a '-e' option and return an object with a 'encoding' property", function() { + args.parse(['-e', 'ascii']); + var r = args.get(); + + expect(r.encoding).toEqual('ascii'); + }); + + it("should accept a '--encoding' option and return an object with a 'encoding' property", function() { + args.parse(['--encoding', 'ascii']); + var r = args.get(); + + expect(r.encoding).toEqual('ascii'); + }); + + it("should accept a '-T' option and return an object with a 'test' property", function() { + args.parse(['-T']); + var r = args.get(); + + expect(r.test).toEqual(true); + }); + + it("should accept a '--test' option and return an object with a 'test' property", function() { + args.parse(['--test']); + var r = args.get(); + + expect(r.test).toEqual(true); + }); + + it("should accept a '-d' option and return an object with a 'destination' property", function() { + args.parse(['-d', 'mydestination']); + var r = args.get(); + + expect(r.destination).toEqual('mydestination'); + }); + + it("should accept a '--destination' option and return an object with a 'destination' property", function() { + args.parse(['--destination', 'mydestination']); + var r = args.get(); + + expect(r.destination).toEqual('mydestination'); + }); + + it("should accept a '-p' option and return an object with a 'private' property", function() { + args.parse(['-p']); + var r = args.get(); + + expect(r['private']).toEqual(true); + }); + + it("should accept a '--private' option and return an object with a 'private' property", function() { + args.parse(['--private']); + var r = args.get(); + + expect(r['private']).toEqual(true); + }); + + it("should accept a '-r' option and return an object with a 'recurse' property", function() { + args.parse(['-r']); + var r = args.get(); + + expect(r.recurse).toEqual(true); + }); + + it("should accept a '--recurse' option and return an object with a 'recurse' property", function() { + args.parse(['--recurse']); + var r = args.get(); + + expect(r.recurse).toEqual(true); + }); + + it("should accept a '-l' option and return an object with a 'lenient' property", function() { + args.parse(['-l']); + var r = args.get(); + + expect(r.lenient).toEqual(true); + }); + + it("should accept a '--lenient' option and return an object with a 'lenient' property", function() { + args.parse(['--lenient']); + var r = args.get(); + + expect(r.lenient).toEqual(true); + }); + + it("should accept a '-h' option and return an object with a 'help' property", function() { + args.parse(['-h']); + var r = args.get(); + + expect(r.help).toEqual(true); + }); + + it("should accept a '--help' option and return an object with a 'help' property", function() { + args.parse(['--help']); + var r = args.get(); + + expect(r.help).toEqual(true); + }); + + it("should accept a '-X' option and return an object with a 'explain' property", function() { + args.parse(['-X']); + var r = args.get(); + + expect(r.explain).toEqual(true); + }); + + it("should accept a '--explain' option and return an object with a 'explain' property", function() { + args.parse(['--explain']); + var r = args.get(); + + expect(r.explain).toEqual(true); + }); + + it("should accept a '-q' option and return an object with a 'query' property", function() { + args.parse(['-q', 'foo=bar&fab=baz']); + var r = args.get(); + + expect(r.query).toEqual({ foo: 'bar', fab: 'baz' }); + }); + + it("should accept a '--query' option and return an object with a 'query' property", function() { + args.parse(['--query', 'foo=bar&fab=baz']); + var r = args.get(); + + expect(r.query).toEqual({ foo: 'bar', fab: 'baz' }); + }); + + it("should use type coercion on the 'query' property so it has real booleans and numbers", function() { + var obj = { + foo: 'fab', + bar: true, + baz: false, + qux: [1, -97] + }; + args.parse(['-q', querystring.stringify(obj)]); + var r = args.get(); + + expect(r.query).toEqual(obj); + }); + + it("should accept a '-t' option and return an object with a 'tutorials' property", function() { + args.parse(['-d', 'mytutorials']); + var r = args.get(); + + expect(r.destination).toEqual('mytutorials'); + }); + + it("should accept a '--tutorials' option and return an object with a 'tutorials' property", function() { + args.parse(['--tutorials', 'mytutorials']); + var r = args.get(); + + expect(r.tutorials).toEqual('mytutorials'); + }); + + it("should accept a '--verbose' option and return an object with a 'verbose' property", function() { + args.parse(['--verbose']); + var r = args.get(); + + expect(r.verbose).toEqual(true); + }); + + it("should accept a '--match' option and return an object with a 'match' property", function() { + args.parse(['--match', '.*tag']); + var r = args.get(); + + expect(r.match).toEqual('.*tag'); + }); + + it("should accept multiple '--match' options and return an object with a 'match' property", function() { + args.parse(['--match', '.*tag', '--match', 'parser']); + var r = args.get(); + + expect(r.match).toEqual(['.*tag', 'parser']); + }); + + it("should accept a '--nocolor' option and return an object with a 'nocolor' property", function() { + args.parse(['--nocolor']); + var r = args.get(); + + expect(r.nocolor).toEqual(true); + }); + + it("should accept a '-v' option and return an object with a 'version' property", function() { + args.parse(['-v']); + var r = args.get(); + + expect(r.version).toEqual(true); + }); + + it("should accept a '--version' option and return an object with a 'version' property", function() { + args.parse(['--version']); + var r = args.get(); + + expect(r.version).toEqual(true); + }); + + it("should accept a naked option (i.e. no '-') and return an object with a '_' property", function() { + args.parse(['myfile1', 'myfile2']); + var r = args.get(); + + expect(r._).toEqual(['myfile1', 'myfile2']); + }); + + //TODO: tests for args that must have values + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/path.js b/data/scripts/jsdoc/test/specs/jsdoc/path.js new file mode 100644 index 00000000..90952b3b --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/path.js @@ -0,0 +1,95 @@ +/*global beforeEach: true, describe: true, expect: true, it: true, spyOn: true, xdescribe: true */ + +describe('jsdoc/path', function() { + var os = require('os'); + var path = require('jsdoc/path'); + var standardPath = require('path'); + + it('should exist', function() { + expect(path).toBeDefined(); + expect(typeof path).toEqual('object'); + }); + + it('should export all functions in the "path" module', function() { + Object.keys(standardPath).forEach(function(item) { + if (typeof standardPath[item] === 'function') { + expect(path[item]).toBeDefined(); + expect(typeof path[item]).toEqual('function'); + } + }); + }); + + it('should export a "commonPrefix" function', function() { + expect(path.commonPrefix).toBeDefined(); + expect(typeof path.commonPrefix).toEqual('function'); + }); + + it('should export a "getResourcePath" function', function() { + expect(path.getResourcePath).toBeDefined(); + expect(typeof path.getResourcePath).toEqual('function'); + }); + + describe('commonPrefix', function() { + beforeEach(function() { + spyOn(process, 'cwd').andCallFake(function() { + return os.platform().match(/^win/) ? 'C:\\Users\\jsdoc' : '/Users/jsdoc'; + }); + }); + + it('finds the correct prefix for a group of relative paths', function() { + var cwd = process.cwd().split(path.sep); + var paths = [ + path.join('foo', 'bar', 'baz', 'qux.js'), + path.join('foo', 'bar', 'baz', 'quux.js'), + path.join('foo', 'bar', 'baz.js') + ]; + // we expect a trailing slash + var expected = cwd.concat('foo', 'bar', '').join(path.sep); + + expect( path.commonPrefix(paths) ).toEqual(expected); + }); + + it('finds the correct prefix for a group of absolute paths', function() { + var cwd = process.cwd().split(path.sep); + var paths = [ + cwd.concat('foo', 'bar', 'baz', 'qux.js').join(path.sep), + cwd.concat('foo', 'bar', 'baz', 'quux.js').join(path.sep), + cwd.concat('foo', 'bar', 'baz.js').join(path.sep) + ]; + // we expect a trailing slash + var expected = cwd.concat('foo', 'bar', '').join(path.sep); + + expect( path.commonPrefix(paths) ).toEqual(expected); + }); + + it('finds the correct prefix for a group of absolute paths and dotted relative paths', + function() { + var cwd = process.cwd().split(path.sep); + var paths = [ + path.join('..', 'jsdoc', 'foo', 'bar', 'baz', 'qux', 'quux', 'test.js'), + cwd.concat('foo', 'bar', 'bazzy.js').join(path.sep), + path.join('..', '..', 'Users', 'jsdoc', 'foo', 'bar', 'foobar.js') + ]; + // we expect a trailing slash + var expected = cwd.concat('foo', 'bar', '').join(path.sep); + + expect( path.commonPrefix(paths) ).toEqual(expected); + }); + + it('returns an empty string when there is no common prefix', function() { + // skip on Windows, since the paths share a drive letter at the start + if ( !os.platform().match(/^win/) ) { + var paths = [ + path.join('foo', 'bar', 'baz', 'qux.js'), + path.join('..', '..', 'Library', 'foo', 'bar', 'baz.js') + ]; + + expect( path.commonPrefix(paths) ).toEqual(''); + } + }); + }); + + xdescribe('getResourcePath', function() { + // TODO + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/plugins.js b/data/scripts/jsdoc/test/specs/jsdoc/plugins.js new file mode 100644 index 00000000..2db3dd19 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/plugins.js @@ -0,0 +1,20 @@ +/*global describe: true, expect: true, it: true, xdescribe: true */ + +describe("jsdoc/plugins", function() { + var plugins = require('jsdoc/plugins'); + + it("should exist", function() { + expect(plugins).toBeDefined(); + expect(typeof plugins).toEqual('object'); + }); + + it("should export an 'installPlugins' function", function() { + expect(plugins.installPlugins).toBeDefined(); + expect(typeof plugins.installPlugins).toEqual('function'); + }); + + + xdescribe("installPlugins", function() { + // TODO + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/readme.js b/data/scripts/jsdoc/test/specs/jsdoc/readme.js new file mode 100644 index 00000000..51af2f75 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/readme.js @@ -0,0 +1,14 @@ +describe("jsdoc/readme", function() { + var jsdoc = {readme: require('jsdoc/readme') }, + html = (new jsdoc.readme('test/fixtures/markdowntest.md')).html; + + it("should parse html out of markdown", function() { + expect(html).toBeDefined(); + expect(typeof html).toEqual("string"); + expect(html).toContain(''); + expect(html).toContain('

      '); + expect(html).toContain('

      '); + expect(html).toContain('

    • '); + }); + +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/src/filter.js b/data/scripts/jsdoc/test/specs/jsdoc/src/filter.js new file mode 100644 index 00000000..c34d0aae --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/src/filter.js @@ -0,0 +1,20 @@ +/*global describe: true, expect: true, it: true */ +describe("jsdoc/src/filter", function() { + var filter = new (require('jsdoc/src/filter').Filter)({ + includePattern: new RegExp(".+\\.js(doc)?$"), + excludePattern: new RegExp("(^|\\/)_"), + exclude: ['.ignore', 'scratch/conf.js'] + }); + + var files = ['yes.js', '/yes.jsdoc', '/_nope.js', '.ignore', process.cwd() + '/scratch/conf.js']; + + files = files.filter(function($) { + return filter.isIncluded($); + }); + + it("should return the correct source files", function() { + expect(files.length).toEqual(2); + expect(files.indexOf("yes.js")).toBeGreaterThan(-1); + expect(files.indexOf("/yes.jsdoc")).toBeGreaterThan(-1); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/src/handlers.js b/data/scripts/jsdoc/test/specs/jsdoc/src/handlers.js new file mode 100644 index 00000000..f43ac711 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/src/handlers.js @@ -0,0 +1,55 @@ +/*global describe: true, expect: true, it: true */ +describe("jsdoc/src/handlers", function() { + var jsdoc = {src: { parser: require('jsdoc/src/parser')}}, + testParser = new jsdoc.src.parser.Parser(), + handlers = require('jsdoc/src/handlers'); + + handlers.attachTo(testParser); + + it("should exist", function() { + expect(handlers).toBeDefined(); + expect(typeof handlers).toEqual("object"); + }); + + it("should export an 'attachTo' function", function() { + expect(handlers.attachTo).toBeDefined(); + expect(typeof handlers.attachTo).toEqual("function"); + }); + + describe("attachTo", function() { + it("should attach a 'jsdocCommentFound' handler to the parser", function() { + var callbacks = testParser.listeners("jsdocCommentFound"); + expect(callbacks).toBeDefined(); + expect(callbacks.length).toEqual(1); + expect(typeof callbacks[0]).toEqual("function"); + }); + + it("should attach a 'symbolFound' handler to the parser", function() { + var callbacks = testParser.listeners("symbolFound"); + expect(callbacks).toBeDefined(); + expect(callbacks.length).toEqual(1); + expect(typeof callbacks[0]).toEqual("function"); + }); + + it("should attach a 'fileComplete' handler to the parser", function() { + var callbacks = testParser.listeners("fileComplete"); + expect(callbacks).toBeDefined(); + expect(callbacks.length).toEqual(1); + expect(typeof callbacks[0]).toEqual("function"); + }); + }); + + describe("jsdocCommentFound handler", function() { + var sourceCode = 'javascript:/** @name bar */', + result = testParser.parse(sourceCode); + + it("should create a doclet for comments with '@name' tags", function() { + expect(result.length).toEqual(1); + expect(result[0].name).toEqual('bar'); + }); + }); + + describe("symbolFound handler", function() { + //TODO + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/src/parser.js b/data/scripts/jsdoc/test/specs/jsdoc/src/parser.js new file mode 100644 index 00000000..d1794dee --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/src/parser.js @@ -0,0 +1,123 @@ +/*global beforeEach: true, describe: true, expect: true, it: true, jasmine: true, xdescribe: true, xit: true */ +describe("jsdoc/src/parser", function() { + var jsdoc = {src: { parser: require('jsdoc/src/parser')}}; + + it("should exist", function() { + expect(jsdoc.src.parser).toBeDefined(); + expect(typeof jsdoc.src.parser).toEqual("object"); + }); + + it("should export a 'Parser' constructor", function() { + expect(jsdoc.src.parser.Parser).toBeDefined(); + expect(typeof jsdoc.src.parser.Parser).toEqual("function"); + }); + + describe("Parser", function() { + var parser; + + function newParser() { + parser = new jsdoc.src.parser.Parser(); + } + + it("should have a 'parse' function", function() { + expect(jsdoc.src.parser.Parser.prototype.parse).toBeDefined(); + expect(typeof jsdoc.src.parser.Parser.prototype.parse).toEqual("function"); + }); + + it("should have a 'results' function", function() { + expect(jsdoc.src.parser.Parser.prototype.results).toBeDefined(); + expect(typeof jsdoc.src.parser.Parser.prototype.results).toEqual("function"); + }); + + describe("parse", function() { + beforeEach(newParser); + + it("should fire 'parseBegin' events before it parses any files", function() { + var spy = jasmine.createSpy(), + sourceFiles = ["javascript:/** @name foo */"]; + + parser.on("parseBegin", spy).parse(sourceFiles); + expect(spy).toHaveBeenCalled(); + expect(spy.mostRecentCall.args[0].sourcefiles).toBe(sourceFiles); + }); + + it("should allow 'parseBegin' handlers to modify the list of source files", function() { + var sourceCode = "javascript:/** @name foo */", + newFiles = ["[[replaced]]"], + evt; + + function handler(e) { + e.sourcefiles = newFiles; + evt = e; + } + + parser.on('parseBegin', handler).parse(sourceCode); + expect(evt.sourcefiles).toBe(newFiles); + }); + + it("should fire 'jsdocCommentFound' events when parsing source containing jsdoc comments", function() { + var spy = jasmine.createSpy(), + sourceCode = ['javascript:/** @name bar */']; + parser.on('jsdocCommentFound', spy).parse(sourceCode); + expect(spy).toHaveBeenCalled(); + expect(spy.mostRecentCall.args[0].comment).toEqual("/** @name bar */"); + }); + + it("should fire 'symbolFound' events when parsing source containing named symbols", function() { + var spy = jasmine.createSpy(), + sourceCode = 'javascript:var foo = 1'; + parser.on('symbolFound', spy).parse(sourceCode); + expect(spy).toHaveBeenCalled(); + }); + + it("should fire 'parseComplete' events after it finishes parsing files", function() { + var spy = jasmine.createSpy(), + sourceCode = ['javascript:var bar = false;']; + parser.on('parseComplete', spy).parse(sourceCode); + expect(spy).toHaveBeenCalled(); + expect(spy.mostRecentCall.args[0].sourcefiles).toEqual(["[[string0]]"]); + }); + + it("should be able to parse its own source file", function() { + var fs = require('jsdoc/fs'), + path = require('path'), + parserSrc = 'javascript:' + fs.readFileSync( path.join(__dirname, + 'lib', 'jsdoc', 'src', 'parser.js'), 'utf8' ), + parse = function() { + parser.parse(parserSrc); + }; + + expect(parse).not.toThrow(); + }); + }); + + describe("results", function() { + beforeEach(newParser); + + xit("contains an empty array before files are parsed", function() { + // TODO + }); + + xit("contains an array of doclets after files are parsed", function() { + + }); + + it("should reflect comment changes made by 'jsdocCommentFound' handlers", function() { + // we test both POSIX and Windows line endings + var source = "javascript:/**\n * replaceme\r\n * @module foo\n */\n\n" + + "/**\n * replaceme\n */\nvar bar;"; + + parser.on('jsdocCommentFound', function(e) { + e.comment = e.comment.replace('replaceme', 'REPLACED!'); + }); + require("jsdoc/src/handlers").attachTo(parser); + + parser.parse(source); + parser.results().forEach(function(doclet) { + expect(doclet.comment).not.toMatch('replaceme'); + expect(doclet.comment).toMatch('REPLACED!'); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/src/scanner.js b/data/scripts/jsdoc/test/specs/jsdoc/src/scanner.js new file mode 100644 index 00000000..3203c2aa --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/src/scanner.js @@ -0,0 +1,21 @@ +/*global describe: true, env: true, expect: true, it: true */ +describe("jsdoc/src/scanner", function() { + var scanner = new (require('jsdoc/src/scanner').Scanner)(), + filter = new (require('jsdoc/src/filter').Filter)({ + includePattern: new RegExp(".+\\.js(doc)?$"), + excludePattern: new RegExp("(^|\\/|\\\\)_") + }), + path = require('path'), + sourceFiles = scanner.scan([path.join(__dirname, 'test', 'fixtures', 'src')], 3, filter); + + sourceFiles = sourceFiles.map(function($) { + return path.relative(__dirname, $); + }); + + it("should return the correct source files", function() { + expect(sourceFiles.length).toEqual(3); + expect(sourceFiles.indexOf(path.join('test', 'fixtures', 'src', 'one.js'))).toBeGreaterThan(-1); + expect(sourceFiles.indexOf(path.join('test', 'fixtures', 'src', 'two.js'))).toBeGreaterThan(-1); + expect(sourceFiles.indexOf(path.join('test', 'fixtures', 'src', 'dir1', 'three.js'))).toBeGreaterThan(-1); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tag.js b/data/scripts/jsdoc/test/specs/jsdoc/tag.js new file mode 100644 index 00000000..a324a9c3 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tag.js @@ -0,0 +1,162 @@ +/*global describe: true, env: true, it: true */ +describe("jsdoc/tag", function() { + var jsdoc = { + tag: require('jsdoc/tag'), + dictionary: require('jsdoc/tag/dictionary'), + type: require('jsdoc/tag/type') + }; + + it('should exist', function() { + expect(jsdoc.tag).toBeDefined(); + expect(typeof jsdoc.tag).toBe('object'); + }); + + it('should export a Tag function', function() { + expect(jsdoc.tag.Tag).toBeDefined(); + expect(typeof jsdoc.tag.Tag).toBe('function'); + }); + + describe('Tag', function() { + var meta = {lineno: 1, filename: 'asdf.js'}, + desc = 'lalblakd lkjasdlib\n lija', + text = '{!number} [foo=1] - ' + desc, + textEg = 'Asdf\n' + + ' * myFunction(1, 2); // returns 3\n' + + ' * myFunction(3, 4); // returns 7\n'; + var tagArg = new jsdoc.tag.Tag('arg ', text, meta), // <-- a symonym of param, space in the title. + tagParam = new jsdoc.tag.Tag('param', '[foo=1]', meta), // no type, but has optional and defaultvalue. + tagEg = new jsdoc.tag.Tag('example', textEg, meta), // <-- for keepsWhitespace + tagType = new jsdoc.tag.Tag('type', 'MyType ', meta); // <-- for onTagText + + it("should have a 'originalTitle' property, a string", function() { + expect(tagArg.originalTitle).toBeDefined(); + expect(typeof tagArg.originalTitle).toBe('string'); + }); + + it("'originalTitle' property should be the initial tag title, trimmed of whitespace", function() { + expect(tagArg.originalTitle).toBe('arg'); + expect(tagEg.originalTitle).toBe('example'); + }); + + it("should have a 'title' property, a string", function() { + expect(tagArg.title).toBeDefined(); + expect(typeof tagArg.title).toBe('string'); + }); + + it("'title' property should be the normalised tag title", function() { + expect(tagArg.title).toBe(jsdoc.dictionary.normalise(tagArg.originalTitle)); + expect(tagEg.title).toBe(jsdoc.dictionary.normalise(tagEg.originalTitle)); + }); + + it("should have a 'text' property. a string", function () { + expect(tagArg.text).toBeDefined(); + expect(typeof tagArg.text).toBe('string'); + }); + + it("should have a 'value' property", function () { + expect(tagArg.value).toBeDefined(); + expect(tagEg.value).toBeDefined(); + expect(tagType.value).toBeDefined(); + }); + + describe("'text' property", function() { + it("'text' property should be the trimmed tag text, with all leading and trailing space removed unless tagDef.keepsWhitespace", function() { + // @example has keepsWhitespace, @param doesn't. + // should realy use module:jsdoc/tag~trim here but it's private. + expect(tagArg.text).toBe(text.replace(/^\s+|\s+$/g, '')); + expect(tagEg.text).toBe(textEg.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '')); + }); + + it("'text' property should have onTagText run on it if it has it.", function() { + var def = jsdoc.dictionary.lookUp('type'); + expect(def.onTagText).toBeDefined(); + expect(typeof def.onTagText).toBe('function'); + + // @type adds {} around the type if necessary. + expect(tagType.text).toBeDefined(); + expect(tagType.text).toBe(def.onTagText('MyType')); + }); + }); + + describe("'value' property", function() { + it("'value' property should equal tag text if tagDef.canHaveType and canHaveName are both false", function() { + // @example can't have type or name + expect(typeof tagEg.value).toBe('string'); + expect(tagEg.value).toBe(tagEg.text); + }); + + it("'value' property should be an object if tagDef can have type or name", function () { + expect(typeof tagType.value).toBe('object'); + expect(typeof tagArg.value).toBe('object'); + }); + + function verifyTagType(tag) { + var def = jsdoc.dictionary.lookUp(tag.title); + expect(def).not.toBe(false); + var info = jsdoc.type.parse(tag.text, def.canHaveName, def.canHaveType); + + var props_that_should_be_copied = ['optional', 'nullable', 'variable', 'defaultvalue']; + for (var i = 0; i < props_that_should_be_copied.length; ++i) { + var prop = props_that_should_be_copied[i]; + if (info.hasOwnProperty(prop)) { + expect(tag.value[prop]).toBe(info[prop]); + } + } + if (info.type && info.type.length) { + expect(tag.value.type).toBeDefined(); + expect(typeof tag.value.type).toBe('object'); + expect(tag.value.type.names).toBeDefined(); + expect(tag.value.type.names).toEqual(info.type); + } + } + it("if the tag has a type, tag.value should contain the type information", function() { + // we assume jsdoc/tag/type.parse works (it has its own tests to verify this); + verifyTagType(tagType); + verifyTagType(tagArg); + verifyTagType(tagParam); + }); + + it("if the tag has a description beyond the name/type, this should be in tag.value.description", function() { + expect(tagType.value.description).not.toBeDefined(); + + expect(tagArg.value.description).toBeDefined(); + expect(tagArg.value.description).toBe(desc); + }); + + it("if the tag can have a name, it should be stored in tag.value.name", function() { + expect(tagArg.value.name).toBeDefined(); + expect(tagArg.value.name).toBe('foo'); + + expect(tagType.value.name).not.toBeDefined(); + }); + }); + + // further tests for this sort of thing are in jsdoc/tag/validator.js tests. + describe("tag validating", function() { + /*jshint evil: true */ + var lenient = !!env.opts.lenient; + + function badTag() { + var tag = new jsdoc.tag.Tag("name"); + return tag; + } + + afterEach(function() { + env.opts.lenient = lenient; + }); + + it("throws an exception for bad tags if the lenient option is not enabled", function() { + env.opts.lenient = false; + + expect(badTag).toThrow(); + }); + + it("doesn't throw an exception for bad tags if the lenient option is enabled", function() { + spyOn(console, 'log'); + env.opts.lenient = true; + + expect(badTag).not.toThrow(); + }); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tag/dictionary.js b/data/scripts/jsdoc/test/specs/jsdoc/tag/dictionary.js new file mode 100644 index 00000000..b6e0544c --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tag/dictionary.js @@ -0,0 +1,98 @@ +describe('jsdoc/tag/dictionary', function() { + var dictionary = require('jsdoc/tag/dictionary'); + + it('should exist', function() { + expect(dictionary).toBeDefined(); + expect(typeof dictionary).toBe('object'); + }); + + it('should export a defineTag function', function() { + expect(dictionary.defineTag).toBeDefined(); + expect(typeof dictionary.defineTag).toBe('function'); + }); + + it('should export a lookUp function', function() { + expect(dictionary.lookUp).toBeDefined(); + expect(typeof dictionary.lookUp).toBe('function'); + }); + + it('should export a isNamespace function', function() { + expect(dictionary.isNamespace).toBeDefined(); + expect(typeof dictionary.isNamespace).toBe('function'); + }); + + it('should export a normalise function', function() { + expect(dictionary.normalise).toBeDefined(); + expect(typeof dictionary.normalise).toBe('function'); + }); + + // TODO: should really remove this tag from the dictionary after, but how? + var tagOptions = { + canHaveValue: true, + isNamespace: true + }, + tagTitle = 'testTag', + tagSynonym = 'testTag2'; + var def = dictionary.defineTag(tagTitle, tagOptions).synonym(tagSynonym); + // Should really test TagDefinition but they are private. + // Instead, we'll just tests all the properties we expect of it. + describe("defineTag", function() { + + // Since TagDefinition is private, I'll just test for its properties here. + it("returns an object with 'title' property", function() { + expect(typeof def).toBe('object'); + // how to test? + expect(def.title).toBeDefined(); + expect(typeof def.title).toBe('string'); + expect(def.title).toBe(dictionary.normalise(tagTitle)); + }); + + it("returned object has all the tag properties copied over", function() { + for (var prop in tagOptions) { + if (tagOptions.hasOwnProperty(prop)) { + expect(def[prop]).toBe(tagOptions[prop]); + } + } + }); + }); + + describe("lookUp", function() { + it("retrieves definition when using the tag's canonical name", function() { + expect(dictionary.lookUp(tagTitle)).toBe(def); + }); + + it("retrieves definition when using a synonym", function() { + expect(dictionary.lookUp(tagSynonym)).toBe(def); + }); + + it("returns FALSE when a tag is not found", function() { + expect(dictionary.lookUp('lkjas1l24jk')).toBe(false); + }); + }); + + describe("isNamespace", function() { + it("returns whether a tag is a namespace when using its canonical name", function() { + expect(dictionary.isNamespace(tagTitle)).toBe(true); + }); + + it("returns whether a tag is a namespace when using its synonym", function() { + expect(dictionary.isNamespace(tagSynonym)).toBe(true); + }); + + it("non-existent tags or non-namespace tags should return false", function() { + expect(dictionary.isNamespace('see')).toBe(false); + expect(dictionary.isNamespace('lkjasd90034')).toBe(false); + }); + }); + + describe("normalise", function() { + it("should return the tag's title if it is not a synonym", function() { + expect(dictionary.normalise('FooBar')).toBe('foobar'); + expect(dictionary.normalise(tagTitle)).toBe(def.title); + }); + + it("should return the canonical name of a tag if the synonym is normalised", function() { + expect(dictionary.normalise(tagSynonym)).toBe(def.title); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tag/dictionary/definitions.js b/data/scripts/jsdoc/test/specs/jsdoc/tag/dictionary/definitions.js new file mode 100644 index 00000000..4abae895 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tag/dictionary/definitions.js @@ -0,0 +1,15 @@ +describe('jsdoc/tag/dictionary/definitions', function() { + var type = require('jsdoc/tag/dictionary/definitions'); + + it('should exist', function() { + expect(type).toBeDefined(); + expect(typeof type).toBe('object'); + }); + + it('should export a defineTags function', function() { + expect(type.defineTags).toBeDefined(); + expect(typeof type.defineTags).toBe('function'); + }); + // whole bit of dictionary.defineTags...but it just calls dictionary.defineTag + // and if I validate that then the rest is automatically validated? +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tag/inline.js b/data/scripts/jsdoc/test/specs/jsdoc/tag/inline.js new file mode 100644 index 00000000..de206a76 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tag/inline.js @@ -0,0 +1,211 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ + +describe('jsdoc/tag/inline', function() { + var jsdoc = { + tag: { + inline: require('jsdoc/tag/inline') + } + }; + + it('should exist', function() { + expect(jsdoc.tag.inline).toBeDefined(); + expect(typeof jsdoc.tag.inline).toBe('object'); + }); + + it('should export a replaceInlineTag function', function() { + expect(jsdoc.tag.inline.replaceInlineTag).toBeDefined(); + expect(typeof jsdoc.tag.inline.replaceInlineTag).toBe('function'); + }); + + it('should export an extractInlineTag function', function() { + expect(jsdoc.tag.inline.extractInlineTag).toBeDefined(); + expect(typeof jsdoc.tag.inline.replaceInlineTag).toBe('function'); + }); + + describe('replaceInlineTag', function() { + it('should throw if the tag is matched and the replacer is invalid', function() { + function badReplacerUndefined() { + jsdoc.tag.inline.replaceInlineTag('{@foo tag}', 'foo'); + } + + function badReplacerString() { + jsdoc.tag.inline.replaceInlineTag('{@foo tag}', 'foo', 'hello'); + } + + expect(badReplacerUndefined).toThrow(); + expect(badReplacerString).toThrow(); + }); + + it('should not find anything if there is no text in braces', function() { + var replacer = jasmine.createSpy('replacer'); + var result = jsdoc.tag.inline.replaceInlineTag('braceless text', 'foo', replacer); + expect(replacer).not.toHaveBeenCalled(); + }); + + it('should cope with bad escapement at the end of the string', function() { + var replacer = jasmine.createSpy('replacer'); + var result = jsdoc.tag.inline.replaceInlineTag('bad {@foo escapement \\', 'foo', + replacer); + expect(replacer).not.toHaveBeenCalled(); + }); + + it('should work if the tag is the entire string', function() { + function replacer(string, tagInfo) { + expect(string).toBe('{@foo text in braces}'); + expect(tagInfo.completeTag).toBe('{@foo text in braces}'); + expect(tagInfo.text).toBe('text in braces'); + + return tagInfo.completeTag; + } + + var result = jsdoc.tag.inline.replaceInlineTag('{@foo text in braces}', 'foo', + replacer); + expect(result.tags[0]).toBeDefined(); + expect(typeof result.tags[0]).toBe('object'); + expect(result.tags[0].tag).toBe('foo'); + expect(result.tags[0].text).toBe('text in braces'); + expect(result.newString).toBe('{@foo text in braces}'); + }); + + it('should work if the tag is at the beginning of the string', function() { + function replacer(string, tagInfo) { + expect(string).toBe('{@foo test string} ahoy'); + expect(tagInfo.completeTag).toBe('{@foo test string}'); + expect(tagInfo.text).toBe('test string'); + + return string; + } + + var result = jsdoc.tag.inline.replaceInlineTag('{@foo test string} ahoy', 'foo', + replacer); + expect(result.tags[0]).toBeDefined(); + expect(typeof result.tags[0]).toBe('object'); + expect(result.tags[0].tag).toBe('foo'); + expect(result.tags[0].text).toBe('test string'); + expect(result.newString).toBe('{@foo test string} ahoy'); + }); + + it('should work if the tag is in the middle of the string', function() { + function replacer(string, tagInfo) { + expect(string).toBe('a {@foo test string} yay'); + expect(tagInfo.completeTag).toBe('{@foo test string}'); + expect(tagInfo.text).toBe('test string'); + + return string; + } + + var result = jsdoc.tag.inline.replaceInlineTag('a {@foo test string} yay', 'foo', + replacer); + expect(result.tags[0]).toBeDefined(); + expect(typeof result.tags[0]).toBe('object'); + expect(result.tags[0].tag).toBe('foo'); + expect(result.tags[0].text).toBe('test string'); + expect(result.newString).toBe('a {@foo test string} yay'); + }); + + it('should work if the tag is at the end of the string', function() { + function replacer(string, tagInfo) { + expect(string).toBe('a {@foo test string}'); + expect(tagInfo.completeTag).toBe('{@foo test string}'); + expect(tagInfo.text).toBe('test string'); + + return string; + } + + var result = jsdoc.tag.inline.replaceInlineTag('a {@foo test string}', 'foo', replacer); + expect(result.tags[0]).toBeDefined(); + expect(typeof result.tags[0]).toBe('object'); + expect(result.tags[0].tag).toBe('foo'); + expect(result.tags[0].text).toBe('test string'); + expect(result.newString).toBe('a {@foo test string}'); + }); + + it('should replace the string with the specified value', function() { + function replacer() { + return 'REPLACED!'; + } + + var result = jsdoc.tag.inline.replaceInlineTag('a {@foo test string}', 'foo', replacer); + expect(result.newString).toBe('REPLACED!'); + }); + + it('should process all occurrences of a tag', function() { + function replacer(string, tagInfo) { + return string.replace(tagInfo.completeTag, 'stuff'); + } + + var result = jsdoc.tag.inline.replaceInlineTag('some {@foo text} with multiple ' + + '{@foo tags}', 'foo', replacer); + + expect(result.tags[0]).toBeDefined(); + expect(typeof result.tags[0]).toBe('object'); + expect(result.tags[0].tag).toBe('foo'); + expect(result.tags[0].text).toBe('text'); + + expect(result.tags[1]).toBeDefined(); + expect(typeof result.tags[1]).toBe('object'); + expect(result.tags[1].tag).toBe('foo'); + expect(result.tags[1].text).toBe('tags'); + + expect(result.newString).toBe('some stuff with multiple stuff'); + }); + + }); + + // largely covered by the replaceInlineTag tests + describe('replaceInlineTags', function() { + it('should work with an empty replacer object', function() { + var replacers = {}; + var text = 'some {@foo text} to parse'; + + var result = jsdoc.tag.inline.replaceInlineTags(text, replacers); + expect(result.newString).toBe(text); + }); + + it('should work with an object with one replacer', function() { + var text = 'some {@foo text} with {@bar multiple} tags'; + var replacers = { + foo: function(string, tagInfo) { + expect(tagInfo.completeTag).toBe('{@foo text}'); + expect(tagInfo.text).toBe('text'); + return string.replace(tagInfo.completeTag, 'stuff'); + } + }; + + var result = jsdoc.tag.inline.replaceInlineTags(text, replacers); + expect(result.newString).toBe('some stuff with {@bar multiple} tags'); + + }); + + it('should work with an object with multiple replacers', function() { + var text = 'some {@foo text} with {@bar multiple} tags'; + var replacers = { + foo: function(string, tagInfo) { + expect(tagInfo.completeTag).toBe('{@foo text}'); + expect(tagInfo.text).toBe('text'); + return string.replace(tagInfo.completeTag, 'stuff'); + }, + bar: function(string, tagInfo) { + expect(tagInfo.completeTag).toBe('{@bar multiple}'); + expect(tagInfo.text).toBe('multiple'); + return string.replace(tagInfo.completeTag, 'awesome'); + } + }; + + var result = jsdoc.tag.inline.replaceInlineTags(text, replacers); + expect(result.newString).toBe('some stuff with awesome tags'); + }); + }); + + // largely covered by the replaceInlineTag tests + describe('extractInlineTag', function() { + it('should work when a tag is specified', function() { + var result = jsdoc.tag.inline.extractInlineTag('some {@tagged text}', 'tagged'); + expect(result.tags[0]).toBeDefined(); + expect(typeof result.tags[0]).toBe('object'); + expect(result.tags[0].tag).toBe('tagged'); + expect(result.tags[0].text).toBe('text'); + expect(result.newString).toBe('some'); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tag/type.js b/data/scripts/jsdoc/test/specs/jsdoc/tag/type.js new file mode 100644 index 00000000..dd9105f3 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tag/type.js @@ -0,0 +1,247 @@ +/*global describe: true, expect: true, it: true */ + +function buildText(type, name, desc) { + var text = ''; + if (type) { + text += '{' + type + '}'; + if (name || desc) { + text += ' '; + } + } + + if (name) { + text += name; + if (desc) { + text += ' '; + } + } + + if (desc) { + text += desc; + } + + return text; +} + +describe('jsdoc/tag/type', function() { + var jsdoc = { + tag: { + type: require('jsdoc/tag/type') + } + }; + + it('should exist', function() { + expect(jsdoc.tag.type).toBeDefined(); + expect(typeof jsdoc.tag.type).toBe('object'); + }); + + it('should export a parse function', function() { + expect(jsdoc.tag.type.parse).toBeDefined(); + expect(typeof jsdoc.tag.type.parse).toBe('function'); + }); + + describe('parse', function() { + it('should return an object with name, type, and text properties', function() { + var info = jsdoc.tag.type.parse(''); + expect(info.name).toBeDefined(); + expect(info.type).toBeDefined(); + expect(info.text).toBeDefined(); + }); + + it('should not extract a name or type if canHaveName and canHaveType are not set', function() { + var desc = '{number} foo The foo parameter.'; + var info = jsdoc.tag.type.parse(desc); + expect(info.type).toEqual([]); + expect(info.name).toBe(''); + expect(info.text).toBe(desc); + }); + + it('should extract a name, but not a type, if canHaveName === true and canHaveType === false', function() { + var name = 'bar'; + var desc = 'The bar parameter.'; + var info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false ); + expect(info.type).toEqual([]); + expect(info.name).toBe(name); + expect(info.text).toBe(desc); + }); + + it('should extract a type, but not a name, if canHaveName === false and canHaveType === true', function() { + var type = 'boolean'; + var desc = 'Set to true on alternate Thursdays.'; + var info = jsdoc.tag.type.parse( buildText(type, null, desc), false, true ); + expect(info.type).toEqual([type]); + expect(info.name).toBe(''); + expect(info.text).toBe(desc); + }); + + it('should extract a name and type if canHaveName and canHaveType are true', function() { + var type = 'string'; + var name = 'baz'; + var desc = 'The baz parameter.'; + var info = jsdoc.tag.type.parse( buildText(type, name, desc), true, true ); + expect(info.type).toEqual([type]); + expect(info.name).toBe(name); + expect(info.text).toBe(desc); + }); + + it('should report optional types correctly no matter which syntax we use', function() { + var desc = '{string} [foo]'; + var info = jsdoc.tag.type.parse(desc, true, true); + expect(info.optional).toBe(true); + + desc = '{string=} [foo]'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.optional).toBe(true); + + desc = '[foo]'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.optional).toBe(true); + }); + + it('should return the types as an array', function() { + var desc = '{string} foo'; + var info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['string'] ); + }); + + it('should recognize the entire list of possible types', function() { + var desc = '{(string|number)} foo'; + var info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['string', 'number'] ); + + desc = '{ ( string | number ) } foo'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['string', 'number'] ); + + desc = '{ ( string | number)} foo'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['string', 'number'] ); + + desc = '{(string|number|boolean|function)} foo'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['string', 'number', 'boolean', 'function'] ); + }); + + it('should not find any type if there is no text in braces', function() { + var desc = 'braceless text'; + var info = jsdoc.tag.type.parse(desc, false, true); + expect(info.type).toEqual([]); + }); + + it('should cope with bad escapement at the end of the string', function() { + var desc = 'bad {escapement \\'; + var info = jsdoc.tag.type.parse(desc, false, true); + expect(info.type).toEqual([]); + expect(info.text).toBe(desc); + }); + + it('should handle escaped braces correctly', function() { + var desc = '{weirdObject."with\\}AnnoyingProperty"}'; + var info = jsdoc.tag.type.parse(desc, false, true); + expect(info.type[0]).toBe('weirdObject."with}AnnoyingProperty"'); + }); + + it('should work if the type expression is the entire string', function() { + var desc = '{textInBraces}'; + var info = jsdoc.tag.type.parse(desc, false, true); + expect(info.type[0]).toBe('textInBraces'); + }); + + it('should work if the type expression is at the beginning of the string', function() { + var desc = '{testString} ahoy'; + var info = jsdoc.tag.type.parse(desc, false, true); + expect(info.type[0]).toBe('testString'); + expect(info.text).toBe('ahoy'); + }); + + it('should work if the type expression is in the middle of the string', function() { + var desc = 'a {testString} yay'; + var info = jsdoc.tag.type.parse(desc, false, true); + expect(info.type[0]).toBe('testString'); + expect(info.text).toBe('a yay'); + }); + + it('should work if the tag is at the end of the string', function() { + var desc = 'a {testString}'; + var info = jsdoc.tag.type.parse(desc, false, true); + expect(info.type[0]).toBe('testString'); + expect(info.text).toBe('a'); + }); + + it('should work when there are nested braces', function() { + var desc = 'some {{double}} braces'; + var info = jsdoc.tag.type.parse(desc, false, true); + // we currently stringify all record types as 'Object' + expect(info.type[0]).toBe('Object'); + expect(info.text).toBe('some braces'); + }); + + it('should override the type expression if an inline @type tag is specified', function() { + var desc = '{Object} cookie {@type Monster}'; + var info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['Monster'] ); + expect(info.text).toBe(''); + + desc = '{Object} cookie - {@type Monster}'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['Monster'] ); + expect(info.text).toBe(''); + + desc = '{Object} cookie - The cookie parameter. {@type Monster}'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['Monster'] ); + expect(info.text).toBe('The cookie parameter.'); + + desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)}'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['Monster', 'Jar'] ); + expect(info.text).toBe('The cookie parameter.'); + + desc = '{Object} cookie - The cookie parameter. {@type (Monster|Jar)} Mmm, cookie.'; + info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['Monster', 'Jar'] ); + expect(info.text).toBe('The cookie parameter. Mmm, cookie.'); + }); + + describe('JSDoc-style type info', function() { + it('should parse JSDoc-style optional parameters', function() { + var name = '[qux]'; + var desc = 'The qux parameter.'; + var info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false ); + expect(info.name).toBe('qux'); + expect(info.text).toBe(desc); + expect(info.optional).toBe(true); + + name = '[ qux ]'; + info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false ); + expect(info.name).toBe('qux'); + expect(info.text).toBe(desc); + expect(info.optional).toBe(true); + + name = '[qux=hooray]'; + info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false ); + expect(info.name).toBe('qux'); + expect(info.text).toBe(desc); + expect(info.optional).toBe(true); + expect(info.defaultvalue).toBe('hooray'); + + name = '[ qux = hooray ]'; + info = jsdoc.tag.type.parse( buildText(null, name, desc), true, false ); + expect(info.name).toBe('qux'); + expect(info.text).toBe(desc); + expect(info.optional).toBe(true); + expect(info.defaultvalue).toBe('hooray'); + }); + }); + + // TODO: add more tests related to how JSDoc mangles the Catharsis parse results + describe('Closure Compiler-style type info', function() { + it('should recognize variable (repeatable) parameters', function() { + var desc = '{...string} foo - Foo.'; + var info = jsdoc.tag.type.parse(desc, true, true); + expect(info.type).toEqual( ['string'] ); + expect(info.variable).toBe(true); + }); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tag/validator.js b/data/scripts/jsdoc/test/specs/jsdoc/tag/validator.js new file mode 100644 index 00000000..ec42c4ab --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tag/validator.js @@ -0,0 +1,96 @@ +/*global afterEach: true, beforeEach: true, describe: true, env: true, expect: true, it: true, spyOn: true */ +describe('jsdoc/tag/validator', function() { + var validator = require('jsdoc/tag/validator'), + tag = require('jsdoc/tag'); + + it('should exist', function() { + expect(validator).toBeDefined(); + expect(typeof validator).toBe('object'); + }); + + it('should export a validate function', function() { + expect(validator.validate).toBeDefined(); + expect(typeof validator.validate).toBe('function'); + }); + + // Note: various Error classes are private so we just test whether *any* + // error was thrown, not against particular types (e.g. UnknownTagError). + describe('validate', function() { + var dictionary = require('jsdoc/tag/dictionary'), + lenient = !!env.opts.lenient, + allowUnknown = !!env.conf.tags.allowUnknownTags, + badTag = {title: 'lkjasdlkjfb'}, + meta = {filename: 'asdf.js', lineno: 1}, + goodTag = new tag.Tag('name', 'MyDocletName', meta), // mustHaveValue + goodTag2 = new tag.Tag('ignore', '', meta); // mustNotHaveValue + + function validateTag(tag) { + return function() { validator.validate(tag, dictionary.lookUp(tag.title), meta); }; + } + + beforeEach(function() { + spyOn(console, 'log'); + }); + + afterEach(function() { + env.opts.lenient = lenient; + env.conf.tags.allowUnknownTags = allowUnknown; + }); + + it("throws an error if the tag is not in the dictionary, conf.tags.allowUnknownTags is false and lenient is false", function() { + env.opts.lenient = false; + env.conf.tags.allowUnknownTags = false; + expect(validateTag(badTag)).toThrow(); + }); + + it("throws NO error if the tag is not in the dictionary, conf.tags.allowUnknownTags is false and lenient is true", function() { + env.opts.lenient = true; + env.conf.tags.allowUnknownTags = false; + expect(validateTag(badTag)).not.toThrow(); + }); + + it("doesn't throw an error if the tag is not in the dictionary and conf.tags.allowUnknownTags is true, regardless of lenience", function() { + // if it doesn't throw an error with lenient false, then it won't throw it with lenient true (we have + // tested lenient already in util/error.js) + env.opts.lenient = false; + env.conf.tags.allowUnknownTags = true; + expect(validateTag(badTag)).not.toThrow(); + }); + + it("throws no error for valid tags", function() { + env.opts.lenient = false; + expect(validateTag(goodTag)).not.toThrow(); + expect(validateTag(goodTag2)).not.toThrow(); + }); + + it("throws an error if the tag has no text but .mustHaveValue is true and lenient is false, or none if it's true", function() { + // the name tag has .mustHaveValue. + var oldText = goodTag.text; + delete goodTag.text; + + env.opts.lenient = false; + expect(validateTag(goodTag)).toThrow(); + + env.opts.lenient = true; + expect(validateTag(goodTag)).not.toThrow(); + + goodTag.text = oldText; + }); + + it("throws an error if the tag has text but .mustNotHaveValue is true and lenient is false, or none if it's true", function() { + var oldVal = goodTag2.mustNotHaveValue, + text = goodTag2.text; + goodTag2.mustNotHaveValue = true; + goodTag2.text = goodTag2.text || 'asdf'; + + env.opts.lenient = false; + expect(validateTag(goodTag2)).toThrow(); + + env.opts.lenient = true; + expect(validateTag(goodTag2)).not.toThrow(); + + goodTag2.mustNotHaveValue = oldVal; + goodTag2.text = oldVal; + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tutorial.js b/data/scripts/jsdoc/test/specs/jsdoc/tutorial.js new file mode 100644 index 00000000..caba78e6 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tutorial.js @@ -0,0 +1,226 @@ +/*global describe: true, env: true, it: true */ +describe("jsdoc/tutorial", function() { + var tutorial = require('jsdoc/tutorial'), + name = "tuteID", + content = "Tutorial content blah blah blah & <", + tute = new tutorial.Tutorial(name, content, tutorial.TYPES.NOTAVALUE), + par = new tutorial.Tutorial('parent', "# This is the parent tutorial's content & stuff A_B X_Y", + tutorial.TYPES.MARKDOWN), + par2 = new tutorial.Tutorial('parent2', "

      This is the second parent tutorial

      ", + tutorial.TYPES.HTML); + + + it("module should exist", function() { + expect(tutorial).toBeDefined(); + expect(typeof tutorial).toBe("object"); + }); + + it("should export a Tutorial function", function() { + expect(tutorial.Tutorial).toBeDefined(); + expect(typeof tutorial.Tutorial).toBe("function"); + }); + + it("should export a TYPES object", function() { + expect(tutorial.TYPES).toBeDefined(); + expect(typeof tutorial.TYPES).toBe("object"); + }); + + describe("tutorial.TYPES", function() { + it("should have a HTML property", function() { + expect(tutorial.TYPES.HTML).toBeDefined(); + }); + + it("should have a MARKDOWN property", function() { + expect(tutorial.TYPES.MARKDOWN).toBeDefined(); + }); + }); + + describe("Tutorial", function() { + it("should have 'setParent' function", function() { + expect(tutorial.Tutorial.prototype.setParent).toBeDefined(); + expect(typeof tutorial.Tutorial.prototype.setParent).toBe("function"); + }); + + it("should have 'removeChild' function", function() { + expect(tutorial.Tutorial.prototype.removeChild).toBeDefined(); + expect(typeof tutorial.Tutorial.prototype.removeChild).toBe("function"); + }); + + it("should have 'addChild' function", function() { + expect(tutorial.Tutorial.prototype.addChild).toBeDefined(); + expect(typeof tutorial.Tutorial.prototype.addChild).toBe("function"); + }); + + it("should have 'parse' function", function() { + expect(tutorial.Tutorial.prototype.parse).toBeDefined(); + expect(typeof tutorial.Tutorial.prototype.parse).toBe("function"); + }); + + it("should have a 'name' property", function() { + expect(tute.name).toBeDefined(); + expect(typeof tute.name).toBe("string"); + expect(tute.name).toBe(name); + }); + + it("should have a 'title' property, by default set to to the tute's name", function() { + expect(tute.title).toBeDefined(); + expect(typeof tute.title).toBe("string"); + expect(tute.title).toBe(name); + // Testing of overriding a tutorial's title in its JSON file is + // covered in tutorial/resolver.js tests. + }); + + it("should have a 'content' property set to the tutorial's content", function() { + expect(tute.content).toBeDefined(); + expect(typeof tute.content).toBe("string"); + expect(tute.content).toBe(content); + }); + + it("should have a 'type' property set to the tutorial's type", function() { + expect(par.type).toBeDefined(); + expect(typeof par.type).toBe(typeof tutorial.TYPES.MARKDOWN); + expect(par.type).toBe(tutorial.TYPES.MARKDOWN); + }); + + it("should have a 'parent' property, initially null", function() { + expect(tute.parent).toBeDefined(); + expect(tute.parent).toBe(null); + }); + + it("should have a 'children' property, an empty array", function() { + expect(tute.children).toBeDefined(); + expect(Array.isArray(tute.children)).toBe(true); + expect(tute.children.length).toBe(0); + }); + + describe("setParent", function() { + it("adding a parent sets the child's 'parent' property", function() { + tute.setParent(par); + expect(tute.parent).toBe(par); + }); + + it("adding a parent adds the child to the parent's 'children' property", function() { + expect(par.children).toContain(tute); + }); + + it("re-parenting removes the child from the previous parent", function() { + tute.setParent(par2); + + expect(tute.parent).toBe(par2); + expect(par2.children).toContain(tute); + expect(par.children).not.toContain(tute); + }); + + it("calling setParent with a null parent unsets the child's parent and removes the child from its previous parent", function() { + expect(par2.children).toContain(tute); + tute.setParent(null); + + expect(tute.parent).toBe(null); + expect(par2.children).not.toContain(tute); + }); + }); + + describe("addChild", function() { + it("adding a child tutorial adds the child to the parent's 'children' property", function() { + tute.setParent(null); + var n = par.children.length; + + par.addChild(tute); + + expect(par.children.length).toBe(n + 1); + expect(par.children).toContain(tute); + }); + + it("adding a child tutorial sets the child's parent to to the parent tutorial", function() { + expect(tute.parent).toBe(par); + }); + + it("adding a child tutorial removes the child from its old parent", function() { + // tue is currently owned by par; we reparent it to par2 + expect(tute.parent).toBe(par); + par2.addChild(tute); + + expect(tute.parent).toBe(par2); + expect(par.children).not.toContain(tute); + expect(par2.children).toContain(tute); + }); + }); + + describe("removeChild", function() { + function removeChild() { + par2.removeChild(par); + } + + it("removing a tutorial that is not a child silently passes", function() { + var n = par2.children.length; + expect(removeChild).not.toThrow(); + expect(par2.children.length).toBe(n); + }); + + it("removing a child removes the child from the parent's 'children' property", function() { + tute.setParent(par2); + expect(par2.children.length).toBe(1); + + par2.removeChild(tute); + + expect(par2.children).not.toContain(tute); + expect(par2.children.length).toBe(0); + }); + + it("removing a child unsets the child's 'parent' property", function() { + expect(tute.parent).toBe(null); + }); + }); + + describe("various inheritance tests with addChild, setParent and removeChild", function() { + it("parenting and unparenting via addChild, setParent and removeChild makes sure inheritance is set accordingly", function() { + // unparent everything. + tute.setParent(null); + par.setParent(null); + par2.setParent(null); + + // let tute belong to par + tute.setParent(par); + expect(tute.parent).toBe(par); + expect(par2.children.length).toBe(0); + expect(par.children.length).toBe(1); + expect(par.children[0]).toBe(tute); + + // addChild tute to par2. its parent should now be par2, and + // it can't be the child of two parents + par2.addChild(tute); + expect(tute.parent).toBe(par2); + expect(par.children.length).toBe(0); + expect(par2.children.length).toBe(1); + expect(par2.children[0]).toBe(tute); + + // removeChild tute from par2. tute should now be unparented. + par2.removeChild(tute); + expect(tute.parent).toBe(null); + expect(par.children.length).toBe(0); + expect(par2.children.length).toBe(0); + }); + }); + + describe("parse", function() { + it("Tutorials with HTML type return content as-is", function() { + expect(par2.parse()).toBe("

      This is the second parent tutorial

      "); + }); + + it("Tutorials with MARKDOWN type go through the markdown parser, respecting configuration options", function() { + var old = env.conf.markdown; + env.conf.markdown = {parser: 'evilstreak'}; + expect(par.parse()).toBe("

      This is the parent tutorial's content & stuff AB XY

      "); + + env.conf.markdown = {parser: 'marked'}; + expect(par.parse()).toBe("

      This is the parent tutorial's content & stuff A_B X_Y

      "); + + env.conf.markdown = old; + }); + + it("Tutorials with unrecognised type are returned as-is", function() { + expect(tute.parse()).toBe(content); + }); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/tutorial/resolver.js b/data/scripts/jsdoc/test/specs/jsdoc/tutorial/resolver.js new file mode 100644 index 00000000..5a4eef13 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/tutorial/resolver.js @@ -0,0 +1,231 @@ +/*global afterEach: true, describe: true, env: true, expect: true, it: true */ +describe("jsdoc/tutorial/resolver", function() { + var resolver = require('jsdoc/tutorial/resolver'), + tutorial = require('jsdoc/tutorial'), + lenient = !!env.opts.lenient, + log = eval(console.log); + + /*jshint evil: true */ + it("should exist", function() { + expect(resolver).toBeDefined(); + expect(typeof resolver).toBe('object'); + }); + + it("should export a 'addTutorial' function", function() { + expect(resolver.addTutorial).toBeDefined(); + expect(typeof resolver.addTutorial).toBe("function"); + }); + + it("should export a 'load' function", function() { + expect(resolver.load).toBeDefined(); + expect(typeof resolver.load).toBe("function"); + }); + + it("should export a 'resolve' function", function() { + expect(resolver.resolve).toBeDefined(); + expect(typeof resolver.resolve).toBe("function"); + }); + + it("should export a 'root' tutorial", function() { + expect(resolver.root).toBeDefined(); + expect(resolver.root instanceof tutorial.Tutorial).toBe(true); + }); + + it("exported 'root' tutorial should export a 'getByName' function", function() { + expect(resolver.root.getByName).toBeDefined(); + expect(typeof resolver.root.getByName).toBe("function"); + }); + + // note: every time we addTutorial or run the resolver, we are *adding* + // to the root tutorial. + + // addTutorial + var tute = new tutorial.Tutorial('myTutorial', '', tutorial.TYPES.HTML); + resolver.addTutorial(tute); + describe("addTutorial", function() { + + it("should add a default parent of the root tutorial", function() { + expect(tute.parent).toBe(resolver.root); + }); + + it("should be added to the root tutorial as a child", function() { + expect(resolver.root.children).toContain(tute); + }); + }); + + // root.getByName + describe("root.getByName", function() { + it("can retrieve tutorials by name", function() { + expect(resolver.root.getByName('myTutorial')).toBe(tute); + }); + + it("returns nothing for non-existent tutorials", function() { + expect(resolver.root.getByName('asdf')).toBeFalsy(); + }); + + it("is careful with tutorials whose names are reserved keywords in JS", function() { + expect(resolver.root.getByName('prototype')).toBeFalsy(); + }); + }); + + // load + resolver.load(__dirname + "/test/tutorials/tutorials"); + var childNames = resolver.root.children.map(function (t) { return t.name; }), + test = resolver.root.getByName('test'), + test2 = resolver.root.getByName('test2'), + test3 = resolver.root.getByName('test3'), + test4 = resolver.root.getByName('test4'), + test6 = resolver.root.getByName('test6'), + constr = resolver.root.getByName('constructor'); + + describe("load", function() { + + it("all tutorials are added, initially as top-level tutorials", function() { + // check they were added + expect(test).toBeDefined(); + expect(test2).toBeDefined(); + expect(test3).toBeDefined(); + expect(test4).toBeDefined(); + expect(test6).toBeDefined(); + expect(constr).toBeDefined(); + // check they are top-level in resolver.root + expect(childNames).toContain('test'); + expect(childNames).toContain('test2'); + expect(childNames).toContain('test3'); + expect(childNames).toContain('test4'); + expect(childNames).toContain('test6'); + }); + + it("tutorials with names equal to reserved keywords in JS still function as expected", function() { + expect(constr instanceof tutorial.Tutorial).toBe(true); + }); + + it("non-tutorials are skipped", function() { + expect(resolver.root.getByName('multiple')).toBeFalsy(); + expect(resolver.root.getByName('test5')).toBeFalsy(); + }); + + it("tutorial types are determined correctly", function() { + // test.html, test2.markdown, test3.html, test4.md, test6.xml + expect(test.type).toBe(tutorial.TYPES.HTML); + expect(test2.type).toBe(tutorial.TYPES.MARKDOWN); + expect(test3.type).toBe(tutorial.TYPES.HTML); + expect(test4.type).toBe(tutorial.TYPES.MARKDOWN); + expect(test6.type).toBe(tutorial.TYPES.HTML); + expect(constr.type).toBe(tutorial.TYPES.MARKDOWN); + }); + + }); + + // resolve + // myTutorial + // constructor + // test + // |- test2 + // |- test6 + // |- test3 + // |- test4 + describe("resolve", function() { + resolver.resolve(); + it("hierarchy is resolved properly no matter how the children property is defined", function() { + // root has child 'test' + expect(resolver.root.children.length).toBe(3); + expect(resolver.root.children).toContain(test); + expect(resolver.root.children).toContain(constr); + expect(test.parent).toBe(resolver.root); + expect(constr.parent).toBe(resolver.root); + + // test has child 'test2' + expect(test.children.length).toBe(1); + expect(test.children).toContain(test2); + expect(test2.parent).toBe(test); + + // test2 has children test3, test6 + expect(test2.children.length).toBe(2); + expect(test2.children).toContain(test3); + expect(test2.children).toContain(test6); + expect(test3.parent).toBe(test2); + expect(test6.parent).toBe(test2); + + // test3 has child test4 + expect(test3.children.length).toBe(1); + expect(test3.children).toContain(test4); + expect(test4.parent).toBe(test3); + }); + + it("tutorials without configuration files have titles matching filenames", function() { + // test6.xml didn't have a metadata + expect(test6.title).toBe('test6'); + }); + + it("tutorials with configuration files have titles as specified in configuration", function() { + // test.json had info for just test.json + expect(test.title).toBe("Test tutorial"); + }); + + it("multiple tutorials can appear in a configuration file", function() { + expect(test2.title).toBe("Test 2"); + expect(test3.title).toBe("Test 3"); + expect(test4.title).toBe("Test 4"); + }); + }); + + // error reporting. + describe("Error reporting", function() { + // Tests for error reporting. + function missingTutorial() { + resolver.load(__dirname + "/test/tutorials/incomplete"); + resolver.resolve(); + } + function duplicateNamedTutorials() { + // can't add a tutorial if another with its name has already been added + resolver.addTutorial(tute); + } + function duplicateDefinedTutorials() { + // can't have a tutorial's metadata defined twice in .json files + resolver.load(__dirname + "/test/tutorials/duplicateDefined"); + resolver.resolve(); + } + + afterEach(function() { + env.opts.lenient = lenient; + console.log = log; + }); + + it("throws an exception for missing tutorials if the lenient option is not enabled", function() { + env.opts.lenient = false; + + expect(missingTutorial).toThrow(); + }); + + it("doesn't throw an exception for missing tutorials if the lenient option is enabled", function() { + console.log = function() {}; + env.opts.lenient = true; + + expect(missingTutorial).not.toThrow(); + }); + + it("throws an exception for duplicate-named tutorials (e.g. test.md, test.html) if the lenient option is not enabled", function() { + env.opts.lenient = false; + expect(duplicateNamedTutorials).toThrow(); + }); + + it("doesn't throw an exception for duplicate-named tutorials (e.g. test.md, test.html) if the lenient option is not enabled", function() { + console.log = function() {}; + env.opts.lenient = true; + expect(duplicateNamedTutorials).not.toThrow(); + }); + + it("throws an exception for tutorials defined twice in .jsons if the lenient option is not enabled", function() { + env.opts.lenient = false; + expect(duplicateDefinedTutorials).toThrow(); + }); + + it("doesn't throw an exception for tutorials defined twice in .jsons if the lenient option is not enabled", function() { + console.log = function() {}; + env.opts.lenient = true; + expect(duplicateDefinedTutorials).not.toThrow(); + }); + }); + +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/util/doop.js b/data/scripts/jsdoc/test/specs/jsdoc/util/doop.js new file mode 100644 index 00000000..217303de --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/util/doop.js @@ -0,0 +1,73 @@ +/*global describe: true, it: true */ +describe('jsdoc/util/doop', function() { + var doop = require('jsdoc/util/doop'); + + it('should exist', function() { + expect(doop).toBeDefined(); + expect(typeof doop).toBe('object'); + }); + + it('should export a doop function', function() { + expect(doop.doop).toBeDefined(); + expect(typeof doop.doop).toBe('function'); + }); + + // deep-clones a simple object. + describe('doop', function() { + it("should return the input object if it's simple (boolan, string etc) or a function", function() { + // .toBe uses === to test. + + // test a number... + expect(doop.doop(3)).toBe(3); + // test a string... + expect(doop.doop('asdf')).toBe('asdf'); + // test a boolean... + expect(doop.doop(true)).toBe(true); + // test a function... + var f = function () {}; + expect(doop.doop(f)).toBe(f); + }); + + it("should return a clone of an array", function() { + var inp = [1,2,3], + out = doop.doop(inp); + // toEqual is a comparison on properties; toBe is === comparison. + expect(inp).toEqual(out); + expect(inp).not.toBe(out); + }); + + it("should return a clone of an object", function() { + var inp = {a:1, b:2, 'asdf-fdsa': 3}; + out = doop.doop(inp); + // toEqual is a comparison on properties; toBe is === comparison. + expect(inp).toEqual(out); + expect(inp).not.toBe(out); + }); + + // checks that a === b if it's not an object or array (or it's af function); + // otherwise recurses down into keys and compares them. + function compareForEquality(a, b) { + if (a instanceof Object && a.constructor != Function) { + // if it's an object and not a function, it should clone. + var keys = Object.keys(a); + expect(Object.keys(a)).toEqual(Object.keys(b)); + for (var i = 0; i < keys.length; ++i) { + compareForEquality(a[keys[i]], b[keys[i]]); + } + } else { + // otherwise, it should be exactly equal. + expect(a).toBe(b); + } + } + + it("should clone recursively", function() { + var inp = {a:1, b:2, 'asdf-fdsa': {a: 'fdsa', b: [1,2,3]}}; + out = doop.doop(inp); + // toEqual is a comparison on properties; toBe is === comparison. + expect(inp).toEqual(out); + expect(inp).not.toBe(out); + // double-check + compareForEquality(inp, out); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/util/dumper.js b/data/scripts/jsdoc/test/specs/jsdoc/util/dumper.js new file mode 100644 index 00000000..25e48141 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/util/dumper.js @@ -0,0 +1,103 @@ +/*global describe: true, expect: true, it: true */ +describe("common/dumper", function() { + var common = {dumper: require('jsdoc/util/dumper')}; + + it("should exist", function() { + expect(common.dumper).toBeDefined(); + expect(typeof common.dumper).toEqual("object"); + }); + + it("should export a 'dump' function", function() { + expect(common.dumper.dump).toBeDefined(); + expect(typeof common.dumper.dump).toEqual("function"); + }); + + it("can dump string values", function() { + expect(common.dumper.dump('hello')).toEqual('"hello"'); + }); + + it("escapes double quotes in string values", function() { + expect(common.dumper.dump('hello "world"')).toEqual('"hello \\"world\\""', 'Double quotes should be escaped.'); + }); + + it("escapes newlines in string values", function() { + expect(common.dumper.dump('hello\nworld')).toEqual('"hello\\nworld"', 'Newlines should be escaped.'); + }); + + it("can dump number values", function() { + expect(common.dumper.dump(1)).toEqual('1'); + expect(common.dumper.dump(0.1)).toEqual('0.1'); + }); + + it("can dump boolean values", function() { + expect(common.dumper.dump(true)).toEqual('true'); + expect(common.dumper.dump(false)).toEqual('false'); + }); + + it("can dump null values", function() { + expect(common.dumper.dump(null)).toEqual('null'); + }); + + it("can dump undefined values", function() { + expect(common.dumper.dump(undefined)).toEqual('"undefined"'); + }); + + it("can dump regex values", function() { + expect(common.dumper.dump(/^[Ff]oo$/gi)).toEqual('""'); + }); + + it("can dump date values", function() { + expect(common.dumper.dump(new Date('January 1, 1901 GMT'))) + .toEqual('""'); + }); + + it("can dump function values", function() { + expect(common.dumper.dump(function myFunc(){})).toEqual('""'); + expect(common.dumper.dump(function(){})).toEqual('""'); + }); + + it("can dump array values", function() { + var actual = common.dumper.dump(["hello", "world"]), + expected = '[\n "hello",\n "world"\n]'; + + expect(actual).toEqual(expected); + }); + + it("can dump simple object values", function() { + var actual = common.dumper.dump({hello: "world"}), + expected = '{\n "hello": "world"\n}'; + + expect(actual).toEqual(expected); + }); + + it("can dump constructed instance values, not displaying prototype members", function() { + function Foo(name){ this.name = name; } + Foo.prototype.sayHello = function(){}; + + var actual = common.dumper.dump(new Foo('hello')), + expected = '{\n "name": "hello"\n}'; + + expect(actual).toEqual(expected); + }); + + it("can dump 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 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]'; + + expect(actual).toEqual(expected); + }); + + it("doesn't crash on circular references", function() { + var a = {}; + a.b = a; + + var actual = common.dumper.dump(a), + expected = '{\n "b": ""\n}'; + + expect(actual).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/jsdoc/util/error.js b/data/scripts/jsdoc/test/specs/jsdoc/util/error.js new file mode 100644 index 00000000..4b77e789 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/util/error.js @@ -0,0 +1,53 @@ +/*global describe: true, env: true, it: true */ +describe("jsdoc/util/error", function() { + var error = require('jsdoc/util/error'), + handle = error.handle; + + it("should exist", function() { + expect(error).toBeDefined(); + expect(typeof error).toEqual("object"); + }); + + it("should export a 'handle' function", function() { + expect(handle).toBeDefined(); + expect(typeof handle).toEqual("function"); + }); + + describe("handle", function() { + /*jshint evil: true */ + var lenient = !!env.opts.lenient; + + function handleError() { + handle( new Error("foo") ); + } + + function handleObject() { + handle( { foo: "bar", baz: "qux"} ); + } + + afterEach(function() { + env.opts.lenient = lenient; + }); + + it("should re-throw errors by default", function() { + expect(handleError).toThrow(); + }); + + it("should re-throw errors if lenient mode is not enabled", function() { + env.opts.lenient = false; + + expect(handleError).toThrow(); + }); + + it("should not re-throw errors if lenient mode is enabled", function() { + env.opts.lenient = true; + spyOn(console, 'log'); + + expect(handleError).not.toThrow(); + }); + + it("should still work if the 'e' param is not an instanceof Error", function() { + expect(handleObject).toThrow(); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/util/include.js b/data/scripts/jsdoc/test/specs/jsdoc/util/include.js new file mode 100644 index 00000000..25e28323 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/util/include.js @@ -0,0 +1,36 @@ +/*global afterEach: true, beforeEach: true, describe: true, env: true, expect: true, it: true, +__globalForIncludeTest__: true */ +describe("jsdoc/util/include", function() { + var include = require('jsdoc/util/include'); + var myGlobal = require('jsdoc/util/global'); + var path = require('path'); + + var fixturePath = 'test/fixtures/include.js'; + + beforeEach(function() { + myGlobal.__globalForIncludeTest__ = 0; + }); + + afterEach(function() { + myGlobal.__globalForIncludeTest__ = undefined; + }); + + it("should exist", function() { + expect(include).toBeDefined(); + expect(typeof include).toEqual('function'); + }); + + it("should work with a path relative to __dirname", function() { + include(fixturePath); + expect(__globalForIncludeTest__).toEqual(1); + }); + + // Note: This test also verifies that include() executes the file each time it's passed in, + // rather than executing it once and caching the result, as with require(). + it("should work with an absolute path", function() { + var _path = path.resolve(__dirname, fixturePath); + + include(_path); + expect(__globalForIncludeTest__).toEqual(1); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/util/markdown.js b/data/scripts/jsdoc/test/specs/jsdoc/util/markdown.js new file mode 100644 index 00000000..fad1222b --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/util/markdown.js @@ -0,0 +1,96 @@ +/*global describe: true, env: true, expect: true, it: true, xit: true */ +describe('jsdoc/util/markdown', function() { + var markdown = require('jsdoc/util/markdown'); + + it('should exist', function() { + expect(markdown).toBeDefined(); + expect(typeof markdown).toEqual('object'); + }); + + it('should export a "getParser" function', function() { + expect(markdown.getParser).toBeDefined(); + expect(typeof markdown.getParser).toEqual('function'); + }); + + describe('getParser', function() { + // couple of convenience functions letting me set conf variables and restore + // them back to the originals later. + function setMarkdownConf(hash) { + if (!env.conf.markdown) { + env.conf.markdown = {}; + } + var keys = Object.keys(hash); + var storage = {}; + for (var i = 0; i < keys.length; ++i) { + storage[keys[i]] = env.conf.markdown[keys[i]]; + // works because hash[key] is a scalar not an array/object + env.conf.markdown[keys[i]] = hash[keys[i]]; + } + return storage; + } + + function restoreMarkdownConf(storage) { + var keys = Object.keys(storage); + for (var i = 0; i < keys.length; ++i) { + env.conf.markdown[keys[i]] = storage[keys[i]]; + } + if (keys.length === 0) { + delete env.conf.markdown; + } + } + + it('should retrieve a function when called with default settings', function() { + var storage = setMarkdownConf({parser: 'evilstreak'}); + + var parser = markdown.getParser(); + expect(typeof parser).toEqual('function'); + + setMarkdownConf({parser: 'marked'}); + parser = markdown.getParser(); + expect(typeof parser).toEqual('function'); + + restoreMarkdownConf(storage); + }); + + it('should use the evilstreak parser when requested', function() { + var storage = setMarkdownConf({parser: 'evilstreak'}); + var parser = markdown.getParser(); + expect(parser._parser).toEqual('markdown'); + restoreMarkdownConf(storage); + }); + + it('should use the marked parser when requested', function() { + var storage = setMarkdownConf({parser: 'marked'}); + var parser = markdown.getParser(); + expect(parser._parser).toEqual('marked'); + restoreMarkdownConf(storage); + }); + + it('should use the marked parser when GFM is requested', function() { + var storage = setMarkdownConf({parser: 'gfm'}); + var parser = markdown.getParser(); + expect(parser._parser).toEqual('marked'); + restoreMarkdownConf(storage); + }); + + it('should not apply formatting to inline tags when the evilstreak parser is enabled', function() { + var storage = setMarkdownConf({parser: 'evilstreak'}); + var parser = markdown.getParser(); + + // get the evilstreak parser and do the test + expect(parser('{@link MyClass#_x} and {@link MyClass#_y}')).toEqual( + '

      {@link MyClass#_x} and {@link MyClass#_y}

      '); + restoreMarkdownConf(storage); + }); + + it('should not apply formatting to inline tags when the marked parser is enabled', function() { + var storage = setMarkdownConf({parser: 'marked'}); + var parser = markdown.getParser(); + + // get the marked parser and do the test + expect(parser('{@link MyClass#_x} and {@link MyClass#_y}')).toEqual( + '

      {@link MyClass#_x} and {@link MyClass#_y}

      '); + restoreMarkdownConf(storage); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/util/templateHelper.js b/data/scripts/jsdoc/test/specs/jsdoc/util/templateHelper.js new file mode 100644 index 00000000..3e8e8cdb --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/util/templateHelper.js @@ -0,0 +1,1353 @@ +/*global afterEach: true, beforeEach: true, describe: true, expect: true, env: true, it: true, +jasmine: true, spyOn: true, xdescribe: true */ +var hasOwnProp = Object.prototype.hasOwnProperty; + +describe("jsdoc/util/templateHelper", function() { + var helper = require('jsdoc/util/templateHelper'), + doclet = require('jsdoc/doclet'), + resolver = require('jsdoc/tutorial/resolver'), + taffy = require('taffydb').taffy; + helper.registerLink('test', 'path/to/test.html'); + + it("should exist", function() { + expect(helper).toBeDefined(); + expect(typeof helper).toBe('object'); + }); + + it("should export a 'setTutorials' function", function() { + expect(helper.setTutorials).toBeDefined(); + expect(typeof helper.setTutorials).toBe("function"); + }); + + it("should export a 'globalName' property", function() { + expect(helper.globalName).toBeDefined(); + expect(typeof helper.globalName).toBe("string"); + }); + + it("should export a 'fileExtension' property", function() { + expect(helper.fileExtension).toBeDefined(); + expect(typeof helper.fileExtension).toBe("string"); + }); + + it("should export a 'scopeToPunc' property", function() { + expect(helper.scopeToPunc).toBeDefined(); + expect(typeof helper.scopeToPunc).toBe("object"); + }); + + it("should export a 'getUniqueFilename' function", function() { + expect(helper.getUniqueFilename).toBeDefined(); + expect(typeof helper.getUniqueFilename).toBe("function"); + }); + + it("should export a 'longnameToUrl' property", function() { + expect(helper.longnameToUrl).toBeDefined(); + expect(typeof helper.longnameToUrl).toBe("object"); + }); + + it("should export a 'linkto' function", function() { + expect(helper.linkto).toBeDefined(); + expect(typeof helper.linkto).toBe("function"); + }); + + it("should export an 'htmlsafe' function", function() { + expect(helper.htmlsafe).toBeDefined(); + expect(typeof helper.htmlsafe).toBe("function"); + }); + + it("should export a 'find' function", function() { + expect(helper.find).toBeDefined(); + expect(typeof helper.find).toBe("function"); + }); + + it("should export a 'getMembers' function", function() { + expect(helper.getMembers).toBeDefined(); + expect(typeof helper.getMembers).toBe("function"); + }); + + it("should export a 'getAttribs' function", function() { + expect(helper.getAttribs).toBeDefined(); + expect(typeof helper.getAttribs).toBe("function"); + }); + + it("should export a 'getSignatureTypes' function", function() { + expect(helper.getSignatureTypes).toBeDefined(); + expect(typeof helper.getSignatureTypes).toBe("function"); + }); + + it("should export a 'getSignatureParams' function", function() { + expect(helper.getSignatureParams).toBeDefined(); + expect(typeof helper.getSignatureParams).toBe("function"); + }); + + it("should export a 'getSignatureReturns' function", function() { + expect(helper.getSignatureReturns).toBeDefined(); + expect(typeof helper.getSignatureReturns).toBe("function"); + }); + + it("should export a 'getAncestorLinks' function", function() { + expect(helper.getAncestorLinks).toBeDefined(); + expect(typeof helper.getAncestorLinks).toBe("function"); + }); + + it("should export a 'addEventListeners' function", function() { + expect(helper.addEventListeners).toBeDefined(); + expect(typeof helper.addEventListeners).toBe("function"); + }); + + it("should export a 'prune' function", function() { + expect(helper.prune).toBeDefined(); + expect(typeof helper.prune).toBe("function"); + }); + + it("should export a 'registerLink' function", function() { + expect(helper.registerLink).toBeDefined(); + expect(typeof helper.registerLink).toBe("function"); + }); + + it("should export a 'tutorialToUrl' function", function() { + expect(helper.tutorialToUrl).toBeDefined(); + expect(typeof helper.tutorialToUrl).toBe("function"); + }); + + it("should export a 'toTutorial' function", function() { + expect(helper.toTutorial).toBeDefined(); + expect(typeof helper.toTutorial).toBe("function"); + }); + + it("should export a 'resolveLinks' function", function() { + expect(helper.resolveLinks).toBeDefined(); + expect(typeof helper.resolveLinks).toBe("function"); + }); + + it("should export a 'resolveAuthorLinks' function", function() { + expect(helper.resolveAuthorLinks).toBeDefined(); + expect(typeof helper.resolveAuthorLinks).toBe("function"); + }); + + it("should export a 'createLink' function", function() { + expect(helper.createLink).toBeDefined(); + expect(typeof helper.createLink).toBe("function"); + }); + + + describe("setTutorials", function() { + // used in tutorialToUrl, toTutorial. + it("setting tutorials to null causes all tutorial lookups to fail", function() { + // bit of a dodgy test but the best I can manage. setTutorials doesn't do much. + helper.setTutorials(null); + // should throw error: no 'getByName' in tutorials. + expect(function () { return helper.tutorialToUrl('asdf'); }).toThrow('Cannot call method "getByName" of null'); + }); + + it("setting tutorials to the root tutorial object lets lookups work", function() { + var lenient = !!env.opts.lenient; + spyOn(console, 'log'); + + // tutorial doesn't exist, we want to muffle that error + env.opts.lenient = true; + + helper.setTutorials(resolver.root); + spyOn(resolver.root, 'getByName'); + helper.tutorialToUrl('asdf'); + expect(resolver.root.getByName).toHaveBeenCalled(); + + env.opts.lenient = lenient; + }); + }); + + describe("globalName", function() { + it("should equal 'global'", function() { + expect(helper.globalName).toBe('global'); + }); + }); + + describe("fileExtension", function() { + it("should equal '.html'", function() { + expect(helper.fileExtension).toBe('.html'); + }); + }); + + describe("scopeToPunc", function() { + it("should map 'static' to '.', 'inner', to '~', 'instance' to '#'", function() { + expect(helper.scopeToPunc).toEqual({static: '.', inner: '~', instance: '#'}); + }); + }); + + describe("getUniqueFilename", function() { + // TODO: needs more tests for unusual values and things that get special treatment (such as + // inner members) + it('should convert a simple string into the string plus the default extension', function() { + var filename = helper.getUniqueFilename('BackusNaur'); + expect(filename).toBe('BackusNaur.html'); + }); + + it('should convert a string with slashes into the text following the last slash plus the default extension', function() { + var filename = helper.getUniqueFilename('tick/tock'); + expect(filename).toMatch(/^tock\.html$/); + }); + + it('should not return the same filename twice', function() { + var name = 'polymorphic'; + var filename1 = helper.getUniqueFilename(name); + var filename2 = helper.getUniqueFilename(name); + + expect(filename1).not.toBe(filename2); + }); + + it('should not consider the same name with different letter case to be unique', function() { + var camel = 'myJavaScriptIdentifier'; + var pascal = 'MyJavaScriptIdentifier'; + var filename1 = helper.getUniqueFilename(camel); + var filename2 = helper.getUniqueFilename(pascal); + + expect( filename1.toLowerCase() ).not.toBe( filename2.toLowerCase() ); + }); + + it('should remove variations from the longname before generating the filename', function() { + var filename = helper.getUniqueFilename('MyClass(foo, bar)'); + expect(filename).toBe('MyClass.html'); + }); + }); + + describe("longnameToUrl", function() { + it("is an object", function() { + expect(typeof helper.longnameToUrl).toBe('object'); + }); + + it("has an entry added into it by calling registerLink", function() { + helper.registerLink('MySymbol', 'asdf.html'); + expect(helper.longnameToUrl.MySymbol).toBeDefined(); + expect(helper.longnameToUrl.MySymbol).toBe('asdf.html'); + + delete helper.longnameToUrl.MySymbol; + }); + + it("adding an entry to it allows me to link with linkto", function() { + helper.longnameToUrl.foo2 = 'bar.html'; + expect(helper.linkto('foo2')).toBe('foo2'); + delete helper.longnameToUrl.foo2; + }); + }); + + describe("linkto", function() { + beforeEach(function() { + helper.longnameToUrl.linktoTest = 'test.html'; + helper.longnameToUrl.LinktoFakeClass = 'fakeclass.html'; + }); + + afterEach(function() { + delete helper.longnameToUrl.linktoTest; + delete helper.longnameToUrl.LinktoFakeClass; + }); + + it('returns the longname if only the longname is specified and has no URL', function() { + var link = helper.linkto('example'); + expect(link).toBe('example'); + }); + + it('returns the link text if only the link text is specified', function() { + var link = helper.linkto(null, 'link text'); + expect(link).toBe('link text'); + }); + + it('returns the link text if the longname does not have a URL, and both the longname and ' + + 'link text are specified', function() { + var link = helper.linkto('example', 'link text'); + expect(link).toBe('link text'); + }); + + it('uses the longname as the link text if no link text is provided', function() { + var link = helper.linkto('linktoTest'); + expect(link).toBe('linktoTest'); + }); + + it('uses the link text if it is specified', function() { + var link = helper.linkto('linktoTest', 'link text'); + expect(link).toBe('link text'); + }); + + it('includes a "class" attribute in the link if a class is specified', function() { + var link = helper.linkto('linktoTest', 'link text', 'myclass'); + expect(link).toBe('link text'); + }); + + it('is careful with longnames that are reserved words in JS', function() { + // we don't have a registered link for 'constructor' so it should return the text 'link text'. + var link = helper.linkto('constructor', 'link text'); + expect(typeof link).toBe('string'); + expect(link).toBe('link text'); + }); + + it('works correctly with type applications if only the longname is specified', function() { + var link = helper.linkto('Array.'); + expect(link).toBe('Array.<LinktoFakeClass>'); + }); + + it('works correctly with type applications if a class is not specified', function() { + var link = helper.linkto('Array.', 'link text'); + expect(link).toBe('Array.<LinktoFakeClass>'); + }); + + it('works correctly with type applications if a class is specified', function() { + var link = helper.linkto('Array.', 'link text', 'myclass'); + expect(link).toBe('Array.<LinktoFakeClass' + + '>'); + }); + + it('works correctly with type applications that include a type union', function() { + var link = helper.linkto('Array.<(linktoTest|LinktoFakeClass)>', 'link text'); + expect(link).toBe('Array.<(linktoTest|' + + 'LinktoFakeClass)>'); + }); + + it('returns a link when a URL is specified', function() { + var link = helper.linkto('http://example.com'); + expect(link).toBe('http://example.com'); + }); + + it('returns a link if a URL wrapped in angle brackets is specified', function() { + var link = helper.linkto(''); + expect(link).toBe('http://example.com'); + }); + + it('returns a link with link text if a URL and link text are specified', function() { + var link = helper.linkto('http://example.com', 'text'); + expect(link).toBe('text'); + }); + + it('returns a link with a fragment ID if a URL and fragment ID are specified', function() { + var link = helper.linkto('LinktoFakeClass', null, null, 'fragment'); + expect(link).toBe('LinktoFakeClass'); + }); + + it('returns the original text if an inline {@link} tag is specified', function() { + var link; + var text = '{@link Foo}'; + + function getLink() { + link = helper.linkto(text); + } + + // make sure we're not trying to parse the inline link as a type expression + expect(getLink).not.toThrow(); + // linkto doesn't process {@link} tags + expect(link).toBe(text); + }); + }); + + describe("htmlsafe", function() { + // turns < into < (doesn't do > or & etc...) + it('should convert all occurences of < to <', function() { + var inp = '

      Potentially dangerous.

      ', + out = helper.htmlsafe(inp); + expect(out).toBe('<h1>Potentially dangerous.</h1>'); + }); + }); + + describe("find", function() { + var array = [ + // match + { number: 2, A: true }, + // match + { number: 1, A: true, D: 'hello', Q: false }, + // match + { number: 3, A: 'maybe', squiggle: '?' }, + // no match (number not in spec) + { number: 4, A: true }, + // no match (missing top-level property) + { A: true } + ]; + var matches = array.slice(0, 3); + var spec = { number: [1, 2, 3], A: [true, 'maybe'] }; + + it('should find the requested items', function() { + expect( helper.find(taffy(array), spec) ).toEqual(matches); + }); + }); + + // we can't use toEqual() because TaffyDB adds its own stuff to the array it returns. + // instead, we make sure arrays a and b are the same length, and that each object in + // array b has all the properties of the corresponding object in array a + // used for getMembers and prune tests. + function compareObjectArrays(a, b) { + expect(a.length).toEqual(b.length); + + for (var i = 0, l = a.length; i < l; i++) { + for (var prop in a[i]) { + if ( hasOwnProp.call(a[i], prop) ) { + expect(b[i][prop]).toBeDefined(); + expect(a[i][prop]).toEqual(b[i][prop]); + } + } + } + } + describe("getMembers", function() { + // instead parse a file from fixtures and verify it? + var classes = [ + {kind: 'class'}, // global + {kind: 'class', memberof: 'SomeNamespace'}, // not global + ]; + var externals = [ + {kind: 'external'}, + ]; + var events = [ + {kind: 'event'}, + ]; + var mixins = [ + {kind: 'mixin'}, + ]; + var modules = [ + {kind: 'module'}, + ]; + var namespaces = [ + {kind: 'namespace'}, + ]; + var misc = [ + {kind: 'function'}, // global + {kind: 'member'}, // global + {kind: 'constant'}, // global + {kind: 'typedef'}, // global + {kind: 'constant', memberof: 'module:one/two'}, // not global + {kind: 'function', name: 'module:foo', longname: 'module:foo'} // not global + ]; + var array = classes.concat(externals.concat(events.concat(mixins.concat(modules.concat(namespaces.concat(misc)))))); + var data = taffy(array); + var members = helper.getMembers(data); + + // check the output object has properties as expected. + it("should have a 'classes' property", function() { + expect(members.classes).toBeDefined(); + }); + + it("should have a 'externals' property", function() { + expect(members.externals).toBeDefined(); + }); + + it("should have a 'events' property", function() { + expect(members.events).toBeDefined(); + }); + + it("should have a 'globals' property", function() { + expect(members.globals).toBeDefined(); + }); + + it("should have a 'mixins' property", function() { + expect(members.mixins).toBeDefined(); + }); + + it("should have a 'modules' property", function() { + expect(members.modules).toBeDefined(); + }); + + it("should have a 'namespaces' property", function() { + expect(members.namespaces).toBeDefined(); + }); + + // check that things were found properly. + it("classes are detected", function() { + compareObjectArrays(classes, members.classes); + }); + + it("externals are detected", function() { + compareObjectArrays(externals, members.externals); + }); + + it("events are detected", function() { + compareObjectArrays(events, members.events); + }); + + it("mixins are detected", function() { + compareObjectArrays(mixins, members.mixins); + }); + + it("modules are detected", function() { + compareObjectArrays(modules, members.modules); + }); + + it("namespaces are detected", function() { + compareObjectArrays(namespaces, members.namespaces); + }); + + it("globals are detected", function() { + compareObjectArrays(misc.slice(0, -2), members.globals); + }); + }); + + describe("getAttribs", function() { + var doc, attribs; + + it('should return an array of strings', function() { + doc = new doclet.Doclet('/** ljklajsdf */', {}); + attribs = helper.getAttribs(doc); + expect(Array.isArray(attribs)).toBe(true); + }); + + // tests is an object of test[doclet src] = + // if false, we expect attribs to either not contain anything in whatNotToContain, + // or be empty (if whatNotToContain was not provided). + function doTests(tests, whatNotToContain) { + for (var src in tests) { + if (tests.hasOwnProperty(src)) { + doc = new doclet.Doclet('/** ' + src + ' */', {}); + attribs = helper.getAttribs(doc); + if (tests[src]) { + expect(attribs).toContain(tests[src]); + } else { + if (whatNotToContain !== undefined) { + if (Array.isArray(whatNotToContain)) { + for (var i = 0; i < whatNotToContain.length; ++i) { + expect(attribs).not.toContain(whatNotToContain[i]); + } + } + } else { + expect(attribs.length).toBe(0); + } + } + } + } + } + + it('should detect if a doclet is virtual', function() { + var tests = { + 'My constant. \n @virtual': 'virtual', + 'asdf': false + }; + doTests(tests); + }); + + it("should detect if a doclet's access is not public", function() { + var tests = {'@private': 'private', + '@access private': 'private', + '@protected': 'protected', + '@access protected': 'protected', + '@public': false, + '@access public': false, + 'asdf': false + }; + doTests(tests); + }); + + it("should detect if a doclet's scope is inner or static AND it is a function or member or constant", function() { + var tests = { + // by default these are members + '@inner': 'inner', + '@instance': false, + '@global': false, + '@static': 'static', + '@name Asdf.fdsa': 'static', + '@name Outer~inner': 'inner', + '@name Fdsa#asdf': false, + '@name .log': false, + // some tests with functions and constants + '@const Asdf#FOO': false, + '@const Asdf\n@inner': 'inner', + '@function Asdf#myFunction': false, + '@function Fdsa.MyFunction': 'static', + '@function Fdsa': false, + // these are not functions or members or constants, they should not have their scope recorded. + '@namespace Fdsa\n@inner': false, + '@class asdf': false + }; + doTests(tests, ['inner', 'static', 'global', 'instance']); + }); + + it("should detect if a doclet is readonly (and its kind is 'member')", function() { + var tests = { + 'asdf\n @readonly': 'readonly', + 'asdf': false, + '@name Fdsa#foo\n@readonly': 'readonly', + // kind is not 'member'. + '@const asdf\n@readonly': false, + '@function asdf\n@readonly': false, + '@function Asdf#bar\n@readonly': false + }; + doTests(tests, 'readonly'); + }); + + it("should detect if the doclet is a for constant", function() { + var tests = { + 'Enum. @enum\n@constant': 'constant', + '@function Foo#BAR\n@const': 'constant', + '@const Asdf': 'constant' + }; + doTests(tests, 'constant'); + }); + + it("should detect multiple attributes", function() { + var doc = new doclet.Doclet('/** @const module:fdsa~FOO\n@readonly\n@private */', {}); + attribs = helper.getAttribs(doc); + expect(attribs).toContain('private'); + //expect(attribs).toContain('readonly'); // kind is 'constant' not 'member'. + expect(attribs).toContain('constant'); + expect(attribs).toContain('inner'); + }); + }); + + describe("getSignatureTypes", function() { + // returns links to allowed types for a doclet. + it("returns an empty array if the doclet has no specified type", function() { + var doc = new doclet.Doclet('/** @const ASDF */', {}), + types = helper.getSignatureTypes(doc); + + expect(Array.isArray(types)).toBe(true); + expect(types.length).toBe(0); + }); + + it("returns a string array of the doclet's types", function() { + var doc = new doclet.Doclet('/** @const {number|Array.} ASDF */', {}), + types = helper.getSignatureTypes(doc); + + expect(types.length).toBe(2); + expect(types).toContain('number'); + expect(types).toContain(helper.htmlsafe('Array.')); // should be HTML safe + }); + + it("creates links for types if relevant", function() { + // make some links. + helper.longnameToUrl.MyClass = 'MyClass.html'; + + var doc = new doclet.Doclet('/** @const {MyClass} ASDF */', {}), + types = helper.getSignatureTypes(doc); + expect(types.length).toBe(1); + expect(types).toContain('MyClass'); + + delete helper.longnameToUrl.MyClass; + }); + + it("uses the cssClass parameter for links if it is provided", function() { + // make some links. + helper.longnameToUrl.MyClass = 'MyClass.html'; + + var doc = new doclet.Doclet('/** @const {MyClass} ASDF */', {}), + types = helper.getSignatureTypes(doc, 'myCSSClass'); + expect(types.length).toBe(1); + expect(types).toContain('MyClass'); + + delete helper.longnameToUrl.MyClass; + }); + }); + + describe("getSignatureParams", function() { + // retrieves parameter names. + // if css class is provided, optional parameters are wrapped in a with that class. + it("returns an empty array if the doclet has no specified type", function() { + var doc = new doclet.Doclet('/** @function myFunction */', {}), + params = helper.getSignatureParams(doc); + expect(Array.isArray(params)).toBe(true); + expect(params.length).toBe(0); + }); + + it("returns a string array of the doclet's parameter names", function() { + var doc = new doclet.Doclet('/** @function myFunction\n @param {string} foo - asdf. */', {}), + params = helper.getSignatureParams(doc); + expect(params.length).toBe(1); + expect(params).toContain('foo'); + }); + + it("wraps optional parameters in if optClass is provided", function() { + var doc = new doclet.Doclet( + '/** @function myFunction\n' + + ' * @param {boolean} foo - explanation.\n' + + ' * @param {number} [bar=1] - another explanation.\n' + + ' * @param {string} [baz] - another explanation.\n' + + ' */', {}), + params = helper.getSignatureParams(doc, 'cssClass'); + + expect(params.length).toBe(3); + expect(params).toContain('foo'); + expect(params).toContain('bar'); + expect(params).toContain('baz'); + }); + + it("doesn't wrap optional parameters in if optClass is not provided", function() { + var doc = new doclet.Doclet( + '/** @function myFunction\n' + + ' * @param {boolean} foo - explanation.\n' + + ' * @param {number} [bar=1] - another explanation.\n' + + ' * @param {string} [baz] - another explanation.\n' + + ' */', {}), + params = helper.getSignatureParams(doc); + + expect(params.length).toBe(3); + expect(params).toContain('foo'); + expect(params).toContain('bar'); + expect(params).toContain('baz'); + }); + }); + + describe("getSignatureReturns", function() { + // retrieves links to types that the member can return. + + it("returns a value with correctly escaped HTML", function() { + var mockDoclet = { + returns: [ + { + type: { + names: [ + 'Array.' + ] + } + } + ] + }; + + var html = helper.getSignatureReturns(mockDoclet); + expect(html).not.toContain('Array.'); + expect(html).toContain('Array.<string>'); + }); + + it("returns an empty array if the doclet has no returns", function() { + var doc = new doclet.Doclet('/** @function myFunction */', {}), + returns = helper.getSignatureReturns(doc); + + expect(Array.isArray(returns)).toBe(true); + expect(returns.length).toBe(0); + }); + + it("returns an empty array if the doclet has @returns but with no type", function() { + var doc = new doclet.Doclet('/** @function myFunction\n@returns an interesting result.*/', {}), + returns = helper.getSignatureReturns(doc); + + expect(Array.isArray(returns)).toBe(true); + expect(returns.length).toBe(0); + }); + + it("creates links for return types if relevant", function() { + // make some links. + helper.longnameToUrl.MyClass = 'MyClass.html'; + + var doc = new doclet.Doclet('/** @function myFunction\n@returns {number|MyClass} an interesting result.*/', {}), + returns = helper.getSignatureReturns(doc); + + expect(returns.length).toBe(2); + expect(returns).toContain('MyClass'); + expect(returns).toContain('number'); + + delete helper.longnameToUrl.MyClass; + }); + + it("uses the cssClass parameter for links if it is provided", function() { + // make some links. + helper.longnameToUrl.MyClass = 'MyClass.html'; + + var doc = new doclet.Doclet('/** @function myFunction\n@returns {number|MyClass} an interesting result.*/', {}), + returns = helper.getSignatureReturns(doc, 'myCssClass'); + + expect(returns.length).toBe(2); + expect(returns).toContain('MyClass'); + expect(returns).toContain('number'); + + delete helper.longnameToUrl.MyClass; + }); + }); + + describe("getAncestorLinks", function() { + // make a hierarchy. + var lackeys = new doclet.Doclet('/** @member lackeys\n@memberof module:mafia/gangs.Sharks~Henchman\n@instance*/', {}), + henchman = new doclet.Doclet('/** @class Henchman\n@memberof module:mafia/gangs.Sharks\n@inner */', {}), + gang = new doclet.Doclet('/** @namespace module:mafia/gangs.Sharks */', {}), + mafia = new doclet.Doclet('/** @module mafia/gangs */', {}), + data = taffy([lackeys, henchman, gang, mafia]); + + // register some links + it("returns an empty array if there are no ancestors", function() { + var links = helper.getAncestorLinks(data, mafia); + expect(Array.isArray(links)).toBe(true); + expect(links.length).toBe(0); + }); + + it("returns an array of ancestor names (with preceding punctuation) if there are ancestors, the direct ancestor with following punctuation too", function() { + var links = helper.getAncestorLinks(data, lackeys); + expect(links.length).toBe(3); + expect(links).toContain('~Henchman#'); + expect(links).toContain('.Sharks'); + expect(links).toContain('mafia/gangs'); + + links = helper.getAncestorLinks(data, henchman); + expect(links.length).toBe(2); + expect(links).toContain('.Sharks~'); + expect(links).toContain('mafia/gangs'); + + links = helper.getAncestorLinks(data, gang); + expect(links.length).toBe(1); + expect(links).toContain('mafia/gangs.'); + }); + + it("adds links if they exist", function() { + // register some links + helper.longnameToUrl['module:mafia/gangs'] = 'mafia_gangs.html'; + helper.longnameToUrl['module:mafia/gangs.Sharks~Henchman'] = 'henchman.html'; + + var links = helper.getAncestorLinks(data, lackeys); + expect(links.length).toBe(3); + expect(links).toContain('~Henchman#'); + expect(links).toContain('.Sharks'); + expect(links).toContain('mafia/gangs'); + + delete helper.longnameToUrl['module:mafia/gangs']; + delete helper.longnameToUrl['module:mafia/gangs.Sharks~Henchman']; + }); + + it("adds cssClass to any link", function() { + // register some links + helper.longnameToUrl['module:mafia/gangs'] = 'mafia_gangs.html'; + helper.longnameToUrl['module:mafia/gangs.Sharks~Henchman'] = 'henchman.html'; + + var links = helper.getAncestorLinks(data, lackeys, 'myClass'); + expect(links.length).toBe(3); + expect(links).toContain('~Henchman#'); + expect(links).toContain('.Sharks'); + expect(links).toContain('mafia/gangs'); + + delete helper.longnameToUrl['module:mafia/gangs']; + delete helper.longnameToUrl['module:mafia/gangs.Sharks~Henchman']; + }); + }); + + describe("addEventListeners", function() { + var doclets = taffy(jasmine.getDocSetFromFile('test/fixtures/listenstag.js').doclets), + ev = helper.find(doclets, {longname: 'module:myModule.event:MyEvent'})[0], + ev2 = helper.find(doclets, {longname: 'module:myModule~Events.event:Event2'})[0], + ev3 = helper.find(doclets, {longname: 'module:myModule#event:Event3'})[0]; + + helper.addEventListeners(doclets); + + it("adds a 'listeners' array to events with the longnames of the listeners", function() { + expect(Array.isArray(ev.listeners)).toBe(true); + expect(Array.isArray(ev2.listeners)).toBe(true); + + expect(ev.listeners.length).toBe(2); + expect(ev.listeners).toContain('module:myModule~MyHandler'); + expect(ev.listeners).toContain('module:myModule~AnotherHandler'); + + expect(ev2.listeners.length).toBe(1); + expect(ev2.listeners).toContain('module:myModule~MyHandler'); + }); + + it("does not add listeners for events with no listeners", function() { + expect(ev3.listeners).not.toBeDefined(); + }); + + it("does not make spurious doclets if something @listens to a non-existent symbol", function() { + expect(helper.find(doclets, {longname: 'event:fakeEvent'}).length).toBe(0); + }); + }); + + describe("prune", function() { + + var array = [ + // keep + {undocumented: false}, + // keep + {ignore: false}, + // keep + {memberof: 'SomeClass'}, + // prune + {undocumented: true}, + // prune + {ignore: true}, + // prune + {memberof: ''} + ]; + var arrayPrivate = [ + // prune (unless env.opts.private is truthy) + {access: 'private'} + ]; + var keep = array.slice(0, 3); + + it('should prune the correct members', function() { + var pruned = helper.prune( taffy(array) )().get(); + compareObjectArrays(keep, pruned); + }); + + it('should prune private members if env.opts.private is falsy', function() { + var priv = !!env.opts.private; + + env.opts.private = false; + var pruned = helper.prune( taffy(arrayPrivate) )().get(); + compareObjectArrays([], pruned); + + env.opts.private = !!priv; + }); + + it('should not prune private members if env.opts.private is truthy', function() { + var priv = !!env.opts.private; + + env.opts.private = true; + var pruned = helper.prune( taffy(arrayPrivate) )().get(); + compareObjectArrays(arrayPrivate, pruned); + + env.opts.private = !!priv; + }); + }); + + describe("registerLink", function() { + it("adds an entry to exports.longnameToUrl", function() { + helper.longnameToUrl.MySymbol = 'asdf.html'; + + expect(helper.longnameToUrl.MySymbol).toBeDefined(); + expect(helper.longnameToUrl.MySymbol).toBe('asdf.html'); + + delete helper.longnameToUrl.MySymbol; + }); + + it("allows linkto to work", function() { + helper.registerLink('MySymbol', 'asdf.html'); + + expect(helper.linkto('MySymbol')).toBe('MySymbol'); + + delete helper.longnameToUrl.MySymbol; + }); + }); + + describe("tutorialToUrl", function() { + var lenient = !!env.opts.lenient; + + function missingTutorial() { + var url = helper.tutorialToUrl("be-a-perfect-person-in-just-three-days"); + } + + beforeEach(function() { + spyOn(console, 'log'); + helper.setTutorials(resolver.root); + }); + + afterEach(function() { + helper.setTutorials(null); + env.opts.lenient = lenient; + }); + + it('throws an exception if the tutorial is missing and the lenient option is not enabled', function() { + env.opts.lenient = false; + expect(missingTutorial).toThrow(); + }); + + it('does not throw an exception if the tutorial is missing and the lenient option is enabled', function() { + env.opts.lenient = true; + + expect(missingTutorial).not.toThrow(); + }); + + it("does not return a tutorial if its name is a reserved JS keyword and it doesn't exist", function() { + env.opts.lenient = false; + expect(function () { helper.tutorialToUrl('prototype'); }).toThrow(); + }); + + it("creates links to tutorials if they exist", function() { + // NOTE: we have to set lenient = true here because otherwise JSDoc will + // cry when trying to resolve the same set of tutorials twice (once + // for the tutorials tests, and once here). + env.opts.lenient = true; + + // load the tutorials we already have for the tutorials tests + resolver.load(__dirname + "/test/tutorials/tutorials"); + resolver.resolve(); + + var url = helper.tutorialToUrl('test'); + expect(typeof url).toBe('string'); + expect(url).toBe('tutorial-test.html'); + }); + + it("creates links for tutorials where the name is a reserved JS keyword", function() { + var url = helper.tutorialToUrl('constructor'); + expect(typeof url).toBe('string'); + expect(url).toBe('tutorial-constructor.html'); + }); + + it("returns the same link if called multiple times on the same tutorial", function() { + expect(helper.tutorialToUrl('test2')).toBe(helper.tutorialToUrl('test2')); + }); + }); + + describe("toTutorial", function() { + var lenient = !!env.opts.lenient; + + function missingParam() { + helper.toTutorial(); + } + + afterEach(function() { + env.opts.lenient = lenient; + helper.setTutorials(null); + }); + + beforeEach(function () { + helper.setTutorials(resolver.root); + }); + + it('throws an exception if the first param is missing and the lenient option is not enabled', function() { + env.opts.lenient = false; + + expect(missingParam).toThrow(); + }); + + it('does not throw an exception if the first param is missing and the lenient option is enabled', function() { + spyOn(console, 'log'); + env.opts.lenient = true; + + expect(missingParam).not.toThrow(); + }); + + // missing tutorials + it("returns the tutorial name if it's missing and no missingOpts is provided", function() { + helper.setTutorials(resolver.root); + var link = helper.toTutorial('qwerty'); + expect(link).toBe('qwerty'); + }); + + it("returns the tutorial name wrapped in missingOpts.tag if provided and the tutorial is missing", function() { + var link = helper.toTutorial('qwerty', 'lkjklqwerty', {tag: 'span'}); + expect(link).toBe('qwerty'); + }); + + it("returns the tutorial name wrapped in missingOpts.tag with class missingOpts.classname if provided and the tutorial is missing", function() { + var link = helper.toTutorial('qwerty', 'lkjklqwerty', {classname: 'missing'}); + expect(link).toBe('qwerty'); + + link = helper.toTutorial('qwerty', 'lkjklqwerty', {tag: 'span', classname: 'missing'}); + expect(link).toBe('qwerty'); + }); + + it("prefixes the tutorial name with missingOpts.prefix if provided and the tutorial is missing", function() { + var link = helper.toTutorial('qwerty', 'lkjklqwerty', {tag: 'span', classname: 'missing', prefix: 'TODO-'}); + expect(link).toBe('TODO-qwerty'); + + link = helper.toTutorial('qwerty', 'lkjklqwerty', {prefix: 'TODO-'}); + expect(link).toBe('TODO-qwerty'); + + link = helper.toTutorial('qwerty', 'lkjklqwerty', {prefix: 'TODO-', classname: 'missing'}); + expect(link).toBe('TODO-qwerty'); + }); + + // now we do non-missing tutorials. + it("returns a link to the tutorial if not missing", function() { + // NOTE: we have to set lenient = true here because otherwise JSDoc will + // cry when trying to resolve the same set of tutorials twice (once + // for the tutorials tests, and once here). + env.opts.lenient = true; + spyOn(console, 'log'); + + // load the tutorials we already have for the tutorials tests + resolver.load(__dirname + "/test/tutorials/tutorials"); + resolver.resolve(); + + + var link = helper.toTutorial('constructor', 'The Constructor tutorial'); + expect(link).toBe('The Constructor tutorial'); + }); + + it("uses the tutorial's title for the link text if no content parameter is provided", function() { + var link = helper.toTutorial('test'); + expect(link).toBe('Test tutorial'); + }); + + it("does not apply any of missingOpts if the tutorial was found", function() { + var link = helper.toTutorial('test', '', {tag: 'span', classname: 'missing', prefix: 'TODO-'}); + expect(link).toBe('Test tutorial'); + }); + }); + + // couple of convenience functions letting me set conf variables and restore + // them back to the originals later. + function setConfTemplatesVariables(hash) { + var keys = Object.keys(hash); + var storage = {}; + for (var i = 0; i < keys.length; ++i) { + storage[keys[i]] = env.conf.templates[keys[i]]; + // works because hash[key] is a scalar not an array/object + env.conf.templates[keys[i]] = hash[keys[i]]; + } + return storage; + } + + function restoreConfTemplates(storage) { + var keys = Object.keys(storage); + for (var i = 0; i < keys.length; ++i) { + env.conf.templates[keys[i]] = storage[keys[i]]; + } + } + + describe("resolveLinks", function() { + it('should translate {@link test} into a HTML link.', function() { + var input = 'This is a {@link test}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is a test.'); + }); + + it('should translate {@link unknown} into a simple text.', function() { + var input = 'This is a {@link unknown}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is a unknown.'); + }); + + it('should translate {@link test} into a HTML links multiple times.', function() { + var input = 'This is a {@link test} and {@link test}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is a test and test.'); + }); + + it('should translate [hello there]{@link test} into a HTML link with the custom content.', function() { + var input = 'This is a [hello there]{@link test}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is a hello there.'); + }); + + it('should ignore [hello there].', function() { + var input = 'This is a [hello there].', + output = helper.resolveLinks(input); + + expect(output).toBe(input); + }); + + it('should translate http links in the tag', function() { + var input = 'Link to {@link http://github.com}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to http://github.com'); + }); + + it('should translate ftp links in the tag', function() { + var input = 'Link to {@link ftp://foo.bar}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to ftp://foo.bar'); + }); + + it('should allow pipe to be used as delimiter between href and text (external link)', function() { + var input = 'Link to {@link http://github.com|Github}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to Github'); + }); + + it('should allow pipe to be used as delimiter between href and text (symbol link)', function() { + var input = 'Link to {@link test|Test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to Test'); + }); + + it('should allow first space to be used as delimiter between href and text (external link)', function() { + var input = 'Link to {@link http://github.com Github}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to Github'); + }); + + it('should allow first space to be used as delimiter between href and text (symbol link)', function() { + var input = 'Link to {@link test My Caption}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to My Caption'); + }); + + it('if pipe and space are present in link tag, use pipe as the delimiter', function() { + var input = 'Link to {@link test|My Caption}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to My Caption'); + }); + + it('Test of {@linkcode } which should be in monospace', function() { + var input = 'Link to {@linkcode test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + }); + + it('Test of {@linkplain } which should be in normal font', function() { + var input = 'Link to {@linkplain test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + }); + + it('should be careful with linking to links whose names are reserved JS keywords', function() { + var input = 'Link to {@link constructor}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to constructor'); + }); + + it('should allow linebreaks between link tag and content', function() { + var input = 'This is a {@link\ntest}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is a test.'); + }); + + it('should allow tabs between link tag and content', function() { + var input = 'This is a {@link\ttest}.', + output = helper.resolveLinks(input); + + expect(output).toBe('This is a test.'); + }); + + // conf.monospaceLinks. check that + // a) it works + it('if conf.monospaceLinks is true, all {@link} should be monospace', function () { + var storage = setConfTemplatesVariables({monospaceLinks: true}); + var input = 'Link to {@link test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + restoreConfTemplates(storage); + }); + + // b) linkcode and linkplain are still respected + it('if conf.monospaceLinks is true, all {@linkcode} should still be monospace', function () { + var storage = setConfTemplatesVariables({monospaceLinks: true}); + var input = 'Link to {@linkcode test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + restoreConfTemplates(storage); + }); + + it('if conf.monospaceLinks is true, all {@linkplain} should still be plain', function () { + var storage = setConfTemplatesVariables({monospaceLinks: true}); + var input = 'Link to {@linkplain test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + restoreConfTemplates(storage); + }); + + // conf.cleverLinks. check that + // a) it works + it('if conf.cleverLinks is true, {@link symbol} should be in monospace', function () { + var storage = setConfTemplatesVariables({cleverLinks: true}); + var input = 'Link to {@link test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + restoreConfTemplates(storage); + }); + + it('if conf.cleverLinks is true, {@link URL} should be in plain text', function () { + var storage = setConfTemplatesVariables({cleverLinks: true}); + var input = 'Link to {@link http://github.com}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to http://github.com'); + restoreConfTemplates(storage); + }); + + // b) linkcode and linkplain are still respected + it('if conf.cleverLinks is true, all {@linkcode} should still be clever', function () { + var storage = setConfTemplatesVariables({cleverLinks: true}); + var input = 'Link to {@linkcode test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + restoreConfTemplates(storage); + }); + + it('if conf.cleverLinks is true, all {@linkplain} should still be plain', function () { + var storage = setConfTemplatesVariables({cleverLinks: true}); + var input = 'Link to {@linkplain test}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test'); + restoreConfTemplates(storage); + }); + + // c) if monospaceLinks is additionally `true` it is ignored in favour + // of cleverLinks + it('if conf.cleverLinks is true and so is conf.monospaceLinks, cleverLinks overrides', function () { + var storage = setConfTemplatesVariables({cleverLinks: true, monospaceLinks: true}); + var input = 'Link to {@link test} and {@link http://github.com}', + output = helper.resolveLinks(input); + expect(output).toBe('Link to test and http://github.com'); + restoreConfTemplates(storage); + }); + + }); + + describe("createLink", function() { + it('should create a url for a simple global.', function() { + var mockDoclet = { + kind: 'function', + longname: 'foo', + name: 'foo' + }, + url = helper.createLink(mockDoclet); + + expect(url).toBe('global.html#foo'); + }); + + it('should create a url for a namespace.', function() { + var mockDoclet = { + kind: 'namespace', + longname: 'foo', + name: 'foo' + }, + url = helper.createLink(mockDoclet); + + expect(url).toBe('foo.html'); + }); + + it('should create a url for a member of a namespace.', function() { + var mockDoclet = { + kind: 'function', + longname: 'ns.foo', + name: 'foo', + memberof: 'ns' + }, + url = helper.createLink(mockDoclet); + + expect(url).toBe('ns.html#foo'); + }); + + var nestedNamespaceDoclet = { + kind: 'function', + longname: 'ns1.ns2.foo', + name: 'foo', + memberof: 'ns1.ns2' + }; + var nestedNamespaceUrl; + + it('should create a url for a member of a nested namespace.', function() { + nestedNamespaceUrl = helper.createLink(nestedNamespaceDoclet); + + expect(nestedNamespaceUrl).toBe('ns1.ns2.html#foo'); + }); + + it('should return the same value when called twice with the same doclet.', function() { + var newUrl = helper.createLink(nestedNamespaceDoclet); + expect(newUrl).toBe(nestedNamespaceUrl); + }); + + it('should create a url for a name with invalid characters.', function() { + var mockDoclet = { + kind: 'function', + longname: 'ns1."!"."*foo"', + name: '"*foo"', + memberof: 'ns1."!"' + }, + url = helper.createLink(mockDoclet); + + expect(url).toEqual('_.html#"*foo"'); + }); + + it('should create a url for a function that is the only symbol exported by a module.', + function() { + var mockDoclet = { + kind: 'function', + longname: 'module:bar', + name: 'module:bar' + }; + var url = helper.createLink(mockDoclet); + + expect(url).toEqual('module-bar.html'); + }); + }); + + describe("resolveAuthorLinks", function() { + // convert Jane Doe to a mailto link. + it('should convert email addresses in angle brackets *after* a name to mailto links', function() { + var str = ' John Doe ', + out = helper.resolveAuthorLinks(str); + expect(out).toBe('John Doe'); + }); + + it('should HTML-safe author names', function() { + var str = ' John ', + out = helper.resolveAuthorLinks(str); + expect(out).toBe('' + helper.htmlsafe('John'); + }); + + it('should simply return the input string, HTML-safe, if no email is detected', function() { + var str = 'John Doe ', + out = helper.resolveAuthorLinks(str); + expect(out).toBe(helper.htmlsafe(str)); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/jsdoc/util/vm.js b/data/scripts/jsdoc/test/specs/jsdoc/util/vm.js new file mode 100644 index 00000000..a141a4cc --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jsdoc/util/vm.js @@ -0,0 +1,66 @@ +/*global describe: true, expect: true, it: true, xit: true */ +describe("jsdoc/util/vm", function() { + var vm = require('jsdoc/util/vm'); + + it("should exist", function() { + expect(vm).toBeDefined(); + expect(typeof vm).toEqual('object'); + }); + + it("should export a 'RHINO' constant", function() { + expect(vm.RHINO).toBeDefined(); + expect(typeof vm.RHINO).toEqual('string'); + }); + + it("should export a 'NODEJS' constant", function() { + expect(vm.NODEJS).toBeDefined(); + expect(typeof vm.NODEJS).toEqual('string'); + }); + + it("should export a 'vm' property", function() { + expect(vm.vm).toBeDefined(); + expect(typeof vm.vm).toEqual('string'); + }); + + it("should export an 'isRhino' function", function() { + expect(vm.isRhino).toBeDefined(); + expect(typeof vm.isRhino).toEqual('function'); + }); + + it("should export an 'isNodejs' function", function() { + expect(vm.isNodejs).toBeDefined(); + expect(typeof vm.isNodejs).toEqual('function'); + }); + + + describe("vm", function() { + it("should match either 'vm.RHINO' or 'vm.NODEJS'", function() { + expect(vm.vm).toEqual(vm.RHINO || vm.NODEJS); + }); + + xit("should return the correct value for the current VM", function() { + // TODO: is there a reasonable test that doesn't just replicate getVm()? + }); + }); + + describe("isRhino", function() { + it("should return a boolean", function() { + expect( typeof vm.isRhino() ).toEqual('boolean'); + }); + + it("should reflect the value of 'vm.vm'", function() { + expect( vm.isRhino() ).toEqual(vm.vm === vm.RHINO ? true : false); + }); + }); + + describe("isNodejs", function() { + it("should return a boolean", function() { + expect( typeof vm.isNodejs() ).toEqual('boolean'); + }); + + it("should reflect the value of 'vm.vm'", function() { + expect( vm.isNodejs() ).toEqual(vm.vm === vm.NODEJS ? true : false); + }); + }); + +}); diff --git a/data/scripts/jsdoc/test/specs/jshint/jshint-clean.js b/data/scripts/jsdoc/test/specs/jshint/jshint-clean.js new file mode 100644 index 00000000..e1a3492c --- /dev/null +++ b/data/scripts/jsdoc/test/specs/jshint/jshint-clean.js @@ -0,0 +1,72 @@ +/*global app: true, beforeEach: true, describe: true, env: true, expect: true, it: true */ +var async = require('async'), + fs = require('jsdoc/fs'), + path = require('path'); + +var config = JSON.parse( fs.readFileSync( path.join(__dirname, '.jshintrc'), 'utf8' ) ); + +function jsHintCheck(filename, callback) { + var JSHINT = require('jshint').JSHINT; + var jsHintErrors; + + fs.readFile(filename, 'utf8', function(err, data) { + if (err) { + callback(err); + } else { + JSHINT(data, config); + if (JSHINT.errors.length) { + jsHintErrors = filename + ' is not JSHint clean: ' + JSON.stringify(JSHINT.errors); + } + + callback(null, jsHintErrors); + } + }); +} + +describe('jshint-clean', function() { + it('should generate JSHint errors for bad code', function(done) { + var file = path.join(__dirname, 'test', 'fixtures', 'jshint', 'badfile.js'); + + jsHintCheck(file, function(err, jsHintErrors) { + expect(err).toBeFalsy(); + expect(jsHintErrors).toBeDefined(); + done(); + }); + }); + + it('should not generate JSHint errors for good code', function(done) { + var file = path.join(__dirname, 'test', 'fixtures', 'jshint', 'goodfile.js'); + + jsHintCheck(file, function(err, jsHintErrors) { + expect(err).toBeFalsy(); + expect(jsHintErrors).toBeUndefined(); + done(); + }); + }); + + it('should not find JSHint errors in JSDoc', function(done) { + var files, + filter, + source; + + // check all .js files unless they're tests; rhino shim files that probably can't be + // delinted; or third-party modules + source = { + includePattern: '.+[\\|/]lib[\\|/].+\\.js$|.+[\\|/]plugins[\\|/]\\w+\\.js$', + excludePattern: '.+[\\|/]test[\\|/].+|.+[\\|/]node_modules[\\|/].+|.+[\\|/]Jake[\\|/].+' + }; + filter = new (require('jsdoc/src/filter').Filter)(source); + + files = app.jsdoc.scanner.scan([__dirname], 10, filter); + + async.forEach(files, function(file, cb) { + jsHintCheck(file, function(err, jsHintErrors) { + expect(jsHintErrors).toBeUndefined(); + cb(err); + }); + }, function(err) { + expect(err).toBeFalsy(); + done(); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/plugins/plugins.js b/data/scripts/jsdoc/test/specs/plugins/plugins.js new file mode 100644 index 00000000..a989be1e --- /dev/null +++ b/data/scripts/jsdoc/test/specs/plugins/plugins.js @@ -0,0 +1,55 @@ +/*global app: true, describe: true, expect: true, it: true, jasmine: true */ + +describe("plugins", function() { + var myGlobal = require('jsdoc/util/global'); + myGlobal.jsdocPluginsTest = myGlobal.jsdocPluginsTest || {}; + + require('jsdoc/plugins').installPlugins(['test/fixtures/testPlugin1', + 'test/fixtures/testPlugin2'], app.jsdoc.parser); + + var docSet = jasmine.getDocSetFromFile("test/fixtures/plugins.js", app.jsdoc.parser); + + it("should fire the plugin's event handlers", function() { + expect(myGlobal.jsdocPluginsTest.plugin1.fileBegin).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin1.fileBegin).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin1.beforeParse).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin1.beforeParse).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin1.jsdocCommentFound).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin1.jsdocCommentFound).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin1.symbolFound).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin1.symbolFound).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin1.newDoclet).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin1.newDoclet).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin1.fileComplete).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin1.fileComplete).toEqual(true); + + expect(myGlobal.jsdocPluginsTest.plugin2.fileBegin).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin2.fileBegin).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin2.beforeParse).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin2.beforeParse).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin2.jsdocCommentFound).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin2.jsdocCommentFound).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin2.symbolFound).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin2.symbolFound).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin2.newDoclet).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin2.newDoclet).toEqual(true); + expect(myGlobal.jsdocPluginsTest.plugin2.fileComplete).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin2.fileComplete).toEqual(true); + }); + + it("should add the plugin's tag definitions to the dictionary", function() { + var test = docSet.getByLongname("test"); + + expect(test[0].longname).toEqual("test"); + expect(test[0].foo).toEqual(true); + }); + + it("should call the plugin's visitNode function", function() { + expect(myGlobal.jsdocPluginsTest.plugin1.visitNode).toBeDefined(); + expect(myGlobal.jsdocPluginsTest.plugin1.visitNode).toEqual(true); + }); + + it("should not call a second plugin's visitNode function if the first stopped propagation", function() { + expect(myGlobal.jsdocPluginsTest.plugin2.visitNode).not.toBeDefined(); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/rhino/fs.js b/data/scripts/jsdoc/test/specs/rhino/fs.js new file mode 100644 index 00000000..5be21719 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/rhino/fs.js @@ -0,0 +1,4 @@ +/*global describe: true */ +describe("fs", function() { + // TODO +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/rhino/os.js b/data/scripts/jsdoc/test/specs/rhino/os.js new file mode 100644 index 00000000..0f6a0aa6 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/rhino/os.js @@ -0,0 +1,4 @@ +/*global describe: true */ +describe("os", function() { + // TODO +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/rhino/path.js b/data/scripts/jsdoc/test/specs/rhino/path.js new file mode 100644 index 00000000..942f3a73 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/rhino/path.js @@ -0,0 +1,35 @@ +/*global describe: true, expect: true, it: true */ +describe("path", function() { + // TODO: more tests + var path = require('path'); + + var pathChunks = [ + "foo", + "bar", + "baz", + "qux.html" + ]; + var joinedPath = path.join.apply(this, pathChunks); + + describe("basename", function() { + it("should exist", function() { + expect(path.basename).toBeDefined(); + }); + + it("should be a function", function() { + expect(typeof path.basename).toEqual("function"); + }); + + it("should work correctly without an 'ext' parameter", function() { + expect( path.basename(joinedPath) ).toEqual( pathChunks[pathChunks.length - 1] ); + }); + + it("should work correctly with an 'ext' parameter", function() { + var fn = pathChunks[pathChunks.length - 1], + ext = Array.prototype.slice.call( fn, fn.indexOf(".") ).join(""); + bn = Array.prototype.slice.call( fn, 0, fn.indexOf(".") ).join(""); + + expect( path.basename(joinedPath, ext) ).toEqual(bn); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/abstracttag.js b/data/scripts/jsdoc/test/specs/tags/abstracttag.js new file mode 100644 index 00000000..be2b7862 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/abstracttag.js @@ -0,0 +1,20 @@ +describe("@abstract tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/abstracttag.js'), + type = docSet.getByLongname('Thingy')[0], + pez = docSet.getByLongname('Thingy#pez')[0]; + + it("should have an undefined 'virtual' property with no '@abstract' tag", function() { + expect(type.virtual).toBeUndefined(); + }); + + it("should set the doclet's 'virtual' property to true when ' @abstract tag is present", function() { + expect(pez.virtual).toBe(true); + }); + + // same as... + + it("should set the doclet's 'virtual' property to true when ' @abstract tag is present", function() { + pez = docSet.getByLongname('OtherThingy#pez')[0]; + expect(pez.virtual).toBe(true); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/accesstag.js b/data/scripts/jsdoc/test/specs/tags/accesstag.js new file mode 100644 index 00000000..3bfa3973 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/accesstag.js @@ -0,0 +1,24 @@ +describe("@access tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/accesstag.js'), + foo = docSet.getByLongname('Thingy~foo')[0], + _bar = docSet.getByLongname('Thingy#_bar')[0], + pez = docSet.getByLongname('Thingy#pez')[0], + foo2 = docSet.getByLongname('OtherThingy~foo')[0], + _bar2 = docSet.getByLongname('OtherThingy#_bar')[0], + pez2 = docSet.getByLongname('OtherThingy#pez')[0]; + + it("should set the doclet's 'access' property to 'private' when there is an @access private tag", function() { + expect(foo.access).toBe('private'); + expect(foo2.access).toBe('private'); + }); + + it("should set the doclet's 'access' property to 'protected' when there is an @access protected tag", function() { + expect(_bar.access).toBe('protected'); + expect(_bar2.access).toBe('protected'); + }); + + it("should set no 'access' property on the doclet when there is an @access public tag", function() { + expect(pez.access).toBeUndefined(); + expect(pez2.access).toBeUndefined(); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/aliastag.js b/data/scripts/jsdoc/test/specs/tags/aliastag.js new file mode 100644 index 00000000..4b2396ef --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/aliastag.js @@ -0,0 +1,11 @@ +describe("@alias tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/alias.js'), + // there are two doclets with longname myObject, we want the second one + myObject = docSet.getByLongname('myObject')[1]; + + it("adds an 'alias' property to the doclet with the tag's value", function() { + expect(myObject.alias).toBeDefined(); + expect(myObject.alias).toBe('myObject'); + }); + // further tests (ensuring alias has the proper effect): documentation/alias.js +}); diff --git a/data/scripts/jsdoc/test/specs/tags/augmentstag.js b/data/scripts/jsdoc/test/specs/tags/augmentstag.js new file mode 100644 index 00000000..fabc2ee0 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/augmentstag.js @@ -0,0 +1,99 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ + describe("@augments tag", function() { + /*jshint unused: false */ + var docSet = jasmine.getDocSetFromFile('test/fixtures/augmentstag.js'), + foo = docSet.getByLongname('Foo')[0], + fooProp1 = docSet.getByLongname('Foo#prop1')[0], + fooProp2 = docSet.getByLongname('Foo#prop2')[0], + fooProp3 = docSet.getByLongname('Foo#prop3')[0], + fooMethod1 = docSet.getByLongname('Foo#method1')[0], + fooMethod2 = docSet.getByLongname('Foo#method2')[0], + bar = docSet.getByLongname('Bar')[0], + barProp1 = docSet.getByLongname('Bar#prop1')[0], + barProp2 = docSet.getByLongname('Bar#prop2')[0], + barProp3 = docSet.getByLongname('Bar#prop3')[0], + barMethod1 = docSet.getByLongname('Bar#method1')[0], + barMethod2 = docSet.getByLongname('Bar#method2')[0], + barMethod2All = docSet.getByLongname('Bar#method2'), + bazProp1 = docSet.getByLongname('Baz#prop1')[0], + bazProp1All = docSet.getByLongname('Baz#prop1'), + bazProp2 = docSet.getByLongname('Baz#prop2')[0], + bazProp3 = docSet.getByLongname('Baz#prop3')[0], + bazMethod1 = docSet.getByLongname('Baz#method1')[0], + bazMethod2 = docSet.getByLongname('Baz#method2')[0], + bazMethod3 = docSet.getByLongname('Baz#method3')[0], + + docSet2 = jasmine.getDocSetFromFile('test/fixtures/augmentstag2.js'), + qux = docSet2.getByLongname('Qux')[0], + + docSet3 = jasmine.getDocSetFromFile('test/fixtures/augmentstag3.js'), + FooMethod1 = docSet3.getByLongname('Foo#method1')[0], + BarMethod2 = docSet3.getByLongname('Bar#method2')[0], + FooBarMethod1 = docSet3.getByLongname('FooBar#method1')[0], + FooBarMethod2 = docSet3.getByLongname('FooBar#method2')[0]; + + it('When a symbol has an @augments tag, the doclet has a augments property that includes that value.', function() { + expect(typeof bar.augments).toBe('object'); + expect(bar.augments[0]).toBe('Foo'); + }); + + it('When an object is extended, the original is not modified', function() { + expect(fooProp3).toBeUndefined(); + }); + + it('When an object is extended, it inherits properties set in parent constructor', function() { + expect(fooProp1.memberof).toBe("Foo"); + expect(barProp1.memberof).toBe("Bar"); + expect(barProp1.description).toBe(fooProp1.description); + }); + + it('When an object is extended, it inherits properties set on parent prototype', function() { + expect(fooProp2.memberof).toBe("Foo"); + expect(barProp2.memberof).toBe("Bar"); + expect(barProp2.description).toBe(fooProp2.description); + }); + + it('When an object is extended, it inherits methods set on parent prototype', function() { + expect(fooMethod1.memberof).toBe("Foo"); + expect(barMethod1.memberof).toBe("Bar"); + expect(barMethod1.description).toBe(fooMethod1.description); + }); + + it('When an object is extended, it may override methods set on parent prototype', function() { + expect(fooMethod2.memberof).toBe("Foo"); + expect(fooMethod2.description).toBe("Second parent method."); + expect(barMethod2.memberof).toBe("Bar"); + expect(barMethod2.description).toBe("Second child method."); + }); + + it('When an object is extended, and it overrides an ancestor method, the child does not include docs for the ancestor method.', function() { + expect(barMethod2All.length).toBe(1); + }); + + it('When an object is extended, it inherits properties set on grandparent prototype', function() { + expect(fooProp1.memberof).toBe("Foo"); + expect(barProp1.memberof).toBe("Bar"); + expect(bazProp1.memberof).toBe("Baz"); + expect(bazProp1.description).toBe("Override prop1"); + expect(bazMethod1.memberof).toBe("Baz"); + expect(bazMethod2.memberof).toBe("Baz"); + expect(bazMethod3.memberof).toBe("Baz"); + }); + + it('When an object is extended, and it overrides an ancestor property, the child does not include docs for the ancestor property.', function() { + expect(bazProp1All.length).toBe(1); + }); + + it('When a symbol has an @augments tag, and the parent is not documented, the doclet still has an augments property', function() { + expect(typeof qux.augments).toBe('object'); + expect(qux.augments[0]).toBe('UndocumentedThing'); + }); + + + it('When a symbol @augments multiple parents, it inherits methods from all parents', function() { + expect(FooBarMethod1).toBeDefined(); + expect(FooBarMethod2).toBeDefined(); + expect(FooBarMethod1.description).toBe(FooMethod1.description); + expect(FooBarMethod2.description).toBe(BarMethod2.description); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/authortag.js b/data/scripts/jsdoc/test/specs/tags/authortag.js new file mode 100644 index 00000000..1a84b336 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/authortag.js @@ -0,0 +1,18 @@ +describe("@author tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/authortag.js'), + Thingy = docSet.getByLongname('Thingy')[0], + Thingy2 = docSet.getByLongname('Thingy2')[0]; + + it('When a symbol has a @author tag, the doclet has a author property with that value.', function() { + expect(Thingy.author).toBeDefined(); + expect(Array.isArray(Thingy.author)).toBe(true); + expect(Thingy.author[0]).toBe('Michael Mathews '); + }); + + it('When a symbol has multiple @author tags, the doclet has a author property, an array with those values.', function() { + expect(Thingy2.author).toBeDefined(); + expect(Array.isArray(Thingy2.author)).toBe(true); + expect(Thingy2.author).toContain('Jane Doe '); + expect(Thingy2.author).toContain('John Doe '); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/borrowstag.js b/data/scripts/jsdoc/test/specs/tags/borrowstag.js new file mode 100644 index 00000000..4a6fdf57 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/borrowstag.js @@ -0,0 +1,24 @@ +describe("@borrows tag", function() { + it('When a symbol has a @borrows-as tag, that is added to the symbol\'s "borrowed" property.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/borrowstag.js'), + util = docSet.getByLongname('util').filter(function($) { + return ! $.undocumented; + })[0]; + expect(util.borrowed.length).toBe(1); + expect(util.borrowed[0].from).toBe('trstr'); + expect(util.borrowed[0].as).toBe('trim'); + }); + + it('When a symbol has a @borrows tag, the borrowed symbol is added to the symbol.', function() { + var borrow = require('jsdoc/borrow'), + docSet = jasmine.getDocSetFromFile('test/fixtures/borrowstag2.js'); + + borrow.resolveBorrows(docSet.doclets); + + var str_rtrim = docSet.getByLongname('str.rtrim').filter(function($) { + return ! $.undocumented; + })[0]; + + expect(typeof str_rtrim).toBe('object'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/classdesctag.js b/data/scripts/jsdoc/test/specs/tags/classdesctag.js new file mode 100644 index 00000000..2a369cf1 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/classdesctag.js @@ -0,0 +1,8 @@ +describe("@classdesc tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/classdesctag.js'), + doc = docSet.getByLongname('Foo')[0]; + + it('adds a classdesc property to the doclet with the description', function() { + expect(doc.classdesc).toBe('A description of the class.'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/classtag.js b/data/scripts/jsdoc/test/specs/tags/classtag.js new file mode 100644 index 00000000..0a28a54d --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/classtag.js @@ -0,0 +1,14 @@ +describe("@class tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/classtag.js'), + ticker = docSet.getByLongname('Ticker')[0], + news = docSet.getByLongname('NewsSource')[0]; + + it('When a symbol has a @class tag, the doclet has a kind property set to "class".', function() { + expect(ticker.kind).toBe('class'); + }); + + it('When a symbol has a @class tag with a value, the doclet has a name property set to that value.', function() { + expect(news.kind).toBe('class'); + expect(news.longname).toBe('NewsSource'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/constanttag.js b/data/scripts/jsdoc/test/specs/tags/constanttag.js new file mode 100644 index 00000000..e33ae980 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/constanttag.js @@ -0,0 +1,28 @@ +describe("@constant tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constanttag.js'), + FOO = docSet.getByLongname('FOO')[0], + BAR = docSet.getByLongname('BAR')[0], + BAZ = docSet.getByLongname('BAZ')[0]; + + it("sets the doclet's 'kind' property to 'constant'", function() { + expect(FOO.kind).toBe('constant'); + expect(BAR.kind).toBe('constant'); + expect(BAZ.kind).toBe('constant'); + }); + + it("If used as a standalone, takes the name from the code", function() { + expect(FOO.name).toBe('FOO'); + }); + + it("If used with just a name, sets the doclet's name to that", function() { + expect(BAR.name).toBe('BAR'); + }); + + it("If used with a name and a type, sets the doclet's name and type appropriately", function() { + expect(BAZ.name).toBe('BAZ'); + expect(typeof BAZ.type).toBe('object'); + expect(BAZ.type.names).toBeDefined(); + expect(BAZ.type.names.length).toBe(1); + expect(BAZ.type.names[0]).toBe('string'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/constructortag.js b/data/scripts/jsdoc/test/specs/tags/constructortag.js new file mode 100644 index 00000000..dd2bedad --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/constructortag.js @@ -0,0 +1,13 @@ +describe("@constructor tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constructortag.js'), + feed = docSet.getByLongname('Feed')[0]; + + it('When a symbol has an @constructor tag, it is documented as a class.', function() { + expect(feed.kind).toBe('class'); + }); + + it('When a symbol has an @constructor tag and a @class tag, the value of the @class tag becomes the classdesc property.', function() { + expect(feed.classdesc).toBe('Describe your class here.'); + expect(feed.description).toBe('Describe your constructor function here.'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/constructstag.js b/data/scripts/jsdoc/test/specs/tags/constructstag.js new file mode 100644 index 00000000..a7198a4c --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/constructstag.js @@ -0,0 +1,54 @@ +describe("@constructs tag", function() { + + it('When a symbol has an @constructs tag, it is documented as a class with that name.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constructstag.js'), + textblock = docSet.getByLongname('TextBlock')[0]; + + expect(textblock.kind).toBe('class'); + expect(textblock.longname).toBe('TextBlock'); + }); + + it('When a symbol has an @constructs tag, it is documented as a class.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constructstag2.js'), + menu = docSet.getByLongname('Menu')[0]; + + expect(menu.name).toBe('Menu'); + expect(menu.kind).toBe('class'); + }); + + it('When a function symbol has an @constructs tag, any this-variables are ducumented as instance members of the class.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constructstag3.js'), + personName = docSet.getByLongname('Person#name')[0]; + + expect(personName.memberof).toBe('Person'); + expect(personName.scope).toBe('instance'); + }); + + it('When a function symbol has an @constructs tag with no value, in a @lends block with a "Name#" value, the function is documented as a constructor of "Name".', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constructstag4.js'), + person = docSet.getByLongname('Person').filter(function($) { + return ! $.undocumented; + })[0]; + + expect(person.kind).toBe('class'); + }); + + it('When a function symbol has an @constructs tag with no value, any this-variables are documented as instance members of the class.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constructstag4.js'), + personName = docSet.getByLongname('Person#name')[0]; + + expect(personName.memberof).toBe('Person'); + expect(personName.scope).toBe('instance'); + }); + + it('When a object literal property has an @constructs tag with no value, and the object has a @lends, the property is documented as the lent class.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/constructstag5.js'), + duck = docSet.getByLongname('Duck').filter(function($) { + return ! $.undocumented; + })[0]; + + expect(duck.longname).toBe('Duck'); + expect(duck.kind).toBe('class'); + expect(duck.description).toBe('Constructs a duck.'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/copyrighttag.js b/data/scripts/jsdoc/test/specs/tags/copyrighttag.js new file mode 100644 index 00000000..c9c7a5f1 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/copyrighttag.js @@ -0,0 +1,8 @@ +describe("@copyright tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/copyrighttag.js'), + Thingy = docSet.getByLongname('Thingy')[0]; + + it('When a symbol has a @copyright tag, the doclet has a copyright property with that value.', function() { + expect(Thingy.copyright).toBe('(c) 2011 Michael Mathews'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/defaulttag.js b/data/scripts/jsdoc/test/specs/tags/defaulttag.js new file mode 100644 index 00000000..ca4b086b --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/defaulttag.js @@ -0,0 +1,39 @@ +describe("@default tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/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]; + + it('When symbol set to null has a @default tag with no text, the doclet\'s defaultValue property should be: null', function() { + expect(request.defaultvalue).toBe('null'); + }); + + it('When symbol set to a string has a @default tag with no text, the doclet\'s defaultValue property should be that quoted string', function() { + expect(response.defaultvalue).toBe('"ok"'); + }); + + it('When symbol set to a number has a @default tag with no text, the doclet\'s defaultValue property should be that number.', function() { + expect(rcode.defaultvalue).toBe('200'); + }); + + it('When symbol has a @default tag with text, the doclet\'s defaultValue property should be that text.', function() { + expect(win.defaultvalue).toBe('the parent window'); + }); + + it('When symbol has a @default tag with true.', function() { + expect(rvalid.defaultvalue).toBe('true'); + }); + + it('When symbol has a @default tag with false.', function() { + expect(rerrored.defaultvalue, 'false'); + }); + + it('When symbol has a @default tag with a function call.', function() { + expect(header.defaultvalue).toBeUndefined(); + }); + +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/deprecatedtag.js b/data/scripts/jsdoc/test/specs/tags/deprecatedtag.js new file mode 100644 index 00000000..0e60ce29 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/deprecatedtag.js @@ -0,0 +1,14 @@ +describe("@deprecated tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/deprecatedtag.js'), + foo = docSet.getByLongname('foo')[0], + bar = docSet.getByLongname('bar')[0]; + + it('When a symbol has a @deprecated tag with no value, the doclet has a deprecated property set to true.', function() { + expect(foo.deprecated).toBe(true); + }); + + it('When a symbol has a @deprecated tag with a value, the doclet has a deprecated property set to that value.', function() { + expect(bar.deprecated).toBe('since version 2.0'); + }); + +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/descriptiontag.js b/data/scripts/jsdoc/test/specs/tags/descriptiontag.js new file mode 100644 index 00000000..855280dc --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/descriptiontag.js @@ -0,0 +1,15 @@ +describe("@description tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/descriptiontag.js'), + doc = docSet.getByLongname('x')[0], + doc2 = docSet.getByLongname('y')[0]; + + it("sets the doclet's 'description' property to the description", function() { + expect(doc2.description).toBeDefined(); + expect(doc2.description).toBe('lkjasdf'); + }); + + it("overrides the default description", function() { + expect(doc.description).toBeDefined(); + expect(doc.description).toBe('halb halb halb'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/enumtag.js b/data/scripts/jsdoc/test/specs/tags/enumtag.js new file mode 100644 index 00000000..4f48727e --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/enumtag.js @@ -0,0 +1,31 @@ +describe("@enum tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/enumtag.js'), + tristate = docSet.getByLongname('TriState')[0]; + + it('When a symbol has a @enum tag, it has a properties array.', function() { + expect(typeof tristate.properties).toBe('object'); + }); + + it('If no @type is given for the property it is inherted from the enum.', function() { + expect(tristate.properties[0].type.names.join(', ')).toBe('number'); + }); + + it('If no no comment is given for the property it is still included in the enum.', function() { + expect(tristate.properties[1].longname).toBe('TriState.FALSE'); + expect(tristate.properties[1].undocumented).toBeUndefined(); + }); + + it('A property of an enum gets its defaultvalue set.', function() { + expect(tristate.properties[1].defaultvalue).toBe('-1'); + }); + + it('If a @type is given for the property it is reflected in the property value.', function() { + expect(tristate.properties[2].type.names.join(', ')).toBe('boolean'); + }); + + it('An enum does not contain any circular references.', function() { + var dump = require("jsdoc/util/dumper").dump; + + expect( dump(tristate) ).not.toMatch(""); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/eventfirestag.js b/data/scripts/jsdoc/test/specs/tags/eventfirestag.js new file mode 100644 index 00000000..55d86f4a --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/eventfirestag.js @@ -0,0 +1,30 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe('@event and @fires/@emits tags', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/eventfirestag.js'), + snowballMethod = docSet.getByLongname('Hurl#snowball')[0], + snowballEvent = docSet.getByLongname('Hurl#event:snowball')[0], + footballMatchMethod = docSet.getByLongname('Hurl#footballMatch')[0]; + + // @event tag + it('When a symbol has an @event tag, the doclet is of kind "event".', function() { + expect(snowballEvent.kind).toBe('event'); + }); + + // @fires/@emits tag + it('When a symbol has a @fires tag, the doclet has an array named "fires".', function() { + expect(typeof snowballMethod.fires).toBe('object'); + }); + + it('When a symbol has an @emits tag, the doclet has an array named "fires".', function() { + expect(typeof footballMatchMethod.fires).toBe('object'); + }); + + it('When a symbol has a "fires" array, the members have the "event:" namespace.', function() { + expect(snowballMethod.fires[0]).toBe('Hurl#event:snowball'); + }); + + it('When a symbol has a "fires" array with a name that already has an "event:" namespace, ' + + 'it does not have a second namespace applied.', function() { + expect(snowballMethod.fires[1]).toBe('Hurl#event:brick'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/exampletag.js b/data/scripts/jsdoc/test/specs/tags/exampletag.js new file mode 100644 index 00000000..8d1673c6 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/exampletag.js @@ -0,0 +1,22 @@ +describe("@example tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/exampletag.js'), + doc = docSet.getByLongname('x')[0], + doc2 = docSet.getByLongname('y')[0], + txt = 'console.log("foo");\nconsole.log("bar");', + txt2 = 'Example 2\n1 + 2;'; + + it("creates an 'examples' property on the doclet with the example", function() { + expect(doc.examples).toBeDefined(); + expect(Array.isArray(doc.examples)).toBe(true); + expect(doc.examples.length).toBe(1); + expect(doc.examples).toContain(txt); + }); + + it("can be specified multiple times on one doclet", function() { + expect(doc2.examples).toBeDefined(); + expect(Array.isArray(doc2.examples)).toBe(true); + expect(doc2.examples.length).toBe(2); + expect(doc2.examples).toContain(txt); + expect(doc2.examples).toContain(txt2); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/exceptiontag.js b/data/scripts/jsdoc/test/specs/tags/exceptiontag.js new file mode 100644 index 00000000..40d3a0c4 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/exceptiontag.js @@ -0,0 +1,17 @@ +describe("@exception tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/exceptiontag.js'), + foo = docSet.getByLongname('foo')[0], + bar = docSet.getByLongname('bar')[0], + pez = docSet.getByLongname('pez')[0]; + + it('When a symbol has an @exception tag, the doclet has a exception property set to that value.', function() { + expect(typeof foo.exceptions).toBe('object'); + expect(foo.exceptions.length).toBe(1); + + expect(typeof bar.exceptions).toBe('object'); + expect(bar.exceptions.length).toBe(1); + + expect(typeof pez.exceptions).toBe('object'); + expect(pez.exceptions.length).toBe(1); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/exportstag.js b/data/scripts/jsdoc/test/specs/tags/exportstag.js new file mode 100644 index 00000000..6d422754 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/exportstag.js @@ -0,0 +1,89 @@ +describe("@exports tag", function() { + + describe("object literals", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/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]; + + it('When an objlit symbol has an @exports tag, the doclet is aliased to "module:" + the tag value.', function() { + expect(typeof shirt).toEqual('object'); + expect(shirt.alias).toEqual('my/shirt'); + }); + + it('When an objlit symbol has an @exports tag, the doclet\'s longname includes the "module:" namespace.', function() { + expect(shirt.longname).toEqual('module:my/shirt'); + }); + + it('When an objlit symbol has an @exports tag, the doclet kind is set to module.', function() { + expect(shirt.kind).toEqual('module'); + }); + + it('When an objlit symbol has an @exports tag, the objlit members are documented as members of the module.', function() { + expect(typeof color).toEqual('object'); + expect(color.memberof).toEqual('module:my/shirt'); + + expect(typeof tneck).toEqual('object'); + expect(typeof size).toEqual('object'); + }); + }); + + describe("functions", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/exportstag2.js'), + coat = docSet.getByLongname('module:my/coat')[0], + wool = docSet.getByLongname('module:my/coat#wool')[0]; + + it('When a function symbol has an @exports tag, the doclet is aliased to "module:" + the tag value.', function() { + expect(typeof coat).toEqual('object'); + expect(coat.alias).toEqual('my/coat'); + }); + + it('When a function symbol has an @exports tag, the doclet\'s longname includes the "module:" namespace.', function() { + expect(coat.longname).toEqual('module:my/coat'); + }); + + it('When a function symbol has an @exports tag, the doclet kind is set to module.', function() { + expect(coat.kind).toEqual('module'); + }); + + it('When a function symbol has an @exports tag, the this members are documented as instance members of the module.', function() { + expect(typeof wool).toEqual('object'); + expect(wool.memberof).toEqual('module:my/coat'); + }); + }); + + describe("functions and 'exports' object", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/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]; + + it('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() { + expect(typeof getstyle).toEqual('object'); + expect(getstyle.memberof).toEqual('module:html/utils'); + }); + + it('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() { + expect(typeof inhead).toEqual('object'); + expect(inhead.memberof).toEqual('module:html/utils'); + }); + }); + + describe("inner classes", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/exportstag4.js'), + module = docSet.getByLongname('module:some/module')[0], + innerClass = docSet.getByLongname('module:some/module~myClass')[0], + method = docSet.getByLongname('module:some/module~myClass#myMethod')[0]; + + it('An inner class declared as a function in a module should be documented.', function() { + expect(typeof innerClass).toEqual('object'); + //expect(getstyle.memberof, 'module:html/utils'); + }); + + it('A method of an inner class declared as a function in a module should be documented.', function() { + expect(typeof method).toEqual('object'); + //expect(inhead.memberof, 'module:html/utils'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/functiontag.js b/data/scripts/jsdoc/test/specs/tags/functiontag.js new file mode 100644 index 00000000..df63f6b9 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/functiontag.js @@ -0,0 +1,18 @@ +describe("@function tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/functiontag.js'), + doc = docSet.getByLongname('Foo')[0], + doc2 = docSet.getByLongname('Bar')[0]; + + it("sets the doclet's kind to 'function'", function() { + expect(doc.kind).toBe('function'); + expect(doc2.kind).toBe('function'); + }); + + it("sets the doclet's name to the tag value, if provided", function() { + expect(doc.name).toBe('Foo'); + expect(doc2.name).toBe('Bar'); + }); + + // parameter etc tests take place elsewhere: on its own, all @func does is + // set doclet.kind to function and sets the doclet's name. +}); diff --git a/data/scripts/jsdoc/test/specs/tags/globaltag.js b/data/scripts/jsdoc/test/specs/tags/globaltag.js new file mode 100644 index 00000000..3aeb34d8 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/globaltag.js @@ -0,0 +1,24 @@ +describe("@global tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/globaltag.js'); + + it('When an inner symbol has a @global tag it is documented as if it were global.', function() { + var found = docSet.getByLongname('foo').filter(function($) { + return ! $.undocumented; + }); + expect(found[0].name).toBe('foo'); + expect(found[0].longname).toBe('foo'); + expect(found[0].memberof).toBeUndefined(); + expect(found[0].scope).toBe('global'); + + }); + + it('When an nested symbol has a @global tag it is documented as if it were global.', function() { + var found = docSet.getByLongname('Bar').filter(function($) { + return ! $.undocumented; + }); + expect(found[0].name).toBe('Bar'); + expect(found[0].longname).toBe('Bar'); + expect(found[0].memberof).toBeUndefined(); + expect(found[0].scope).toBe('global'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/ignoretag.js b/data/scripts/jsdoc/test/specs/tags/ignoretag.js new file mode 100644 index 00000000..8536143c --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/ignoretag.js @@ -0,0 +1,18 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe("@ignore tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/ignoretag.js'), + foo = docSet.getByLongname('foo')[0]; + + it('When a symbol has an @ignore tag, the doclet has a ignore property set to true.', function() { + expect(foo.ignore).toBe(true); + }); + + it('When a symbol has an @ignore tag with a value an error is thrown', function() { + try { + docSet = jasmine.getDocSetFromFile('test/fixtures/ignoretag2.js'); + foo = docSet.getByLongname('foo')[0]; + } catch (e) { + expect(e instanceof Error).toBe(true); + } + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/kindtag.js b/data/scripts/jsdoc/test/specs/tags/kindtag.js new file mode 100644 index 00000000..49d9cd5d --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/kindtag.js @@ -0,0 +1,8 @@ +describe("@kind tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/kindtag.js'), + doc = docSet.getByLongname('x')[0]; + it("sets the doclet's 'kind' property to the tag value", function() { + expect(doc.kind).toBeDefined(); + expect(doc.kind).toBe('function'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/lendstag.js b/data/scripts/jsdoc/test/specs/tags/lendstag.js new file mode 100644 index 00000000..0f4a685d --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/lendstag.js @@ -0,0 +1,16 @@ +describe("@lends tag", function() { + // see also specs/documentation/lends.js for tests on @lends behaviour. + var doclet = require('jsdoc/doclet'), + doc = new doclet.Doclet('/** @lends */', {}), + doc2 = new doclet.Doclet('/** @lends MyClass# */', {}); + + it("sets the doclet's 'alias' property to the tag value or ", function() { + expect(doc.alias).toBe(''); + expect(doc2.alias).toBe('MyClass#'); + }); + + it("sets the doclet's 'undocumented' property to 'true'", function() { + expect(doc.undocumented).toBeTruthy(); + expect(doc2.undocumented).toBeTruthy(); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/licensetag.js b/data/scripts/jsdoc/test/specs/tags/licensetag.js new file mode 100644 index 00000000..e37b1c63 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/licensetag.js @@ -0,0 +1,8 @@ +describe("@license tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/licensetag.js'), + doc = docSet.getByLongname('x')[0]; + + it("sets the doclet's 'license' property to the tag value", function() { + expect(doc.license).toBe('GPL v2'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/listenstag.js b/data/scripts/jsdoc/test/specs/tags/listenstag.js new file mode 100644 index 00000000..742d5085 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/listenstag.js @@ -0,0 +1,15 @@ +describe("@listens tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/listenstag.js'), + doc = docSet.getByLongname('module:myModule~MyHandler')[0]; + + it("should create a 'listens' property on the doclet, an array, with the events that are listened to (with event namespace)", function() { + expect(Array.isArray(doc.listens)).toBeTruthy(); + expect(doc.listens).toContain('module:myModule.event:MyEvent'); + expect(doc.listens).toContain('module:myModule~Events.event:Event2'); + }); + + it("includes events even if non-existent", function() { + expect(doc.listens.length).toBe(3); + expect(doc.listens).toContain('event:fakeEvent'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/memberoftag.js b/data/scripts/jsdoc/test/specs/tags/memberoftag.js new file mode 100644 index 00000000..46d61201 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/memberoftag.js @@ -0,0 +1,83 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe("@memberof tag", function() { + + it('When a symbol has an @member tag, the doclet has a long name that includes the parent.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/memberoftag.js'), + Data = docSet.getByLongname('mathlib.Data')[0], + point = docSet.getByLongname('mathlib.Data#point')[0]; + + expect(typeof Data).toBe('object'); + expect(typeof point).toBe('object'); + + expect(Data.memberof).toBe('mathlib'); + expect(Data.name).toBe('Data'); + }); + + it('A symbol within a namespace for which no scope is specified.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/memberoftag4.js'), + doOtherStuff = docSet.getByLongname('doStuff.doOtherStuff')[0]; + + expect(doOtherStuff).toBeDefined(); + expect(doOtherStuff.scope).toBe('static'); + }); + + it('A symbol in which name === memberof.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/memberoftag4.js'), + doStuff = docSet.getByLongname('doStuff.doStuff')[0]; + + expect(doStuff).toBeDefined(); + expect(doStuff.scope).toBe('static'); + }); + + describe ("static", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/memberoftag2.js'), + publish = docSet.getByLongname('Observable#publish')[0], + cache = docSet.getByLongname('Observable.cache')[0]; + + it('A symbol is documented as a static @memberof a class.', function() { + //it should appear as a static member of that class + expect(typeof cache).toBe('object'); + expect(cache.memberof).toBe('Observable'); + expect(cache.scope).toBe('static'); + expect(cache.name).toBe('cache'); + expect(cache.longname).toBe('Observable.cache'); + }); + + it('A symbol is documented as a static @memberof a class prototype.', function() { + //it should appear as an instance member of that class + expect(typeof publish).toBe('object'); + expect(publish.memberof).toBe('Observable'); + expect(publish.scope).toBe('instance'); + expect(publish.name).toBe('publish'); + expect(publish.longname).toBe('Observable#publish'); + }); + }); + + describe ("forced", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/memberoftagforced.js'), + maproutes = docSet.getByLongname('map.routes')[0], + datapointy = docSet.getByLongname('Data#point.y')[0]; + + it('A nested symbol with a @memberof! tag set to .', function() { + expect(maproutes.name, 'map.routes', 'Has a shortname that includes the nested names.'); + }); + + it('A nested symbol with a @memberof! tag set to another symbol.', function() { + expect(datapointy.name, 'point.y', 'Has a shortname that includes the nested names.'); + }); + }); + + it('A symbol that is a nested class with a @memberof tag.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/memberoftag3.js'), + tree = docSet.getByLongname('module:terrain.Forest#Tree')[0]; + + expect(tree.longname, 'module:terrain.Forest#Tree'); + }); + + it('A symbol that is an instance member of a nested class with a @memberof tag.', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/memberoftag3.js'), + leaf = docSet.getByLongname('module:terrain.Forest#Tree#leaf')[0]; + + expect(leaf.longname, 'module:terrain.Forest#Tree#leaf'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/membertag.js b/data/scripts/jsdoc/test/specs/tags/membertag.js new file mode 100644 index 00000000..cfea6de3 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/membertag.js @@ -0,0 +1,25 @@ +describe("@member tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/membertag.js'), + doc = docSet.getByLongname('x')[0], + doc2 = docSet.getByLongname('foobar')[0], + doc3 = docSet.getByLongname('baz')[0]; + + it("sets the doclet's 'kind' property to 'member'", function() { + expect(doc.kind).toBe('member'); + expect(doc2.kind).toBe('member'); + expect(doc3.kind).toBe('member'); + }); + + it("If specified with a name, sets the doclet's name property", function() { + expect(doc.name).toBe('x'); + expect(doc2.name).toBe('foobar'); + expect(doc3.name).toBe('baz'); + }); + + it("If specified with a type and name, sets the doclet's type appropriately", function() { + expect(doc3.type).toBeDefined(); + expect(Array.isArray(doc3.type.names)).toBeTruthy(); + expect(doc3.type.names.length).toBe(1); + expect(doc3.type.names[0]).toBe('string'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/mixestag.js b/data/scripts/jsdoc/test/specs/tags/mixestag.js new file mode 100644 index 00000000..6bb4f827 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/mixestag.js @@ -0,0 +1,19 @@ +describe("@mixes tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/mixintag.js'), + FormButton = docSet.getByLongname('FormButton')[0], + MyClass = docSet.getByLongname('MyClass')[0]; + + it("When a symbol has a @mixes tag, it gets an array property 'mixes' with the name of the mixin", function() { + expect(FormButton.mixes).toBeDefined(); + expect(Array.isArray(FormButton.mixes)).toBe(true); + expect(FormButton.mixes.length).toBe(1); + expect(FormButton.mixes[0]).toBe('Eventful'); + }); + + it("A symbol can @mixes multiple mixins and they are all added.", function() { + expect(MyClass.mixes).toBeDefined(); + expect(MyClass.mixes.length).toBe(2); + expect(MyClass.mixes).toContain('Eventful'); + expect(MyClass.mixes).toContain('AnotherMixin'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/mixintag.js b/data/scripts/jsdoc/test/specs/tags/mixintag.js new file mode 100644 index 00000000..4c928fb2 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/mixintag.js @@ -0,0 +1,13 @@ +describe("@mixin tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/mixintag.js'), + Eventful = docSet.getByLongname('Eventful')[0], + Mixin = docSet.getByLongname('AnotherMixin')[0]; + + it("When a symbol has a @mixin tag, the doclet's 'kind' property is set to 'mixin'", function() { + expect(Eventful.kind).toBe('mixin'); + }); + + it("When a symbol has a @mixin tag, its name is set to the tag's value (if present)", function() { + expect(Mixin).toBeDefined(); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/moduletag.js b/data/scripts/jsdoc/test/specs/tags/moduletag.js new file mode 100644 index 00000000..e2b44e30 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/moduletag.js @@ -0,0 +1,41 @@ +describe("@module tag", function() { + describe("using 'this'", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/moduletag.js'), + book = docSet.getByLongname('module:bookshelf.Book')[0], + title = docSet.getByLongname('module:bookshelf.Book#title')[0]; + + it('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() { + expect(typeof book).toBe('object'); + expect(book.memberof).toBe('module:bookshelf'); + }); + + it('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() { + expect(typeof title).toBe('object'); + expect(title.memberof).toBe('module:bookshelf.Book'); + }); + }); + + describe("misc", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/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]; + + it('When a @module tag defines a module module, a symbol of kind "module" is documented', function() { + expect(typeof mixer).toBe('object'); + expect(mixer.kind).toBe('module'); + }); + + it('When an object literal is lent to a module with a @lends tag, A member of that object literal is documented as a member of the module', function() { + expect(typeof blend).toBe('object'); + expect(blend.kind).toBe('function'); + }); + + it('When a documented symbol is a member of a namespace "exports", it is documented as a member of the module', function() { + expect(typeof darken).toBe('object'); + expect(darken.kind).toBe('function'); + }); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/namespacetag.js b/data/scripts/jsdoc/test/specs/tags/namespacetag.js new file mode 100644 index 00000000..182ce0f3 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/namespacetag.js @@ -0,0 +1,25 @@ +describe("@namespace tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/namespacetag.js'), + doc = docSet.getByLongname('x')[0], + doc2 = docSet.getByLongname('Foo')[0], + doc3 = docSet.getByLongname('Bar')[0]; + + it("sets the doclet's kind to 'namespace'", function () { + expect(doc.kind).toBe('namespace'); + expect(doc2.kind).toBe('namespace'); + expect(doc3.kind).toBe('namespace'); + }); + + it("sets the doclet's name to the tag value (if provided)", function() { + expect(doc.name).toBe('x'); + expect(doc2.name).toBe('Foo'); + expect(doc3.name).toBe('Bar'); + }); + + it("sets the doclet's type (if provided in @namespace)", function() { + expect(doc3.type).toBeDefined(); + expect(Array.isArray(doc3.type.names)).toBeTruthy(); + expect(doc3.type.names.length).toBe(1); + expect(doc3.type.names[0]).toBe('function'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/overviewtag.js b/data/scripts/jsdoc/test/specs/tags/overviewtag.js new file mode 100644 index 00000000..6087be5b --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/overviewtag.js @@ -0,0 +1,18 @@ +/*global describe: true, env: true, expect: true, it: true */ + +describe("@overview tag", function() { + var parser = require('jsdoc/src/parser'), + srcParser = new parser.Parser(), + doclets; + + require('jsdoc/src/handlers').attachTo(srcParser); + doclets = srcParser.parse(__dirname + '/test/fixtures/file.js'); + + it('When a file overview tag appears in a doclet, the name of the doclet should contain the path to the file.', function() { + expect(doclets[0].name).toMatch(/^(fixtures[\/\\]file\.js)$/); + }); + + it("The name and longname should be equal", function() { + expect(doclets[0].name).toBe(doclets[0].longname); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/paramtag.js b/data/scripts/jsdoc/test/specs/tags/paramtag.js new file mode 100644 index 00000000..7145c83f --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/paramtag.js @@ -0,0 +1,80 @@ +describe("@param tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/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], + request = docSet.getByLongname('request')[0]; + + it('When a symbol has an @param tag with a type before the name, the doclet has a params property that includes that param.', function() { + expect(typeof find.params).toBe('object'); + expect(find.params.length).toBe(1); + expect(find.params[0].type.names.join(', ')).toBe('String, Array.'); + expect(find.params[0].name).toBe('targetName'); + expect(find.params[0].description).toBe('The name (or names) of what to find.'); + }); + + it('When a symbol has an @param tag with only a type and name, the doclet has a params property that includes that param.', function() { + expect(typeof bind.params).toBe('object'); + expect(bind.params.length).toBe(1); + expect(bind.params[0].type.names.join(', ')).toBe('function'); + expect(bind.params[0].name).toBe('callback'); + expect(bind.params[0].description).toBeUndefined(); + }); + + it('When a symbol has an @param tag with only a type, the doclet has a params property that includes that param.', function() { + expect(typeof unbind.params).toBe('object'); + expect(unbind.params.length).toBe(1); + expect(unbind.params[0].type.names.join(', ')).toBe('function'); + expect(unbind.params[0].description).toBeUndefined(); + }); + + it('When a symbol has an @param tag with no type, the doclet has a params property that includes that param.', function() { + expect(typeof getElement.params).toBe('object'); + expect(getElement.params.length).toBe(1); + expect(getElement.params[0].type).toBeUndefined(); + expect(getElement.params[0].name).toBe('id'); + expect(getElement.params[0].description).toBe('The id of the element.'); + }); + + it('When a symbol has an @param tag with a non-alpha name like "...", the doclet has a params property that includes that param.', function() { + expect(typeof combine.params).toBe('object'); + expect(combine.params.length).toBe(1); + expect(combine.params[0].type).toBeUndefined(); + expect(combine.params[0].name).toBe('...'); + expect(combine.params[0].description).toBe('Two or more elements.'); + }); + + it('When a symbol has an @param tag with name followed by a dash, the doclet has a params property that includes that param.', function() { + expect(typeof split.params).toBe('object'); + expect(split.params.length).toBe(1); + expect(split.params[0].type).toBeUndefined(); + expect(split.params[0].name).toBe('delimiter'); + expect(split.params[0].description).toBe('What to split on.'); + }); + + it('When a symbol has an @param tag with no name or type, the doclet has a params property that includes that param.', function() { + expect(typeof commit.params).toBe('object'); + expect(commit.params.length).toBe(1); + expect(commit.params[0].type).toBeUndefined(); + expect(commit.params[0].description).toBe('If true make the commit atomic.'); + }); + + it('When a symbol has a @param tag with no type but a name that indicates a default value or optional type, this is copied over to the params property.', function() { + expect(typeof request.params).toBe('object'); + expect(request.params.length).toBe(1); + expect(request.params[0].type).toBeUndefined(); + expect(request.params[0].name).toBe('async'); + expect(request.params[0].defaultvalue).toBe('true'); + expect(request.params[0].optional).toBe(true); + expect(request.params[0].description).toBe('whether to be asynchronous'); + }); + + it('When a symbol has an @param tag with no name and a name is given in the code, the doclet has a params property that includes that param with the name from the code.', function() { + expect(commit.params[0].name).toBe('atomic'); + }); + +}); diff --git a/data/scripts/jsdoc/test/specs/tags/privatetag.js b/data/scripts/jsdoc/test/specs/tags/privatetag.js new file mode 100644 index 00000000..a4d5aceb --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/privatetag.js @@ -0,0 +1,9 @@ +describe("@private tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/privatetag.js'), + foo = docSet.getByLongname('Foo')[0], + bar = docSet.getByLongname('Foo#bar')[0]; + + it('When a symbol has an @private tag, the doclet has an access property that is "private".', function() { + expect(foo.access).toBe('private'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/propertytag.js b/data/scripts/jsdoc/test/specs/tags/propertytag.js new file mode 100644 index 00000000..5ef94231 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/propertytag.js @@ -0,0 +1,15 @@ +describe("@property tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/propertytag.js'), + myobject = docSet.getByLongname('myobject')[0]; + + it('When a symbol has an @property tag with a those properties appear in the parsed object.', function() { + expect(typeof myobject.properties).toBe('object'); + expect(myobject.properties.length).toBe(3); + expect(myobject.properties[0].name).toBe('defaults'); + expect(myobject.properties[1].name).toBe('defaults.a'); + expect(myobject.properties[2].name).toBe('defaults.b'); + expect(myobject.properties[0].description).toBe('The default values.'); + expect(myobject.properties[0].type.names[0]).toBe('Object'); + }); + +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/readonlytag.js b/data/scripts/jsdoc/test/specs/tags/readonlytag.js new file mode 100644 index 00000000..8465cb80 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/readonlytag.js @@ -0,0 +1,9 @@ +describe("@readonly tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/readonlytag.js'), + Collection = docSet.getByLongname('Collection')[0], + length = docSet.getByLongname('Collection#length')[0]; + + it('When a symbol has an @readonly tag, the doclet has an readonly property that is true.', function() { + expect(length.readonly).toBe(true); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/requirestag.js b/data/scripts/jsdoc/test/specs/tags/requirestag.js new file mode 100644 index 00000000..40eae164 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/requirestag.js @@ -0,0 +1,14 @@ +describe("@requires tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/requirestag.js'), + foo = docSet.getByLongname('foo')[0], + bar = docSet.getByLongname('bar')[0]; + + it('When a symbol has an @requires tag, the doclet has a requires property that includes that value, with the "module:" namespace added.', function() { + expect(typeof foo.requires).toBe('object'); + expect(foo.requires[0]).toBe('module:foo/helper'); + + expect(typeof bar.requires).toBe('object'); + expect(bar.requires[0]).toBe('module:foo'); + expect(bar.requires[1]).toBe('module:Pez#blat'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/returnstag.js b/data/scripts/jsdoc/test/specs/tags/returnstag.js new file mode 100644 index 00000000..2fcf2e59 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/returnstag.js @@ -0,0 +1,18 @@ +describe("@returns tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/returnstag.js'), + find = docSet.getByLongname('find')[0], + bind = docSet.getByLongname('bind')[0]; + + it('When a symbol has an @returns tag with a type and description, the doclet has a returns array that includes that return.', function() { + expect(typeof find.returns).toBe('object'); + expect(find.returns.length).toBe(1); + expect(find.returns[0].type.names.join(', ')).toBe('String, Array.'); + expect(find.returns[0].description).toBe('The names of the found item(s).'); + }); + + it('When a symbol has an @returns tag with only a description, the doclet has a returns array property that includes that return.', function() { + expect(typeof bind.returns).toBe('object'); + expect(bind.returns.length).toBe(1); + expect(bind.returns[0].description).toBe('The binding id.'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/scopetags.js b/data/scripts/jsdoc/test/specs/tags/scopetags.js new file mode 100644 index 00000000..0b18a58c --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/scopetags.js @@ -0,0 +1,31 @@ +describe('scope tags', function () { + var docSet = jasmine.getDocSetFromFile('test/fixtures/scopetags.js'); + + // @inner, @instance, @static (@global has its own file) + describe("@inner tag", function() { + var doc = docSet.getByLongname('module:scopetags~myInner')[0]; + + it("sets the doclet's 'scope' property to 'inner'", function() { + expect(doc.scope).toBeDefined(); + expect(doc.scope).toBe('inner'); + }); + }); + + describe("@instance tag", function() { + var doc = docSet.getByLongname('module:scopetags#myInstance')[0]; + + it("sets the doclet's 'scope' property to 'instance'", function() { + expect(doc.scope).toBeDefined(); + expect(doc.scope).toBe('instance'); + }); + }); + + describe("@static tag", function() { + var doc = docSet.getByLongname('module:scopetags.myStatic')[0]; + + it("sets the doclet's 'scope' property to 'static'", function() { + expect(doc.scope).toBeDefined(); + expect(doc.scope).toBe('static'); + }); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/seetag.js b/data/scripts/jsdoc/test/specs/tags/seetag.js new file mode 100644 index 00000000..d6228a59 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/seetag.js @@ -0,0 +1,13 @@ +describe("@see tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/seetag.js'), + foo = docSet.getByLongname('foo')[0], + bar = docSet.getByLongname('bar')[0]; + + it('When a symbol has an @see tag, the doclet has a see property that includes that value.', function() { + expect(typeof foo.see).toBe('object'); + expect(foo.see[0]).toBe('{@link bar}'); + + expect(typeof bar.see).toBe('object'); + expect(bar.see[0]).toBe('http://example.com/someref'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/sincetag.js b/data/scripts/jsdoc/test/specs/tags/sincetag.js new file mode 100644 index 00000000..91ab3d83 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/sincetag.js @@ -0,0 +1,8 @@ +describe("@since tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/sincetag.js'), + foo = docSet.getByLongname('foo')[0]; + + it('When a symbol has an @since tag, the doclet has a since property set to true.', function() { + expect(foo.since).toBe('1.2.3'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/summarytag.js b/data/scripts/jsdoc/test/specs/tags/summarytag.js new file mode 100644 index 00000000..a4627153 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/summarytag.js @@ -0,0 +1,8 @@ +describe("@summary tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/summarytag.js'), + doc = docSet.getByLongname('Sam')[0]; + it("sets the doclet's 'summary' property to the tag value", function() { + expect(doc.summary).toBe('I do not like green eggs and ham!'); + }); +}); + diff --git a/data/scripts/jsdoc/test/specs/tags/thistag.js b/data/scripts/jsdoc/test/specs/tags/thistag.js new file mode 100644 index 00000000..5fac4ae6 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/thistag.js @@ -0,0 +1,16 @@ +describe("@this tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/thistag.js'), + setName = docSet.getByLongname('setName')[0], + fooName = docSet.getByLongname('Foo#name')[0]; + + it('When a symbol has a @this tag, the doclet has a this property that is set to that value.', function() { + expect(setName['this']).toBe('Foo'); + }); + + it('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() { + expect(typeof fooName).toBe('object'); + expect(fooName.name).toBe('name'); + expect(fooName.memberof).toBe('Foo'); + expect(fooName.scope).toBe('instance'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/todotag.js b/data/scripts/jsdoc/test/specs/tags/todotag.js new file mode 100644 index 00000000..410bb55f --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/todotag.js @@ -0,0 +1,13 @@ +describe("@todo tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/todotag.js'), + doc = docSet.getByLongname('x')[0]; + + it("adds the entries into a 'todo' array on the doclet", function() { + expect(doc.todo).toBeDefined(); + expect(Array.isArray(doc.todo)).toBeTruthy(); + expect(doc.todo.length).toBe(2); + + expect(doc.todo).toContain('something'); + expect(doc.todo).toContain('something else'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/tutorialtag.js b/data/scripts/jsdoc/test/specs/tags/tutorialtag.js new file mode 100644 index 00000000..b334a266 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/tutorialtag.js @@ -0,0 +1,13 @@ +describe("@tutorial tag", function() { + // these are tests for the block usage, not the inline usage. see util/templateHelper for that. + var docSet = jasmine.getDocSetFromFile('test/fixtures/tutorialtag.js'), + doc = docSet.getByLongname('x')[0]; + + it("adds the listed tutorials to a 'tutorials' array on the doclet", function () { + expect(Array.isArray(doc.tutorials)).toBeTruthy(); + expect(doc.tutorials.length).toBe(2); + expect(doc.tutorials).toContain('tute1'); + expect(doc.tutorials).toContain('tute2'); + }); +}); + diff --git a/data/scripts/jsdoc/test/specs/tags/typedeftag.js b/data/scripts/jsdoc/test/specs/tags/typedeftag.js new file mode 100644 index 00000000..a3cacebb --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/typedeftag.js @@ -0,0 +1,34 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ +describe('@typedef tag', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/typedeftag.js'); + var numberlike = docSet.getByLongname('calc.NumberLike')[0]; + var operator = docSet.getByLongname('calc.Operator')[0]; + var result = docSet.getByLongname('calc.Result')[0]; + + it('When a comment has a @typedef tag, the doclet has a kind property set to "typedef".', function() { + expect(numberlike.kind).toBe('typedef'); + }); + + it('When a comment has a @typedef tag with a type, the doclet has a type property set to that type.', function() { + expect(typeof numberlike.type).toBe('object'); + expect(typeof numberlike.type.names).toBe('object'); + expect(numberlike.type.names.length).toBe(2); + expect(numberlike.type.names[0]).toBe('string'); + expect(numberlike.type.names[1]).toBe('number'); + }); + + it('When a comment has a @typedef tag with a name, the doclet has a name property set to that name.', function() { + expect(numberlike.name).toBe('NumberLike'); + expect(numberlike.longname).toBe('calc.NumberLike'); + }); + + it('When a symbol has a @typedef tag without a name, the doclet has a name property set to the symbol name.', function() { + expect(operator.name).toBe('Operator'); + expect(operator.longname).toBe('calc.Operator'); + }); + + it('When a symbol has a @typedef tag with a name, the name in the tag takes precedence over the symbol name.', function() { + expect(result.name).toBe('Result'); + expect(result.longname).toBe('calc.Result'); + }); +}); diff --git a/data/scripts/jsdoc/test/specs/tags/typekind.js b/data/scripts/jsdoc/test/specs/tags/typekind.js new file mode 100644 index 00000000..aea60db0 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/typekind.js @@ -0,0 +1,15 @@ +describe("@kind tag with type", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/typekind.js'), + blog = docSet.getByLongname('module:blog/server')[0], + port = docSet.getByLongname('module:blog/server.port')[0]; + + it('When a module symbol has an kind tag, that includes a {type} clause, the doclet has a type property set to that {type} clause', function() { + expect(typeof blog.type).toBe('object'); + expect(blog.type.names.join(', ')).toBe('ConnectServer'); + }); + + it('When a property symbol has an kind tag, that includes a {type} clause, the doclet has a type property set to that {type} clause', function() { + expect(typeof port.type).toBe('object'); + expect(port.type.names.join(', ')).toBe('number'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/typetag.js b/data/scripts/jsdoc/test/specs/tags/typetag.js new file mode 100644 index 00000000..055fa81d --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/typetag.js @@ -0,0 +1,15 @@ +describe("@type tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/typetag.js'), + foo = docSet.getByLongname('foo')[0], + bar = docSet.getByLongname('bar')[0]; + + it('When a symbol has an @type tag, the doclet has a type property set to that value\'s type.', function() { + expect(typeof foo.type).toBe('object'); + expect(typeof foo.type.names).toBe('object'); + expect(foo.type.names.join(', ')).toBe('string, Array.'); + }); + + it('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() { + expect(bar.type.names.join(', ')).toBe('integer'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/specs/tags/undocumentedtag.js b/data/scripts/jsdoc/test/specs/tags/undocumentedtag.js new file mode 100644 index 00000000..90052234 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/undocumentedtag.js @@ -0,0 +1,13 @@ +describe("@undocumented tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/undocumentedtag.js'), + doc = docSet.getByLongname('x')[0]; + + it("sets the doclet's 'undocumented' property to true", function () { + expect(doc.undocumented).toBeTruthy(); + }); + + it("clears the doclet's 'comment' property", function () { + expect(doc.comment).toBe(''); + }); +}); + diff --git a/data/scripts/jsdoc/test/specs/tags/versiontag.js b/data/scripts/jsdoc/test/specs/tags/versiontag.js new file mode 100644 index 00000000..1b50ada2 --- /dev/null +++ b/data/scripts/jsdoc/test/specs/tags/versiontag.js @@ -0,0 +1,8 @@ +describe("@version tag", function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/versiontag.js'), + foo = docSet.getByLongname('foo')[0]; + + it('When a symbol has an @version tag, the doclet has a version property set to that value.', function() { + expect(foo.version).toBe('1.2.3'); + }); +}); \ No newline at end of file diff --git a/data/scripts/jsdoc/test/tutorials/build.sh b/data/scripts/jsdoc/test/tutorials/build.sh new file mode 100644 index 00000000..8937f26d --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/build.sh @@ -0,0 +1,2 @@ +rm -rf out +../../jsdoc -u tutorials src -d out diff --git a/data/scripts/jsdoc/test/tutorials/duplicateDefined/asdf.html b/data/scripts/jsdoc/test/tutorials/duplicateDefined/asdf.html new file mode 100644 index 00000000..1f8a6622 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/duplicateDefined/asdf.html @@ -0,0 +1 @@ +

      tutorial ASDF

      diff --git a/data/scripts/jsdoc/test/tutorials/duplicateDefined/asdf.json b/data/scripts/jsdoc/test/tutorials/duplicateDefined/asdf.json new file mode 100644 index 00000000..c769dc54 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/duplicateDefined/asdf.json @@ -0,0 +1 @@ +{"title": "Conflicting title"} diff --git a/data/scripts/jsdoc/test/tutorials/duplicateDefined/index.json b/data/scripts/jsdoc/test/tutorials/duplicateDefined/index.json new file mode 100644 index 00000000..9ceb182f --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/duplicateDefined/index.json @@ -0,0 +1,5 @@ +{ + "asdf": { + "title": "Tutorial Asdf" + } +} diff --git a/data/scripts/jsdoc/test/tutorials/incomplete/parent.html b/data/scripts/jsdoc/test/tutorials/incomplete/parent.html new file mode 100644 index 00000000..aa4a4ff9 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/incomplete/parent.html @@ -0,0 +1,3 @@ +

      Test.html

      + +

      {@link Test}

      diff --git a/data/scripts/jsdoc/test/tutorials/incomplete/parent.json b/data/scripts/jsdoc/test/tutorials/incomplete/parent.json new file mode 100644 index 00000000..f2e4765a --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/incomplete/parent.json @@ -0,0 +1 @@ +{"title": "missing child tutorial", "children": ["child"]} diff --git a/data/scripts/jsdoc/test/tutorials/src/x.js b/data/scripts/jsdoc/test/tutorials/src/x.js new file mode 100644 index 00000000..987f8f98 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/src/x.js @@ -0,0 +1,8 @@ +/** + * Test {@tutorial test2} {@tutorial dupa} + * + * @class + * @tutorial test + * @tutorial jasia + */ +function Test() {} diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/constructor.md b/data/scripts/jsdoc/test/tutorials/tutorials/constructor.md new file mode 100644 index 00000000..2f181f08 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/constructor.md @@ -0,0 +1 @@ +This tutorial has a tricksy name to make sure we are not loading Array.constructor or Object.constructor. diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/multiple.json b/data/scripts/jsdoc/test/tutorials/tutorials/multiple.json new file mode 100644 index 00000000..994d8655 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/multiple.json @@ -0,0 +1,12 @@ +{ + "test2": { + "title": "Test 2", + "children": ["test3", "test6"] + }, + "test3": { + "title": "Test 3", + "children": { + "test4": {"title": "Test 4"} + } + } +} diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/test.html b/data/scripts/jsdoc/test/tutorials/tutorials/test.html new file mode 100644 index 00000000..aa4a4ff9 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/test.html @@ -0,0 +1,3 @@ +

      Test.html

      + +

      {@link Test}

      diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/test.json b/data/scripts/jsdoc/test/tutorials/tutorials/test.json new file mode 100644 index 00000000..d894f21e --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/test.json @@ -0,0 +1 @@ +{"title": "Test tutorial", "children": ["test2"]} diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/test2.markdown b/data/scripts/jsdoc/test/tutorials/tutorials/test2.markdown new file mode 100644 index 00000000..09510c03 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/test2.markdown @@ -0,0 +1 @@ +# test2.markdown diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/test3.htm b/data/scripts/jsdoc/test/tutorials/tutorials/test3.htm new file mode 100644 index 00000000..8eac0524 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/test3.htm @@ -0,0 +1,3 @@ +

      Test3.html

      + +

      {@link Test}

      diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/test4.md b/data/scripts/jsdoc/test/tutorials/tutorials/test4.md new file mode 100644 index 00000000..0be836ad --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/test4.md @@ -0,0 +1 @@ +# test4.md diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/test5.txt b/data/scripts/jsdoc/test/tutorials/tutorials/test5.txt new file mode 100644 index 00000000..1769f0de --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/test5.txt @@ -0,0 +1 @@ +Should not be included as a tutorial. diff --git a/data/scripts/jsdoc/test/tutorials/tutorials/test6.xml b/data/scripts/jsdoc/test/tutorials/tutorials/test6.xml new file mode 100644 index 00000000..1c489be3 --- /dev/null +++ b/data/scripts/jsdoc/test/tutorials/tutorials/test6.xml @@ -0,0 +1 @@ +

      test 6 - has no metadata

      diff --git a/doc/Code Docs/generate-docs.bat b/doc/Code Docs/generate-docs.bat new file mode 100644 index 00000000..e4495c22 --- /dev/null +++ b/doc/Code Docs/generate-docs.bat @@ -0,0 +1,17 @@ +:: Easy!Appointments +:: +:: This script generates the code documentation of the system +:: using the jsdoc and apigen tools. In order to run properly +:: the system should have Java and ApiGen installed already. +:: +:: THIS SCRIPT RUNS ONLY ON WINDOWS. YOU MIGHT NEED TO CHANGE +:: THE APIGEN SCRIPT PATH TO WHERE YOUR INSTALLATION EXISTS. +:: +:: You might need to change the mermory_limit setting on your +:: php.ini file to successfully generate the php documentation. +::@echo off +TITLE Easy!Appointments +DEL "js\*.*" /Q +DEL "php\*.*" /Q +CALL ..\..\data\scripts\jsdoc\jsdoc ..\..\src\assets\js\frontend ..\..\src\assets\js\backend ..\..\src\assets\js -d js +CALL C:\wamp\bin\php\php5.4.3\apigen --source ..\..\src\application\controllers --source ..\..\src\application\models --destination php \ No newline at end of file diff --git a/doc/Code Docs/js/bookAppointment.html b/doc/Code Docs/js/bookAppointment.html new file mode 100644 index 00000000..692a57db --- /dev/null +++ b/doc/Code Docs/js/bookAppointment.html @@ -0,0 +1,720 @@ + + + + + JSDoc: Class: bookAppointment + + + + + + + + + + +
      + +

      Class: bookAppointment

      + + + + + +
      + +
      +

      + bookAppointment +

      + +
      Implelements the js part of the appointment booking page.
      + +
      + +
      + + + + + + + + + + + + + + + +

      Methods

      + +
      + +
      +

      <static> bindEventHandlers()

      + + +
      +
      + + +
      + This method binds the necessary event handlers +for the book appointments page. +
      + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + + + + + + + +
      + + + + + + + + + + + + + +
      + + + +
      +

      <static> calcEndDatetime() → {string}

      + + +
      +
      + + +
      + This method calculates the end datetime of the current appointment. +End datetime is depending on the service and start datetime fieldss. +
      + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + + + + + + + +
      + + + + + + + + + + + +
      Returns:
      + + +
      + Returns the end datetime in string format. +
      + + + +
      +
      + Type +
      +
      + +string + + +
      +
      + + + + +
      + + + +
      +

      <static> getAvailableHours(selDate)

      + + +
      +
      + + +
      + This function makes an ajax call and returns the available +hours for the selected service, provider and date. +
      + + + + + + + +
      Parameters:
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      selDate + + +string + + + + The selected date of which the available +hours we need to receive.
      + + + +
      + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + + + + + + + +
      + + + + + + + + + + + + + +
      + + + +
      +

      <static> initialize(bindEventHandlers)

      + + +
      +
      + + +
      + This method initializes the book appointment page. +
      + + + + + + + +
      Parameters:
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      NameTypeDescription
      bindEventHandlers + + +bool + + + + (OPTIONAL) Determines wether +the default event handlers will be binded to the dom elements.
      + + + +
      + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + + + + + + + +
      + + + + + + + + + + + + + +
      + + + +
      +

      <static> updateConfirmData()

      + + +
      +
      + + +
      + Every time this function is executed, it updates the confirmation +page with the latest customer settigns and input for the appointment +booking. +
      + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + + + + + + + +
      + + + + + + + + + + + + + +
      + + + +
      +

      <static> validateCustomerDataForm() → {bool}

      + + +
      +
      + + +
      + This function validates the customer's data input. +It only checks for empty fields by the time. +
      + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + + + + + + + +
      + + + + + + + + + + + +
      Returns:
      + + +
      + Returns the validation result. +
      + + + +
      +
      + Type +
      +
      + +bool + + +
      +
      + + + + +
      + +
      + + + + + +
      + +
      + + + + +
      + + + +
      + +
      + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 18:38:06 GMT+0300 (EEST) +
      + + + + + \ No newline at end of file diff --git a/doc/Code Docs/js/book_appointment.js.html b/doc/Code Docs/js/book_appointment.js.html new file mode 100644 index 00000000..9c4a0a73 --- /dev/null +++ b/doc/Code Docs/js/book_appointment.js.html @@ -0,0 +1,362 @@ + + + + + JSDoc: Source: frontend/book_appointment.js + + + + + + + + + + +
      + +

      Source: frontend/book_appointment.js

      + + + + + +
      +
      +
      /**
      + * This class implements the book appointment page functionality. 
      + * Once the initialize() method is called the page is fully functional 
      + * and can serve the appointment booking process.
      + * 
      + * @class Implelements the js part of the appointment booking page.
      + */
      +var bookAppointment = {
      +    /**
      +     * This method initializes the book appointment page.
      +     * 
      +     * @param {bool} bindEventHandlers (OPTIONAL) Determines wether 
      +     * the default event handlers will be binded to the dom elements.
      +     */
      +    initialize : function(bindEventHandlers) {
      +        if (bindEventHandlers == undefined) {
      +            bindEventHandlers = true; // Default value
      +        }
      +        
      +        // Initialize page's components (tooltips, datepickers etc).
      +        $('.book-step').qtip({
      +            position: {
      +                my: 'top center',
      +                at: 'bottom center'
      +            },
      +            style: {
      +                classes: 'qtip-green qtip-shadow custom-qtip'
      +            }
      +        });
      +        
      +        $('#select-date').datepicker({
      +            dateFormat  : 'dd-mm-yy',
      +            minDate     : 0,
      +            defaultDate : Date.today(),
      +            onSelect    : function(dateText, instance) {
      +                bookAppointment.getAvailableHours(dateText);
      +                bookAppointment.updateConfirmData();
      +            }
      +        });
      +       
      +        // Bind the event handlers (might not be necessary every time
      +        // we use this class).
      +        if (bindEventHandlers) {
      +            bookAppointment.bindEventHandlers();
      +        }
      +       
      +        // Execute other necessary operations on startup.
      +        $('#select-service').trigger('change');
      +    },
      +    
      +    /**
      +     * This method binds the necessary event handlers 
      +     * for the book appointments page.
      +     */
      +    bindEventHandlers : function() {
      +        /**
      +         * Event : Selected Provider "Changed"
      +         */
      +        $('#select-provider').change(function() {
      +            bookAppointment.getAvailableHours(Date.today().toString('dd-MM-yyyy'));
      +            bookAppointment.updateConfirmData();
      +        });
      +        
      +        /**
      +         * Event : Selected Service "Changed"
      +         * 
      +         * When the user clicks on a service, its available providers should 
      +         * become visible. 
      +         */
      +        $('#select-service').change(function() {
      +            var currServiceId = $('#select-service').val();
      +            $('#select-provider').empty();
      +
      +            $.each(GlobalVariables.providers, function(indexProvider, provider) {
      +                $.each(provider['services'], function(indexService, serviceId) {
      +                    // If the current provider is able to provide the selected 
      +                    // service, add him to the listbox. 
      +                    if (serviceId == currServiceId) { 
      +                        var optionHtml = '<option value="' + provider['id'] + '">' 
      +                            + provider['last_name']  + ' ' + provider['first_name'] 
      +                            + '</option>';
      +                        $('#select-provider').append(optionHtml);
      +                    }
      +                });
      +            });
      +
      +            bookAppointment.getAvailableHours(Date.today().toString('dd-MM-yyyy'));
      +            bookAppointment.updateConfirmData();
      +        });
      +        
      +        /**
      +         * Event : Next Step Button "Clicked"
      +         * 
      +         * This handler is triggered every time the user pressed the 
      +         * "next" button on the book wizard. Some special tasks might 
      +         * be perfomed, depending the current wizard step.
      +         */
      +        $('.button-next').click(function() {
      +            // If we are on the 3rd tab then we will need to validate the user's 
      +            // input before proceeding to the next step.
      +            if ($(this).attr('data-step_index') == '3') {
      +                if (!bookAppointment.validateCustomerDataForm()) {
      +                    return; // Validation failed, do not continue.
      +                } else {
      +                    bookAppointment.updateConfirmData();
      +                }
      +            }
      +            
      +            // Display the next step tab (uses jquery animation effect).
      +            var nextTabIndex = parseInt($(this).attr('data-step_index')) + 1;
      +
      +            $(this).parents().eq(1).hide('fade', function() {    
      +                $('.active-step').removeClass('active-step');
      +                $('#step-' + nextTabIndex).addClass('active-step');
      +                $('#book-appointment-' + nextTabIndex).show('fade');
      +            });
      +        });
      +
      +        /**
      +         * Event : Back Step Button "Clicked"
      +         * 
      +         * This handler is triggered every time the user pressed the 
      +         * "back" button on the book wizard.
      +         */
      +        $('.button-back').click(function() {
      +            var prevTabIndex = parseInt($(this).attr('data-step_index')) - 1;
      +
      +            $(this).parents().eq(1).hide('fade', function() {    
      +                $('.active-step').removeClass('active-step');
      +                $('#step-' + prevTabIndex).addClass('active-step');
      +                $('#book-appointment-' + prevTabIndex).show('fade');
      +            });
      +        });
      +
      +        /**
      +         * Event : Available Hour "Click"
      +         * 
      +         * Triggered whenever the user clicks on an available hour
      +         * for his appointment.
      +         */
      +        $('#available-hours').on('click', '.available-hour', function() {
      +            $('.selected-hour').removeClass('selected-hour');
      +            $(this).addClass('selected-hour');
      +            bookAppointment.updateConfirmData();
      +        });
      +    },
      +    
      +    /**
      +     * This function makes an ajax call and returns the available 
      +     * hours for the selected service, provider and date.
      +     * 
      +     * @param {string} selDate The selected date of which the available
      +     * hours we need to receive.
      +     */
      +    getAvailableHours : function(selDate) {
      +        // Find the selected service duration (it is going to 
      +        // be send within the "postData" object.
      +        var selServiceDuration = 15; // Default value of duration (in minutes).
      +        $.each(GlobalVariables.services, function(index, service) {
      +            if (service['id'] == $('#select-service').val()) {
      +                selServiceDuration = service['duration']; 
      +            }
      +        });
      +
      +        var postData = {
      +            'service_id'         : $('#select-service').val(),
      +            'provider_id'        : $('#select-provider').val(),
      +            'selected_date'      : selDate,
      +            'service_duration'   : selServiceDuration
      +        };
      +
      +        // Make ajax post request and get the available hours.
      +        var ajaxurl = GlobalVariables.baseUrl + 'appointments/ajax_get_available_hours';
      +        jQuery.post(ajaxurl, postData, function(postResponse) {
      +            ////////////////////////////////////////////////////////////////////////////////
      +            console.log('\n\n Get Available Hours Post Response :', postResponse, '\n\n');
      +            ////////////////////////////////////////////////////////////////////////////////
      +
      +            try {
      +                var jsonResponse = jQuery.parseJSON(postResponse);
      +                ////////////////////////////////////////////////////////////////////////////////
      +                //console.log('\n\n Get Available Hours JSON Response :', jsonResponse, '\n\n');
      +                ////////////////////////////////////////////////////////////////////////////////
      +
      +                // Fill the available time div
      +                var currColumn = 1;
      +                $('#available-hours').html('<div style="width:50px; float:left;"></div>');
      +                $.each(jsonResponse, function(index, availableHour) {
      +                    if ((currColumn * 10) < (index + 1)) {
      +                        currColumn++;
      +                        $('#available-hours').append('<div style="width:50px; float:left;"></div>');
      +                    }
      +
      +                    $('#available-hours div:eq(' + (currColumn - 1) + ')')
      +                        .append('<span class="available-hour">' + availableHour + '</span><br/>');
      +                });
      +
      +                // Set the first item as selected.
      +                $('.available-hour:eq(0)').addClass('selected-hour');
      +                bookAppointment.updateConfirmData();
      +            } catch(exception) {
      +                GeneralFunctions.displayMessageBox('Unexpected Error', 'An unexpected error occured ' 
      +                    + 'during the available hours calculation. Please refresh the page and try again.');
      +            }
      +        });
      +    },
      +
      +    /**
      +     * This function validates the customer's data input.
      +     * It only checks for empty fields by the time.
      +     * 
      +     * @return {bool} Returns the validation result.
      +     */
      +    validateCustomerDataForm : function() {
      +        var validationResult = true;
      +        $('.required').css('border', '');
      +
      +        $('.required').each(function() {
      +            if ($(this).val() == '') {
      +                validationResult = false; 
      +                $(this).css('border', '2px solid red');
      +            }
      +        });
      +
      +        return validationResult;
      +    },
      +
      +    /**
      +     * Every time this function is executed, it updates the confirmation
      +     * page with the latest customer settigns and input for the appointment 
      +     * booking.
      +     */
      +    updateConfirmData : function() {
      +        /*** SET APPOINTMENT INFO ***/
      +        var selectedDate = $('#select-date').datepicker('getDate');
      +        if (selectedDate !== null) {
      +            selectedDate = Date.parse(selectedDate).toString('dd-MM-yyyy');
      +        }
      +
      +        $('#appointment-info').html(
      +            '<h4>' + $('#select-service option:selected').text() + '</h4>' +
      +            $('#select-provider option:selected').text() + '<br/>' + 
      +            '<strong class="text-info">' + selectedDate + ' ' 
      +                    + $('.selected-hour').text() + '</strong>'
      +        );
      +
      +        /*** SET CUSTOMER'S INFO ***/
      +        $('#customer-info').html(
      +            '<h4>' + $('#last-name').val() + ' ' + $('#first-name').val() + '</h4>' + 
      +            'Phone: ' + $('#phone-number').val() + '<br/>' + 
      +            'Email: ' + $('#email').val() + '<br/>' + 
      +            'Address: ' + $('#address').val() + '<br/>' + 
      +            'City: ' + $('#city').val() + '<br/>' + 
      +            'Zip Code: ' + $('#zip-code').val()
      +        );
      +            
      +        /*** UPDATE FORM POST DATA ***/
      +        var postData = new Object();
      +        
      +        postData['customer'] = {
      +            'last_name'      : $('#last-name').val(),
      +            'first_name'     : $('#first-name').val(),
      +            'email'          : $('#email').val(),
      +            'phone_number'   : $('#phone-number').val(),
      +            'address'        : $('#address').val(),
      +            'city'           : $('#city').val(),
      +            'zip_code'       : $('#zip-code').val()
      +        };
      +        
      +        postData['appointment'] = {
      +            'start_datetime'    : $('#select-date').datepicker('getDate').toString('yyyy-MM-dd') 
      +                                        + ' ' + $('.selected-hour').text() + ':00',
      +            'end_datetime'      : bookAppointment.calcEndDatetime(),
      +            'notes'             : $('#notes').val(),
      +            'id_users_provider' : $('#select-provider').val(),
      +            'id_services'       : $('#select-service').val()
      +        };
      +        
      +        $('input[name="post_data"]').val(JSON.stringify(postData));
      +    },
      +    
      +    /** 
      +     * This method calculates the end datetime of the current appointment. 
      +     * End datetime is depending on the service and start datetime fieldss.
      +     * 
      +     * @return {string} Returns the end datetime in string format.
      +     */
      +    calcEndDatetime : function() {
      +        // Find selected service duration. 
      +        var selServiceDuration = undefined;
      +        
      +        $.each(GlobalVariables.services, function(index, service) {
      +            if (service.id == $('#select-service').val()) {
      +                selServiceDuration = service.duration;
      +                return; // Stop searching ... 
      +            }
      +        });
      +        
      +        // Add the duration to the start datetime.
      +        var startDatetime = $('#select-date').datepicker('getDate').toString('dd-MM-yyyy') 
      +                + ' ' + $('.selected-hour').text();
      +        startDatetime = Date.parseExact(startDatetime, 'dd-MM-yyyy HH:mm');
      +        var endDatetime = undefined;
      +        
      +        if (selServiceDuration !== undefined && startDatetime !== null) {
      +            endDatetime = startDatetime.add({ 'minutes' : parseInt(selServiceDuration) });
      +        } else {
      +            endDatetime = new Date();
      +        }
      +        
      +        return endDatetime.toString('yyyy-MM-dd HH:mm:ss');
      +    }
      +}
      +
      +
      + + + + +
      + + + +
      + +
      + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 18:38:05 GMT+0300 (EEST) +
      + + + + + diff --git a/doc/Code Docs/js/functions..html b/doc/Code Docs/js/functions..html new file mode 100644 index 00000000..910dc27e --- /dev/null +++ b/doc/Code Docs/js/functions..html @@ -0,0 +1,124 @@ + + + + + JSDoc: Namespace: General javascript functions. + + + + + + + + + + +
      + +

      Namespace: General javascript functions.

      + + + + + +
      + +
      +

      + General javascript functions. +

      + +
      + +
      +
      + + + + +
      This file contains the Genral Functions javascript namespace. +It contains functions that apply both on the front and back +end of the application.
      + + + +
      + + + + + + + + + + + + + + + + + + + +
      Source:
      +
      + + + + + + + +
      + + + + +
      + + + + + + + + + + + + + + + + + + +
      + +
      + + + + +
      + + + +
      + +
      + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 18:38:06 GMT+0300 (EEST) +
      + + + + + \ No newline at end of file diff --git a/doc/Code Docs/js/general_functions.js.html b/doc/Code Docs/js/general_functions.js.html new file mode 100644 index 00000000..bae1137b --- /dev/null +++ b/doc/Code Docs/js/general_functions.js.html @@ -0,0 +1,134 @@ + + + + + JSDoc: Source: general_functions.js + + + + + + + + + + +
      + +

      Source: general_functions.js

      + + + + + +
      +
      +
      /**
      + * This file contains the Genral Functions javascript namespace.
      + * It contains functions that apply both on the front and back 
      + * end of the application.
      + * 
      + * @namespace General javascript functions.
      + */
      +var GeneralFunctions = {};
      +
      +/**
      + * This functions displays a message box in
      + * the admin array. It is usefull when user
      + * decisions or verifications are needed.
      + *  
      + * @param {string} title The title of the message box.
      + * @param {string} message The message of the dialog.
      + * @param {array} messageButtons Contains the dialog  
      + * buttons along with their functions.
      + */
      +GeneralFunctions.displayMessageBox = function(title, message, messageButtons) {
      +    // Check arguments integrity.
      +    if (title == undefined || title == "") {
      +        title = "<No Title Given>";
      +    }   
      +        
      +    if (message == undefined || message == "") {
      +        message = "<No Message Given>";
      +    } 
      +    
      +    if (messageButtons == undefined) {
      +        messageButtons = {
      +            Close: function() {
      +                jQuery("#message_box").dialog("close");
      +            }
      +        };
      +    }
      +    
      +    // Destroy previous dialog instances.
      +    jQuery("#message_box").dialog("destroy");
      +    jQuery("#message_box").remove();
      +    
      +    // Create the html of the message box.
      +    jQuery("body").append(
      +        "<div id='message_box' title='" + title + "'>" +
      +        "<p>" + message + "</p>" +
      +        "</div>"
      +    );    
      +
      +    jQuery("#message_box").dialog({
      +        autoOpen        : false,
      +        modal           : true,
      +        resize          : "auto",
      +        width           : 400,
      +        height          : "auto",
      +        resizable       : false,
      +        buttons         : messageButtons,
      +        closeOnEscape   : false
      +    });
      +    
      +    jQuery("#message_box").dialog("open"); 
      +    jQuery("#message_box .ui-dialog-titlebar-close").hide();
      +}
      +
      +/**
      + * This method centers a DOM element vertically and horizontally
      + * on the page.
      + * 
      + * @param {object} elementHandle The object that is going to be 
      + * centered.
      + */
      +GeneralFunctions.centerElementOnPage = function(elementHandle) {
      +    // Center main frame vertical middle
      +    $(window).resize(function() {
      +        var elementLeft = ($(window).width() - elementHandle.outerWidth()) / 2;
      +        var elementTop = ($(window).height() - elementHandle.outerHeight()) / 2;
      +        elementTop = (elementTop > 0 ) ? elementTop : 20;
      +        
      +        elementHandle.css({
      +            position    : 'absolute',
      +            left        : elementLeft,
      +            top         : elementTop
      +        }); 
      +    });
      +    $(window).resize();
      +}
      +
      +
      + + + + +
      + + + +
      + +
      + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 18:38:05 GMT+0300 (EEST) +
      + + + + + diff --git a/doc/Code Docs/js/index.html b/doc/Code Docs/js/index.html new file mode 100644 index 00000000..c7094b53 --- /dev/null +++ b/doc/Code Docs/js/index.html @@ -0,0 +1,63 @@ + + + + + JSDoc: Index + + + + + + + + + + +
      + +

      Index

      + + + + + + + +

      + + + + + + + + + + + + + + + + + + + +
      + + + +
      + +
      + Documentation generated by JSDoc 3.2.0-dev on Mon May 20 2013 18:38:05 GMT+0300 (EEST) +
      + + + + + \ No newline at end of file diff --git a/doc/Code Docs/js/scripts/linenumber.js b/doc/Code Docs/js/scripts/linenumber.js new file mode 100644 index 00000000..a0c570d5 --- /dev/null +++ b/doc/Code Docs/js/scripts/linenumber.js @@ -0,0 +1,17 @@ +(function() { + var counter = 0; + var numbered; + var source = document.getElementsByClassName('prettyprint source'); + + if (source && source[0]) { + source = source[0].getElementsByTagName('code')[0]; + + numbered = source.innerHTML.split('\n'); + numbered = numbered.map(function(item) { + counter++; + return '' + item; + }); + + source.innerHTML = numbered.join('\n'); + } +})(); diff --git a/doc/Code Docs/js/scripts/prettify/Apache-License-2.0.txt b/doc/Code Docs/js/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/doc/Code Docs/js/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/doc/Code Docs/js/scripts/prettify/lang-css.js b/doc/Code Docs/js/scripts/prettify/lang-css.js new file mode 100644 index 00000000..041e1f59 --- /dev/null +++ b/doc/Code Docs/js/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/doc/Code Docs/js/scripts/prettify/prettify.js b/doc/Code Docs/js/scripts/prettify/prettify.js new file mode 100644 index 00000000..eef5ad7e --- /dev/null +++ b/doc/Code Docs/js/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p + + + + + + + Page not found + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Appointments.html b/doc/Code Docs/php/class-Appointments.html new file mode 100644 index 00000000..66642888 --- /dev/null +++ b/doc/Code Docs/php/class-Appointments.html @@ -0,0 +1,256 @@ + + + + + + + Class Appointments + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Appointments_Model.html b/doc/Code Docs/php/class-Appointments_Model.html new file mode 100644 index 00000000..5a256f9a --- /dev/null +++ b/doc/Code Docs/php/class-Appointments_Model.html @@ -0,0 +1,558 @@ + + + + + + + Class Appointments_Model + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Customers_Model.html b/doc/Code Docs/php/class-Customers_Model.html new file mode 100644 index 00000000..3c3e8241 --- /dev/null +++ b/doc/Code Docs/php/class-Customers_Model.html @@ -0,0 +1,589 @@ + + + + + + + Class Customers_Model + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Google.html b/doc/Code Docs/php/class-Google.html new file mode 100644 index 00000000..417578d3 --- /dev/null +++ b/doc/Code Docs/php/class-Google.html @@ -0,0 +1,216 @@ + + + + + + + Class Google + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Providers_Model.html b/doc/Code Docs/php/class-Providers_Model.html new file mode 100644 index 00000000..73484c34 --- /dev/null +++ b/doc/Code Docs/php/class-Providers_Model.html @@ -0,0 +1,385 @@ + + + + + + + Class Providers_Model + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Roles_Model.html b/doc/Code Docs/php/class-Roles_Model.html new file mode 100644 index 00000000..c12b6d8b --- /dev/null +++ b/doc/Code Docs/php/class-Roles_Model.html @@ -0,0 +1,218 @@ + + + + + + + Class Roles_Model + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Services_Model.html b/doc/Code Docs/php/class-Services_Model.html new file mode 100644 index 00000000..e50144fb --- /dev/null +++ b/doc/Code Docs/php/class-Services_Model.html @@ -0,0 +1,346 @@ + + + + + + + Class Services_Model + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Settings_Model.html b/doc/Code Docs/php/class-Settings_Model.html new file mode 100644 index 00000000..58fd11de --- /dev/null +++ b/doc/Code Docs/php/class-Settings_Model.html @@ -0,0 +1,322 @@ + + + + + + + Class Settings_Model + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/class-Test.html b/doc/Code Docs/php/class-Test.html new file mode 100644 index 00000000..cb5e0618 --- /dev/null +++ b/doc/Code Docs/php/class-Test.html @@ -0,0 +1,274 @@ + + + + + + + Class Test + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/elementlist.js b/doc/Code Docs/php/elementlist.js new file mode 100644 index 00000000..889ec029 --- /dev/null +++ b/doc/Code Docs/php/elementlist.js @@ -0,0 +1,3 @@ + +var ApiGen = ApiGen || {}; +ApiGen.elements = [["c","Appointments"],["c","Appointments_Model"],["c","Customers_Model"],["c","Google"],["c","Providers_Model"],["c","Roles_Model"],["c","Services_Model"],["c","Settings_Model"],["c","Test"]]; diff --git a/doc/Code Docs/php/index.html b/doc/Code Docs/php/index.html new file mode 100644 index 00000000..6da39bc1 --- /dev/null +++ b/doc/Code Docs/php/index.html @@ -0,0 +1,134 @@ + + + + + + + Overview + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/resources/collapsed.png b/doc/Code Docs/php/resources/collapsed.png new file mode 100644 index 00000000..56e73239 Binary files /dev/null and b/doc/Code Docs/php/resources/collapsed.png differ diff --git a/doc/Code Docs/php/resources/combined.js b/doc/Code Docs/php/resources/combined.js new file mode 100644 index 00000000..39c6bb6f --- /dev/null +++ b/doc/Code Docs/php/resources/combined.js @@ -0,0 +1,1264 @@ + +var ApiGen = ApiGen || {}; +ApiGen.config = {"require":{"min":"2.8.0"},"resources":{"resources":"resources"},"templates":{"common":{"overview.latte":"index.html","combined.js.latte":"resources\/combined.js","elementlist.js.latte":"elementlist.js","404.latte":"404.html"},"optional":{"sitemap":{"filename":"sitemap.xml","template":"sitemap.xml.latte"},"opensearch":{"filename":"opensearch.xml","template":"opensearch.xml.latte"},"robots":{"filename":"robots.txt","template":"robots.txt.latte"}},"main":{"package":{"filename":"package-%s.html","template":"package.latte"},"namespace":{"filename":"namespace-%s.html","template":"namespace.latte"},"class":{"filename":"class-%s.html","template":"class.latte"},"constant":{"filename":"constant-%s.html","template":"constant.latte"},"function":{"filename":"function-%s.html","template":"function.latte"},"source":{"filename":"source-%s.html","template":"source.latte"},"tree":{"filename":"tree.html","template":"tree.latte"},"deprecated":{"filename":"deprecated.html","template":"deprecated.latte"},"todo":{"filename":"todo.html","template":"todo.latte"}}},"options":{"elementDetailsCollapsed":true,"elementsOrder":"natural"},"config":"C:\\wamp\\bin\\php\\php5.4.3\\data\\ApiGen\\templates\\default\\config.neon"}; + + +/*! jQuery v1.7 jquery.com | jquery.org/license */ +(function(a,b){function cA(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cx(a){if(!cm[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cn||(cn=c.createElement("iframe"),cn.frameBorder=cn.width=cn.height=0),b.appendChild(cn);if(!co||!cn.createElement)co=(cn.contentWindow||cn.contentDocument).document,co.write((c.compatMode==="CSS1Compat"?"":"")+""),co.close();d=co.createElement(a),co.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cn)}cm[a]=e}return cm[a]}function cw(a,b){var c={};f.each(cs.concat.apply([],cs.slice(0,b)),function(){c[this]=a});return c}function cv(){ct=b}function cu(){setTimeout(cv,0);return ct=f.now()}function cl(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ck(){try{return new a.XMLHttpRequest}catch(b){}}function ce(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bB(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function br(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bi,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bq(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bp(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bp)}function bp(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bo(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bn(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bm(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(){return!0}function M(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.add(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;B.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return a!=null&&m.test(a)&&!isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
      a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,unknownElems:!!a.getElementsByTagName("nav").length,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",enctype:!!c.createElement("form").enctype,submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.lastChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-999px",top:"-999px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
      ",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
      t
      ",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;f(function(){var a,b,d,e,g,h,i=1,j="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",l="visibility:hidden;border:0;",n="style='"+j+"border:5px solid #000;padding:0;'",p="
      "+""+"
      ";m=c.getElementsByTagName("body")[0];!m||(a=c.createElement("div"),a.style.cssText=l+"width:0;height:0;position:static;top:0;margin-top:"+i+"px",m.insertBefore(a,m.firstChild),o=c.createElement("div"),o.style.cssText=j+l,o.innerHTML=p,a.appendChild(o),b=o.firstChild,d=b.firstChild,g=b.nextSibling.firstChild.firstChild,h={doesNotAddBorder:d.offsetTop!==5,doesAddBorderForTableAndCells:g.offsetTop===5},d.style.position="fixed",d.style.top="20px",h.fixedPosition=d.offsetTop===20||d.offsetTop===15,d.style.position=d.style.top="",b.style.overflow="hidden",b.style.position="relative",h.subtractsBorderForOverflowNotVisible=d.offsetTop===-5,h.doesNotIncludeMarginInBodyOffset=m.offsetTop!==i,m.removeChild(a),o=a=null,f.extend(k,h))}),o.innerHTML="",n.removeChild(o),o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[f.expando]:a[f.expando]&&f.expando,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[f.expando]=n=++f.uuid:n=f.expando),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[f.expando]:f.expando;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)?b=b:b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" "));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];if(!arguments.length){if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}return b}e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!a||j===3||j===8||j===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g},removeAttr:function(a,b){var c,d,e,g,h=0;if(a.nodeType===1){d=(b||"").split(p),g=d.length;for(;h=0}})});var z=/\.(.*)$/,A=/^(?:textarea|input|select)$/i,B=/\./g,C=/ /g,D=/[^\w\s.|`]/g,E=/^([^\.]*)?(?:\.(.+))?$/,F=/\bhover(\.\S+)?/,G=/^key/,H=/^(?:mouse|contextmenu)|click/,I=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,J=function(a){var b=I.exec(a);b&& +(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},K=function(a,b){return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||a.id===b[2])&&(!b[3]||b[3].test(a.className))},L=function(a){return f.event.special.hover?a:a.replace(F,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=L(c).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"",(g||!e)&&c.preventDefault();if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,n=null;for(m=e.parentNode;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l=0:t===b&&(t=o[s]=r.quick?K(m,r.quick):f(m).is(s)),t&&q.push(r);q.length&&j.push({elem:m,matches:q})}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),G.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),H.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

      ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
      ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",Z=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,_=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,ba=/<([\w:]+)/,bb=/",""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},bk=X(c);bj.optgroup=bj.option,bj.tbody=bj.tfoot=bj.colgroup=bj.caption=bj.thead,bj.th=bj.td,f.support.htmlSerialize||(bj._default=[1,"div
      ","
      "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after" +,arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Z,""):null;if(typeof a=="string"&&!bd.test(a)&&(f.support.leadingWhitespace||!$.test(a))&&!bj[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(_,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bn(a,d),e=bo(a),g=bo(d);for(h=0;e[h];++h)g[h]&&bn(e[h],g[h])}if(b){bm(a,d);if(c){e=bo(a),g=bo(d);for(h=0;e[h];++h)bm(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bc.test(k))k=b.createTextNode(k);else{k=k.replace(_,"<$1>");var l=(ba.exec(k)||["",""])[1].toLowerCase(),m=bj[l]||bj._default,n=m[0],o=b.createElement("div");b===c?bk.appendChild(o):X(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=bb.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&$.test(k)&&o.insertBefore(b.createTextNode($.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bt.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bs,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bs.test(g)?g.replace(bs,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bB(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bC=function(a,c){var d,e,g;c=c.replace(bu,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bD=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bv.test(f)&&bw.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bB=bC||bD,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bF=/%20/g,bG=/\[\]$/,bH=/\r?\n/g,bI=/#.*$/,bJ=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bK=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bL=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bM=/^(?:GET|HEAD)$/,bN=/^\/\//,bO=/\?/,bP=/)<[^<]*)*<\/script>/gi,bQ=/^(?:select|textarea)/i,bR=/\s+/,bS=/([?&])_=[^&]*/,bT=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bU=f.fn.load,bV={},bW={},bX,bY,bZ=["*/"]+["*"];try{bX=e.href}catch(b$){bX=c.createElement("a"),bX.href="",bX=bX.href}bY=bT.exec(bX.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bU)return bU.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
      ").append(c.replace(bP,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bQ.test(this.nodeName)||bK.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bH,"\r\n")}}):{name:b.name,value:c.replace(bH,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?cb(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),cb(a,b);return a},ajaxSettings:{url:bX,isLocal:bL.test(bY[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bZ},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:b_(bV),ajaxTransport:b_(bW),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cd(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=ce(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bJ.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bI,"").replace(bN,bY[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bR),d.crossDomain==null&&(r=bT.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bY[1]&&r[2]==bY[2]&&(r[3]||(r[1]==="http:"?80:443))==(bY[3]||(bY[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),ca(bV,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bM.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bO.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bS,"$1_="+x);d.url=y+(y===d.url?(bO.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bZ+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=ca(bW,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)cc(g,a[g],c,e);return d.join("&").replace(bF,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cf=f.now(),cg=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cf++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cg.test(b.url)||e&&cg.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cg,l),b.url===j&&(e&&(k=k.replace(cg,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ch=a.ActiveXObject?function(){for(var a in cj)cj[a](0,1)}:!1,ci=0,cj;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ck()||cl()}:ck,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ch&&delete cj[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++ci,ch&&(cj||(cj={},f(a).unload(ch)),cj[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cm={},cn,co,cp=/^(?:toggle|show|hide)$/,cq=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cr,cs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],ct;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cw("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cz.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cz.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cA(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cA(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); +/** + * Cookie plugin + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + * used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + * If set to null or omitted, the cookie will be a session cookie and will not be retained + * when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + * require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Get the value of a cookie with the given name. + * + * @example $.cookie('the_cookie'); + * @desc Get the value of a cookie. + * + * @param String name The name of the cookie. + * @return The value of the cookie. + * @type String + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +jQuery.cookie = function(name, value, options) { + if (typeof value != 'undefined') { // name and value given, set cookie + options = options || {}; + if (value === null) { + value = ''; + options.expires = -1; + } + var expires = ''; + if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { + var date; + if (typeof options.expires == 'number') { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else { + date = options.expires; + } + expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE + } + // CAUTION: Needed to parenthesize options.path and options.domain + // in the following expressions, otherwise they evaluate to undefined + // in the packed version for some reason... + var path = options.path ? '; path=' + (options.path) : ''; + var domain = options.domain ? '; domain=' + (options.domain) : ''; + var secure = options.secure ? '; secure' : ''; + document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); + } else { // only name given, get cookie + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } +}; +/*! + * sprintf and vsprintf for jQuery + * somewhat based on http://jan.moesen.nu/code/javascript/sprintf-and-printf-in-javascript/ + * Copyright (c) 2008 Sabin Iacob (m0n5t3r) + * @license http://www.gnu.org/licenses/gpl.html + * @project jquery.sprintf + */ +(function(d){var a={b:function(e){return parseInt(e,10).toString(2)},c:function(e){return String.fromCharCode(parseInt(e,10))},d:function(e){return parseInt(e,10)},u:function(e){return Math.abs(e)},f:function(f,e){e=parseInt(e,10);f=parseFloat(f);if(isNaN(e&&f)){return NaN}return e&&f.toFixed(e)||f},o:function(e){return parseInt(e,10).toString(8)},s:function(e){return e},x:function(e){return(""+parseInt(e,10).toString(16)).toLowerCase()},X:function(e){return(""+parseInt(e,10).toString(16)).toUpperCase()}};var c=/%(?:(\d+)?(?:\.(\d+))?|\(([^)]+)\))([%bcdufosxX])/g;var b=function(f){if(f.length==1&&typeof f[0]=="object"){f=f[0];return function(i,h,k,j,g,m,l){return a[g](f[j])}}else{var e=0;return function(i,h,k,j,g,m,l){if(g=="%"){return"%"}return a[g](f[e++],k)}}};d.extend({sprintf:function(f){var e=Array.apply(null,arguments).slice(1);return f.replace(c,b(e))},vsprintf:function(f,e){return f.replace(c,b(e))}})})(jQuery); + +/*! + * jQuery Autocomplete plugin 1.1 + * + * Copyright (c) 2009 Jörn Zaefferer + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $ + */ + +;(function($) { + +$.fn.extend({ + autocomplete: function(urlOrData, options) { + var isUrl = typeof urlOrData == "string"; + options = $.extend({}, $.Autocompleter.defaults, { + url: isUrl ? urlOrData : null, + data: isUrl ? null : urlOrData, + delay: isUrl ? $.Autocompleter.defaults.delay : 10, + max: options && !options.scroll ? 10 : 150 + }, options); + + // if highlight is set to false, replace it with a do-nothing function + options.highlight = options.highlight || function(value) { return value; }; + + // if the formatMatch option is not specified, then use formatItem for backwards compatibility + options.formatMatch = options.formatMatch || options.formatItem; + + options.show = options.show || function(list) {}; + + return this.each(function() { + new $.Autocompleter(this, options); + }); + }, + result: function(handler) { + return this.bind("result", handler); + }, + search: function(handler) { + return this.trigger("search", [handler]); + }, + flushCache: function() { + return this.trigger("flushCache"); + }, + setOptions: function(options){ + return this.trigger("setOptions", [options]); + }, + unautocomplete: function() { + return this.trigger("unautocomplete"); + } +}); + +$.Autocompleter = function(input, options) { + + var KEY = { + UP: 38, + DOWN: 40, + DEL: 46, + TAB: 9, + RETURN: 13, + ESC: 27, + COMMA: 188, + PAGEUP: 33, + PAGEDOWN: 34, + BACKSPACE: 8 + }; + + // Create $ object for input element + var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); + + var timeout; + var previousValue = ""; + var cache = $.Autocompleter.Cache(options); + var hasFocus = 0; + var lastKeyPressCode; + var config = { + mouseDownOnSelect: false + }; + var select = $.Autocompleter.Select(options, input, selectCurrent, config); + + // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all + $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { + // a keypress means the input has focus + // avoids issue where input had focus before the autocomplete was applied + hasFocus = 1; + // track last key pressed + lastKeyPressCode = event.keyCode; + switch(event.keyCode) { + + case KEY.UP: + event.preventDefault(); + if ( select.visible() ) { + select.prev(); + } else { + onChange(0, true); + } + break; + + case KEY.DOWN: + event.preventDefault(); + if ( select.visible() ) { + select.next(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEUP: + event.preventDefault(); + if ( select.visible() ) { + select.pageUp(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEDOWN: + event.preventDefault(); + if ( select.visible() ) { + select.pageDown(); + } else { + onChange(0, true); + } + break; + + // matches also semicolon + case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: + case KEY.TAB: + case KEY.RETURN: + if( selectCurrent() ) { + //event.preventDefault(); + //return false; + } + break; + + case KEY.ESC: + select.hide(); + break; + + default: + clearTimeout(timeout); + timeout = setTimeout(onChange, options.delay); + break; + } + }).focus(function(){ + // track whether the field has focus, we shouldn't process any + // results if the field no longer has focus + hasFocus++; + }).blur(function() { + hasFocus = 0; + if (!config.mouseDownOnSelect) { + hideResults(); + } + }).click(function() { + // show select when clicking in a focused field + if ( hasFocus++ > 1 && !select.visible() ) { + onChange(0, true); + } + }).bind("search", function() { + // TODO why not just specifying both arguments? + var fn = (arguments.length > 1) ? arguments[1] : null; + function findValueCallback(q, data) { + var result; + if( data && data.length ) { + for (var i=0; i < data.length; i++) { + if( data[i].result.toLowerCase() == q.toLowerCase() ) { + result = data[i]; + break; + } + } + } + if( typeof fn == "function" ) fn(result); + else $input.trigger("result", result && [result.data, result.value]); + } + $.each(trimWords($input.val()), function(i, value) { + request(value, findValueCallback, findValueCallback); + }); + }).bind("flushCache", function() { + cache.flush(); + }).bind("setOptions", function() { + $.extend(options, arguments[1]); + // if we've updated the data, repopulate + if ( "data" in arguments[1] ) + cache.populate(); + }).bind("unautocomplete", function() { + select.unbind(); + $input.unbind(); + $(input.form).unbind(".autocomplete"); + }); + + + function selectCurrent() { + var selected = select.selected(); + if( !selected ) + return false; + + var v = selected.result; + previousValue = v; + + if ( options.multiple ) { + var words = trimWords($input.val()); + if ( words.length > 1 ) { + var seperator = options.multipleSeparator.length; + var cursorAt = $(input).selection().start; + var wordAt, progress = 0; + $.each(words, function(i, word) { + progress += word.length; + if (cursorAt <= progress) { + wordAt = i; + return false; + } + progress += seperator; + }); + words[wordAt] = v; + // TODO this should set the cursor to the right position, but it gets overriden somewhere + //$.Autocompleter.Selection(input, progress + seperator, progress + seperator); + v = words.join( options.multipleSeparator ); + } + v += options.multipleSeparator; + } + + $input.val(v); + hideResultsNow(); + $input.trigger("result", [selected.data, selected.value]); + return true; + } + + function onChange(crap, skipPrevCheck) { + if( lastKeyPressCode == KEY.DEL ) { + select.hide(); + return; + } + + var currentValue = $input.val(); + + if ( !skipPrevCheck && currentValue == previousValue ) + return; + + previousValue = currentValue; + + currentValue = lastWord(currentValue); + if ( currentValue.length >= options.minChars) { + $input.addClass(options.loadingClass); + if (!options.matchCase) + currentValue = currentValue.toLowerCase(); + request(currentValue, receiveData, hideResultsNow); + } else { + stopLoading(); + select.hide(); + } + }; + + function trimWords(value) { + if (!value) + return [""]; + if (!options.multiple) + return [$.trim(value)]; + return $.map(value.split(options.multipleSeparator), function(word) { + return $.trim(value).length ? $.trim(word) : null; + }); + } + + function lastWord(value) { + if ( !options.multiple ) + return value; + var words = trimWords(value); + if (words.length == 1) + return words[0]; + var cursorAt = $(input).selection().start; + if (cursorAt == value.length) { + words = trimWords(value) + } else { + words = trimWords(value.replace(value.substring(cursorAt), "")); + } + return words[words.length - 1]; + } + + // fills in the input box w/the first match (assumed to be the best match) + // q: the term entered + // sValue: the first matching result + function autoFill(q, sValue){ + // autofill in the complete box w/the first match as long as the user hasn't entered in more data + // if the last user key pressed was backspace, don't autofill + if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { + // fill in the value (keep the case the user has typed) + $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); + // select the portion of the value not typed by the user (so the next character will erase) + $(input).selection(previousValue.length, previousValue.length + sValue.length); + } + }; + + function hideResults() { + clearTimeout(timeout); + timeout = setTimeout(hideResultsNow, 200); + }; + + function hideResultsNow() { + var wasVisible = select.visible(); + select.hide(); + clearTimeout(timeout); + stopLoading(); + if (options.mustMatch) { + // call search and run callback + $input.search( + function (result){ + // if no value found, clear the input box + if( !result ) { + if (options.multiple) { + var words = trimWords($input.val()).slice(0, -1); + $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); + } + else { + $input.val( "" ); + $input.trigger("result", null); + } + } + } + ); + } + }; + + function receiveData(q, data) { + if ( data && data.length && hasFocus ) { + stopLoading(); + select.display(data, q); + autoFill(q, data[0].value); + select.show(); + } else { + hideResultsNow(); + } + }; + + function request(term, success, failure) { + if (!options.matchCase) + term = term.toLowerCase(); + var data = cache.load(term); + // recieve the cached data + if (data && data.length) { + success(term, data); + // if an AJAX url has been supplied, try loading the data now + } else if( (typeof options.url == "string") && (options.url.length > 0) ){ + + var extraParams = { + timestamp: +new Date() + }; + $.each(options.extraParams, function(key, param) { + extraParams[key] = typeof param == "function" ? param() : param; + }); + + $.ajax({ + // try to leverage ajaxQueue plugin to abort previous requests + mode: "abort", + // limit abortion to this input + port: "autocomplete" + input.name, + dataType: options.dataType, + url: options.url, + data: $.extend({ + q: lastWord(term), + limit: options.max + }, extraParams), + success: function(data) { + var parsed = options.parse && options.parse(data) || parse(data); + cache.add(term, parsed); + success(term, parsed); + } + }); + } else { + // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match + select.emptyList(); + failure(term); + } + }; + + function parse(data) { + var parsed = []; + var rows = data.split("\n"); + for (var i=0; i < rows.length; i++) { + var row = $.trim(rows[i]); + if (row) { + row = row.split("|"); + parsed[parsed.length] = { + data: row, + value: row[0], + result: options.formatResult && options.formatResult(row, row[0]) || row[0] + }; + } + } + return parsed; + }; + + function stopLoading() { + $input.removeClass(options.loadingClass); + }; + +}; + +$.Autocompleter.defaults = { + inputClass: "ac_input", + resultsClass: "ac_results", + loadingClass: "ac_loading", + minChars: 1, + delay: 400, + matchCase: false, + matchSubset: true, + matchContains: false, + cacheLength: 10, + max: 100, + mustMatch: false, + extraParams: {}, + selectFirst: true, + formatItem: function(row) { return row[0]; }, + formatMatch: null, + autoFill: false, + width: 0, + multiple: false, + multipleSeparator: ", ", + highlight: function(value, term) { + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + }, + scroll: true, + scrollHeight: 180 +}; + +$.Autocompleter.Cache = function(options) { + + var data = {}; + var length = 0; + + function matchSubset(s, sub) { + if (!options.matchCase) + s = s.toLowerCase(); + var i = s.indexOf(sub); + if (options.matchContains == "word"){ + i = s.toLowerCase().search("\\b" + sub.toLowerCase()); + } + if (i == -1) return false; + return i == 0 || options.matchContains; + }; + + function add(q, value) { + if (length > options.cacheLength){ + flush(); + } + if (!data[q]){ + length++; + } + data[q] = value; + } + + function populate(){ + if( !options.data ) return false; + // track the matches + var stMatchSets = {}, + nullData = 0; + + // no url was specified, we need to adjust the cache length to make sure it fits the local data store + if( !options.url ) options.cacheLength = 1; + + // track all options for minChars = 0 + stMatchSets[""] = []; + + // loop through the array and create a lookup structure + for ( var i = 0, ol = options.data.length; i < ol; i++ ) { + var rawValue = options.data[i]; + // if rawValue is a string, make an array otherwise just reference the array + rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; + + var value = options.formatMatch(rawValue, i+1, options.data.length); + if ( value === false ) + continue; + + var firstChar = value.charAt(0).toLowerCase(); + // if no lookup array for this character exists, look it up now + if( !stMatchSets[firstChar] ) + stMatchSets[firstChar] = []; + + // if the match is a string + var row = { + value: value, + data: rawValue, + result: options.formatResult && options.formatResult(rawValue) || value + }; + + // push the current match into the set list + stMatchSets[firstChar].push(row); + + // keep track of minChars zero items + if ( nullData++ < options.max ) { + stMatchSets[""].push(row); + } + }; + + // add the data items to the cache + $.each(stMatchSets, function(i, value) { + // increase the cache size + options.cacheLength++; + // add to the cache + add(i, value); + }); + } + + // populate any existing data + setTimeout(populate, 25); + + function flush(){ + data = {}; + length = 0; + } + + return { + flush: flush, + add: add, + populate: populate, + load: function(q) { + if (!options.cacheLength || !length) + return null; + /* + * if dealing w/local data and matchContains than we must make sure + * to loop through all the data collections looking for matches + */ + if( !options.url && options.matchContains ){ + // track all matches + var csub = []; + // loop through all the data grids for matches + for( var k in data ){ + // don't search through the stMatchSets[""] (minChars: 0) cache + // this prevents duplicates + if( k.length > 0 ){ + var c = data[k]; + $.each(c, function(i, x) { + // if we've got a match, add it to the array + if (matchSubset(x.value, q)) { + csub.push(x); + } + }); + } + } + return csub; + } else + // if the exact item exists, use it + if (data[q]){ + return data[q]; + } else + if (options.matchSubset) { + for (var i = q.length - 1; i >= options.minChars; i--) { + var c = data[q.substr(0, i)]; + if (c) { + var csub = []; + $.each(c, function(i, x) { + if (matchSubset(x.value, q)) { + csub[csub.length] = x; + } + }); + return csub; + } + } + } + return null; + } + }; +}; + +$.Autocompleter.Select = function (options, input, select, config) { + var CLASSES = { + ACTIVE: "ac_over" + }; + + var listItems, + active = -1, + data, + term = "", + needsInit = true, + element, + list; + + // Create results + function init() { + if (!needsInit) + return; + element = $("
      ") + .hide() + .addClass(options.resultsClass) + .css("position", "absolute") + .appendTo(document.body); + + list = $("
        ").appendTo(element).mouseover( function(event) { + if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { + active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); + $(target(event)).addClass(CLASSES.ACTIVE); + } + }).click(function(event) { + $(target(event)).addClass(CLASSES.ACTIVE); + select(); + // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus + input.focus(); + return false; + }).mousedown(function() { + config.mouseDownOnSelect = true; + }).mouseup(function() { + config.mouseDownOnSelect = false; + }); + + if( options.width > 0 ) + element.css("width", options.width); + + needsInit = false; + } + + function target(event) { + var element = event.target; + while(element && element.tagName != "LI") + element = element.parentNode; + // more fun with IE, sometimes event.target is empty, just ignore it then + if(!element) + return []; + return element; + } + + function moveSelect(step) { + listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); + movePosition(step); + var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); + if(options.scroll) { + var offset = 0; + listItems.slice(0, active).each(function() { + offset += this.offsetHeight; + }); + if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { + list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); + } else if(offset < list.scrollTop()) { + list.scrollTop(offset); + } + } + }; + + function movePosition(step) { + active += step; + if (active < 0) { + active = listItems.size() - 1; + } else if (active >= listItems.size()) { + active = 0; + } + } + + function limitNumberOfItems(available) { + return options.max && options.max < available + ? options.max + : available; + } + + function fillList() { + list.empty(); + var max = limitNumberOfItems(data.length); + for (var i=0; i < max; i++) { + if (!data[i]) + continue; + var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); + if ( formatted === false ) + continue; + var li = $("
      • ").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; + $.data(li, "ac_data", data[i]); + } + listItems = list.find("li"); + if ( options.selectFirst ) { + listItems.slice(0, 1).addClass(CLASSES.ACTIVE); + active = 0; + } + // apply bgiframe if available + if ( $.fn.bgiframe ) + list.bgiframe(); + } + + return { + display: function(d, q) { + init(); + data = d; + term = q; + fillList(); + }, + next: function() { + moveSelect(1); + }, + prev: function() { + moveSelect(-1); + }, + pageUp: function() { + if (active != 0 && active - 8 < 0) { + moveSelect( -active ); + } else { + moveSelect(-8); + } + }, + pageDown: function() { + if (active != listItems.size() - 1 && active + 8 > listItems.size()) { + moveSelect( listItems.size() - 1 - active ); + } else { + moveSelect(8); + } + }, + hide: function() { + element && element.hide(); + listItems && listItems.removeClass(CLASSES.ACTIVE); + active = -1; + }, + visible : function() { + return element && element.is(":visible"); + }, + current: function() { + return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); + }, + show: function() { + var offset = $(input).offset(); + element.css({ + width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).innerWidth(), + top: offset.top + input.offsetHeight, + left: offset.left + }).show(); + options.show(element); + if(options.scroll) { + list.scrollTop(0); + list.css({ + maxHeight: options.scrollHeight, + overflow: 'auto' + }); + + if($.browser.msie && typeof document.body.style.maxHeight === "undefined") { + var listHeight = 0; + listItems.each(function() { + listHeight += this.offsetHeight; + }); + var scrollbarsVisible = listHeight > options.scrollHeight; + list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight ); + if (!scrollbarsVisible) { + // IE doesn't recalculate width when scrollbar disappears + listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) ); + } + } + + } + }, + selected: function() { + var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); + return selected && selected.length && $.data(selected[0], "ac_data"); + }, + emptyList: function (){ + list && list.empty(); + }, + unbind: function() { + element && element.remove(); + } + }; +}; + +$.fn.selection = function(start, end) { + if (start !== undefined) { + return this.each(function() { + if( this.createTextRange ){ + var selRange = this.createTextRange(); + if (end === undefined || start == end) { + selRange.move("character", start); + selRange.select(); + } else { + selRange.collapse(true); + selRange.moveStart("character", start); + selRange.moveEnd("character", end); + selRange.select(); + } + } else if( this.setSelectionRange ){ + this.setSelectionRange(start, end); + } else if( this.selectionStart ){ + this.selectionStart = start; + this.selectionEnd = end; + } + }); + } + var field = this[0]; + if ( field.createTextRange ) { + var range = document.selection.createRange(), + orig = field.value, + teststring = "<->", + textLength = range.text.length; + range.text = teststring; + var caretAt = field.value.indexOf(teststring); + field.value = orig; + this.selection(caretAt, caretAt + textLength); + return { + start: caretAt, + end: caretAt + textLength + } + } else if( field.selectionStart !== undefined ){ + return { + start: field.selectionStart, + end: field.selectionEnd + } + } +}; + +})(jQuery); +/** + * jQuery.fn.sortElements + * -------------- + * @author James Padolsey (http://james.padolsey.com) + * @version 0.11 + * @updated 18-MAR-2010 + * -------------- + * @param Function comparator: + * Exactly the same behaviour as [1,2,3].sort(comparator) + * + * @param Function getSortable + * A function that should return the element that is + * to be sorted. The comparator will run on the + * current collection, but you may want the actual + * resulting sort to occur on a parent or another + * associated element. + * + * E.g. $('td').sortElements(comparator, function(){ + * return this.parentNode; + * }) + * + * The
      ) will be sorted instead + * of the
      's parent (
      itself. + */ +jQuery.fn.sortElements = (function(){ + + var sort = [].sort; + + return function(comparator, getSortable) { + + getSortable = getSortable || function(){return this;}; + + var placements = this.map(function(){ + + var sortElement = getSortable.call(this), + parentNode = sortElement.parentNode, + + // Since the element itself will change position, we have + // to have some way of storing it's original position in + // the DOM. The easiest way is to have a 'flag' node: + nextSibling = parentNode.insertBefore( + document.createTextNode(''), + sortElement.nextSibling + ); + + return function() { + + if (parentNode === this) { + throw new Error( + "You can't sort elements if any one is a descendant of another." + ); + } + + // Insert before flag: + parentNode.insertBefore(this, nextSibling); + // Remove flag: + parentNode.removeChild(nextSibling); + + }; + + }); + + return sort.call(this, comparator).each(function(i){ + placements[i].call(getSortable.call(this)); + }); + + }; + +})(); +/*! + * ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + * + * Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) + * Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) + * Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) + * + * For the full copyright and license information, please view + * the file LICENSE.md that was distributed with this source code. + */ + +$(function() { + var $document = $(document); + var $left = $('#left'); + var $right = $('#right'); + var $rightInner = $('#rightInner'); + var $splitter = $('#splitter'); + var $groups = $('#groups'); + var $content = $('#content'); + + // Menu + + // Hide deep packages and namespaces + $('ul span', $groups).click(function(event) { + event.preventDefault(); + event.stopPropagation(); + $(this) + .toggleClass('collapsed') + .parent() + .next('ul') + .toggleClass('collapsed'); + }).click(); + + $active = $('ul li.active', $groups); + if ($active.length > 0) { + // Open active + $('> a > span', $active).click(); + } else { + $main = $('> ul > li.main', $groups); + if ($main.length > 0) { + // Open first level of the main project + $('> a > span', $main).click(); + } else { + // Open first level of all + $('> ul > li > a > span', $groups).click(); + } + } + + // Content + + // Search autocompletion + var autocompleteFound = false; + var autocompleteFiles = {'c': 'class', 'co': 'constant', 'f': 'function', 'm': 'class', 'mm': 'class', 'p': 'class', 'mp': 'class', 'cc': 'class'}; + var $search = $('#search input[name=q]'); + $search + .autocomplete(ApiGen.elements, { + matchContains: true, + scrollHeight: 200, + max: 20, + formatItem: function(data) { + return data[1].replace(/^(.+\\)(.+)$/, '$1$2'); + }, + formatMatch: function(data) { + return data[1]; + }, + formatResult: function(data) { + return data[1]; + }, + show: function($list) { + var $items = $('li span', $list); + var maxWidth = Math.max.apply(null, $items.map(function() { + return $(this).width(); + })); + // 10px padding + $list + .width(Math.max(maxWidth + 10, $search.innerWidth())) + .css('left', $search.offset().left + $search.outerWidth() - $list.outerWidth()); + } + }).result(function(event, data) { + autocompleteFound = true; + var location = window.location.href.split('/'); + location.pop(); + var parts = data[1].split(/::|$/); + var file = $.sprintf(ApiGen.config.templates.main[autocompleteFiles[data[0]]].filename, parts[0].replace(/[^\w]/g, '.')); + if (parts[1]) { + file += '#' + ('mm' === data[0] || 'mp' === data[0] ? 'm' : '') + parts[1].replace(/([\w]+)\(\)/, '_$1'); + } + location.push(file); + window.location = location.join('/'); + + // Workaround for Opera bug + $(this).closest('form').attr('action', location.join('/')); + }).closest('form') + .submit(function() { + var query = $search.val(); + if ('' === query) { + return false; + } + + var label = $('#search input[name=more]').val(); + if (!autocompleteFound && label && -1 === query.indexOf('more:')) { + $search.val(query + ' more:' + label); + } + + return !autocompleteFound && '' !== $('#search input[name=cx]').val(); + }); + + // Save natural order + $('table.summary tr[data-order]', $content).each(function(index) { + do { + index = '0' + index; + } while (index.length < 3); + $(this).attr('data-order-natural', index); + }); + + // Switch between natural and alphabetical order + var $caption = $('table.summary', $content) + .filter(':has(tr[data-order])') + .find('caption'); + $caption + .click(function() { + var $this = $(this); + var order = $this.data('order') || 'natural'; + order = 'natural' === order ? 'alphabetical' : 'natural'; + $this.data('order', order); + $.cookie('order', order, {expires: 365}); + var attr = 'alphabetical' === order ? 'data-order' : 'data-order-natural'; + $this + .closest('table') + .find('tr').sortElements(function(a, b) { + return $(a).attr(attr) > $(b).attr(attr) ? 1 : -1; + }); + return false; + }) + .addClass('switchable') + .attr('title', 'Switch between natural and alphabetical order'); + if ((null === $.cookie('order') && 'alphabetical' === ApiGen.config.options.elementsOrder) || 'alphabetical' === $.cookie('order')) { + $caption.click(); + } + + // Open details + if (ApiGen.config.options.elementDetailsCollapsed) { + $('tr', $content).filter(':has(.detailed)') + .click(function() { + var $this = $(this); + $('.short', $this).hide(); + $('.detailed', $this).show(); + }); + } + + // Splitter + var splitterWidth = $splitter.width(); + function setSplitterPosition(position) + { + $left.width(position); + $right.css('margin-left', position + splitterWidth); + $splitter.css('left', position); + } + function setNavigationPosition() + { + var width = $rightInner.width(); + $rightInner + .toggleClass('medium', width <= 960) + .toggleClass('small', width <= 650); + } + $splitter.mousedown(function() { + $splitter.addClass('active'); + + $document.mousemove(function(event) { + if (event.pageX >= 230 && $document.width() - event.pageX >= 600 + splitterWidth) { + setSplitterPosition(event.pageX); + setNavigationPosition(); + } + }); + + $() + .add($splitter) + .add($document) + .mouseup(function() { + $splitter + .removeClass('active') + .unbind('mouseup'); + $document + .unbind('mousemove') + .unbind('mouseup'); + + $.cookie('splitter', parseInt($splitter.css('left')), {expires: 365}); + }); + + return false; + }); + var splitterPosition = $.cookie('splitter'); + if (null !== splitterPosition) { + setSplitterPosition(parseInt(splitterPosition)); + } + setNavigationPosition(); + $(window).resize(setNavigationPosition); + + // Select selected lines + var matches = window.location.hash.substr(1).match(/^\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*$/); + if (null !== matches) { + var lists = matches[0].split(','); + for (var i = 0; i < lists.length; i++) { + var lines = lists[i].split('-'); + lines[1] = lines[1] || lines[0]; + for (var j = lines[0]; j <= lines[1]; j++) { + $('#' + j).addClass('selected'); + } + } + + var $firstLine = $('#' + parseInt(matches[0])); + if ($firstLine.length > 0) { + $right.scrollTop($firstLine.offset().top); + } + } + + // Save selected lines + var lastLine; + $('a.l').click(function(event) { + event.preventDefault(); + + var $selectedLine = $(this).parent(); + var selectedLine = parseInt($selectedLine.attr('id')); + + if (event.shiftKey) { + if (lastLine) { + for (var i = Math.min(selectedLine, lastLine); i <= Math.max(selectedLine, lastLine); i++) { + $('#' + i).addClass('selected'); + } + } else { + $selectedLine.addClass('selected'); + } + } else if (event.ctrlKey) { + $selectedLine.toggleClass('selected'); + } else { + var $selected = $('.l.selected') + .not($selectedLine) + .removeClass('selected'); + if ($selected.length > 0) { + $selectedLine.addClass('selected'); + } else { + $selectedLine.toggleClass('selected'); + } + } + + lastLine = $selectedLine.hasClass('selected') ? selectedLine : null; + + // Update hash + var lines = $('.l.selected') + .map(function() { + return parseInt($(this).attr('id')); + }) + .get() + .sort(function(a, b) { + return a - b; + }); + + var hash = []; + var list = []; + for (var j = 0; j < lines.length; j++) { + if (0 === j && j + 1 === lines.length) { + hash.push(lines[j]); + } else if (0 === j) { + list[0] = lines[j]; + } else if (lines[j - 1] + 1 !== lines[j] && j + 1 === lines.length) { + hash.push(list.join('-')); + hash.push(lines[j]); + } else if (lines[j - 1] + 1 !== lines[j]) { + hash.push(list.join('-')); + list = [lines[j]]; + } else if (j + 1 === lines.length) { + list[1] = lines[j]; + hash.push(list.join('-')); + } else { + list[1] = lines[j]; + } + } + + window.location.hash = hash.join(','); + }); +}); + diff --git a/doc/Code Docs/php/resources/footer.png b/doc/Code Docs/php/resources/footer.png new file mode 100644 index 00000000..51f4a7fa Binary files /dev/null and b/doc/Code Docs/php/resources/footer.png differ diff --git a/doc/Code Docs/php/resources/inherit.png b/doc/Code Docs/php/resources/inherit.png new file mode 100644 index 00000000..957079b8 Binary files /dev/null and b/doc/Code Docs/php/resources/inherit.png differ diff --git a/doc/Code Docs/php/resources/resize.png b/doc/Code Docs/php/resources/resize.png new file mode 100644 index 00000000..fb98a7a3 Binary files /dev/null and b/doc/Code Docs/php/resources/resize.png differ diff --git a/doc/Code Docs/php/resources/sort.png b/doc/Code Docs/php/resources/sort.png new file mode 100644 index 00000000..0d0fea14 Binary files /dev/null and b/doc/Code Docs/php/resources/sort.png differ diff --git a/doc/Code Docs/php/resources/style.css b/doc/Code Docs/php/resources/style.css new file mode 100644 index 00000000..f3614742 --- /dev/null +++ b/doc/Code Docs/php/resources/style.css @@ -0,0 +1,608 @@ +/*! + * ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + * + * Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) + * Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) + * Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) + * + * For the full copyright and license information, please view + * the file LICENSE.md that was distributed with this source code. + */ + +body { + font: 13px/1.5 Verdana, 'Geneva CE', lucida, sans-serif; + margin: 0; + padding: 0; + background: #ffffff; + color: #333333; +} + +h1, h2, h3, h4, caption { + font-family: 'Trebuchet MS', 'Geneva CE', lucida, sans-serif; + color: #053368; +} + +h1 { + color: #1e5eb6; + font-size: 230%; + font-weight: normal; + margin: .3em 0; +} + +h2 { + color: #1e5eb6; + font-size: 150%; + font-weight: normal; + margin: -.3em 0 .3em 0; +} + +h3 { + font-size: 1.6em; + font-weight: normal; + margin-bottom: 2px; +} + +h4 { + font-size: 100%; + font-weight: bold; + padding: 0; + margin: 0; +} + +caption { + border: 1px solid #cccccc; + background: #ecede5; + font-weight: bold; + font-size: 1.2em; + padding: 3px 5px; + text-align: left; + margin-bottom: 0; +} + +p { + margin: .7em 0 1em; + padding: 0; +} + +hr { + margin: 2em 0 1em; + border: none; + border-top: 1px solid #cccccc; + height: 0; +} + +a { + color: #006aeb; + padding: 3px 1px; + text-decoration: none; +} + +h1 a { + color: #1e5eb6; +} + +a:hover, a:active, a:focus, a:hover b, a:hover var { + background-color: #006aeb; + color: #ffffff !important; +} + +code, var, pre { + font-family: monospace; +} + +var { + font-weight: bold; + font-style: normal; + color: #ca8a04; +} + +pre { + margin: 0; +} + +code a b { + color: #000000; +} + +.deprecated { + text-decoration: line-through; +} + +.invalid { + color: #e71818; +} + +.hidden { + display: none; +} + +/* Left side */ +#left { + overflow: auto; + width: 270px; + height: 100%; + position: fixed; +} + +/* Menu */ +#menu { + padding: 10px; +} + +#menu ul { + list-style: none; + padding: 0; + margin: 0; +} + +#menu ul ul { + padding-left: 10px; +} + +#menu li { + white-space: nowrap; + position: relative; +} + +#menu a { + display: block; + padding: 0 2px; +} + +#menu .active > a, #menu > span { + color: #333333; + background: none; + font-weight: bold; +} + +#menu .active > a.invalid { + color: #e71818; +} + +#menu .active > a:hover, #menu .active > a:active, #menu .active > a:focus { + background-color: #006aeb; +} + +#menu #groups span { + position: absolute; + top: 4px; + right: 2px; + cursor: pointer; + display: block; + width: 12px; + height: 12px; + background: url('collapsed.png') transparent 0 0 no-repeat; +} + +#menu #groups span:hover { + background-position: -12px 0; +} + +#menu #groups span.collapsed { + background-position: 0 -12px; +} + +#menu #groups span.collapsed:hover { + background-position: -12px -12px; +} + +#menu #groups ul.collapsed { + display: none; +} + +/* Right side */ +#right { + overflow: auto; + margin-left: 275px; + height: 100%; + position: fixed; + left: 0; + right: 0; +} + +#rightInner { + max-width: 1000px; + min-width: 350px; +} + +/* Search */ +#search { + float: right; + margin: 3px 8px; +} + +#search input.text { + padding: 3px 5px; + width: 250px; +} + +/* Autocomplete */ +.ac_results { + padding: 0; + border: 1px solid #cccccc; + background-color: #ffffff; + overflow: hidden; + z-index: 99999; +} + +.ac_results ul { + width: 100%; + list-style-position: outside; + list-style: none; + padding: 0; + margin: 0; +} + +.ac_results li { + margin: 0; + padding: 2px 5px; + cursor: default; + display: block; + font: 12px 'Trebuchet MS', 'Geneva CE', lucida, sans-serif; + line-height: 16px; + overflow: hidden; + white-space: nowrap; +} + +.ac_results li strong { + color: #000000; +} + +.ac_odd { + background-color: #eeeeee; +} + +.ac_over { + background-color: #006aeb; + color: #ffffff; +} + +.ac_results li.ac_over strong { + color: #ffffff; +} + +/* Navigation */ +#navigation { + padding: 3px 8px; + background-color: #f6f6f4; + height: 26px; +} + +#navigation ul { + list-style: none; + margin: 0 8px 4px 0; + padding: 0; + overflow: hidden; + float: left; +} + +#navigation ul + ul { + border-left: 1px solid #000000; + padding-left: 8px; +} + +#navigation ul li { + float: left; + margin: 2px; + padding: 0 3px; + font-family: Verdana, 'Geneva CE', lucida, sans-serif; + color: #808080; +} + +#navigation ul li.active { + background-color: #053368; + color: #ffffff; + font-weight: bold; +} + +#navigation ul li a { + color: #000000; + font-weight: bold; + padding: 0; +} + +#navigation ul li span { + float: left; + padding: 0 3px; +} + +#navigation ul li a:hover span, #navigation ul li a:active span, #navigation ul li a:focus span { + background-color: #006aeb; +} + +/* Content */ +#content { + clear: both; + padding: 5px 15px; +} + +.description pre { + padding: .6em; + background: #fcfcf7; +} + +#content > .description { + background: #ecede5; + padding: 1px 8px; + margin: 1.2em 0; +} + +#content > .description pre { + margin: .5em 0; +} + +dl.tree { + margin: 1.2em 0; +} + +dl.tree dd { + margin: 0; + padding: 0; +} + +.info { + margin: 1.2em 0; +} + +.summary { + border: 1px solid #cccccc; + border-collapse: collapse; + font-size: 1em; + width: 100%; + margin: 1.2em 0 2.4em; +} + +.summary caption { + border-width: 1px 1px 0; +} + +.summary caption.switchable { + background: #ecede5 url('sort.png') no-repeat center right; + cursor: pointer; +} + +.summary td { + border: 1px solid #cccccc; + margin: 0; + padding: 3px 10px; + font-size: 1em; + vertical-align: top; +} + +.summary td:first-child { + text-align: right; +} + +#packages.summary td:first-child, #namespaces.summary td:first-child, .inherited.summary td:first-child, .used.summary td:first-child { + text-align: left; +} + +.summary tr:hover td { + background: #f6f6f4; +} + +.summary .description pre { + border: .5em solid #ecede5; +} + +.summary .description p { + margin: 0; +} + +.summary .description p + p, .summary .description ul { + margin: 3px 0 0 0; +} + +.summary .description.detailed h4 { + margin-top: 3px; +} + +.summary dl { + margin: 0; +} + +.summary dd { + margin: 0 0 0 25px; +} + +.name, .attributes { + white-space: nowrap; +} + +.value { + white-space: pre-wrap; +} + +td.name, td.attributes { + width: 1%; +} + +.class #methods .name { + width: auto; + white-space: normal; +} + +.class #methods .name > div > code { + white-space: pre-wrap; +} + +.class #methods .name > div > code span, .function .value > code { + white-space: nowrap; +} + +.class #methods td.name > div, .class td.description > div { + position: relative; + padding-right: 1em; +} + +.anchor { + position: absolute; + top: 0; + right: 0; + line-height: 1; + font-size: 85%; + margin: 0; + color: #006aeb !important; +} + +.list { + margin: 0 0 5px 25px; +} + +div.invalid { + background-color: #fae4e0; + padding: 10px; +} + +/* Splitter */ +#splitter { + position: fixed; + height: 100%; + width: 5px; + left: 270px; + background: #1e5eb6 url('resize.png') left center no-repeat; + cursor: e-resize; +} + +#splitter.active { + opacity: .5; +} + +/* Footer */ +#footer { + border-top: 1px solid #e9eeef; + clear: both; + color: #a7a7a7; + font-size: 8pt; + text-align: center; + padding: 20px 0 0; + margin: 3em 0 0; + height: 90px; + background: #ffffff url('footer.png') no-repeat center top; +} + +/* Tree */ +div.tree ul { + list-style: none; + background: url('tree-vertical.png') left repeat-y; + padding: 0; + margin-left: 20px; +} + +div.tree li { + margin: 0; + padding: 0; +} + +div.tree div { + padding-left: 30px; +} + +div.tree div.notlast { + background: url('tree-hasnext.png') left 10px no-repeat; +} + +div.tree div.last { + background: url('tree-last.png') left -240px no-repeat; +} + +div.tree li.last { + background: url('tree-cleaner.png') left center repeat-y; +} + +div.tree span.padding { + padding-left: 15px; +} + +/* Source code */ +.php-keyword1 { + color: #e71818; + font-weight: bold; +} + +.php-keyword2 { + font-weight: bold; +} + +.php-var { + color: #d59401; + font-weight: bold; +} + +.php-num { + color: #cd0673; +} + +.php-quote { + color: #008000; +} + +.php-comment { + color: #929292; +} + +.xlang { + color: #ff0000; + font-weight: bold; +} + +span.l { + display: block; +} + +span.l.selected { + background: #f6f6f4; +} + +span.l a { + color: #333333; +} + +span.l a:hover, div.l a:active, div.l a:focus { + background: transparent; + color: #333333 !important; +} + +span.l .php-var a { + color: #d59401; +} + +span.l .php-var a:hover, span.l .php-var a:active, span.l .php-var a:focus { + color: #d59401 !important; +} + +span.l a.l { + padding-left: 2px; + color: #c0c0c0; +} + +span.l a.l:hover, span.l a.l:active, span.l a.l:focus { + background: transparent; + color: #c0c0c0 !important; +} + +#rightInner.medium #navigation { + height: 52px; +} + +#rightInner.medium #navigation ul:first-child + ul { + clear: left; + border: none; + padding: 0; +} + +#rightInner.medium .name, #rightInner.medium .attributes { + white-space: normal; +} + +#rightInner.small #search { + float: left; +} + +#rightInner.small #navigation { + height: 78px; +} + +#rightInner.small #navigation ul:first-child { + clear: both; +} diff --git a/doc/Code Docs/php/resources/tree-cleaner.png b/doc/Code Docs/php/resources/tree-cleaner.png new file mode 100644 index 00000000..2eb9085b Binary files /dev/null and b/doc/Code Docs/php/resources/tree-cleaner.png differ diff --git a/doc/Code Docs/php/resources/tree-hasnext.png b/doc/Code Docs/php/resources/tree-hasnext.png new file mode 100644 index 00000000..91d6b79a Binary files /dev/null and b/doc/Code Docs/php/resources/tree-hasnext.png differ diff --git a/doc/Code Docs/php/resources/tree-last.png b/doc/Code Docs/php/resources/tree-last.png new file mode 100644 index 00000000..7f319f8f Binary files /dev/null and b/doc/Code Docs/php/resources/tree-last.png differ diff --git a/doc/Code Docs/php/resources/tree-vertical.png b/doc/Code Docs/php/resources/tree-vertical.png new file mode 100644 index 00000000..384908b2 Binary files /dev/null and b/doc/Code Docs/php/resources/tree-vertical.png differ diff --git a/doc/Code Docs/php/source-class-Appointments.html b/doc/Code Docs/php/source-class-Appointments.html new file mode 100644 index 00000000..108d1313 --- /dev/null +++ b/doc/Code Docs/php/source-class-Appointments.html @@ -0,0 +1,305 @@ + + + + + + + + File appointments.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Appointments_Model.html b/doc/Code Docs/php/source-class-Appointments_Model.html new file mode 100644 index 00000000..f73477a3 --- /dev/null +++ b/doc/Code Docs/php/source-class-Appointments_Model.html @@ -0,0 +1,378 @@ + + + + + + + + File appointments_model.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Customers_Model.html b/doc/Code Docs/php/source-class-Customers_Model.html new file mode 100644 index 00000000..570ffb3a --- /dev/null +++ b/doc/Code Docs/php/source-class-Customers_Model.html @@ -0,0 +1,371 @@ + + + + + + + + File customers_model.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Google.html b/doc/Code Docs/php/source-class-Google.html new file mode 100644 index 00000000..c9daef35 --- /dev/null +++ b/doc/Code Docs/php/source-class-Google.html @@ -0,0 +1,119 @@ + + + + + + + + File google.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Providers_Model.html b/doc/Code Docs/php/source-class-Providers_Model.html new file mode 100644 index 00000000..5003dcc0 --- /dev/null +++ b/doc/Code Docs/php/source-class-Providers_Model.html @@ -0,0 +1,208 @@ + + + + + + + + File providers_model.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Roles_Model.html b/doc/Code Docs/php/source-class-Roles_Model.html new file mode 100644 index 00000000..00bc553b --- /dev/null +++ b/doc/Code Docs/php/source-class-Roles_Model.html @@ -0,0 +1,109 @@ + + + + + + + + File roles_model.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Services_Model.html b/doc/Code Docs/php/source-class-Services_Model.html new file mode 100644 index 00000000..95aac4bb --- /dev/null +++ b/doc/Code Docs/php/source-class-Services_Model.html @@ -0,0 +1,169 @@ + + + + + + + + File services_model.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Settings_Model.html b/doc/Code Docs/php/source-class-Settings_Model.html new file mode 100644 index 00000000..ff648f35 --- /dev/null +++ b/doc/Code Docs/php/source-class-Settings_Model.html @@ -0,0 +1,186 @@ + + + + + + + + File settings_model.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/source-class-Test.html b/doc/Code Docs/php/source-class-Test.html new file mode 100644 index 00000000..6cdd07ec --- /dev/null +++ b/doc/Code Docs/php/source-class-Test.html @@ -0,0 +1,123 @@ + + + + + + + + File test.php + + + + + + + + +
      + +
      + +
      + + + + diff --git a/doc/Code Docs/php/tree.html b/doc/Code Docs/php/tree.html new file mode 100644 index 00000000..e849c6fb --- /dev/null +++ b/doc/Code Docs/php/tree.html @@ -0,0 +1,170 @@ + + + + + + + Tree + + + + + + + + +
      + +
      + +
      + + + + diff --git a/src/application/controllers/appointments.php b/src/application/controllers/appointments.php index 7a8683f0..babc58f5 100644 --- a/src/application/controllers/appointments.php +++ b/src/application/controllers/appointments.php @@ -43,7 +43,6 @@ class Appointments extends CI_Controller { . 'you an email. Please backup the appointment details so that you can restore them ' . 'later.

      Error:
      ' . $not_exc->getMessage() . ''; } - // Load the book appointment view. $this->load->view('appointments/book_success', $view_data); @@ -51,35 +50,135 @@ class Appointments extends CI_Controller { } /** + * [AJAX] Get the available appointment hours for the given date. + * * This method answers to an AJAX request. It calculates the * available hours for the given service, provider and date. * * @param array $_POST['post_data'] An associative array that - * contains the user selected service, provider and date. + * contains the user selected 'service_id', 'provider_id', + * 'selected_date' and 'service_duration' in minutes. + * @return Returns a json object with the available hours. */ public function ajax_get_available_hours() { - /*** CHECK BUSINESS WORKING HOURS ***/ - /*** CHECK PROVIDERS WORKING HOURS ***/ - /*** CHECK ALREADY BOOKED APPOINTMENTS ***/ - /*** RETURN AVAILABLE HOURS ***/ + $this->load->model('Providers_Model'); + $this->load->model('Appointments_Model'); + $this->load->model('Settings_Model'); - // ------------------------------------------------------------------ - // For now we just need to return a sample array. Dynamic calculation - // will be adding in future development. - $start_time = strtotime($_POST['selected_date'] . ' 08:00') / 60; // Convert to minutes - $end_time = strtotime($_POST['selected_date'] . ' 19:00') / 60; // Convert to minutes + // Get the provider's working plan and reserved appointments. + $working_plan = json_decode($this->Providers_Model + ->get_value('working_plan', $_POST['provider_id']), true); - for ($i=0; ($start_time*60 + $i*60)<=($end_time*60); $i+=intval($_POST['service_duration'])) { - $available_hours[] = date('H:i', $start_time * 60 + $i * 60); + $reserved_appointments = $this->Appointments_Model->get_batch( + array( + 'DATE(start_datetime)' => date('Y-m-d', strtotime($_POST['selected_date'])), + 'id_users_provider' => $_POST['provider_id'], + 'id_services' => $_POST['service_id'] + )); + + // Find the empty spaces on the plan. The first split between + // the plan is due to a break (if exist). After that every reserved + // appointment is considered to be a taken space in the plan. + $sel_date_working_plan = $working_plan[strtolower(date('l', + strtotime($_POST['selected_date'])))]; + $empty_spaces_with_breaks = array(); + + if (isset($sel_date_working_plan['breaks'])) { + foreach($sel_date_working_plan['breaks'] as $index=>$break) { + // Split the working plan to available time periods that do not + // contain the breaks in them. + $last_break_index = $index - 1; + + if (count($empty_spaces_with_breaks) === 0) { + $start_hour = $sel_date_working_plan['start']; + $end_hour = $break['start']; + } else { + $start_hour = $sel_date_working_plan['breaks'][$last_break_index]['end']; + $end_hour = $break['start']; + } + + $empty_spaces_with_breaks[] = array( + 'start' => $start_hour, + 'end' => $end_hour + ); + } + + // Add the space from the last break to the end of the day. + $empty_spaces_with_breaks[] = array( + 'start' => $sel_date_working_plan['breaks'][$index]['end'], + 'end' => $sel_date_working_plan['end'] + ); + } + + // Break the empty spaces with the reserved appointments. + $empty_spaces_with_appointments = array(); + if (count($reserved_appointments) > 0) { + foreach($empty_spaces_with_breaks as $space) { + foreach($reserved_appointments as $index=>$appointment) { + $appointment_start = date('H:i', strtotime($appointment['start_datetime'])); + $appointment_end = date('H:i', strtotime($appointment['end_datetime'])); + $space_start = date('H:i', strtotime($space['start'])); + $space_end = date('H:i', strtotime($space['end'])); + + if ($space_start < $appointment_start && $space_end > $appointment_end) { + // Current appointment is within the current empty space. So + // we need to break the empty space into two other spaces that + // don't include the appointment. + $empty_spaces_with_appointments[] = array( + 'start' => $space_start, + 'end' => $appointment_start + ); + $empty_spaces_with_appointments[] = array( + 'start' => $appointment_end, + 'end' => $space_end + ); + } else { + $empty_spaces_with_appointments[] = array( + 'start' => $space_start, + 'end' => $space_end + ); + } + } + } + } else { + $empty_spaces_with_appointments = $empty_spaces_with_breaks; } - // If the selected date is today, remove past hours. + $empty_spaces = $empty_spaces_with_appointments; + + // Calculate the available appointment hours for the given date. + // The empty spaces are broken down to 15 min and if the service + // fit in each quarter then a new available hour is added to the + // $available hours array. + $available_hours = array(); + + foreach($empty_spaces as $space) { + $start_hour = new DateTime($_POST['selected_date'] . ' ' . $space['start']); + $end_hour = new DateTime($_POST['selected_date'] . ' ' . $space['end']); + $curr_hour = $start_hour; + + $diff = $curr_hour->diff($end_hour); + while(($diff->h * 60 + $diff->i) > intval($_POST['service_duration'])) { + $available_hours[] = $curr_hour->format('H:i'); + $curr_hour->add(new DateInterval("PT15M")); + $diff = $curr_hour->diff($end_hour); + } + } + + // If the selected date is today, remove past hours. It is important + // include the timeout before booking that is set in the backoffice + // the system. Normally we might want the customer to book an appointment + // that is at least half or one hour from now. The setting is stored in + // minutes. if (date('m/d/Y', strtotime($_POST['selected_date'])) == date('m/d/Y')) { + $book_advance_timeout = $this->Settings_Model->get_setting('book_advance_timeout'); + foreach($available_hours as $index=>$value) { - $available_hour = date('m/d/Y H:i', strtotime($value)); - $current_hour = date('m/d/Y H:i'); - - if ($available_hour < $current_hour) { + $available_hour = strtotime($value); + $current_hour = strtotime('+' . $book_advance_timeout + . ' minutes', strtotime('now')); + + if ($available_hour <= $current_hour) { unset($available_hours[$index]); } } diff --git a/src/application/models/customers_model.php b/src/application/models/customers_model.php index e74b7d96..370a0841 100644 --- a/src/application/models/customers_model.php +++ b/src/application/models/customers_model.php @@ -179,6 +179,7 @@ class Customers_Model extends CI_Model { return TRUE; } catch (Exception $exc) { + echo $exc->getMessage(); return FALSE; } } diff --git a/src/application/views/appointments/book.php b/src/application/views/appointments/book.php index b2f63811..abf98183 100644 --- a/src/application/views/appointments/book.php +++ b/src/application/views/appointments/book.php @@ -74,7 +74,7 @@ @@ -104,10 +104,10 @@
      -
      -

      Select Service & Provider

      -
      - +
      +

      Select Service & Provider

      +
      + - +
      @@ -130,15 +130,17 @@