/*global env: true */
const hasOwnProp = Object.prototype.hasOwnProperty;
// Work around an issue with hasOwnProperty in JSDoc's templateHelper.js.
//TODO Fix in JSDoc.
Object.prototype.hasOwnProperty = function(property) {
return property in this;
};
const template = require('jsdoc/lib/jsdoc/template');
const fs = require('jsdoc/lib/jsdoc/fs');
const path = require('jsdoc/lib/jsdoc/path');
const taffy = require('taffydb').taffy;
const handle = require('jsdoc/lib/jsdoc/util/error').handle;
const helper = require('jsdoc/lib/jsdoc/util/templateHelper');
const _ = require('underscore');
const htmlsafe = helper.htmlsafe;
const linkto = helper.linkto;
const resolveAuthorLinks = helper.resolveAuthorLinks;
const outdir = env.opts.destination;
// Work around an issue with hasOwnProperty in JSDoc's templateHelper.js.
//TODO Fix in JSDoc.
Object.prototype.hasOwnProperty = hasOwnProp;
let view;
let data;
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;
}
let url = helper.createLink(doclet);
url = url.replace(/(#.+|$)/, hash);
return '' + hash + '';
}
function needsSignature(doclet) {
let needsSig = false;
// function and class definitions always get a signature
if (doclet.kind === 'function' || doclet.kind === 'class') {
needsSig = true;
} else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names &&
doclet.type.names.length) {
// typedefs that contain functions get a signature, too
for (let 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) {
const params = helper.getSignatureParams(f, 'optional');
f.signature = (f.signature || '') + '(' + params.join(', ') + ')';
}
function addSignatureReturns(f) {
const returnTypes = helper.getSignatureReturns(f);
f.signature = '' + (f.signature || '') + '';
if (returnTypes.length) {
f.signature += '' + (returnTypes.length ? '{' + returnTypes.join('|') + '}' : '') + '';
}
}
function addSignatureTypes(f) {
const types = helper.getSignatureTypes(f);
f.signature = (f.signature || '') + '' + (types.length ? ' :' + types.join('|') : '') + ' ';
}
function shortenPaths(files, commonPrefix) {
// always use forward slashes
const 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;
}
const filepath = doclet.meta.path && doclet.meta.path !== 'null' ?
doclet.meta.path + '/' + doclet.meta.filename.split(/[\/\\]/).pop() :
doclet.meta.filename;
return filepath;
}
function generate(title, docs, filename, resolveLinks) {
resolveLinks = resolveLinks === false ? false : true;
const docData = {
filename: filename,
title: title,
docs: docs,
packageInfo: (find({kind: 'package'}) || []) [0]
};
const outpath = path.join(outdir, filename);
let 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) {
let source;
// links are keyed to the shortened path in each doclet's `meta.filename` property
const 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) {
const symbols = {};
// build a lookup table
doclets.forEach(function(symbol) {
symbols[symbol.longname] = symbol;
});
modules.forEach(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