Merge branch 'master' of github.com:jsdoc3/jsdoc

This commit is contained in:
Michael Mathews 2012-07-08 01:12:12 +01:00
commit fdf37fe137
16 changed files with 882 additions and 883 deletions

View File

@ -4,7 +4,7 @@
"eqeqeq": false,
"forin": true,
"immed": true,
"latedef": false,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": false,

View File

@ -91,29 +91,6 @@ include.resolve = function(filepath) {
return env.dirname + '/' + filepath;
}
/**
Data that must be shared across the entire application.
@namespace
*/
app = {
jsdoc: {
scanner: new (require('jsdoc/src/scanner').Scanner)(),
parser: new (require('jsdoc/src/parser').Parser)(),
name: require('jsdoc/name')
}
}
try { main(); }
catch(e) {
if (e.rhinoException != null) {
e.rhinoException.printStackTrace();
} else {
throw e;
}
}
finally { env.run.finish = new Date(); }
/** Print string/s out to the console.
@param {string} ... String/s to print out to console.
*/
@ -185,6 +162,19 @@ function indexAll(docs) {
}
/**
Data that must be shared across the entire application.
@namespace
*/
app = {
jsdoc: {
scanner: new (require('jsdoc/src/scanner').Scanner)(),
parser: new (require('jsdoc/src/parser').Parser)(),
name: require('jsdoc/name')
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
@ -324,3 +314,13 @@ function main() {
}
}
}
try { main(); }
catch(e) {
if (e.rhinoException != null) {
e.rhinoException.printStackTrace();
} else {
throw e;
}
}
finally { env.run.finish = new Date(); }

View File

