// 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, docs: null }; 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 <a href="foodoc.html">foo</a> if (searchEnabled) { searchableDocuments[filename] = { "id": filename, "title": title, "body": searchData(html) }; } fs.writeFileSync(tutorialPath, html, 'utf8'); }
// TODO: move the tutorial functions to templateHelper.js function generateTutorial(title, tutorial, fileName, originalFileName, isHtmlTutorial) { var tutorialData = { docs: null, // If there is no "docs" prop, Erros in layout.tmpl. (For left-nav member listing control) isTutorial: true, env: env, title: title, header: tutorial.title, children: tutorial.children, isHtmlTutorial: isHtmlTutorial, package: find({kind: 'package'})[0] }; if (isHtmlTutorial) { _.extend(tutorialData, generateHtmlTutorialData(tutorial, fileName, originalFileName)); } else { tutorialData.content = tutorial.parse(); } 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 <a href="foodoc.html">foo</a> fs.writeFileSync(tutorialPath, html, 'utf8'); }
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) }
// 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); var html = view.render('tutorial.tmpl', tutorialData); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> fs.writeFileSync(tutorialPath, html, 'utf8'); }
function generateIndex(title, filename) { var classes = find({kind: 'class', longname: longname}); var data = { title: title, docs: classes }; var path = outdir + '/' + filename, html = view.render(product + '-index.tmpl', data); html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> fs.writeFileSync(path, html) }
// 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('./', filename); var markdown = view.render('tutorial.tmpl', tutorialData); markdown = markdown.replace(/\n+/g, '\n'); // yes, you can use {@link} in tutorials too! markdown = helper.resolveLinks(markdown); // turn {@link foo} into <a href="foodoc.html">foo</a> fs.writeFileSync(tutorialPath, markdown, 'utf8'); }
// 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, filename: filename }; var tutorialPath = path.join(outdir, filename), html = view.render('tutorial.tmpl', tutorialData); // turn {@link foo} into <a href="foodoc.html">foo</a> html = helper.resolveLinks(html); if(tutorial.demo || (tutorial.parent && tutorial.parent.demo)) { tutorialPath = tutorialPath.replace('tutorial-', 'demo-'); } fs.writeFileSync(tutorialPath, html, 'utf8'); }
function generate(title, docs, filename) { var data = { title: title, docs: docs }; var path = outdir + '/' + filename, html = view.render('container.tmpl', data), titleLower = title.toLowerCase(); html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> if (titleLower in types_json) { types_json[titleLower].push(filename); } else { types_json[titleLower] = [ filename ]; } var pos = 0, pos2, member, memberLower, match = 'h4 class="name" id="', matchLen = match.length; while ((pos = html.indexOf(match, pos)) >= 0) { pos += matchLen; if ((pos2 = html.indexOf('"', pos)) > 0) { member = html.substring(pos, pos2); memberLower = member.toLowerCase(); if (memberLower !== titleLower) { if (memberLower in types_json) { // Note ".toString" and ".valueOf" used to be discarded here, before .toLowerCase() was used. if (typeof types_json[memberLower].push === 'function') { types_json[memberLower].push(filename + '#' + member); } } else { types_json[memberLower] = [ filename + '#' + member ]; } } } } fs.writeFileSync(path, html); }
exports.publish = function(taffyData, opts, tutorials) { data = taffyData; var conf = env.conf.templates || {}; conf['default'] = conf['default'] || {}; conf['weswit'] = conf['weswit'] || {}; 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'); var indexAllUrl = helper.getUniqueFilename('index-all'); // 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); var externs = {}; function searchDescription(className,methodName) { var _class = find({longname:className}); for (var c=0; c<_class.length; c++) { if (_class[c].augments) { for (var a = 0; a < _class[c].augments.length; a++) { var _superMethod = find({longname: _class[c].augments[a] + "#" + methodName}); for (var s = 0; s < _superMethod.length; s++) { if (_superMethod[s].inherits) { return _superMethod[s].inherits; } else if (_superMethod[s].description) { return _superMethod[s].longname; } } var inherits = searchDescription(_class[c].augments[a], methodName); if (inherits) { return inherits; } } } } return null; } //create summary if missing var methods = find({kind: 'function'}); methods.forEach(function(m) { if (!m.summary && m.description) { var d = m.description; m.summary = d.indexOf(".") > -1 ? d.substring(0,d.indexOf(".")+1) : d; } if (!m.inherited) { m.inherited = false; } if (m.reimplemented !== true && m.reimplemented !== false) { var others = find({kind: 'function', longname: m.longname, inherited: true, memberof: m.memberof}); var f = 0; others.forEach(function(o) { f++; if (!m.inherited) { m.inherits = o.inherits; o.reimplemented = true; } else if (f>1) { o.reimplemented = true; } else { o.reimplemented = false; } }); if (!m.description && !m.inherits) { m.inherits = searchDescription(m.memberof, m.name); if (m.inherits) { m.inherited = true; } } } }); var stuffWithSource = find({kind: ['class', 'module', 'global']}); stuffWithSource.forEach(function(m) { m.printSourceLink = conf['default'] && conf['default'].outputSourceFiles === true; }); 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*<caption>([\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; } } }); var namesForIndex = []; data().each(function(doclet) { //prepare alphabetic index (currently handles methods classes and modules TODO complete) //TODO might we use doclet.kind? var longname = doclet.longname if (longname.indexOf("~") > -1) { //hide private stuff } else if (longname.indexOf("#") > -1) { //instance methods var shortName = longname.substring(longname.indexOf("#")+1); shortName = shortName.replace('"','',"g"); var cName = longname.substring(0,longname.indexOf("#")); cName = cName.substring(cName.indexOf(":")+1); if (cName == "undefined") { cName = "Globals"; } namesForIndex.push({name: shortName, definition: "Instance method in " + cName, extern: cName+".prototype."+shortName+" = function() {};", longname: longname, memberof: cName}); } else if (longname.indexOf(".") > -1) { //static methods var shortName = longname.substring(longname.indexOf(".")+1); shortName = shortName.replace('"','',"g"); var cName = longname.substring(0,longname.indexOf(".")); cName = cName.substring(cName.indexOf(":")+1); if (cName == "undefined") { cName = "Globals"; } namesForIndex.push({name: shortName, definition: "Static method in " + cName, extern: cName+"."+shortName+" = function() {};", longname: longname, memberof: cName}); } else if (longname.indexOf(":") > -1) { //name undefined means globals //modules var shortName = longname.substring(longname.indexOf(":")+1); shortName = shortName.replace('"','',"g"); if (shortName == "undefined") { shortName="Globals"; } namesForIndex.push({name: shortName, definition: "Module " + shortName, extern: shortName+" = {};", longname: longname}); } else { //classes var shortName = longname.replace('"','',"g"); namesForIndex.push({name: shortName, extern: shortName+" = function() {};", definition: "Class " + longname, longname: longname}); } 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); } }); namesForIndex = namesForIndex.sort(function(a,b) { var al = a.name.toLowerCase(); var bl = b.name.toLowerCase(); if (al == bl) { return 0; } return al < bl ? -1 : 1; }); var byLetterIndex = [];//would be easier with a {} var curr = null; namesForIndex.forEach(function(el) { var l = el.name[0].toUpperCase(); if (l != curr) { byLetterIndex.push([]); curr = l; } byLetterIndex[byLetterIndex.length-1].push(el); }); namesForIndex = namesForIndex.sort(function(a,b) { if (a.memberof == b.memberof) { return 0; } else if (!a.memberof) { if (!b.memberof) { return 0; } return -1; } else if (!b.memberof) { return 1; } return a.memberof < b.memberof ? -1 : 1; }); var externsIndex = []; externsIndex.push([]); curr = null; namesForIndex.forEach(function(el) { var l = el.memberof; if (l != curr) { externsIndex.push([]); curr = l; } externsIndex[externsIndex.length-1].push(el); }); // 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,conf["weswit"].logo); attachModuleSymbols( find({ kind: ['class', 'function'], longname: {left: 'module:'} }), members.modules ); // do not output pretty-printed source files by default; do this before generating any other pages, so // that the other pages can link to the source files if (!conf['default'] || conf['default'].outputSourceFiles === true) { generateSourceFiles(sourceFiles, opts.encoding, conf["weswit"]); } if (members.globals.length) { generate('Global', [{kind: 'globalobj'}], globalUrl, conf["weswit"]); } // index page displays information from package.json and lists files var files = find({kind: 'file'}), packages = find({kind: 'package'}); var libName = conf["weswit"] && conf["weswit"].extendedLibraryName ? conf["weswit"].extendedLibraryName : "Index"; var summaryText = conf["weswit"].summaryFile ? fs.readFileSync( conf["weswit"].summaryFile, 'utf8' ) : (!opts.readme ? "Javascript Documentation" : null); generate(libName, packages.concat( [{ kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page', summary:summaryText }] ).concat(files), indexUrl, conf["weswit"]); generateIndex("Index", packages.concat( [{ kind: 'index', longname: 'Index Page', }]).concat(files), indexAllUrl, conf["weswit"], byLetterIndex); generateExterns(externsIndex); // 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], conf["weswit"]); } var myModules = helper.find(modules, {longname: longname}); if (myModules.length) { generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname], conf["weswit"]); } var myNamespaces = helper.find(namespaces, {longname: longname}); if (myNamespaces.length) { generate('Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname], conf["weswit"]); } var myMixins = helper.find(mixins, {longname: longname}); if (myMixins.length) { generate('Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname], conf["weswit"]); } var myExternals = helper.find(externals, {longname: longname}); if (myExternals.length) { generate('External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname], conf["weswit"]); } }); // 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 <a href="foodoc.html">foo</a> 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); };
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'; extendTutorialsObj(tutorials, "external"); extendTutorialsObj(tutorials, "demo"); console.log(tutorials.children[0]); // 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*(?:<p>)?\s*<caption>([\s\S]+?)<\/caption>\s*(?:<\/p>)?[\s\r\n]*([\s\S]+)$/i)) { caption = RegExp.$1; code = RegExp.$2; } 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; view.members = members; //@davidshimjs: To make navigation for customizing // 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); for (var longname in helper.longnameToUrl) { if ( hasOwnProp.call(helper.longnameToUrl, 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, filename: filename }; var tutorialPath = path.join(outdir, filename), html = view.render('tutorial.tmpl', tutorialData); // turn {@link foo} into <a href="foodoc.html">foo</a> html = helper.resolveLinks(html); if(tutorial.demo || (tutorial.parent && tutorial.parent.demo)) { tutorialPath = tutorialPath.replace('tutorial-', 'demo-'); } 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) { // Do not generate static pages for the main 'headers' if(child.parent.name !== 'index' && !child.external) { generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); } saveChildren(child); }); } function getExtendedTutorialProperties (parsedJSON, extension, list, name) { var init, children; if(!list) { var list = []; init = true; parsedJSON = parsedJSON.index; } else { init = false; } if(parsedJSON.hasOwnProperty(extension)) { list.push({ 'name': name || 'index', 'extensionValue': parsedJSON[extension] }); } if(parsedJSON.hasOwnProperty('children')) { children = Object.keys(parsedJSON.children); for(var i = 0; i < children.length; i++) { getExtendedTutorialProperties(parsedJSON.children[children[i]], extension, list, children[i]); } } if(init) { return list; } } function setExtendedTutorialProperties (tutorials, extension, extended) { for(var i = 0; i < extended.length; i++) { tutorials.getByName(extended[i].name)[extension] = extended[i].extensionValue; } } function extendTutorialsObj (tutorials, extensions) { var extensionsArray = [], extended, json, parsed; if(typeof extensions === 'string') { extensionsArray.push(extensions); } json = fs.readFileSync(env.opts.template + '/' + env.opts.tutorials + '/' + env.opts.tutorials + '.json' , env.opts.encoding); parsed = JSON.parse(json); for(var i = 0; i < extensionsArray.length; i++) { extended = getExtendedTutorialProperties (parsed, extensionsArray[i]); setExtendedTutorialProperties(tutorials, extensionsArray[i], extended); } return tutorials; } saveChildren(tutorials); };
exports.publish = function ( taffyData, opts, tutorials ) { data = taffyData; 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, lang; if ( example.match( /^\s*<caption>([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i ) ) { caption = RegExp.$1; code = RegExp.$3; } var lang = /{@lang (.*?)}/.exec( example ); if ( lang && lang[1] ) { example = example.replace( lang[0], "" ); lang = lang[1]; } else { lang = null; } return { caption : caption || '', code : code || example, lang : (lang && navOptions.highlightTutorialCode) ? lang : "javascript" }; } ); } if ( doclet.see ) { doclet.see.forEach( function ( seeItem, i ) { doclet.see[i] = hashToLink( doclet, seeItem ); } ); } // build a list of source files var sourcePath; if ( doclet.meta ) { sourcePath = getPathFromDoclet( doclet ); sourceFiles[sourcePath] = { resolved : sourcePath, shortened : null }; sourceFilePaths.push( sourcePath ); } } ); // 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 static files to outdir var fromDir = path.join( templatePath, 'static' ), staticFiles = fs.ls( fromDir, 3 ); staticFiles.forEach( function ( fileName ) { var toDir = fs.toDir( fileName.replace( fromDir, outdir ) ); fs.mkPath( toDir ); fs.copyFileSync( fileName, toDir ); } ); if ( sourceFilePaths.length ) { var payload = navOptions.sourceRootPath; if ( !payload ) { payload = path.commonPrefix( sourceFilePaths ); } sourceFiles = shortenPaths( sourceFiles, payload ); } data().each( function ( doclet ) { var url = helper.createLink( doclet ); helper.registerLink( doclet.longname, url ); // add a shortened version of the full path var docletPath; if ( doclet.meta ) { docletPath = getPathFromDoclet( doclet ); if ( !_.isEmpty( sourceFiles[docletPath] ) ) { docletPath = sourceFiles[docletPath].shortened; if ( docletPath ) { doclet.meta.shortpath = 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; view.moment = moment; // once for all buildNav( members ); view.nav = navigationMaster; view.navOptions = navOptions; 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 ( navOptions.outputSourceFiles ) { generateSourceFiles( sourceFiles ); } if ( members.globals.length ) { generate( 'global', 'Global', [ {kind : 'globalobj'} ], globalUrl ); } // some browsers can't make the dropdown work if ( view.nav.module && view.nav.module.members.length ) { generate( 'module', view.nav.module.title, [ {kind : 'sectionIndex', contents : view.nav.module} ], navigationMaster.module.link ); } if ( view.nav.class && view.nav.class.members.length ) { generate( 'class', view.nav.class.title, [ {kind : 'sectionIndex', contents : view.nav.class} ], navigationMaster.class.link ); } if ( view.nav.namespace && view.nav.namespace.members.length ) { generate( 'namespace', view.nav.namespace.title, [ {kind : 'sectionIndex', contents : view.nav.namespace} ], navigationMaster.namespace.link ); } if ( view.nav.mixin && view.nav.mixin.members.length ) { generate( 'mixin', view.nav.mixin.title, [ {kind : 'sectionIndex', contents : view.nav.mixin} ], navigationMaster.mixin.link ); } if ( view.nav.external && view.nav.external.members.length ) { generate( 'external', view.nav.external.title, [ {kind : 'sectionIndex', contents : view.nav.external} ], navigationMaster.external.link ); } if ( view.nav.tutorial && view.nav.tutorial.members.length ) { generate( 'tutorial', view.nav.tutorial.title, [ {kind : 'sectionIndex', contents : view.nav.tutorial} ], navigationMaster.tutorial.link ); } // index page displays information from package.json and lists files var files = find( {kind : 'file'} ), packages = find( {kind : 'package'} ); generate( 'index', '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 ); for ( var longname in helper.longnameToUrl ) { if ( hasOwnProp.call( helper.longnameToUrl, longname ) ) { var myClasses = helper.find( classes, {longname : longname} ); if ( myClasses.length ) { generate( 'class', 'Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname] ); } var myModules = helper.find( modules, {longname : longname} ); if ( myModules.length ) { generate( 'module', 'Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname] ); } var myNamespaces = helper.find( namespaces, {longname : longname} ); if ( myNamespaces.length ) { generate( 'namespace', 'Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname] ); } var myMixins = helper.find( mixins, {longname : longname} ); if ( myMixins.length ) { generate( 'mixin', 'Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname] ); } var myExternals = helper.find( externals, {longname : longname} ); if ( myExternals.length ) { generate( 'external', '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, docs : null }; 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 <a href="foodoc.html">foo</a> 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 ); };
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'); var sourceFiles = {}; var sourceFilePaths = []; processInheritance(); data().each(function (doclet) { if (doclet.name) doclet.name = doclet.name.replace(/']/g, '').replace(/\['/g, ''); if (doclet.longname) doclet.longname = doclet.longname.replace(/']/g, '').replace(/\['/g, ''); doclet.attribs = ''; if (doclet.examples) { doclet.examples = doclet.examples.map(function (example) { var caption, code; if (example.match(/^\s*<caption>([\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 static files to outdir var fromDir = path.join(templatePath, 'static'), staticFiles = fs.ls(fromDir, 3); staticFiles.forEach(function (fileName) { var toDir = fs.toDir(fileName.replace(fromDir, 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); // 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); members.functions = helper.find(data, {kind: 'function', includeDoc: true}); var functions = taffy(members.functions); var counter=0; for (var longname in helper.longnameToUrl) { if (hasOwnProp.call(helper.longnameToUrl, longname)) { var myClasses = helper.find(classes, {longname: longname}); if (myClasses.length) { generate(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],undefined, members); } 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]); } var myFunctions = helper.find(functions, {longname: longname, includeDoc: true}); if (myFunctions.length) { var newName = myFunctions[0].memberof +'.'+myFunctions[0].name+'.html'; // newName = newName.replace(/#/ig,''); // newName = newName.replace(/[']/ig,''); // log(myFunctions[0].memberof); generate(myFunctions[0].name, myFunctions, newName); } } } // 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 <a href="foodoc.html">foo</a> 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); };
exports.publish = function(taffyData, opts, tutorials) { data = taffyData; var conf = env.conf.templates || {}; conf.default = conf.default || {}; var templatePath = path.normalize(opts.template); view = new template.Template( path.join(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 = opts.layoutFile; // 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*<caption>([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { caption = RegExp.$1; code = RegExp.$3; } return { caption: caption || '', code: code || example }; }); } if (doclet.codeExamples) { doclet.codeExamples = doclet.codeExamples.map(function(codeExample) { var caption, code; if (codeExample.match(/^\s*<caption>([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { caption = RegExp.$1; code = RegExp.$3; } return { caption: caption || '', code: code || codeExample }; }); } if (doclet.see) { doclet.see.forEach(function(seeItem, i) { doclet.see[i] = hashToLink(doclet, seeItem); }); } // build a list of source files var sourcePath; if (doclet.meta) { sourcePath = getPathFromDoclet(doclet); sourceFiles[sourcePath] = { resolved: sourcePath, shortened: null }; if (sourceFilePaths.indexOf(sourcePath) === -1) { sourceFilePaths.push(sourcePath); } } }); /* * Handle the defaul values for non optional properties correctly. * */ data().each(function(doclet) { if (doclet.properties) { doclet.properties = doclet.properties.map(function(property) { var separator = " - ", separatorLength = separator.length; var defaultvalue = property.defaultvalue; var description = property.description; if( property.defaultvalue !== 'undefined' && !property.optional && description.indexOf(separator) > 0) { var index = description.indexOf(separator); defaultvalue += " " + description.substr(separatorLength, index-separatorLength); description = "<p>" + description.substr(index + separatorLength, description.length); } return { defaultvalue: defaultvalue, description: description, type: property.type, name: property.name } }); } }); // 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) { // The canonical property name is `include`. We accept `paths` for backwards compatibility // with a bug in JSDoc 3.2.x. staticFilePaths = conf.default.staticFiles.include || 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; filePath = path.resolve(env.pwd, filePath); extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); extraStaticFiles.forEach(function(fileName) { var sourcePath = fs.toDir(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); // add a shortened version of the full path var docletPath; if (doclet.meta) { docletPath = getPathFromDoclet(doclet); docletPath = sourceFiles[docletPath].shortened; if (docletPath) { doclet.meta.shortpath = docletPath; } } var sourceLink; if (doclet.meta) { sourceLink = getLinkFromDoclet(doclet); doclet.meta.sourceLink = sourceLink; } }); 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'; doclet.constant = true; } }); data().each(function(doclet) { if(!doclet.ignore) { var parent = find({longname: doclet.memberof})[0]; if( !parent ) { doclet.scopeEf = doclet.scope; } else { if(doclet.scope === 'static' && parent.kind !== 'class') { doclet.scopeEf = 'instance'; } else if(doclet.scope === 'static' && parent.static && parent.kind === 'class') { doclet.scopeEf = 'instance'; } else { doclet.scopeEf = doclet.scope; } } } }); // handle summary, description and class description default values properly data().each(function(doclet) { if(!doclet.ignore) { var desc; if(!doclet.summary && (desc = (doclet.description || doclet.classdesc))) { // Try to split when a "." or a ".</htmlTag>" is found. /* ^ - start of string \s* - optional leading space <tagName> - optional tag, whose tagName is captured in m[1] and \1 \s* - optional white space (.|\n|\r)+?\. - summary text, that can cross lines, and ends in a period; captured in m[2] \s* - optional white space ( $ - the end of the string, or \r|\n - line-break, or </tagName> - the closing tag from the corresponding to the beginning opening tag ) */ var m = (/^\s*(?:<(\w+)>)?\s*((?:.|\n|\r)+?\.)\s*?($|(\r|\n)|(<\/\1>))/i).exec(desc); if(m) { // MATCHED! var tagName = m[1]; var summary = m[2]; if(tagName) summary = "<" + tagName + ">" + summary + "</" + tagName + ">"; doclet.summary = summary; //console.log("summary: " + summary); //console.log(" description: " + desc); //console.log(" "); } else { console.warn("Could not determine summary for: " + desc); } } var checkP = function(prop) { if(!prop) return; prop = prop.replace(/<p><p>/g, "<p>"); if(prop.indexOf("<p>") == -1) { return "<p>" + prop + "</p>"; } return prop; }; var replaceCode = function(string) { if(!string) return; var flip = true; var idx = string.indexOf("`"); while(idx > -1) { string = string.substr(0, idx) + (flip ? "<code>" : "</code>") + string.substr(idx + 1); flip = !flip; idx = string.indexOf("`"); } return string; }; doclet.summary = replaceCode(checkP(doclet.summary)); doclet.description = replaceCode(checkP(doclet.description)); doclet.classdesc = replaceCode(checkP(doclet.classdesc)); } }); //handle splits and joins on names data().each(function(doclet) { if(!doclet.ignore) { var split = function(str, sep) { if(str) { return str.split(sep).join(''); } } //dont split for code if(doclet.description && doclet.description.indexOf("syntax.javascript") == -1) { doclet.description = split(doclet.description, '<br>'); } if(doclet.classdesc && doclet.classdesc.indexOf("syntax.javascript") == -1) { doclet.classdesc = split(doclet.classdesc, '<br>'); } if(doclet.summary && doclet.summary.indexOf("syntax.javascript") == -1) { doclet.summary = split(doclet.summary, '<br>'); } doclet.parsedName = split(doclet.name, '"') doclet.parsedLongname = split(doclet.longname, '"') } }); 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(findMembers(data, 'namespace')); attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); 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('Home', 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); var interfaces = taffy(members.interfaces); Object.keys(helper.longnameToUrl).forEach(function(longname) { var myModules = helper.find(modules, {longname: longname}); if (myModules.length) { generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); } var myClasses = helper.find(classes, {longname: longname}); if (myClasses.length) { generate('Class: ' + myClasses[0].name, myClasses, 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]); } var myInterfaces = helper.find(interfaces, {longname: longname}); if (myInterfaces.length) { generate('Interface: ' + myInterfaces[0].name, myInterfaces, 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 <a href="foodoc.html">foo</a> 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); };
publish = function(data, opts, tutorials) { var out = '', view = new template.Template(__dirname + '/templates/default/tmpl'); // set up templating view.layout = 'layout.tmpl'; // set up tutorials for helper helper.setTutorials(tutorials); function find(spec) { return data.get( data.find(spec) ); } function htmlsafe(str) { return str.replace(/</g, '<'); } function addSignatureParams(f) { var pnames = []; if (f.params) { f.params.forEach(function(p) { if (p.name && p.name.indexOf('.') === -1) { if (p.optional) { pnames.push('<span class="optional">'+p.name+'</span>'); } else { pnames.push(p.name); } } }); } f.signature = (f.signature || '') + '('+pnames.join(', ')+')'; } function generateAncestry(thisdoc) { var ancestors = [], doc = thisdoc; while (doc = doc.memberof) { doc = find({longname: doc}); if (doc) { doc = doc[0]; } if (!doc) break; ancestors.unshift( linkto(doc.longname, (scopeToPunc[doc.scope] || '') + doc.name) ); } if (ancestors.length) { ancestors[ancestors.length-1] += (scopeToPunc[thisdoc.scope] || ''); } return ancestors; } function addSignatureReturns(f) { var returnTypes = []; if (f.returns) { f.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); }); } f.signature = '<span class="signature">'+(f.signature || '') + '</span>' + '<span class="type-signature">'+(returnTypes.length? ' → {'+returnTypes.join('|')+'}' : '')+'</span>'; } function addSignatureType(f) { var types = []; if (f.type && f.type.names) { types = f.type.names; } if (types && types.length) { types = types.map(function(t) { return linkto(t, htmlsafe(t)); }); } f.signature = (f.signature || '') + '<span class="type-signature">'+(types.length? ' :'+types.join('|') : '')+'</span>'; } function addAttribs(f) { var attribs = []; if (f.virtual) { attribs.push('virtual'); } if (f.access && f.access !== 'public') { attribs.push(f.access); } if (f.scope && f.scope !== 'instance' && f.scope !== 'global') { if (f.kind == 'function' || f.kind == 'member' || f.kind == 'constant') attribs.push(f.scope); } if (f.readonly === true) { if (f.kind == 'member') attribs.push('readonly'); } if (f.kind === 'constant') { attribs.push('constant'); f.kind = 'member'; } f.attribs = '<span class="type-signature">'+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+'</span>'; } data.remove({undocumented: true}); data.remove({ignore: true}); if (!opts.private) { data.remove({access: 'private'}); } data.remove({memberof: '<anonymous>'}); var packageInfo = (find({kind: 'package'}) || []) [0]; //function renderLinks(text) { // return helper.resolveLinks(text); //} data.forEach(function(doclet) { doclet.attribs = ''; if (doclet.examples) { doclet.examples = doclet.examples.map(function(example) { var caption, code; if (example.match(/^\s*<caption>([\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); }); } }); data.orderBy(['longname', 'version', 'since']); // kinds of containers var globals = find( {kind: ['member', 'function', 'constant', 'typedef'], memberof: {isUndefined: true}} ), modules = find({kind: 'module'}), externals = find({kind: 'external'}), mixins = find({kind: 'mixin'}), namespaces = find({kind: 'namespace'}), glslTypes = find({kind: 'glsl'}); var outdir = opts.destination; if (packageInfo && packageInfo.name) { outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; } fs.mkPath(outdir); // copy static files to outdir var fromDir = __dirname + '/templates/default/static', staticFiles = fs.ls(fromDir, 3); staticFiles.forEach(function(fileName) { var toDir = fs.toDir(fileName.replace(fromDir, outdir)); fs.mkPath(toDir); fs.copyFile(fileName, toDir); }); function linkto(longname, linktext) { var url = helper.longnameToUrl[longname]; return url? '<a href="'+url+'">'+(linktext || longname)+'</a>' : (linktext || longname); } function tutoriallink(tutorial) { return helper.toTutorial(tutorial); } var containers = ['class', 'module', 'external', 'namespace', 'mixin']; data.forEach(function(doclet) { var url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); }); data.forEach(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 (doclet.kind === 'function' || doclet.kind === 'class') { addSignatureParams(doclet); addSignatureReturns(doclet); addAttribs(doclet); } }) // do this after the urls have all been generated data.forEach(function(doclet) { doclet.ancestors = generateAncestry(doclet); doclet.signature = ''; if (doclet.kind === 'member') { addSignatureType(doclet); addAttribs(doclet) } if (doclet.kind === 'constant') { addSignatureType(doclet); addAttribs(doclet) } }); var nav = '', seen = {}; nav += '<ul id="ClassList"><div id="classItems">'; // Start single class list, begin JavaScript div var classNames = find({kind: 'class'}); if (classNames.length) { //nav += '<h3>Classes</h3><ul>'; classNames.forEach(function(c) { var moduleSameName = find({kind: 'module', longname: c.longname}); if (moduleSameName.length) { c.name = c.name.replace('module:', 'require(')+')'; moduleSameName[0].module = c; } if (!seen.hasOwnProperty(c.longname) ) nav += '<li>'+linkto(c.longname, c.name)+'</li>'; seen[c.longname] = true; }); //nav += '</ul>'; } var moduleNames = find({kind: 'module'}); if (moduleNames.length) { //nav += '<h3>Modules</h3><ul>'; moduleNames.forEach(function(m) { if ( !seen.hasOwnProperty(m.longname) ) nav += '<li>'+linkto(m.longname, m.name)+'</li>'; seen[m.longname] = true; }); //nav += '</ul>'; } var externalNames = find({kind: 'external'}); if (externalNames.length) { //nav += '<h3>Externals</h3><ul>'; externalNames.forEach(function(e) { if ( !seen.hasOwnProperty(e.longname) ) nav += '<li>'+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'</li>'; seen[e.longname] = true; }); //nav += '</ul>'; } var namespaceNames = find({kind: 'namespace'}); if (namespaceNames.length) { //nav += '<h3>Namespaces</h3><ul>'; namespaceNames.forEach(function(n) { if ( !seen.hasOwnProperty(n.longname) ) nav += '<li>'+linkto(n.longname, n.name)+'</li>'; seen[n.longname] = true; }); //nav += '</ul>'; } var glslNames = find({kind: 'glsl'}); nav += '</div><div id="glslItems">'; // End classItems div, start GLSL div if (glslNames.length) { glslNames.forEach(function(g) { if ( !seen.hasOwnProperty(g.longname) ) nav += '<li>'+linkto(g.longname, g.name)+'</li>'; seen[g.longname] = true; }); } nav += '</div></ul>'; // End GLSL div, end Class list // var constantNames = find({kind: 'constants'}); // if (constantNames.length) { // nav += '<h3>Constants</h3><ul>'; // constantNames.forEach(function(c) { // if ( !seen.hasOwnProperty(c.longname) ) nav += '<li>'+linkto(c.longname, c.name)+'</li>'; // seen[c.longname] = true; // }); // // nav += '</ul>'; // } /* var mixinNames = find({kind: 'mixin'}); if (mixinNames.length) { nav += '<h3>Mixins</h3><ul>'; mixinNames.forEach(function(m) { if ( !seen.hasOwnProperty(m.longname) ) nav += '<li>'+linkto(m.longname, m.name)+'</li>'; seen[m.longname] = true; }); nav += '</ul>'; } if (tutorials.children.length) { nav += '<h3>Tutorials</h3><ul>'; tutorials.children.forEach(function(t) { nav += '<li>'+tutoriallink(t.name)+'</li>'; }); nav += '</ul>'; } var globalNames = find({kind: ['members', 'function', 'constant', 'typedef'], 'memberof': {'isUndefined': true}}); if (globalNames.length) { nav += '<h3>Global</h3><ul>'; globalNames.forEach(function(g) { if ( g.kind !== 'typedef' && !seen.hasOwnProperty(g.longname) ) nav += '<li>'+linkto(g.longname, g.name)+'</li>'; seen[g.longname] = true; }); nav += '</ul>'; } */ // add template helpers view.find = find; view.linkto = linkto; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; // once for all view.nav = nav; var types_json = {}; for (var longname in helper.longnameToUrl) { var classes = find({kind: 'class', longname: longname}); if (classes.length) generate(classes[0].name, classes, helper.longnameToUrl[longname]); var glslTypes = find({kind: 'glsl', longname: longname}); if (glslTypes.length) generate(glslTypes[0].name, glslTypes, helper.longnameToUrl[longname]); var modules = find({kind: 'module', longname: longname}); if (modules.length) generate(modules[0].name, modules, helper.longnameToUrl[longname]); var namespaces = find({kind: 'namespace', longname: longname}); if (namespaces.length) generate('Namespace: '+namespaces[0].name, namespaces, helper.longnameToUrl[longname]); // var constants = find({kind: 'constant', longname: longname}); // if (constants.length) generate('Constant: '+constants[0].name, constants, helper.longnameToUrl[longname]); var mixins = find({kind: 'mixin', longname: longname}); if (mixins.length) generate('Mixin: '+mixins[0].name, mixins, helper.longnameToUrl[longname]); var externals = find({kind: 'external', longname: longname}); if (externals.length) generate('External: '+externals[0].name, externals, helper.longnameToUrl[longname]); } //if (globals.length) generate('Global', [{kind: 'globalobj'}], 'global.html'); view.layout = 'index.tmpl'; var indexHtml = view.render('empty.tmpl', { title: 'Cesium Documentation' }); fs.writeFileSync(outdir + '/index.html', indexHtml); fs.writeFileSync(outdir + '/types.txt', JSON.stringify(types_json)); 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> if (title in types_json) { types_json[title].push(filename); } else { types_json[title] = [ filename ]; } var pos = 0, pos2, member, match = 'h4 class="name" id="', matchLen = match.length; while ((pos = html.indexOf(match, pos)) >= 0) { pos += matchLen; if ((pos2 = html.indexOf('"', pos)) > 0) { member = html.substring(pos, pos2); if (member !== title) { if (member in types_json) { // Note that ".toString" and ".valueOf" are discarded here. if (typeof types_json[member].push === 'function') { types_json[member].push(filename + '#' + member); } } else { types_json[member] = [ filename + '#' + member ]; } } } } fs.writeFileSync(path, html); } function generateTutorial(title, tutorial, filename) { var data = { title: title, header: tutorial.title, content: tutorial.parse(), children: tutorial.children }; var path = outdir + '/' + filename, html = view.render('tutorial.tmpl', data); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> fs.writeFileSync(path, html); } // 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); }
exports.publish = function (taffyData, opts, tutorials) { data = taffyData var conf = env.conf.templates || {} conf.default = conf.default || {} var templatePath = path.normalize(opts.template) view = new template.Template(path.join(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 = conf.default.layoutFile ? path.getResourcePath( path.dirname(conf.default.layoutFile), path.basename(conf.default.layoutFile) ) : "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*<caption>([\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 if (doclet.meta) { sourcePath = getPathFromDoclet(doclet) sourceFiles[sourcePath] = { resolved: sourcePath, shortened: null } if (sourceFilePaths.indexOf(sourcePath) === -1) { sourceFilePaths.push(sourcePath) } } }) // 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) { // The canonical property name is `include`. We accept `paths` for backwards compatibility // with a bug in JSDoc 3.2.x. staticFilePaths = conf.default.staticFiles.include || 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.toDir(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) // add a shortened version of the full path var docletPath if (doclet.meta) { docletPath = getPathFromDoclet(doclet) docletPath = sourceFiles[docletPath].shortened if (docletPath) { doclet.meta.shortpath = 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 // output pretty-printed source files by default var outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false ? true : false // add template helpers view.find = find view.linkto = linkto view.resolveAuthorLinks = resolveAuthorLinks view.tutoriallink = tutoriallink view.htmlsafe = htmlsafe view.outputSourceFiles = outputSourceFiles // once for all view.nav = buildNav(members) attachModuleSymbols(find({longname: {left: "module:"}}), members.modules) // generate the pretty-printed source files first so other pages can link to them if (outputSourceFiles) { generateSourceFiles(sourceFiles, opts.encoding) } if (members.globals.length) { generate("", "Global", [{kind: "globalobj"}], globalUrl) } // index page displays information from package.json and lists files var files = find({kind: "file"}) var packages = find({kind: "package"}) generate( "", "Home", 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) var interfaces = taffy(members.interfaces) Object.keys(helper.longnameToUrl).forEach(function (longname) { var myModules = helper.find(modules, {longname: longname}) if (myModules.length) { generate( "Module", myModules[0].name, myModules, helper.longnameToUrl[longname] ) } var myClasses = helper.find(classes, {longname: longname}) if (myClasses.length) { generate( "Class", myClasses[0].name, myClasses, 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] ) } var myInterfaces = helper.find(interfaces, {longname: longname}) if (myInterfaces.length) { generate( "Interface", myInterfaces[0].name, myInterfaces, 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) var html = view.render("tutorial.tmpl", tutorialData) // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html) // turn {@link foo} into <a href="foodoc.html">foo</a> 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( child.title, child, helper.tutorialToUrl(child.name) ) saveChildren(child) }) } saveChildren(tutorials) }
exports.publish = function(taffyData, opts, tutorials) { data = taffyData; var conf = env.conf.templates || {}; conf.default = conf.default || {}; var templatePath = path.normalize(opts.template); view = new template.Template( path.join(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 = conf.default.layoutFile ? path.getResourcePath(path.dirname(conf.default.layoutFile), path.basename(conf.default.layoutFile) ) : '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*<caption>([\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; if (doclet.meta) { sourcePath = getPathFromDoclet(doclet); sourceFiles[sourcePath] = { resolved: sourcePath, shortened: null }; if (sourceFilePaths.indexOf(sourcePath) === -1) { sourceFilePaths.push(sourcePath); } } }); // 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) { // The canonical property name is `include`. We accept `paths` for backwards compatibility // with a bug in JSDoc 3.2.x. staticFilePaths = conf.default.staticFiles.include || 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.toDir(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); // add a shortened version of the full path var docletPath; if (doclet.meta) { docletPath = getPathFromDoclet(doclet); docletPath = sourceFiles[docletPath].shortened; if (docletPath) { doclet.meta.shortpath = 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; // output pretty-printed source files by default var outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false ? true : false; // add template helpers view.find = find; view.linkto = linkto; view.resolveAuthorLinks = resolveAuthorLinks; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; view.outputSourceFiles = outputSourceFiles; // once for all view.nav = buildNav(members); attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); // generate the pretty-printed source files first so other pages can link to them if (outputSourceFiles) { generateSourceFiles(sourceFiles, opts.encoding); } if (members.globals.length) { generate('', 'Global', [{kind: 'globalobj'}], globalUrl); } // index page displays information from package.json and lists files var files = find({kind: 'file'}); var packages = find({kind: 'package'}); generate('', 'Home', [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}], 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); var interfaces = taffy(members.interfaces); Object.keys(helper.longnameToUrl).forEach(function(longname) { var myModules = helper.find(modules, {longname: longname}); if (myModules.length) { generate('Module', myModules[0].name, myModules, helper.longnameToUrl[longname]); } var myClasses = helper.find(classes, {longname: longname}); if (myClasses.length) { generate('Class', myClasses[0].name, myClasses, 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]); } var myInterfaces = helper.find(interfaces, {longname: longname}); if (myInterfaces.length) { generate('Interface', myInterfaces[0].name, myInterfaces, 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); var html = view.render('tutorial.tmpl', tutorialData); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> 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); };
exports.publish = function(taffyData, opts, tutorials) { data = taffyData; 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 // set up templating view.layout = conf['default'].layoutFile ? path.getResourcePath(path.dirname(conf['default'].layoutFile), path.basename(conf['default'].layoutFile) ) : 'layout.tmpl'; // set up tutorials for helper helper.setTutorials(tutorials); data = helper.prune(data); var sortOption = navOptions.sort === undefined ? opts.sort : navOptions.sort; sortOption = sortOption === undefined ? true : sortOption; sortOption = sortOption === true ? 'longname, version, since' : sortOption; if (sortOption) { data.sort(sortOption); } 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, lang; // allow using a markdown parser on the examples captions (surrounded by useless HTML p tags) if (example.match(/^\s*(<p>)?<caption>([\s\S]+?)<\/caption>(\s*)([\s\S]+?)(<\/p>)?$/i)) { caption = RegExp.$2; example = RegExp.$4 + (RegExp.$1 ? '' : RegExp.$5); } var lang = /{@lang (.*?)}/.exec(example); if (lang && lang[1]) { example = example.replace(lang[0], ""); lang = lang[1]; } else { lang = null; } return { caption: caption || '', code: example, lang: lang || "javascript" }; }); } if (doclet.see) { doclet.see.forEach(function(seeItem, i) { doclet.see[i] = hashToLink(doclet, seeItem); }); } // build a list of source files var sourcePath; if (doclet.meta) { sourcePath = getPathFromDoclet(doclet); sourceFiles[sourcePath] = { resolved: sourcePath, shortened: null }; //Check to see if the array of source file paths already contains // the source path, if not then add it if (sourceFilePaths.indexOf(sourcePath) === -1) { sourceFilePaths.push(sourcePath) } } }); // update outdir if necessary, then create outdir var packageInfo = (find({ kind: 'package' }) || [])[0]; if (navOptions.disablePackagePath !== true && packageInfo && packageInfo.name) { if (packageInfo.version) { outdir = path.join(outdir, packageInfo.name, packageInfo.version); } else { outdir = path.join(outdir, packageInfo.name); } } 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) { // The canonical property name is `include`. We accept `paths` for backwards compatibility // with a bug in JSDoc 3.2.x. staticFilePaths = conf.default.staticFiles.include || 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.toDir(filePath); var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); fs.mkPath(toDir); fs.copyFileSync(fileName, toDir); }); }); } if (sourceFilePaths.length) { var payload = navOptions.sourceRootPath; if (!payload) { payload = path.commonPrefix(sourceFilePaths); } sourceFiles = shortenPaths(sourceFiles, payload); } data().each(function(doclet) { var url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); // add a shortened version of the full path var docletPath; if (doclet.meta) { docletPath = getPathFromDoclet(doclet); if (!_.isEmpty(sourceFiles[docletPath])) { docletPath = sourceFiles[docletPath].shortened; if (docletPath) { doclet.meta.shortpath = 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; view.moment = moment; // once for all buildNav(members); view.nav = navigationMaster; view.navOptions = navOptions; 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 (navOptions.outputSourceFiles) { generateSourceFiles(sourceFiles); } if (members.globals.length) { generate('global', 'Global', [{ kind: 'globalobj' }], globalUrl); } // some browsers can't make the dropdown work if (view.nav.module && view.nav.module.members.length) { generate('module', view.nav.module.title, [{ kind: 'sectionIndex', contents: view.nav.module }], navigationMaster.module.link); } if (view.nav.class && view.nav.class.members.length) { generate('class', view.nav.class.title, [{ kind: 'sectionIndex', contents: view.nav.class }], navigationMaster.class.link); } if (view.nav.namespace && view.nav.namespace.members.length) { generate('namespace', view.nav.namespace.title, [{ kind: 'sectionIndex', contents: view.nav.namespace }], navigationMaster.namespace.link); } if (view.nav.mixin && view.nav.mixin.members.length) { generate('mixin', view.nav.mixin.title, [{ kind: 'sectionIndex', contents: view.nav.mixin }], navigationMaster.mixin.link); } if (view.nav.interface && view.nav.interface.members.length) { generate('interface', view.nav.interface.title, [{ kind: 'sectionIndex', contents: view.nav.interface }], navigationMaster.interface.link); } if (view.nav.external && view.nav.external.members.length) { generate('external', view.nav.external.title, [{ kind: 'sectionIndex', contents: view.nav.external }], navigationMaster.external.link); } if (view.nav.tutorial && view.nav.tutorial.members.length) { generate('tutorial', view.nav.tutorial.title, [{ kind: 'sectionIndex', contents: view.nav.tutorial }], navigationMaster.tutorial.link); } // index page displays information from package.json and lists files var files = find({ kind: 'file' }), packages = find({ kind: 'package' }); generate('index', '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 interfaces = taffy(members.interfaces); var externals = taffy(members.externals); for (var longname in helper.longnameToUrl) { if (hasOwnProp.call(helper.longnameToUrl, longname)) { var myClasses = helper.find(classes, { longname: longname }); if (myClasses.length) { generate('class', 'Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); } var myModules = helper.find(modules, { longname: longname }); if (myModules.length) { generate('module', 'Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); } var myNamespaces = helper.find(namespaces, { longname: longname }); if (myNamespaces.length) { generate('namespace', 'Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); } var myMixins = helper.find(mixins, { longname: longname }); if (myMixins.length) { generate('mixin', 'Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); } var myInterfaces = helper.find(interfaces, { longname: longname }); if (myInterfaces.length) { generate('interface', 'Interface: ' + myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]); } var myExternals = helper.find(externals, { longname: longname }); if (myExternals.length) { generate('external', '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, docs: null }; 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 <a href="foodoc.html">foo</a> searchableDocuments[filename] = { "id": filename, "title": title, "body": searchData(html) }; 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); }); } function generateQuickTextSearch(templatePath, searchableDocuments, navOptions) { var data = { searchableDocuments: JSON.stringify(searchableDocuments), navOptions: navOptions }; var tmplString = fs.readFileSync(templatePath + "/quicksearch.tmpl").toString(), tmpl = _.template(tmplString); var html = tmpl(data), outpath = path.join(outdir, "quicksearch.html"); fs.writeFileSync(outpath, html, "utf8"); } saveChildren(tutorials); generateQuickTextSearch(templatePath + '/tmpl', searchableDocuments, navOptions); };
publish = function(data, opts, tutorials) { // The 'product' variable is specified on the command line, and it used to customize output for Montage and Screening. // for example: // jsdoc -t templates/tetsubo --query product=montage -r ../montage/ui ../montage/core var product; if(opts.query) { product = opts.query.product; if ((product === "montage") || (product === "screening")) { console.log("\tBuilding docs for: " + product); } else { console.log("\tInvalid product specified '" + product + "'."); return; } } else { console.log("\n\tNo product specified in --query parameter. Example usage: "); console.log("\t./jsdoc -t templates/unity -q product=montage -r ../montage/core ../montage/ui ../montage/data"); return; } var out = '', view = new template.Template(__dirname + '/templates/unity/tmpl'); // set up templating view.layout = product + '-layout.tmpl'; // set up tutorials for helper helper.setTutorials(tutorials); function find(spec) { return data.get( data.find(spec) ); } function htmlsafe(str) { return str.replace(/</g, '<'); } function addSignatureParams(f) { var pnames = []; if (f.params) { f.params.forEach(function(p) { if (p.name && p.name.indexOf('.') === -1) { if (p.optional) { pnames.push('<span class="optional">'+p.name+'</span>'); } else { pnames.push(p.name); } } }); } f.signature = (f.signature || '') + '('+pnames.join(', ')+')'; } function generateAncestry(thisdoc) { var ancestors = [], doc = thisdoc; while (doc = doc.memberof) { doc = find({longname: doc}); if (doc) { doc = doc[0]; } if (!doc) break; ancestors.unshift( linkto(doc.longname, (scopeToPunc[doc.scope] || '') + doc.name) ); } if (ancestors.length) { ancestors[ancestors.length-1] += (scopeToPunc[thisdoc.scope] || ''); } return ancestors; } function addSignatureReturns(f) { var returnTypes = []; if (f.returns) { f.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); }); } f.signature = '<span class="signature">'+(f.signature || '') + '</span>' + '<span class="type-signature">'+(returnTypes.length? ' → {'+returnTypes.join('|')+'}' : '')+'</span>'; } function addSignatureType(f) { var types = []; if (f.type && f.type.names) { types = f.type.names; } if (types && types.length) { types = types.map(function(t) { return linkto(t, htmlsafe(t)); }); } f.signature = (f.signature || '') + '<span class="type-signature">'+(types.length? ' :'+types.join('|') : '')+'</span>'; } function removeQuotesFromReelModule(module) { var moduleName = module.name; if(moduleName.indexOf(".reel\"") !== -1) { moduleName = moduleName.replace(".reel\"", ".reel"); moduleName = moduleName.replace("\"", ""); } module.name = moduleName; } function addAttribs(f) { var attribs = []; if (f.virtual) { attribs.push('virtual'); } if (f.access && f.access !== 'public') { attribs.push(f.access); } if (f.scope && f.scope !== 'instance' && f.scope !== 'global') { if (f.kind == 'function' || f.kind == 'member' || f.kind == 'constant') attribs.push(f.scope); } if (f.readonly === true) { if (f.kind == 'member') attribs.push('readonly'); } if (f.kind === 'constant') { attribs.push('constant'); f.kind = 'member'; } f.attribs = '<span class="type-signature">'+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+'</span>'; } data.remove({undocumented: true}); data.remove({ignore: true}); if (!opts.private) { data.remove({access: 'private'}); } data.remove({memberof: '<anonymous>'}); var packageInfo = (find({kind: 'package'}) || []) [0]; //function renderLinks(text) { // return helper.resolveLinks(text); //} data.forEach(function(doclet) { doclet.attribs = ''; if (doclet.examples) { doclet.examples = doclet.examples.map(function(example) { var caption, code; if (example.match(/^\s*<caption>([\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); }); } if (doclet.kind === 'module') { removeQuotesFromReelModule(doclet); } }); data.orderBy(['longname', 'version', 'since']); // kinds of containers var globals = find( {kind: ['member', 'function', 'constant', 'typedef'], memberof: {isUndefined: true}} ), modules = find({kind: 'module'}), externals = find({kind: 'external'}), mixins = find({kind: 'mixin'}), namespaces = find({kind: 'namespace'}); var outdir = opts.destination; if (packageInfo && packageInfo.name) { outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; } fs.mkPath(outdir); // copy product-specific static files to outdir var fromDir = __dirname + '/templates/unity/static/' + product + '/styles', staticFiles = fs.ls(fromDir, 3); staticFiles.forEach(function(fileName) { var toDir = fs.toDir(fileName.replace(fromDir, outdir)); fs.mkPath(toDir); fs.copyFile(fileName, toDir); }); function linkto(longname, linktext) { var url = helper.longnameToUrl[longname]; return url? '<a class="prototype" href="'+url+'">'+(linktext || longname)+'</a>' : (linktext || longname); } function tutoriallink(tutorial) { return helper.toTutorial(tutorial); } var containers = ['class', 'module', 'external', 'namespace', 'mixin']; data.forEach(function(doclet) { var url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); }); data.forEach(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 (doclet.kind === 'function' || doclet.kind === 'class') { addSignatureParams(doclet); addSignatureReturns(doclet); addAttribs(doclet); } }) // do this after the urls have all been generated data.forEach(function(doclet) { doclet.ancestors = generateAncestry(doclet); doclet.signature = ''; if (doclet.kind === 'member') { addSignatureType(doclet); addAttribs(doclet) } if (doclet.kind === 'constant') { addSignatureType(doclet); addAttribs(doclet) } }); var nav = '', seen = {}; var classNames = find({kind: 'class'}); if (classNames.length) { classNames.sort(alphabetical); nav += '<h3>Prototypes</h3>'; nav += '<div id="class-list">'; nav += '<input id="search" class="search" placeholder="Filter by name" /><ul class=\"list\">'; classNames.forEach(function(c) { var moduleSameName = find({kind: 'module', longname: c.longname}); if (moduleSameName.length) { c.name = c.name.replace('module:', 'require(')+')'; moduleSameName[0].module = c; } if (!seen.hasOwnProperty(c.longname) ) nav += '<li>'+linkto(c.longname, c.name)+'</li>'; seen[c.longname] = true; }); nav += '</ul></div>'; } var externalNames = find({kind: 'external'}); if (externalNames.length) { nav = nav + '<h3>Externals</h3>'; nav += '<div id="externals-list">'; externalNames.sort(alphabetical); nav += '<input id="search" class="search" placeholder="Filter by name" /><ul class=\"list\">'; externalNames.forEach(function(e) { if ( !seen.hasOwnProperty(e.longname) ) nav += '<li>'+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'</li>'; seen[e.longname] = true; }); nav += '</ul>'; nav += '</div>'; } var moduleNames = find({kind: 'module'}); if (moduleNames.length) { nav += '<h3>Modules</h3>'; nav += '<div id="modules-list">'; moduleNames.sort(alphabetical); nav += '<input id="search" class="search" placeholder="Filter by name" /><ul class=\"list\">'; moduleNames.forEach(function(m) { if ( !seen.hasOwnProperty(m.longname) ) nav += '<li>'+linkto(m.longname, m.name)+'</li>'; seen[m.longname] = true; }); nav += '</ul>'; nav += '</div>'; } var namespaceNames = find({kind: 'namespace'}); if (namespaceNames.length) { nav += '<h3>Namespaces</h3>'; nav += '<ul class=\"list\">'; namespaceNames.forEach(function(n) { if ( !seen.hasOwnProperty(n.longname) ) nav += '<li>'+linkto(n.longname, n.name)+'</li>'; seen[n.longname] = true; }); nav += '</ul>'; } // var constantNames = find({kind: 'constants'}); // if (constantNames.length) { // nav += '<h3>Constants</h3><ul>'; // constantNames.forEach(function(c) { // if ( !seen.hasOwnProperty(c.longname) ) nav += '<li>'+linkto(c.longname, c.name)+'</li>'; // seen[c.longname] = true; // }); // // nav += '</ul>'; // } var mixinNames = find({kind: 'mixin'}); if (mixinNames.length) { nav += '<h3>Mixins</h3><ul>'; mixinNames.forEach(function(m) { if ( !seen.hasOwnProperty(m.longname) ) nav += '<li>'+linkto(m.longname, m.name)+'</li>'; seen[m.longname] = true; }); nav += '</ul>'; } if (tutorials.children.length) { nav += '<h3>Tutorials</h3><ul>'; tutorials.children.forEach(function(t) { nav += '<li>'+tutoriallink(t.name)+'</li>'; }); nav += '</ul>'; } var globalNames = find({kind: ['member', 'function', 'constant', 'typedef'], 'memberof': {'isUndefined': true}}); if (globalNames.length) { nav = nav + '<h3>Globals</h3>'; nav += '<div id="globals-list">'; nav += '<input id="search" class="search" placeholder="Filter by name" /><ul class=\"list\">'; globalNames.forEach(function(g) { if ( g.kind !== 'typedef' && !seen.hasOwnProperty(g.longname) ) nav += '<li>'+linkto(g.longname, g.name)+'</li>'; seen[g.longname] = true; }); nav += '</ul>'; nav += '</div>'; } // add template helpers view.find = find; view.linkto = linkto; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; // once for all view.nav = nav; for (var longname in helper.longnameToUrl) { var classes = find({kind: 'class', longname: longname}); if (classes.length) generate('Prototype: '+classes[0].name, classes, helper.longnameToUrl[longname]); var modules = find({kind: 'module', longname: longname}); if (modules.length) generate('Module: '+modules[0].name, modules, helper.longnameToUrl[longname]); var namespaces = find({kind: 'namespace', longname: longname}); if (namespaces.length) generate('Namespace: '+namespaces[0].name, namespaces, helper.longnameToUrl[longname]); // var constants = find({kind: 'constant', longname: longname}); // if (constants.length) generate('Constant: '+constants[0].name, constants, helper.longnameToUrl[longname]); var mixins = find({kind: 'mixin', longname: longname}); if (mixins.length) generate('Mixin: '+mixins[0].name, mixins, helper.longnameToUrl[longname]); var externals = find({kind: 'external', longname: longname}); if (externals.length) generate('External: '+externals[0].name, externals, helper.longnameToUrl[longname]); } if (globals.length) generate('Global', [{kind: 'globalobj'}], 'global.html'); generateIndex('Index', '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 generateIndex(title, filename) { var classes = find({kind: 'class', longname: longname}); var data = { title: title, docs: classes }; var path = outdir + '/' + filename, html = view.render(product + '-index.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, header: tutorial.title, content: tutorial.parse(), children: tutorial.children }; var path = outdir + '/' + filename, html = view.render('tutorial.tmpl', data); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> fs.writeFileSync(path, html) } // 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); }
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 = conf['default'].layoutFile ? path.getResourcePath(path.dirname(conf['default'].layoutFile), path.basename(conf['default'].layoutFile) ) : '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 = ''; switch(doclet.ngdoc) { case "provider": doclet.kind = "class"; break; case "service": doclet.kind = "class"; break; case "type": doclet.kind = "typedef"; break; case "property": doclet.kind = "member"; break; case "event": doclet.kind = "function"; break; } if (doclet.ngdoc == "provider" || doclet.ngdoc == "service") { doclet.kind = "class"; } if (doclet.examples) { doclet.examples = doclet.examples.map(function(example) { var caption, code; if (example.match(/^\s*<caption>([\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'), 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.toDir(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; //grimbo: pass the conf to the template. view.outputSourceReference = (true === conf['default'].outputSourceReference); // 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); for (var longname in helper.longnameToUrl) { if ( hasOwnProp.call(helper.longnameToUrl, longname) ) { var myClasses = helper.find(classes, {longname: longname}); if (myClasses.length) { var titlePrefix = myClasses[0].ngdoc ? myClasses[0].ngdoc : 'Class'; var iconPrefix = ''; switch (titlePrefix.toLowerCase()) { case "class": case "provider": case "service": iconPrefix = '<i class="icon-shield" style="color:#0186d1"></i> '; break; case "directive": iconPrefix = '<i class="icon-archive"></i> '; break; case "type": iconPrefix = '<i class="icon-key"></i> '; break; case "property": iconPrefix = '<i class="icon-maxcdn"></i> '; break; case "module": case "modules": iconPrefix = '<i class="icon-puzzle-piece"></i> '; break; case "event": iconPrefix = '<i class="icon-cogs"></i> '; break; } generate(iconPrefix + titlePrefix + ': ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); } var myModules = helper.find(modules, {longname: longname}); if (myModules.length) { generate('<i class="icon-puzzle-piece"></i> ' + 'Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); } var myNamespaces = helper.find(namespaces, {longname: longname}); if (myNamespaces.length) { generate('<i class="icon-code"></i> ' + 'Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); } var myMixins = helper.find(mixins, {longname: longname}); if (myMixins.length) { generate('<i class="icon-beaker"></i> ' + '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 <a href="foodoc.html">foo</a> 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); };
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*<caption>([\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; ////**debug** //if (debugMode) { // debugHtml += ('<h3>members.classes</h3>\n<pre class="source-code">\nmembers.classes =\n' + JSON.stringify(members.classes, null, " ") + '</pre>\n'); //} ////**debug** // add template helpers view.find = find; view.linkto = linkto; view.resolveAuthorLinks = resolveAuthorLinks; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; view.hljs = hljs; // once for all view.nav = buildNav(members); attachModuleSymbols( find({ kind: ['class', 'function'], longname: {left: 'module:'} }), members.modules ); // output source files by default; do this before generating any other pages, so // that the other pages can link to the source files if (!conf['default'] || conf['default'].outputSourceFiles !== false) { generateSourceFiles(sourceFiles, opts.encoding); } if (members.globals.length) { generate(null, 'Global', [{kind: 'globalobj'}], globalUrl); } // index page displays information from package.json and lists files var files = find({kind: 'file'}), packages = find({kind: 'package'}); //**debug** if (debugMode) { debugHtml += ('<h3>mainpage docs</h3>\n<pre class="source-code">\ndocs =\n' + JSON.stringify(packages.concat([{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}]), null, " ") + '</pre>\n'); } //**debug** generate('OpenSeadragonImaging API', 'OpenSeadragonImaging API', 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(null, 'Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); } var myModules = helper.find(modules, {longname: longname}); if (myModules.length) { generate(null, 'Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); } var myNamespaces = helper.find(namespaces, {longname: longname}); if (myNamespaces.length) { generate(null, 'Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); } var myMixins = helper.find(mixins, {longname: longname}); if (myMixins.length) { generate(null, 'Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); } var myExternals = helper.find(externals, {longname: longname}); if (myExternals.length) { generate(null, '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 <a href="foodoc.html">foo</a> 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); if (logMode) { fs.writeFileSync(logTextFile, logText, 'utf8'); } if (debugMode) { fs.writeFileSync(debugHtmlFile, debugHtmlHeader + debugHtml + debugHtmlFooter, 'utf8'); } };
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*<caption>([\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) ); } // sets the meta.filename property on each doclet 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; } } }); // sets the id property for each doclet 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 (set sign types and attribs) 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'; } }); // inherited members/methods dont show up by default in the file which extends // their parent class. let's rectify that. //console.log(data()); //returns a taffy object with taffy functions data().each(function(doclet) { var docletMembersMethodsObj, parentMembersMethodsObj; // console.log('data().each() --',doclet.kind,':',doclet.longname); if (doclet.kind == 'class' && doclet.augments) { // console.log('>>> CLASS:', doclet.name); // console.log('>> AUGMENTS:', doclet.augments); docletMembersMethodsObj = getMembersMethodsObj(doclet); // console.log('>>> docletMembersMethodsObj', docletMembersMethodsObj); doclet.augments.forEach(function(aug) { // console.log('checking:', aug); // console.log('>>>>> parent Class', aug); parentMembersMethodsObj = getMembersMethodsObj(find({ kind: 'class', longname: aug })[0]); // console.log('>>>>>>> parentMembersMethodsObj', parentMembersMethodsObj); // find overridden members/methods and update the db // console.log(' flagOverrides for methods'); flagOverrides(docletMembersMethodsObj.methods, parentMembersMethodsObj.methods); // console.log(' flagOverrides for members'); flagOverrides(docletMembersMethodsObj.members, parentMembersMethodsObj.members); // find inherited members/methods and update the db // addInherited(docletMembersMethodsObj.members, parentMembersMethodsObj.members, doclet.longname); // addInherited(docletMembersMethodsObj.methods, parentMembersMethodsObj.methods, doclet.longname); }); } }); // end inheritance enhancement 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; // custom view.taffyResultsToObj = taffyResultsToObj; view.killQuotes = killQuotes; // view.findInherited = findInherited; // view.findOverrides = findOverrides; // 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 /** * These vars returns an object: * { [Function] * insert: [Function], * merge: [Function], * TAFFY: true, * sort: [Function], * settings: [Function], * store: [Function] } */ 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 <a href="foodoc.html">foo</a> 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); };
exports.publish = function(taffyData, opts, tutorials) { data = taffyData; var templatePath = opts.template; view = new template.Template(templatePath + '/tmpl'); // set up templating view.layout = 'layout.tmpl'; // set up tutorials for helper helper.setTutorials(tutorials); data = helper.prune(data); data.sort('longname, version, since'); data().each(function(doclet) { doclet.attribs = ''; if (doclet.examples) { doclet.examples = doclet.examples.map(function(example) { var caption, code; if (example.match(/^\s*<caption>([\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); }); } }); // 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 static files to outdir var fromDir = path.join(templatePath, 'static'), staticFiles = fs.ls(fromDir, 3); staticFiles.forEach(function(fileName) { var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); fs.mkPath(toDir); fs.copyFileSync(fileName, toDir); }); data().each(function(doclet) { var url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); }); 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 (doclet.kind === 'function' || doclet.kind === 'class') { addSignatureParams(doclet); addSignatureReturns(doclet); addAttribs(doclet); } }); // do this after the urls have all been generated data().each(function(doclet) { doclet.ancestors = getAncestorLinks(doclet); doclet.signature = ''; 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.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; // once for all view.nav = buildNav(members); for (var longname in helper.longnameToUrl) { if ( hasOwnProp.call(helper.longnameToUrl, longname) ) { // reuse 'members', which speeds things up a bit var classes = taffy(members.classes); classes = helper.find(classes, {longname: longname}); if (classes.length) { generate('Class: ' + classes[0].longname, classes, helper.longnameToUrl[longname]); } var modules = taffy(members.modules); modules = helper.find(modules, {longname: longname}); if (modules.length) { generate('Module: ' + modules[0].longname, modules, helper.longnameToUrl[longname]); } var namespaces = taffy(members.namespaces); namespaces = helper.find(namespaces, {longname: longname}); if (namespaces.length) { generate('Namespace: ' + namespaces[0].longname, namespaces, helper.longnameToUrl[longname]); } var mixins = taffy(members.mixins); mixins = helper.find(mixins, {longname: longname}); if (mixins.length) { generate('Mixin: ' + mixins[0].longname, mixins, helper.longnameToUrl[longname]); } var externals = taffy(members.externals); externals = helper.find(externals, {longname: longname}); if (externals.length) { generate('External: ' + externals[0].longname, externals, helper.longnameToUrl[longname]); } } } if (members.globals.length) { generate('Global', members.globals, 'global.html'); } // 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), 'index.html'); // 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 <a href="foodoc.html">foo</a> 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); };
exports.publish = function (taffyData, opts, tutorials) { data = taffyData; //console.log(env) 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 = conf['default'].layoutFile ? path.getResourcePath(path.dirname(conf['default'].layoutFile), path.basename(conf['default'].layoutFile)) : '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*<caption>([\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; if (doclet.meta) { sourcePath = getPathFromDoclet(doclet); sourceFiles[sourcePath] = { resolved: sourcePath, shortened: null }; if (sourceFilePaths.indexOf(sourcePath) === -1) { sourceFilePaths.push(sourcePath); } } }); if (sourceFilePaths.length) { sourceFiles = shortenPaths(sourceFiles, path.commonPrefix(sourceFilePaths)); } data().each(function (doclet) { var url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); // add a shortened version of the full path var docletPath; if (doclet.meta) { docletPath = getPathFromDoclet(doclet); docletPath = sourceFiles[docletPath].shortened; if (docletPath) { doclet.meta.shortpath = 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; // output pretty-printed source files by default var outputSourceFiles = conf['default'] && conf['default'].outputSourceFiles !== false ? true : false; // add template helpers view.find = find; view.linkto = linkto; view.resolveAuthorLinks = resolveAuthorLinks; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; view.outputSourceFiles = outputSourceFiles; // generate the pretty-printed source files first so other pages can link to them if (outputSourceFiles) { registerSourceFiles(sourceFiles, opts.encoding); } // once for all attachModuleSymbols(find({ kind: ['class', 'function'], longname: { left: 'module:' } }), members.modules); if (members.globals.length) { generate('Global', [ { kind: 'globalobj' } ], globalUrl); } // index page displays information from package.json and lists files var files = find({ kind: 'file' }); var packages = find({ kind: 'package' }); // 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('./', filename); var markdown = view.render('tutorial.tmpl', tutorialData); markdown = markdown.replace(/\n+/g, '\n'); // yes, you can use {@link} in tutorials too! markdown = helper.resolveLinks(markdown); // turn {@link foo} into <a href="foodoc.html">foo</a> fs.writeFileSync(tutorialPath, markdown, '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); };
publish = function(data, opts, tutorials) { var defaultTemplatePath = 'templates/default'; var templatePath = (opts.template) ? opts.template : defaultTemplatePath; var out = '', view = new template.Template(env.dirname + '/' + templatePath + '/tmpl'); // set up templating view.layout = 'layout.tmpl'; // set up tutorials for helper helper.setTutorials(tutorials); function find(spec) { return data.get( data.find(spec) ); } function htmlsafe(str) { return str.replace(/</g, '<'); } function addSignatureParams(f) { var pnames = []; if (f.params) { f.params.forEach(function(p) { if (p.name && p.name.indexOf('.') === -1) { if (p.optional) { pnames.push('<span class="optional">'+p.name+'</span>'); } else { pnames.push(p.name); } } }); } f.signature = (f.signature || '') + '('+pnames.join(', ')+')'; } function generateAncestry(thisdoc) { var ancestors = [], doc = thisdoc; while (doc = doc.memberof) { doc = find({longname: doc}); if (doc) { doc = doc[0]; } if (!doc) { break; } ancestors.unshift( linkto(doc.longname, (scopeToPunc[doc.scope] || '') + doc.name) ); } if (ancestors.length) { ancestors[ancestors.length-1] += (scopeToPunc[thisdoc.scope] || ''); } return ancestors; } function addSignatureReturns(f) { var returnTypes = []; if (f.returns) { f.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); }); } f.signature = '<span class="signature">'+(f.signature || '') + '</span>' + '<span class="type-signature">'+(returnTypes.length? ' → {'+returnTypes.join('|')+'}' : '')+'</span>'; } function addSignatureType(f) { var types = []; if (f.type && f.type.names) { types = f.type.names; } if (types && types.length) { types = types.map(function(t) { return linkto(t, htmlsafe(t)); }); } f.signature = (f.signature || '') + '<span class="type-signature">'+(types.length? ' :'+types.join('|') : '')+'</span>'; } function addAttribs(f) { var attribs = []; if (f.virtual) { attribs.push('virtual'); } if (f.access && f.access !== 'public') { attribs.push(f.access); } if (f.scope && f.scope !== 'instance' && f.scope !== 'global') { if (f.kind == 'function' || f.kind == 'member' || f.kind == 'constant') { attribs.push(f.scope); } } if (f.readonly === true) { if (f.kind == 'member') { attribs.push('readonly'); } } if (f.kind === 'constant') { attribs.push('constant'); f.kind = 'member'; } f.attribs = '<span class="type-signature">'+htmlsafe(attribs.length? '<'+attribs.join(', ')+'> ' : '')+'</span>'; } data.remove({undocumented: true}); data.remove({ignore: true}); if (!opts.private) { data.remove({access: 'private'}); } data.remove({memberof: '<anonymous>'}); var packageInfo = (find({kind: 'package'}) || []) [0]; //function renderLinks(text) { // return helper.resolveLinks(text); //} data.forEach(function(doclet) { doclet.attribs = ''; if (doclet.examples) { doclet.examples = doclet.examples.map(function(example) { var caption, code; if (example.match(/^\s*<caption>([\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); }); } }); data.orderBy(['longname', 'version', 'since']); // kinds of containers var globals = find( {kind: ['member', 'function', 'constant', 'typedef'], memberof: {isUndefined: true}} ), modules = find({kind: 'module'}), externals = find({kind: 'external'}), mixins = find({kind: 'mixin'}), namespaces = find({kind: 'namespace'}); var outdir = opts.destination; if (packageInfo && packageInfo.name) { outdir += '/' + packageInfo.name + '/' + packageInfo.version + '/'; } fs.mkPath(outdir); // copy static files to outdir var fromDir = env.dirname + '/' + templatePath + '/static', staticFiles = fs.ls(fromDir, 3); staticFiles.forEach(function(fileName) { var toDir = fs.toDir(fileName.replace(fromDir, outdir)); fs.mkPath(toDir); fs.copyFile(fileName, toDir); }); function linkto(longname, linktext) { var url = helper.longnameToUrl[longname]; return url? '<a href="'+url+'">'+(linktext || longname)+'</a>' : (linktext || longname); } function tutoriallink(tutorial) { return helper.toTutorial(tutorial); } var containers = ['class', 'module', 'external', 'namespace', 'mixin']; data.forEach(function(doclet) { var url = helper.createLink(doclet); helper.registerLink(doclet.longname, url); }); data.forEach(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 (doclet.kind === 'function' || doclet.kind === 'class') { addSignatureParams(doclet); addSignatureReturns(doclet); addAttribs(doclet); } }); // do this after the urls have all been generated data.forEach(function(doclet) { doclet.ancestors = generateAncestry(doclet); doclet.signature = ''; if (doclet.kind === 'member') { addSignatureType(doclet); addAttribs(doclet); } if (doclet.kind === 'constant') { addSignatureType(doclet); addAttribs(doclet); } }); var nav = '<h2><a href="index.html">Index</a></h2>', seen = {}; var moduleNames = find({kind: 'module'}); moduleNames.sort(function(a, b) { return a.name > b.name; }); if (moduleNames.length) { nav += '<h3>Modules</h3><ul>'; moduleNames.forEach(function(m) { if ( !hasOwnProp.call(seen, m.longname) ) { nav += '<li>'+linkto(m.longname, m.name)+'</li>'; } seen[m.longname] = true; }); nav += '</ul>'; } var externalNames = find({kind: 'external'}); externalNames.sort(function(a, b) { return a.name > b.name; }); if (externalNames.length) { nav += '<h3>Externals</h3><ul>'; externalNames.forEach(function(e) { if ( !hasOwnProp.call(seen, e.longname) ) { nav += '<li>'+linkto( e.longname, e.name.replace(/(^"|"$)/g, '') )+'</li>'; } seen[e.longname] = true; }); nav += '</ul>'; } var classNames = find({kind: 'class'}); classNames.sort(function(a, b) { return a.name > b.name; }); if (classNames.length) { var moduleClasses = 0; classNames.forEach(function(c) { var moduleSameName = find({kind: 'module', longname: c.longname}); if (moduleSameName.length) { c.name = c.name.replace('module:', 'require("')+'")'; moduleClasses++; moduleSameName[0].module = c; } if (moduleClasses !== -1 && moduleClasses < classNames.length) { nav += '<h3>Classes</h3><ul>'; moduleClasses = -1; } if ( !hasOwnProp.call(seen, c.longname) ) { nav += '<li>'+linkto(c.longname, c.name)+'</li>'; } seen[c.longname] = true; }); nav += '</ul>'; } var namespaceNames = find({kind: 'namespace'}); namespaceNames.sort(function(a, b) { return a.name > b.name; }); if (namespaceNames.length) { nav += '<h3>Namespaces</h3><ul>'; namespaceNames.forEach(function(n) { if ( !hasOwnProp.call(seen, n.longname) ) { nav += '<li>'+linkto(n.longname, n.name)+'</li>'; } seen[n.longname] = true; }); nav += '</ul>'; } // var constantNames = find({kind: 'constants'}); // if (constantNames.length) { // nav += '<h3>Constants</h3><ul>'; // constantNames.forEach(function(c) { // if ( !hasOwnProp.call(seen, c.longname) ) { // nav += '<li>'+linkto(c.longname, c.name)+'</li>'; // } // seen[c.longname] = true; // }); // // nav += '</ul>'; // } var mixinNames = find({kind: 'mixin'}); mixinNames.sort(function(a, b) { return a.name > b.name; }); if (mixinNames.length) { nav += '<h3>Mixins</h3><ul>'; mixinNames.forEach(function(m) { if ( !hasOwnProp.call(seen, m.longname) ) { nav += '<li>'+linkto(m.longname, m.name)+'</li>'; } seen[m.longname] = true; }); nav += '</ul>'; } if (tutorials.children.length) { nav += '<h3>Tutorials</h3><ul>'; tutorials.children.forEach(function(t) { nav += '<li>'+tutoriallink(t.name)+'</li>'; }); nav += '</ul>'; } var globalNames = find({kind: ['member', 'function', 'constant', 'typedef'], 'memberof': {'isUndefined': true}}); globalNames.sort(function(a, b) { return a.name > b.name; }); if (globalNames.length) { nav += '<h3>Global</h3><ul>'; globalNames.forEach(function(g) { if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { nav += '<li>'+linkto(g.longname, g.name)+'</li>'; } seen[g.longname] = true; }); nav += '</ul>'; } // add template helpers view.find = find; view.linkto = linkto; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; // 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}); if (classes.length) { generate('Class: '+classes[0].name, classes, helper.longnameToUrl[longname]); } var modules = find({kind: 'module', longname: longname}); if (modules.length) { generate('Module: '+modules[0].name, modules, helper.longnameToUrl[longname]); } var namespaces = find({kind: 'namespace', longname: longname}); if (namespaces.length) { generate('Namespace: '+namespaces[0].name, namespaces, helper.longnameToUrl[longname]); } // var constants = find({kind: 'constant', longname: longname}); // if (constants.length) { generate('Constant: '+constants[0].name, constants, helper.longnameToUrl[longname]); } var mixins = find({kind: 'mixin', longname: longname}); if (mixins.length) { generate('Mixin: '+mixins[0].name, mixins, helper.longnameToUrl[longname]); } var externals = find({kind: 'external', longname: longname}); if (externals.length) { generate('External: '+externals[0].name, externals, helper.longnameToUrl[longname]); } } } if (globals.length) { generate('Global', [{kind: 'globalobj'}], 'global.html'); } // 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) , 'index.html'); function generateTutorial(title, tutorial, filename) { var data = { title: title, header: tutorial.title, content: tutorial.parse(), children: tutorial.children }; var path = outdir + '/' + filename, html = view.render('tutorial.tmpl', data); // yes, you can use {@link} in tutorials too! html = helper.resolveLinks(html); // turn {@link foo} into <a href="foodoc.html">foo</a> fs.writeFileSync(path, html); } // 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); };