UNPKG

leafdoc

Version:

A lightweight NaturalDocs-like LeafletJS-style documentation generator

1,013 lines (811 loc) 33.4 kB
var leafdoc = (function (fs, path, Handlebars, MarkdownRenderer, xRegExp, unicodeRegExpIDStart, unicodeRegExpIDContinue, mec) { 'use strict'; fs = fs && Object.prototype.hasOwnProperty.call(fs, 'default') ? fs['default'] : fs; path = path && Object.prototype.hasOwnProperty.call(path, 'default') ? path['default'] : path; Handlebars = Handlebars && Object.prototype.hasOwnProperty.call(Handlebars, 'default') ? Handlebars['default'] : Handlebars; MarkdownRenderer = MarkdownRenderer && Object.prototype.hasOwnProperty.call(MarkdownRenderer, 'default') ? MarkdownRenderer['default'] : MarkdownRenderer; xRegExp = xRegExp && Object.prototype.hasOwnProperty.call(xRegExp, 'default') ? xRegExp['default'] : xRegExp; unicodeRegExpIDStart = unicodeRegExpIDStart && Object.prototype.hasOwnProperty.call(unicodeRegExpIDStart, 'default') ? unicodeRegExpIDStart['default'] : unicodeRegExpIDStart; unicodeRegExpIDContinue = unicodeRegExpIDContinue && Object.prototype.hasOwnProperty.call(unicodeRegExpIDContinue, 'default') ? unicodeRegExpIDContinue['default'] : unicodeRegExpIDContinue; mec = mec && Object.prototype.hasOwnProperty.call(mec, 'default') ? mec['default'] : mec; var templateDir = path.join(path.resolve(), 'templates', 'basic'); var templates = Object.create(null); function setTemplateDir(newDir) { templates = Object.create(null); templateDir = newDir; } function getTemplate(templateName) { if (!templates[templateName]) { templates[templateName] = Handlebars.compile(fs.readFileSync(path.join(templateDir, (templateName + ".hbs"))).toString()); } return templates[templateName]; } var _AKAs = {}; function setAKAs(akas) { // console.log('Template thing updating AKAs'); _AKAs = akas; } /// TODO: Allow setting markdown-it options (enable/disable tables, autolinks, abbrevs, etc) var markdownRenderer; function markdown(str) { if (!markdownRenderer) { markdownRenderer = MarkdownRenderer(/* markdown-it options */); } return markdownRenderer.render(str); } // Helper to replace AKAs in markdown links. function replaceAKAs(str) { str = str.trim(); // [...](#a) → [...](#b) str = str.replace(/\[([^\[\]]*)\]\(#([^\(\)]*)\)/g, function (str, a, b) { if (b in _AKAs) { // console.log(' Replacing link AKA: ', `[${a}](#${[b]}) → [${a}](#${ _AKAs[b] })`); return ("[" + a + "](#" + (_AKAs[b]) + ")"); } else { // console.log(' Ignoring link AKA: ', `[${a}](#${[b]})`); return str; } }); // `a` → [`a`](#b) str = str.replace(/`([^`]*)`/g, function (str, a) { if (a in _AKAs) { // console.log(' Replacing code literal AKA: ', `\`${a}\` → [\`${a}\`](#${ _AKAs[a] })`); return ("[`" + a + "`](#" + (_AKAs[a]) + ")"); } else { // console.log(' Ignoring code literal AKA: ', '`' + a + '`'); return str; } }); // Remove links inside links (bug #63) // [pre[`code`](trash)post](url) → [pre`code`post](url) // regexp is [.*[`.*`](.*).*](.*) , but with each .* replaced with // ([^`\[\]\(\)]*), i.e. a capture group of anything that is not ()[]` . str = str.replace(/\[([^`\[\]\(\)]*)\[`([^`\[\]\(\)]*)`\]\([^`\[\]\(\)]*\)([^`\[\]\(\)]*)\]\(([^`\[\]\(\)]*)\)/g, function (str, pre, code, post, url) { //console.log(" Removing nested link: ", ` ${str} → [${pre}\`${code}\`${post}](${url})`); return ("[" + pre + "`" + code + "`" + post + "](" + url + ")"); } ); return str; } Handlebars.registerHelper('markdown', function markdownHelper(str) { if (!str) { return ''; } if (str instanceof Array) { str = str.join('\n').trim(); // str = str.join(' '); } return markdown(replaceAKAs(str)) .trim() .replace('<p>', '') .replace('</p>', ''); }); Handlebars.registerHelper('rawmarkdown', function rawmarkdownHelper(str) { if (!str) { return ''; } if (str instanceof Array) { str = str.join('\n'); } return markdown(replaceAKAs(str)); }); // Automatically link to AKAs, mostly used on method/function/param/option data types. Handlebars.registerHelper('type', function typeHelper(str) { if (!str) { return ''; } if (str in _AKAs) { var id = _AKAs[str]; return ("<a href='#" + id + "'>" + str + "</a>"); } else { // Should be a built-in type return str; } }); // JSON stringify the stuff. Handlebars.registerHelper('json', function jsonHelper(obj) { return JSON.stringify(obj, undefined, 1); }); // Comparison helper. Inspired from https://github.com/helpers/handlebars-helpers Handlebars.registerHelper('equals', function equalityHelper(a, b) { return a == b; }); // One or more lines starting with whitespace and two or more forward slashes, // or // whitespace-slash-asterisk whatever asterisk-slash. var commentBlock = xRegExp('^(?<multiline> ( (?!\\n) \\s*\\/{2,}\\s*.*\\n )+ ) \ | \ (\\s*\\/\\* (?<block> [\\s\\S]*?) \\*\\/) \ ', 'gmnx'); var leafdocFile = xRegExp('^(?<block> [\\s\\S]+ )$', 'gmnx'); // Inside each line of a comment /* */ block, skips the leading spaces / asterisk (if any) var leadingBlock = xRegExp('^ ( \\s* \\* \\s? )? (?<line> .* ) $', 'nx'); // Inside each line of a comment // block, skips the leading // var leadingLine = xRegExp('^ \\s*/{0,4}\\s{0,1} (?<line> .* ) $', 'nx'); // Inside .leafdoc files, match any line without skipping anything var anyLine = xRegExp('^ (?<line> .* ) $', 'nx'); // Parses a 🍂 directive, init'd at redoLeafDirective() global.leafDirective = redoLeafDirective('🍂'); function getLeafDirective() { return global.leafDirective; } // Re-builds the 🍂 directive based on a different leading character function redoLeafDirective(char) { global.leafDirective = new RegExp(("\\s*" + char + "(?<directive>\\S+)(\\s+(?<content>.+?))?(?:; |$)"), 'g'); return global.leafDirective; } // Parses an identifier, allowing only unicode ID_Start and ID_Continue characters // An identifier allows dots in it, to allow for namespacing identifiers. // TODO: An identifier shall allow an underscore or dollar at the beginning, as JS does. var identifier = xRegExp.build('^(({{ID_Start}} | _ | \$) ( {{ID_Continue}} | \\. | : )*)$', { ID_Start: unicodeRegExpIDStart, // eslint-disable-line camelcase ID_Continue: unicodeRegExpIDContinue // eslint-disable-line camelcase }, 'nx'); // Parses a function name, its return type, and its parameters // Funny thing about functions is that not all printable characters are allowed. Thus, // use unicode ID_Start and ID_Continue character sets via 'identifier' sub-regexp. // eslint-disable-next-line no-useless-escape var functionDefinition = xRegExp.build('^ (?<name> {{identifier}} ) (?<required> (\\?{0,1}) ) \\s* (?<params> \\( .* \\) ){0,1} \\s* ( \\: \\s* (?<type> .+? ) )? ( = \\s* (?<default> .+ ) \\s* ){0,1} \$', { identifier: identifier }, 'nx'); // var functionParam = xRegExp.build('^ \\s* (?<name> {{identifier}}) \\s* ( \\: \\s* (?<type> .+ ) \\s* ) $', {identifier: identifier}, 'nx'); // var functionParam = xRegExp.build('\\s* (?<name> ( {{identifier}} | … ) \\?{0,1} ) \\s* ( \\: \\s* (?<type> [^,]+ ) \\s* ) (, | \\)) ', {identifier: identifier}, 'gnx'); var functionParam = xRegExp.build('\\s* (?<name> ( {{identifier}} | … ) \\?{0,1} ) \\s* ( \\: \\s* (?<type> [^,]+ ) \\s* )? (, | \\)) ', {identifier: identifier}, 'gnx'); // Parses a miniclass name and its real class between parentheses. var miniclassDefinition = xRegExp('^ (?<miniclass> .+ ) \\s* \\( (?<realclass> .+ ) \\) $', 'nx'); // Parses a UML-like relationship definition var relationshipDefinition = xRegExp("^\n(?<type> \\S+ ) \\s*\n(?<namespace> [^,\\s]+ ) \\s*\n(, \\s* (?<cardinalityFrom> [^,\\s]*) )? \\s*\n(, \\s* (?<cardinalityTo> [^,\\s]*) )? \\s*\n(, \\s* (?<label> .+ )? )?\n\\s* $", 'nx'); // Trivial file/Str parser. // Assumes that the whole string is leafdoc docstrings. // This is the old 'isSource=false' behaviour. function trivialParser(str) { // Return an array with just one element, the whole string. return [str]; } // Generic parser, depends on multiline-extract-comments and returns a simplified // version of the comments data structure function multilangParser(str, filename) { var mecBlocks = mec(str, {filename: filename || 'leafdoc_tmp.js'}); var blocks = Object.values(mecBlocks).map(function (mecBlock) { return mecBlock.content.trim(); }).filter(function (block) { return block && block !== ''; }); return blocks; } // 🍂class Leafdoc; Represents the Leafdoc parser var Leafdoc = function Leafdoc(options) { this._namespaces = {}; this._knownDocumentables = [ 'example', 'constructor', 'destructor', 'factory', 'option', 'event', 'method', 'function', 'property' ]; this._documentableLabels = { 'example': 'Usage example', 'factory': 'Creation', 'constructor': 'Constructor', 'destructor': 'Destructor', 'option': 'Options', 'event': 'Events', 'method': 'Methods', 'function': 'Functions', 'property': 'Properties' }; this._inheritableDocumentables = [ 'method', 'function', 'event', 'option', 'property' ]; // Holds a list of miniclasses, with the miniclass as key and the real // class as value. // Maybe a better name would be "subnamespaces" or whatever. this._miniclasses = {}; this._AKAs = {}; // 🍂section // 🍂aka Leafdoc options if (options) { // 🍂option templateDir: String = 'templates/basic' // Defines which subdirectory (relative to the directory the curent JS // script is running) holds the handlebars template files for building up the HTML. if (options.templateDir) { setTemplateDir(options.templateDir); } // 🍂option showInheritancesWhenEmpty: Boolean = false // When `true`, child classes/namespaces will display documentables from ancestors, even if the child class doesn't have any of such documentables. // e.g. display inherited events even if the child doesn't define any new events. this.showInheritancesWhenEmpty = options.showInheritancesWhenEmpty || false; // 🍂option leadingCharacter: String = '🍂' // Overrides the Leaf symbol as the leading character for documentation lines. // See also [`setLeadingCharacter`](#leafdoc-setleadingcharacter). if (options.leadingCharacter) { this.setLeadingCharacter(options.leadingCharacter); } // 🍂option customDocumentables: Map = {} // A key-value map. Each pair will be passed to [`registerDocumentable`](#leafdoc-registerdocumentable). if (options.customDocumentables) { for (var i in options.customDocumentables) { this.registerDocumentable(i, options.customDocumentables[i]); } } // 🍂option verbose: Boolean = false // Set to `true` to display more information as files are being read. if (options.verbose) { this._verbose = options.verbose; } } }; // 🍂method registerDocumentable (name: String, label?: String, inheritable?: Boolean): this // Registers a new documentable type, beyond the preset ones (function, // property, etc). New documentable should also not be an already used // keyword (class, namespace, inherits, etc). // When registering new documentables, make sure that there is an appropriate // template file for it. // Set `label` to the text for the sections in the generated docs. // `inheritable` parameter determines documentable can be inherited via inherits keyword in a subclass. Leafdoc.prototype.registerDocumentable = function registerDocumentable (name, label, inheritable) { this._knownDocumentables.push(name, label); if (label) { this._documentableLabels[name] = label; } if (inheritable) { this._inheritableDocumentables.push(name); } return this; }; // 🍂method getTemplateEngine(): Handlebars // Returns handlebars template engine used to render templates. // You can use it for override helpers or register a new one. Leafdoc.prototype.getTemplateEngine = function getTemplateEngine () { return template.engine; }; // 🍂method setLeadingCharacter(char: String): this // In the rare case you don't want to use &#x1f342; as the leading character for // leaf directives, run this function with the desired character, e.g. // `setLeadingCharacter('@');` // The new leading character will apply only to files/dirs/strings parsed from // that moment on, so it's a good idea to call this before anything else. Leafdoc.prototype.setLeadingCharacter = function setLeadingCharacter (char) { // console.log('Setting leading character to', char); redoLeafDirective(char); }; // 🍂method addDir (dirname: String, extensions?: String[]): this // Recursively scans a directory, and parses any files that match the // given `extensions` (by default `.js` and `.leafdoc`, mind the dots). // Files with a `.leafdoc` extension will be treated as leafdoc-only // instead of source. Leafdoc.prototype.addDir = function addDir (dirname, extensions) { if (!extensions) { extensions = ['.js', '.leafdoc']; } var filenames = fs.readdirSync(dirname); for (var i in filenames) { var filename = path.join(dirname, filenames[i]); // Check if dir, recurse if so var stats = fs.lstatSync(filename); if (stats.isDirectory()) { this.addDir(filename, extensions); } else if (extensions.indexOf(path.extname(filename)) !== -1) { if (this._verbose) { console.log('Leafdoc processing file: ', filename); } this.addFile(filename); } } return this; }; // 🍂method addFile(filename: String): this // Parses the given file using [`addBuffer`](#leafdoc-addbuffer). Leafdoc.prototype.addFile = function addFile (filename) { return this.addBuffer(fs.readFileSync(filename), filename); }; // 🍂method addBuffer(buf: Buffer, filename?: String): this // Parses the given buffer using [`addStr`](#leafdoc-addstr) underneath. Leafdoc.prototype.addBuffer = function addBuffer (buf, filename) { return this.addStr(buf.toString(), filename); }; // 🍂method addStr(str: String, filename?: String): this // Parses the given string for Leafdoc directives. Leafdoc.prototype.addStr = function addStr (str, filename) { var assign; // Leaflet files use DOS line feeds, which screw up things. str = str.replace(/\r\n?/g, '\n'); var ns = '__default';// namespace (or class) var sec = '__default';// section var dt = '';// Type of documentable var dc = '';// Name of documentable var alt = 0;// Will auto-increment for documentables with 🍂alternative var altAppliesTo = null;// Ensures 'alt' resets when documentable changes // Scope of the current line (parser state): ns, sec or dc. // (namespace, section, documentable) var scope = ''; var namespaces = this._namespaces; var currentNamespace, currentSection, currentDocumentable; // Temporal placeholder - section comments and AKAs are dangling until // the documentable type is known var sectionComments = []; var sectionAKA = []; var sectionIsUninheritable = false; var parser = path.extname(filename) === '.leafdoc' ? trivialParser : multilangParser; var parsedBlocks = parser(str, filename); // 1: Fetch comment blocks (from the parser). For each block... for (var i = 0, l = parsedBlocks.length; i < l; i++) { var commentBlock = parsedBlocks[i]; var blockIsEmpty = true; // Edge case: some comment blocks in markdown might choke up if (!commentBlock) { break; } // 2: Split into lines var lines = commentBlock.split('\n'); // 3: Split lines into directives (separated by ";") var directives = []; for (var i$1 in lines) { var line = lines[i$1]; var lineIsValid = false; var parsedCharacters = 0; var match = (void 0); // In "param foo, bar", directive is "param" and content is "foo, bar" while (match = getLeafDirective().exec(line)) { if (match.groups.content) { match.groups.content = match.groups.content.trim(); } directives.push([ match.groups.directive, match.groups.content ]); // console.log('directive match: ', match); blockIsEmpty = false; lineIsValid = true; parsedCharacters = match.index + match[0].length; } if (lineIsValid) { var trailing = line.substr(parsedCharacters).trim(); if (trailing) { directives.push(['comment', trailing]); } } if (!lineIsValid && !blockIsEmpty) { // implicit 🍂comment directive. directives.push(['comment', line]); } } for (var i$2 in directives) { var directive = directives[i$2][0], content = directives[i$2][1]; // 4: Parse 🍂 directives if (directive === 'class' || directive === 'namespace') { ns = content.trim(); sec = '__default'; scope = 'ns'; } else if (directive === 'miniclass') { var split = miniclassDefinition.exec(content); if (!split) { console.error('Invalid miniclass definition: ', content); console.log(split); } else { ns = split[1].trim(); var miniparent = split[2]; sec = '__default'; scope = 'ns'; this._miniclasses[ns] = miniparent; } } else if (directive === 'section') { sec = content || '__default'; scope = 'sec'; } else if (this._knownDocumentables.indexOf(directive) !== -1) { scope = 'dc'; dt = directive; dc = '';// The name of the documentable will be set later } // console.log(scope, '-', directive, '-', content); if (scope === 'ns') { if (!namespaces.hasOwnProperty(ns)) { // console.log('Defining class/namespace ', ns); namespaces[ns] = { name: ns, aka: [], comments: [], supersections: {}, inherits: [], relationships: [], }; } currentNamespace = namespaces[ns]; if (directive === 'aka') { currentNamespace.aka.push(content); } if (directive === 'comment') { currentNamespace.comments.push(content); } if (directive === 'inherits') { currentNamespace.inherits.push(content); } if (directive === 'relationship') { var split = relationshipDefinition.exec(content); currentNamespace.relationships.push({ type: split[1], namespace: split[2], cardinalityFrom: split[3], cardinalityTo: split[4], label: split[5], }); } } if (scope === 'sec') { if (directive === 'comment') { sectionComments.push(content); } if (directive === 'aka') { sectionAKA.push(content); } if (directive === 'uninheritable') { sectionIsUninheritable = true; } } if (scope === 'dc') { if (!currentNamespace) { console.error('Error: No class/namespace set when parsing through:'); console.error(commentBlock); } if (!currentNamespace.supersections.hasOwnProperty(dt)) { currentNamespace.supersections[dt] = { name: dt, aka: [], comments: [], sections: {} }; } if (!currentNamespace.supersections[dt].sections.hasOwnProperty(sec)) { currentNamespace.supersections[dt].sections[sec] = { name: sec, aka: sectionAKA, comments: sectionComments, uninheritable: sectionIsUninheritable, documentables: {}, type: dt }; sectionAKA = []; sectionComments = []; sectionIsUninheritable = false; } currentSection = currentNamespace.supersections[dt].sections[sec]; // console.log(currentSection); // console.log(directive); if (this._knownDocumentables.indexOf(directive) !== -1) { // Documentables might have more than their name as content. // All documentables will follow the syntax for functions, // with optional parameters, optional required flag, optional type, and optional default value. // console.log(content, ', ', alt); var name = (void 0), paramString = (void 0), params = {}, type = null, defaultValue = null, optional = false; if (content) { var split$1 = functionDefinition.exec(content); if (!split$1) { console.error(("Invalid " + directive + " definition: "), content); } else { optional = split$1[2] == '?'; (assign = split$1, name = assign[1], paramString = assign[3], type = assign[4], defaultValue = assign[5]); // name = split[1]; // paramString = split[3]; // type = split[4]; // defaultValue = split[5]; if (paramString) { var match$1 = (void 0); while (match$1 = functionParam.exec(paramString)) { params[ match$1[1] ] = {name: match$1[1], type: match$1[2]}; } // console.log("\"" + paramString + "\"\n\t", params); } } } else { name = '__default'; } // Handle alternatives - just modify the name if 'alt' and 'altAppliesTo' match if (altAppliesTo === name) { dc = name + "-alternative-" + alt; } else { dc = name; alt = 0; altAppliesTo = null; } if (!currentSection.documentables.hasOwnProperty(dc)) { currentSection.documentables[dc] = { name: name, aka: [], comments: [], params: params,// Only for functions/methods/factories type: type ? type.trim() : null, optional: optional, defaultValue: defaultValue || null// Only for options, properties }; } currentDocumentable = currentSection.documentables[dc]; } else if (directive === 'alternative') { alt++; // Alternative applies to current documentable name; if name // doesn't match, alternative has no effect. altAppliesTo = currentDocumentable.name; } else if (directive === 'param') { // Params are param name, type. /// TODO: Think about default values, or param explanation. var split = content.split(':'); var paramName = split[0].trim(); var paramType = split[1] ? split[1].trim() : ''; currentDocumentable.params[paramName] = {name: paramName, type: paramType}; } else if (directive === 'aka') { currentDocumentable.aka.push(content); } else if (directive === 'comment') { // console.log('Doing stuff with a method comments: ', content); currentDocumentable.comments.push(content); } } } } // console.log(this._namespaces.Leafdoc.__default[0]); // console.log(this._namespaces.Marker.__default[0]); // console.log(this._namespaces); // console.log(this._namespaces.Marker.supersections.method.sections.__default); // console.log('namespaces after addStr', this._namespaces); return this; }; /* * 🍂method outputStr: String * Outputs the documentation to a string. * Use only after all the needed files have been parsed. */ Leafdoc.prototype.outputStr = function outputStr () { this._resolveAKAs(); var out = ''; for (var ns in this._namespaces) { out += this._stringifyNamespace(this._namespaces[ns]); } // console.log('miniclasses: ', this._miniclasses); return (getTemplate('html'))({body: out}); }; /* * 🍂method outputJSON: String * Outputs the internal documentation tree to a JSON blob, without any formatting. * Use only after all the needed files have been parsed. */ Leafdoc.prototype.outputJSON = function outputJSON () { this._resolveAKAs(); return JSON.stringify(this._namespaces, undefined, 1); }; Leafdoc.prototype._stringifyNamespace = function _stringifyNamespace (namespace, isMini) { if (!isMini && this._miniclasses.hasOwnProperty(namespace.name)) { return ''; } var out = ''; var ancestors = this._flattenInheritances(namespace.name); /// Ensure explicit order of the supersections (known types of documentable: /// example, factory, options, events, methods, properties for (var i in this._knownDocumentables) { var s = this._knownDocumentables[i]; var supersectionHasSomething = namespace.supersections.hasOwnProperty(s); if (s !== 'example' && this.showInheritancesWhenEmpty && !supersectionHasSomething) { // console.log('checking for empty section with inherited stuff, ', namespace.name, s, ancestors); for (var i in ancestors) { var ancestor = ancestors[i]; // console.log(ancestor, this._namespaces[ancestor].supersections.hasOwnProperty(s)); if (this._namespaces[ancestor].supersections.hasOwnProperty(s)) { for (var sec in this._namespaces[ancestor].supersections[s].sections) { if (!this._namespaces[ancestor].supersections[s].sections[sec].uninheritable) { supersectionHasSomething = true; } } // console.log(this._namespaces[ancestor].supersections[s]); if (supersectionHasSomething) { namespace.supersections[s] = { name: this._namespaces[ancestor].supersections[s].name, sections: {} }; } } } } if (supersectionHasSomething) { out += this._stringifySupersection(namespace.supersections[s], ancestors, namespace.name, isMini); } } if (!isMini) { for (var i in this._miniclasses) { if (this._miniclasses[i] === namespace.name) { out += this._stringifyNamespace(this._namespaces[i], true); // console.log('out is now', out); } } } // console.log(namespace); return (getTemplate('namespace'))({ name: isMini ? undefined : namespace.name, id: namespace.id, comments: namespace.comments, supersections: out, inherits: namespace.inherits, relationships: namespace.relationships }); }; Leafdoc.prototype._stringifySupersection = function _stringifySupersection (supersection, ancestors, namespacename, isMini) { var sections = ''; var inheritances = ''; // The "__default" section should show above any named sections if ('__default' in supersection.sections) { var oldSections = supersection.sections; supersection.sections = {__default: oldSections.__default}; for (var s in oldSections) { if (s !== '__default') { supersection.sections[s] = oldSections[s]; } } } for (var s in supersection.sections) { sections += this._stringifySection(supersection.sections[s], supersection.name, false); } var name = supersection.name; var label = this._documentableLabels[name] || name; // Calculate inherited documentables. // In the order of the ancestors, check if each documentable has already been // selected for output, skip it if so. Group rest into inherited sections. if (this._inheritableDocumentables.indexOf(name) !== -1) { if (ancestors.length) { // inheritances += 'Inherits stuff from: ' + inheritances.join(','); var inheritedSections = []; // Build a list of the documentables which have been already outputted var skip = []; for (var s in supersection.sections) { var section = supersection.sections[s]; for (var d in section.documentables) { skip.push(d); } } // console.log('Will skip: ', skip); for (var i in ancestors) { var parent = ancestors[i]; // console.log('Processing ancestor ', parent); if (this._namespaces[parent].supersections.hasOwnProperty(name)) { var parentSupersection = this._namespaces[parent].supersections[name]; for (var s in parentSupersection.sections) { var parentSection = parentSupersection.sections[s]; if (parentSection && !parentSection.uninheritable) { var inheritedSection = { name: parentSection.name === '__default' ? label : parentSection.name, parent: parent, documentables: [], id: parentSection.id }; for (var d in parentSection.documentables) { // console.log('Checking if should show inherited ', d); if (skip.indexOf(d) === -1) { skip.push(d); inheritedSection.documentables.push(parentSection.documentables[d]); } } // console.log(inheritedSection.documentables); if (inheritedSection.documentables.length) { inheritedSections.push(inheritedSection); } } } } } // Inherited sections have been calculated, template them away. for (var i in inheritedSections) { var inheritedSection = inheritedSections[i]; inheritances += (getTemplate('inherited'))({ name: inheritedSection.name, ancestor: inheritedSection.parent, inherited: this._stringifySection(inheritedSection, supersection.name, namespacename), id: inheritedSection.id }); } } } return (getTemplate('supersection'))({ name: isMini ? namespacename : label, id: supersection.id, comments: supersection.comments, sections: sections, inheritances: inheritances }); }; Leafdoc.prototype._stringifySection = function _stringifySection (section, documentableType, inheritingNamespace, supersectionLabel) { var name = (section.name === '__default' || inheritingNamespace) ? '' : section.name; // if (name) console.log('Named section:', section); // console.log('Section:', section); // If inheriting, recreate the documentable changing the ID. var docs = section.documentables; if (inheritingNamespace) { docs = []; for (var i in section.documentables) { var oldDoc = section.documentables[i]; docs.push({ name: oldDoc.name, comments: oldDoc.comments, params: oldDoc.params, type: oldDoc.type, defaultValue: oldDoc.defaultValue, id: this._normalizeName(inheritingNamespace, oldDoc.name), }); } } // console.log(documentableType, section.name === '__default'); return (getTemplate('section'))({ name: name, id: section.id, comments: section.comments, documentables: (getTemplate(documentableType))({ documentables: docs }), isSecondarySection: (section.name !== '__default' && documentableType !== 'example' && !inheritingNamespace), isInherited: !!inheritingNamespace, supersectionLabel: this._documentableLabels[documentableType] || documentableType }); }; // Loop through all the documentables, create an _anchor property, and // return a plain object containing a map of all valid link names to their anchors. Leafdoc.prototype._resolveAKAs = function _resolveAKAs () { for (var ns in this._namespaces) { var namespace = this._namespaces[ns]; namespace.id = this._normalizeName(namespace.name); this._assignAKAs(namespace.id, namespace.aka); this._assignAKAs(namespace.id, [namespace.name]); // console.log('Resolve namespace AKAs: ', namespace.id, namespace.name, namespace.aka); for (var ss in namespace.supersections) { // console.log(namespace.supersections[ss]); var supersection = namespace.supersections[ss]; var documentableType = supersection.name; supersection.id = this._normalizeName(namespace.name, supersection.name); this._assignAKAs(supersection.id, [((supersection.id) + "s")]); for (var s in supersection.sections) { var section = supersection.sections[s]; section.id = this._normalizeName(namespace.name, section.name === '__default' ? documentableType : section.name); this._assignAKAs(section.id, section.aka); // console.log('Resolve section AKAs: ', section.id, section.name, section.aka); for (var d in section.documentables) { var doc = section.documentables[d]; if (doc.name !== '__default') {// Skip comments and examples doc.id = this._normalizeName(namespace.name, doc.name); this._assignAKAs(doc.id, doc.aka); // console.log('Resolve doc AKAs: ', doc.id, doc.name, doc.aka); } } } } } setAKAs(this._AKAs); // console.log(this._AKAs); }; Leafdoc.prototype._normalizeName = function _normalizeName (namespace, name) { var id = namespace + (name ? ("-" + name) : ''); id = id.trim().replace(/[\s\.]/g, '-'); return id.toLowerCase(); }; Leafdoc.prototype._assignAKAs = function _assignAKAs (id, akas) { for (var i in akas) { this._AKAs[akas[i].trim()] = id; } }; // Given a class/namespace, recurse through inherited classes to build // up an ordered list of clases/namespaces this class inherits from. Leafdoc.prototype._flattenInheritances = function _flattenInheritances (classname, inheritancesSoFar) { if (!inheritancesSoFar) { // console.log('Resolving inheritances for ', classname); inheritancesSoFar = []; } if (this._namespaces.hasOwnProperty(classname)) { for (var i in this._namespaces[classname].inherits) { var parent = this._namespaces[classname].inherits[i]; if (inheritancesSoFar.indexOf(parent) === -1) { inheritancesSoFar.push(parent); inheritancesSoFar = this._flattenInheritances(parent, inheritancesSoFar); } } } else { console.warn('Warning: Ancestor class/namespace «', classname, '» not found!'); return []; } // console.log(classname, '→', inheritancesSoFar); // console.log(this._namespaces[classname].inherits); return inheritancesSoFar; }; return Leafdoc; }(fs, path, Handlebars, MarkdownRenderer, xRegExp, unicodeRegExpIDStart, unicodeRegExpIDContinue, mec)); //# sourceMappingURL=leafdoc.browser.js.map