@ -5,6 +5,19 @@ exports.readFileSync = function(filename, encoding) {
return readFile(filename, encoding);
};
var stat = exports.stat = exports.statSync = function(path, encoding) {
var f = new java.io.File(path);
return {
isFile: function() {
return f.isFile();
},
isDirectory: function() {
return f.isDirectory();
}
};
};
var readdirSync = exports.readdirSync = function(path) {
var dir,
files;
@ -64,28 +77,17 @@ var ls = exports.ls = function(dir, recurse, _allFiles, _path) {
return _allFiles;
};
var stat = exports.stat = exports.statSync = function(path, encoding) {
var toDir = exports.toDir = function(path) {
var f = new java.io.File(path);
return {
isFile: function() {
return f.isFile();
},
isDirectory: function() {
return f.isDirectory();
}
};
};
exports.mkPath = function(/**Array*/ path) {
if (path.constructor != Array){path = path.split(/[\\\/]/);}
var make = "";
for (var i = 0, l = path.length; i < l; i++) {
make += path[i] + '/';
if (! exists(make)) {
makeDir(make);
}
if (f.isDirectory()){
return path;
}
var parts = path.split(/[\\\/]/);
parts.pop();
return parts.join('/');
};
function makeDir(/**string*/ path) {
@ -108,17 +110,20 @@ function exists(path) {
return true;
}
var toDir = exports.toDir = function(path) {
var f = new java.io.File(path);
if (f.isDirectory()){
return path;
exports.mkPath = function(/**Array*/ path) {
if (path.constructor != Array){path = path.split(/[\\\/]/);}
var make = "";
for (var i = 0, l = path.length; i < l; i++) {
make += path[i] + '/';
if (! exists(make)) {
makeDir(make);
}
}
};
var toFile = exports.toFile = function(path) {
var parts = path.split(/[\\\/]/);
parts.pop();
return parts.join('/');
return parts.pop();
};
exports.copyFile = function(inFile, outDir, fileName) {
@ -139,11 +144,6 @@ exports.copyFile = function(inFile, outDir, fileName) {
bis.close();
};
var toFile = exports.toFile = function(path) {
var parts = path.split(/[\\\/]/);
return parts.pop();
};
exports.writeFileSync = function(filename, data, encoding) {
encoding = encoding || 'utf-8';

View File

@ -21,8 +21,6 @@ const defaults = {
"jsVersion": 180
};
module.exports = Config;
/**
@class
@classdesc Represents a JSDoc application configuration.
@ -34,6 +32,8 @@ function Config(json) {
this._config = util.mergeRecurse(defaults, json);
}
module.exports = Config;
/**
Get the merged configuration values.
*/

View File

@ -18,7 +18,109 @@ var jsdoc = {
},
name: require('jsdoc/name')
};
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 = [];
docletSrc = unwrap(docletSrc);
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.
@ -228,105 +330,3 @@ exports.Doclet.prototype.setMeta = function(meta) {
}
}
}
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;
}
/**
Convert the raw source of the doclet comment into an array of Tag objects.
@private
*/
function toTags(docletSrc) {
var tagSrcs,
tags = [];
docletSrc = unwrap(docletSrc);
tagSrcs = split(docletSrc);
for (var i = 0, l = tagSrcs.length; i < l; i++) {
tags.push( {title: tagSrcs[i].title, text: tagSrcs[i].text} );
}
return tags;
}
function unwrap(docletSrc) {
if (!docletSrc) { return ''; }
// note: keep trailing whitespace for @examples
// extra opening/closing stars are ignored
// left margin is considered a star and a space
// use the /m flag on regex to avoid having to guess what this platform's newline is
docletSrc =
docletSrc.replace(/^\/\*\*+/, '') // remove opening slash+stars
.replace(/\**\*\/$/, "\\Z") // replace closing star slash with end-marker
.replace(/^\s*(\* ?|\\Z)/gm, '') // remove left margin like: spaces+star or spaces+end-marker
.replace(/\s*\\Z$/g, ''); // remove end-marker
return docletSrc;
}
function fixDescription(docletSrc) {
if (!/^\s*@/.test(docletSrc)) {
docletSrc = '@description ' + docletSrc;
}
return docletSrc;
}
function split(docletSrc) {
var tagSrcs = [],
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;
}

View File

@ -12,35 +12,9 @@
@author Ben Blank <ben.blank@gmail.com>
*/
module.exports = ReadMe;
var fs = require('fs'),
conf = env.conf.markdown;
/**
@class
@classdesc Represents a README file.
@param {string} path - The filepath to the README.
*/
function ReadMe(path) {
var content = fs.readFileSync(path),
parse;
// determine which parser should be used based on configuration options, if any
if (conf && conf.parser) {
parse = getParser(conf.parser, conf);
} else if (conf && conf.githubRepoOwner && conf.githubRepoName) {
// use GitHub-friendly parser if GitHub-specific options are present
parse = getParser('gfm', conf);
} else {
// evilstreak is the default parser
parse = getParser('evilstreak', conf);
}
this.html = parse(content);
}
function getParser(parser, conf) {
conf = conf || {};
@ -66,3 +40,28 @@ function getParser(parser, conf) {
}
}
/**
@class
@classdesc Represents a README file.
@param {string} path - The filepath to the README.
*/
function ReadMe(path) {
var content = fs.readFileSync(path),
parse;
// determine which parser should be used based on configuration options, if any
if (conf && conf.parser) {
parse = getParser(conf.parser, conf);
} else if (conf && conf.githubRepoOwner && conf.githubRepoName) {
// use GitHub-friendly parser if GitHub-specific options are present
parse = getParser('gfm', conf);
} else {
// evilstreak is the default parser
parse = getParser('evilstreak', conf);
}
this.html = parse(content);
}
module.exports = ReadMe;

View File

@ -39,7 +39,8 @@ exports.attachTo = function(parser) {
});
function newSymbolDoclet(docletSrc, e) {
var newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
var memberofName = null,
newDoclet = new jsdoc.doclet.Doclet(docletSrc, e);
// an undocumented symbol right after a virtual comment? rhino mistakenly connected the two
if (newDoclet.name) { // there was a @name in comment
@ -64,8 +65,7 @@ exports.attachTo = function(parser) {
else if (e.code && e.code.name) { // we need to get the symbol name from code
newDoclet.addTag('name', e.code.name);
if (!newDoclet.memberof && e.astnode) {
var memberofName = null,
basename = null,
var basename = null,
scope = '';
if ( /^((module.)?exports|this)(\.|$)/.test(newDoclet.name) ) {
var nameStartsWith = RegExp.$1;

View File

@ -115,6 +115,337 @@ 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 objectliterals documentable by giving them a dummy name
.replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/\s*)\{/g, '$1 ____ = {') // like return @lends {
.replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/)(\s*)return(\s*)\{/g, '$2$3 return $1 ____ = {'); // like @lends return {
}
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) {
about.name = nodeToString(node.left);
if (node.type === Token.COLON) {
// objlit keys with unsafe variable-name characters must be quoted
if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) {
about.name = '"'+about.name.replace(/"/g, '\\"')+'"';
}
}
about.node = node.right;
about.value = nodeToString(about.node);
about.type = getTypeName(node.right);
if (about.type === 'FUNCTION' && about.node.name) {
about.node.type = tkn.NAMEDFUNCTIONSTATEMENT;
}
}
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, len = paramCount; i < len; i++) {
paramNames.push(''+about.node.getParamOrVarName(i));
}
about.paramnames = paramNames;
}
}
return about;
}
/** @private
@memberof module:src/parser.Parser
*/
function isValidJsdoc(commentSrc) {
return commentSrc && commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/
}
/** @private
* @memberof module:src/parser.Parser
*/
function makeVarsFinisher(funcDoc) {
var func = 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;
}
}
return func;
}
/** @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 */
function visitNode(node) {
var e,
nodeComments,
comment,
commentSrc,
i,
l;
// look for stand-alone doc comments
if (node.type === Token.SCRIPT && node.comments) {
// note: ALL comments are seen in this block...
nodeComments = node.comments.toArray();
for (i = 0, l = nodeComments.length; i < l; i++) {
comment = nodeComments[i];
if (comment.commentType !== Token.CommentType.JSDOC) {
continue;
}
if (commentSrc = ''+comment.toSource()) {
e = {
comment: commentSrc,
lineno: comment.getLineno(),
filename: currentSourceName
};
if ( isValidJsdoc(commentSrc) ) {
currentParser.fire('jsdocCommentFound', e, currentParser);
}
}
}
e = null;
}
else if (node.type === Token.ASSIGN) {
e = {
id: 'astnode'+node.hashCode(), // the id of the ASSIGN node
comment: String(node.getJsDoc()||'@undocumented'),
lineno: node.left.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
var basename = getBasename(e.code.name);
if (basename !== 'this') {
e.code.funcscope = currentParser.resolveVar(node, basename);
}
}
else if (node.type === Token.COLON) { // assignment within an object literal
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.left.getJsDoc()||'@undocumented'),
lineno: node.left.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef, currentParser.resolveEnum]
};
}
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() ); }
//node.jsDoc = node.parent.jsDoc;
}
e = {
id: 'astnode'+node.hashCode(), // the id of the VARIABLE node
comment: String(node.getJsDoc()||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
// keep track of vars in a function or global scope
var func = "__global__",
funcDoc = null;
if (node.enclosingFunction) {
func = 'astnode'+node.enclosingFunction.hashCode();
}
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || {};
funcDoc.meta.vars[e.code.name] = false;
e.finishers.push(makeVarsFinisher(funcDoc));
}
}
else if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) {
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.getJsDoc()||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
e.code.name = (node.type == tkn.NAMEDFUNCTIONSTATEMENT)? '' : String(node.name) || '';
//console.log(':: e.code.name is', e.code.name);
// keep track of vars in a function or global scope
var func = "__global__",
funcDoc = null;
if (node.enclosingFunction) {
func = 'astnode'+node.enclosingFunction.hashCode();
}
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || {};
funcDoc.meta.vars[e.code.name] = false;
e.finishers.push(makeVarsFinisher(funcDoc));
}
var basename = getBasename(e.code.name)
e.code.funcscope = currentParser.resolveVar(node, basename);
}
if (!e) { e = {finishers: []}; }
for(var i = 0, l = currentParser._visitors.length; i < l; i++) {
currentParser._visitors[i].visitNode(node, e, currentParser, currentSourceName);
if (e.stopPropagation) { break; }
}
if (!e.preventDefault && isValidJsdoc(e.comment)) {
currentParser.fire(e.event, e, currentParser);
}
for (var i = 0, l = e.finishers.length; i < l; i++) {
e.finishers[i].call(currentParser, e);
}
return true;
}
/** @private */
exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) {
var e = {filename: sourceName};
@ -141,21 +472,6 @@ exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) {
currentSourceName = '';
}
function pretreat(code) {
return code
// make starbangstar comments look like real jsdoc comments
.replace(/\/\*\!\*/g, '/**')
// merge adjacent doclets
.replace(/\*\/\/\*\*+/g, '@also')
// make lent objectliterals documentable by giving them a dummy name
.replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/\s*)\{/g, '$1 ____ = {') // like return @lends {
.replace(/(\/\*\*[^\*\/]*?@lends\b[^\*\/]*?\*\/)(\s*)return(\s*)\{/g, '$2$3 return $1 ____ = {'); // like @lends return {
}
var tkn = { NAMEDFUNCTIONSTATEMENT: -1001 };
exports.Parser.tkn = tkn;
/**
* Given a node, determine what the node is a member of.
* @param {astnode} node
@ -331,322 +647,6 @@ exports.Parser.prototype.resolveEnum = function(e) {
}
}
/** @private */
function visitNode(node) {
var e,
nodeComments,
comment,
commentSrc,
i,
l;
// look for stand-alone doc comments
if (node.type === Token.SCRIPT && node.comments) {
// note: ALL comments are seen in this block...
nodeComments = node.comments.toArray();
for (i = 0, l = nodeComments.length; i < l; i++) {
comment = nodeComments[i];
if (comment.commentType !== Token.CommentType.JSDOC) {
continue;
}
if (commentSrc = ''+comment.toSource()) {
e = {
comment: commentSrc,
lineno: comment.getLineno(),
filename: currentSourceName
};
if ( isValidJsdoc(commentSrc) ) {
currentParser.fire('jsdocCommentFound', e, currentParser);
}
}
}
e = null;
}
else if (node.type === Token.ASSIGN) {
e = {
id: 'astnode'+node.hashCode(), // the id of the ASSIGN node
comment: String(node.getJsDoc()||'@undocumented'),
lineno: node.left.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
var basename = getBasename(e.code.name);
if (basename !== 'this') {
e.code.funcscope = currentParser.resolveVar(node, basename);
}
}
else if (node.type === Token.COLON) { // assignment within an object literal
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.left.getJsDoc()||'@undocumented'),
lineno: node.left.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef, currentParser.resolveEnum]
};
}
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() ); }
//node.jsDoc = node.parent.jsDoc;
}
e = {
id: 'astnode'+node.hashCode(), // the id of the VARIABLE node
comment: String(node.getJsDoc()||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
// keep track of vars in a function or global scope
var func = "__global__",
funcDoc = null;
if (node.enclosingFunction) {
func = 'astnode'+node.enclosingFunction.hashCode();
}
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || {};
funcDoc.meta.vars[e.code.name] = false;
e.finishers.push(makeVarsFinisher(funcDoc));
}
}
else if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONSTATEMENT) {
e = {
id: 'astnode'+node.hashCode(), // the id of the COLON node
comment: String(node.getJsDoc()||'@undocumented'),
lineno: node.getLineno(),
filename: currentSourceName,
astnode: node,
code: aboutNode(node),
event: "symbolFound",
finishers: [currentParser.addDocletRef]
};
e.code.name = (node.type == tkn.NAMEDFUNCTIONSTATEMENT)? '' : String(node.name) || '';
//console.log(':: e.code.name is', e.code.name);
// keep track of vars in a function or global scope
var func = "__global__",
funcDoc = null;
if (node.enclosingFunction) {
func = 'astnode'+node.enclosingFunction.hashCode();
}
funcDoc = currentParser.refs[func];
if (funcDoc) {
funcDoc.meta.vars = funcDoc.meta.vars || {};
funcDoc.meta.vars[e.code.name] = false;
e.finishers.push(makeVarsFinisher(funcDoc));
}
var basename = getBasename(e.code.name)
e.code.funcscope = currentParser.resolveVar(node, basename);
}
if (!e) { e = {finishers: []}; }
for(var i = 0, l = currentParser._visitors.length; i < l; i++) {
currentParser._visitors[i].visitNode(node, e, currentParser, currentSourceName);
if (e.stopPropagation) { break; }
}
if (!e.preventDefault && isValidJsdoc(e.comment)) {
currentParser.fire(e.event, e, currentParser);
}
for (var i = 0, l = e.finishers.length; i < l; i++) {
e.finishers[i].call(currentParser, e);
}
return true;
}
/** @private */
function parserFactory() {
var cx = Packages.org.mozilla.javascript.Context.getCurrentContext();
var ce = new Packages.org.mozilla.javascript.CompilerEnvirons();
ce.setRecordingComments(true);
ce.setRecordingLocalJsDocComments(true);
ce.setLanguageVersion(180);
ce.initFromContext(cx);
return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter());
}
/**
* Attempts to find the name and type of the given node.
* @private
* @memberof module:src/parser.Parser
*/
function aboutNode(node) {
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) {
about.name = nodeToString(node.left);
if (node.type === Token.COLON) {
// objlit keys with unsafe variable-name characters must be quoted
if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) {
about.name = '"'+about.name.replace(/"/g, '\\"')+'"';
}
}
about.node = node.right;
about.value = nodeToString(about.node);
about.type = getTypeName(node.right);
if (about.type === 'FUNCTION' && about.node.name) {
about.node.type = tkn.NAMEDFUNCTIONSTATEMENT;
}
}
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, len = paramCount; i < len; i++) {
paramNames.push(''+about.node.getParamOrVarName(i));
}
about.paramnames = paramNames;
}
}
return about;
}
/** @private
@memberof module:src/parser.Parser
*/
function nodeToString(node) {
var str;
if (!node) { return; }
if (node.type === Token.GETPROP) {
str = [nodeToString(node.target), node.property.string].join('.');
}
else if (node.type === Token.VAR) {
str = nodeToString(node.target)
}
else if (node.type === Token.NAME) {
str = node.string;
}
else if (node.type === Token.STRING) {
str = node.value;
}
else if (node.type === Token.NUMBER) {
str = node.value;
}
else if (node.type === Token.THIS) {
str = 'this';
}
else if (node.type === Token.GETELEM) {
str = node.toSource(); // like: Foo['Bar']
}
else if (node.type === Token.NEG || node.type === Token.TRUE || node.type === Token.FALSE) {
str = node.toSource(); // like -1
}
else {
str = getTypeName(node);
}
return '' + str;
}
/** @private
@memberof module:src/parser.Parser
*/
function getTypeName(node) {
var type = '';
if (node) {
type = ''+ Packages.org.mozilla.javascript.Token.typeToName(node.getType());
}
return type;
}
/** @private
@memberof module:src/parser.Parser
*/
function isValidJsdoc(commentSrc) {
return commentSrc && commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/
}
/** @private
* @memberof module:src/parser.Parser
*/
function makeVarsFinisher(funcDoc) {
var func = 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;
}
}
return func;
}
/** @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;
}
/**
Fired whenever the parser encounters a JSDoc comment in the current source code.
@event jsdocCommentFound

View File

@ -22,6 +22,45 @@ var jsdoc = {
}
};
function trim(text, newlines) {
if (!text) { return ''; }
if (newlines) {
return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
}
else {
return text.replace(/^\s+|\s+$/g, '');
}
}
/**
Parse the parameter name and parameter desc from the tag text.
@inner
@method parseParamText
@memberof module:jsdoc/tag
@param {string} tagText
@returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault].
*/
function parseParamText(tagText) {
var pname, pdesc, poptional, pdefault;
// like: pname, pname pdesc, or name - pdesc
tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/);
pname = RegExp.$1;
pdesc = RegExp.$3;
if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) {
pname = RegExp.$1;
poptional = true;
if ( /^(.+?)\s*=\s*(.+)$/.test(pname) ) {
pname = RegExp.$1;
pdefault = RegExp.$2;
}
}
return { name: pname, desc: pdesc, optional: poptional, default: pdefault };
}
/**
Constructs a new tag object. Calls the tag validator.
@class
@ -99,42 +138,3 @@ exports.Tag = function(tagTitle, tagBody, meta) {
}
}
}
function trim(text, newlines) {
if (!text) { return ''; }
if (newlines) {
return text.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
}
else {
return text.replace(/^\s+|\s+$/g, '');
}
}
/**
Parse the parameter name and parameter desc from the tag text.
@inner
@method parseParamText
@memberof module:jsdoc/tag
@param {string} tagText
@returns {Array.<string, string, boolean, boolean>} [pname, pdesc, poptional, pdefault].
*/
function parseParamText(tagText) {
var pname, pdesc, poptional, pdefault;
// like: pname, pname pdesc, or name - pdesc
tagText.match(/^(\[[^\]]+\]|\S+)((?:\s*\-\s*|\s+)(\S[\s\S]*))?$/);
pname = RegExp.$1;
pdesc = RegExp.$3;
if ( /^\[\s*(.+?)\s*\]$/.test(pname) ) {
pname = RegExp.$1;
poptional = true;
if ( /^(.+?)\s*=\s*(.+)$/.test(pname) ) {
pname = RegExp.$1;
pdefault = RegExp.$2;
}
}
return { name: pname, desc: pdesc, optional: poptional, default: pdefault };
}

