/*global env: true */
'use strict';
var template = require('jsdoc/template'),
fs = require('jsdoc/fs'),
path = require('jsdoc/path'),
taffy = require('taffydb').taffy,
logger = require('jsdoc/util/logger'),
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 getCategoryLink(className, cat) {
return '' + cat + ' methods';
}
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 && 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 ?
// we want the template output to say 'abstract', not 'virtual'
'<' + attribs.join(', ').replace('virtual', 'abstract') + '> ' : '') + '';
}
function shortenPaths(files, commonPrefix) {
Object.keys(files).forEach(function(file) {
files[file].shortened = files[file].resolved.replace(commonPrefix, '')
// always use forward slashes
.replace(/\\/g, '/');
});
return files;
}
function getPathFromDoclet(doclet) {
if (!doclet.meta) {
return;
}
return doclet.meta.path && doclet.meta.path !== 'null' ?
path.join(doclet.meta.path, doclet.meta.filename) :
doclet.meta.filename;
}
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
}
// Ensure
tags have pretty print class
html = html.replace(/
/g, '
');
fs.writeFileSync(outpath, html, 'utf8');
}
function generateSourceFiles(sourceFiles, encoding) {
encoding = encoding || 'utf8';
Object.keys(sourceFiles).forEach(function(file) {
var source;
// links are keyed to the shortened path in each doclet's `meta.shortpath` 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, encoding) )
};
}
catch(e) {
logger.error('Error while generating source file %s: %s', file, e.message);
}
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("') + '")';
}
});
}
function buildReadmeNav(readme) {
var nav = '';
var prevLevel = '0';
nav += '
';
readme = readme.replace(/([^<]*)<\/h[23]>/g, function(match, level, title) {
if (title.trim().length > 0) {
var titlelink = title.toLowerCase().replace(/[^a-z]/g, '-');
if (level === '2') {
if (prevLevel === '2' || prevLevel === '3') {
nav += '
';
}
prevLevel = level;
match = '' + match;
}
return match;
});
nav += '
';
return { nav: nav, readme: readme };
}
/**
* Create the navigation sidebar.
* @param {String} readmeNav The readme TOC
* @param {object} members The members that will be used to create the sidebar.
* @param {array