View File

@ -7,6 +7,7 @@
var _synonyms = {},
_definitions = {},
_namespaces = [],
dictionary,
hasOwnProp = Object.prototype.hasOwnProperty;
/** @private */
@ -29,7 +30,7 @@ TagDefinition.prototype.synonym = function(synonymName) {
}
/** @exports jsdoc/tag/dictionary */
var dictionary = {
dictionary = {
/** @function */
defineTag: function(title, opts) {
_definitions[title] = new TagDefinition(title, opts);

View File

@ -7,6 +7,91 @@
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/** @private */
function setDocletKindToTitle(doclet, tag) {
doclet.addTag( 'kind', tag.title );
}
function setDocletScopeToTitle(doclet, tag) {
doclet.addTag( 'scope', tag.title );
}
function setDocletNameToValue(doclet, tag) {
if (tag.value && tag.value.description) { // as in a long tag
doclet.addTag( 'name', tag.value.description);
}
else if (tag.text) { // or a short tag
doclet.addTag('name', tag.text);
}
}
function setDocletDescriptionToValue(doclet, tag) {
if (tag.value) {
doclet.addTag( 'description', tag.value );
}
}
function setNameToFile(doclet, tag) {
if (doclet.meta.filename) {
var name = 'file:';
if (doclet.meta.path) { name += doclet.meta.path + java.lang.System.getProperty("file.separator"); }
doclet.addTag( 'name', name + doclet.meta.filename );
}
}
function setDocletMemberof(doclet, tag) {
if (tag.value && tag.value !== '<global>') {
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) {
var name = (doclet.meta.path ? (doclet.meta.path + java.lang.System.getProperty("file.separator")) : "") + 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
*/
@ -561,88 +646,3 @@ exports.defineTags = function(dictionary) {
}
});
}
/** @private */
function setDocletKindToTitle(doclet, tag) {
doclet.addTag( 'kind', tag.title );
}
function setDocletScopeToTitle(doclet, tag) {
doclet.addTag( 'scope', tag.title );
}
function setDocletNameToValue(doclet, tag) {
if (tag.value && tag.value.description) { // as in a long tag
doclet.addTag( 'name', tag.value.description);
}
else if (tag.text) { // or a short tag
doclet.addTag('name', tag.text);
}
}
function setDocletDescriptionToValue(doclet, tag) {
if (tag.value) {
doclet.addTag( 'description', tag.value );
}
}
function setNameToFile(doclet, tag) {
if (doclet.meta.filename) {
var name = 'file:';
if (doclet.meta.path) { name += doclet.meta.path + java.lang.System.getProperty("file.separator"); }
doclet.addTag( 'name', name + doclet.meta.filename );
}
}
function setDocletMemberof(doclet, tag) {
if (tag.value && tag.value !== '<global>') {
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) {
var name = (doclet.meta.path ? (doclet.meta.path + java.lang.System.getProperty("file.separator")) : "") + 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 ''; }
}

View File

@ -6,6 +6,95 @@
*/
function parseOptional(type) {
var optional = null;
// {sometype=} means optional
if ( /(.+)=$/.test(type) ) {
type = RegExp.$1;
optional = true;
}
return { type: type, optional: optional };
}
function parseNullable(type) {
var nullable = null;
// {?sometype} means nullable, {!sometype} means not-nullable
if ( /^([\?\!])(.+)$/.test(type) ) {
type = RegExp.$2;
nullable = (RegExp.$1 === '?')? true : false;
}
return { type: type, nullable: nullable };
}
function parseVariable(type) {
var variable = null;
// {...sometype} means variable number of that type
if ( /^(\.\.\.)(.+)$/.test(type) ) {
type = RegExp.$2;
variable = true;
}
return { type: type, variable: variable };
}
function parseTypes(type) {
var types = [];
if ( type.indexOf('|') !== -1 ) {
// remove optional parens, like: { ( string | number ) }
// see: http://code.google.com/closure/compiler/docs/js-for-compiler.html#types
if ( /^\s*\(\s*(.+)\s*\)\s*$/.test(type) ) {
type = RegExp.$1;
}
types = type.split(/\s*\|\s*/g);
}
else if (type) {
types = [type];
}
return types;
}
/** @private */
function trim(text) {
return text.trim();
}
function getTagType(tagValue) {
var type = '',
text = '',
count = 0;
// type expressions start with '{'
if (tagValue[0] === '{') {
count++;
// find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; }
if (count === 0) {
type = trim(tagValue.slice(1, i))
.replace(/\\\{/g, '{') // unescape escaped curly braces
.replace(/\\\}/g, '}');
text = trim(tagValue.slice(i+1));
break;
}
}
}
return { type: type, text: text };
}
exports.getTagType = getTagType;
/**
@param {string} tagValue
@returns {object} Hash with type, text, optional, nullable, and variable properties
@ -42,91 +131,3 @@ exports.parse = function(tagValue) {
variable: variable.variable
};
}
function parseOptional(type) {
var optional = null;
// {sometype=} means optional
if ( /(.+)=$/.test(type) ) {
type = RegExp.$1;
optional = true;
}
return { type: type, optional: optional };
}
function parseNullable(type) {
var nullable = null;
// {?sometype} means nullable, {!sometype} means not-nullable
if ( /^([\?\!])(.+)$/.test(type) ) {
type = RegExp.$2;
nullable = (RegExp.$1 === '?')? true : false;
}
return { type: type, nullable: nullable };
}
function parseVariable(type) {
var variable = null;
// {...sometype} means variable number of that type
if ( /^(\.\.\.)(.+)$/.test(type) ) {
type = RegExp.$2;
variable = true;
}
return { type: type, variable: variable };
}
function parseTypes(type) {
var types = [];
if ( type.indexOf('|') !== -1 ) {
// remove optional parens, like: { ( string | number ) }
// see: http://code.google.com/closure/compiler/docs/js-for-compiler.html#types
if ( /^\s*\(\s*(.+)\s*\)\s*$/.test(type) ) {
type = RegExp.$1;
}
types = type.split(/\s*\|\s*/g);
}
else if (type) {
types = [type];
}
return types;
}
function getTagType(tagValue) {
var type = '',
text = '',
count = 0;
// type expressions start with '{'
if (tagValue[0] === '{') {
count++;
// find matching closer '}'
for (var i = 1, leni = tagValue.length; i < leni; i++) {
if (tagValue[i] === '\\') { i++; continue; } // backslash escapes the next character
if (tagValue[i] === '{') { count++; }
else if (tagValue[i] === '}') { count--; }
if (count === 0) {
type = trim(tagValue.slice(1, i))
.replace(/\\\{/g, '{') // unescape escaped curly braces
.replace(/\\\}/g, '}');
text = trim(tagValue.slice(i+1));
break;
}
}
}
return { type: type, text: text };
}
exports.getTagType = getTagType;
/** @private */
function trim(text) {
return text.trim();
}

View File

@ -10,6 +10,24 @@
var dictionary = require('jsdoc/tag/dictionary');
function UnknownTagError(tagName, meta) {
this.name = 'UnknownTagError';
this.message = 'The @' + tagName + ' tag is not a known tag. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
UnknownTagError.prototype = Error.prototype;
function TagValueRequiredError(tagName, meta) {
this.name = 'TagValueRequiredError';
this.message = 'The @' + tagName + ' tag requires a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
TagValueRequiredError.prototype = Error.prototype;
function TagValueNotPermittedError(tagName, meta) {
this.name = 'TagValueNotPermittedError';
this.message = 'The @' + tagName + ' tag does not permit a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
TagValueNotPermittedError.prototype = Error.prototype;
/**
Validate the given tag.
*/
@ -31,22 +49,3 @@ exports.validate = function(tag, meta) {
}
}
}
function UnknownTagError(tagName, meta) {
this.name = 'UnknownTagError';
this.message = 'The @' + tagName + ' tag is not a known tag. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
UnknownTagError.prototype = Error.prototype;
function TagValueRequiredError(tagName, meta) {
this.name = 'TagValueRequiredError';
this.message = 'The @' + tagName + ' tag requires a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
TagValueRequiredError.prototype = Error.prototype;
function TagValueNotPermittedError(tagName, meta) {
this.name = 'TagValueNotPermittedError';
this.message = 'The @' + tagName + ' tag does not permit a value. File: ' + meta.filename + ', Line: ' + meta.lineno + '\n' + meta.comment;
}
TagValueNotPermittedError.prototype = Error.prototype;

View File

@ -5,18 +5,6 @@
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
*/
/**
@param {any} object
*/
exports.dump = function(object) {
indentBy = 0;
output = '';
walk(object);
outdent(false);
return output;
}
const INDENTATION = ' '; // 4 spaces
var indentBy,
output;
@ -62,6 +50,45 @@ seen.has = function(object) {
return false;
}
function stringify(o) {
return JSON.stringify(o);
}
function getValue(o) { // see: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/typeof
if (o === null) { return 'null'; }
if ( /^(string|boolean|number|undefined)$/.test(typeof o) ) {
return ''+stringify(o);
}
}
function isUnwalkable(o) { // some objects are unwalkable, like Java native objects
return (typeof o === 'object' && typeof o.constructor === 'undefined');
}
function isArray(o) {
return o && (o instanceof Array) || o.constructor === Array;
}
function isRegExp(o) {
return (o instanceof RegExp) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'RegExp');
}
function isDate(o) {
return o && (o instanceof Date) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Date');
}
function isFunction(o) {
return o && (typeof o === 'function' || o instanceof Function);// ||
//(typeof o.constructor !== 'undefined' && (o.constructor||{}).name === 'Function');
}
function isObject(o) {
return o && o instanceof Object ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Object');
}
function walk(object) {
var value;
@ -116,42 +143,14 @@ function walk(object) {
}
}
function getValue(o) { // see: https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/typeof
if (o === null) { return 'null'; }
if ( /^(string|boolean|number|undefined)$/.test(typeof o) ) {
return ''+stringify(o);
}
/**
@param {any} object
*/
exports.dump = function(object) {
indentBy = 0;
output = '';
walk(object);
outdent(false);
return output;
}
function stringify(o) {
return JSON.stringify(o);
}
function isUnwalkable(o) { // some objects are unwalkable, like Java native objects
return (typeof o === 'object' && typeof o.constructor === 'undefined');
}
function isArray(o) {
return o && (o instanceof Array) || o.constructor === Array;
}
function isRegExp(o) {
return (o instanceof RegExp) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'RegExp');
}
function isDate(o) {
return o && (o instanceof Date) ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Date');
}
function isFunction(o) {
return o && (typeof o === 'function' || o instanceof Function);// ||
//(typeof o.constructor !== 'undefined' && (o.constructor||{}).name === 'Function');
}
function isObject(o) {
return o && o instanceof Object ||
(typeof o.constructor !== 'undefined' && o.constructor.name === 'Object');
}

View File

@ -5,59 +5,23 @@
var hash = require('pajhome/hash');
var dictionary = require('jsdoc/tag/dictionary');
exports.globalName = 'global';
exports.fileExtension = '.html';
/** Find symbol {@link ...} and {@tutorial ...} strings in text and turn into html links */
exports.resolveLinks = function(str) {
str = str.replace(/(?:\[(.+?)\])?\{@link +(.+?)\}/gi,
function(match, content, longname) {
return toLink(longname, content);
}
);
str = str.replace(/(?:\[(.+?)\])?\{@tutorial +(.+?)\}/gi,
function(match, content, tutorial) {
return toTutorial(tutorial, content);
}
);
return str;
}
// two-way lookup
var linkMap = {
longnameToUrl: {},
urlToLongname: {}
};
exports.registerLink = function(longname, url) {
linkMap.longnameToUrl[longname] = url;
linkMap.urlToLongname[url] = longname;
}
var files = {};
// each container gets its own html file
var containers = ['class', 'module', 'external', 'namespace', 'mixin'];
/** Turn a doclet into a URL. */
exports.createLink = function(doclet) {
var url = '';
if (containers.indexOf(doclet.kind) < 0) {
var longname = doclet.longname,
filename = strToFilename(doclet.memberof || exports.globalName);
url = filename + exports.fileExtension + '#' + getNamespace(doclet.kind) + doclet.name;
}
else {
var longname = doclet.longname,
filename = strToFilename(longname);
url = filename + exports.fileExtension;
}
return url;
}
/** @external {jsdoc.tutorial.Tutorial} */
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';
function getNamespace(kind) {
if (dictionary.isNamespace(kind)) {
@ -66,6 +30,15 @@ function getNamespace(kind) {
return '';
}
function makeFilenameUnique(filename, str) {
//add suffix underscore until filename gets unique
while (filename in files && files[filename] !== str) {
filename += '_';
}
files[filename] = str;
return filename;
}
// compute it here just once
var nsprefix = /^(event|module|external):/;
@ -79,15 +52,15 @@ function strToFilename(str) {
return makeFilenameUnique(basename, str);
}
var files = {};
// two-way lookup
var linkMap = {
longnameToUrl: {},
urlToLongname: {}
};
function makeFilenameUnique(filename, str) {
//add suffix underscore until filename gets unique
while (filename in files && files[filename] !== str) {
filename += '_';
}
files[filename] = str;
return filename;
exports.registerLink = function(longname, url) {
linkMap.longnameToUrl[longname] = url;
linkMap.urlToLongname[url] = longname;
}
function toLink(longname, content) {
@ -121,16 +94,6 @@ function toLink(longname, content) {
}
}
/** @external {jsdoc.tutorial.Tutorial} */
var tutorials;
/** Sets tutorials map.
@param {jsdoc.tutorial.Tutorial} root - Root tutorial node.
*/
exports.setTutorials = function(root) {
tutorials = root;
};
var toTutorial = exports.toTutorial = function(tutorial, content) {
if (!tutorial) {
throw new Error('Missing required parameter: tutorial');
@ -147,6 +110,43 @@ var toTutorial = exports.toTutorial = function(tutorial, content) {
return '<a href="'+exports.tutorialToUrl(tutorial)+'">'+content+'</a>';
}
/** Find symbol {@link ...} and {@tutorial ...} strings in text and turn into html links */
exports.resolveLinks = function(str) {
str = str.replace(/(?:\[(.+?)\])?\{@link +(.+?)\}/gi,
function(match, content, longname) {
return toLink(longname, content);
}
);
str = str.replace(/(?:\[(.+?)\])?\{@tutorial +(.+?)\}/gi,
function(match, content, tutorial) {
return toTutorial(tutorial, content);
}
);
return str;
}
/** Turn a doclet into a URL. */
exports.createLink = function(doclet) {
var url = '';
if (containers.indexOf(doclet.kind) < 0) {
var longname = doclet.longname,
filename = strToFilename(doclet.memberof || exports.globalName);
url = filename + exports.fileExtension + '#' + getNamespace(doclet.kind) + doclet.name;
}
else {
var longname = doclet.longname,
filename = strToFilename(longname);
url = filename + exports.fileExtension;
}
return url;
}
exports.longnameToUrl = linkMap.longnameToUrl;
exports.tutorialToUrl = function(tutorial) {

View File

@ -381,6 +381,20 @@
// once for all
view.nav = nav;
function generate(title, docs, filename) {
var data = {
title: title,
docs: docs
};
var path = outdir + '/' + filename,
html = view.render('container.tmpl', data);
html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a>
fs.writeFileSync(path, html)
}
for (var longname in helper.longnameToUrl) {
if ( hasOwnProp.call(helper.longnameToUrl, longname) ) {
var classes = find({kind: 'class', longname: longname});
@ -416,20 +430,6 @@
, 'index.html');
function generate(title, docs, filename) {
var data = {
title: title,
docs: docs
};
var path = outdir + '/' + filename,
html = view.render('container.tmpl', data);
html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a>
fs.writeFileSync(path, html)
}
function generateTutorial(title, tutorial, filename) {
var data = {
title: title,