zjslqshqz-docma
Version:
A powerful dev-tool to easily generate beautiful HTML documentation from Javascript (JSDoc), Markdown and HTML files.
924 lines (863 loc) • 31 kB
JavaScript
/* global docma, Utils, dust, EventEmitter, XMLHttpRequest */
/* eslint no-nested-ternary:0, max-depth:0, no-var:0, prefer-template:0, prefer-arrow-callback:0, prefer-spread:0, object-shorthand:0 */
// Note: This is for use in the browser. ES2015 rules don't apply here (yet).
// --------------------------------
// CLASS: DocmaWeb
// https://github.com/onury/docma
// --------------------------------
/**
* Gets Docma version which the documentation is built with.
* @name DocmaWeb#version
* @type {String}
*/
/**
* Docma (web) core.
*
* When you build the documentation with a template, `docma-web.js` will be
* generated (and linked in the main HTML); which is the core engine for the
* documentation web app. This will include everything the app needs such as
* the documentation data, compiled partials, dustjs engine, etc...
*
* <blockquote>An instance of this object is globally accessible within the generated SPA
* as <code>docma</code>. Note that the size of the `docma-web.js` script depends primarily
* on the generated documentation data.</blockquote>
*
* @class
* @name DocmaWeb
* @hideconstructor
* @emits DocmaWeb~event:ready
* @emits DocmaWeb~event:render
* @emits DocmaWeb~event:route
* @emits DocmaWeb~event:navigate
*/
function DocmaWeb(data) {
this._ = data || {};
// Flag for page load. Used for triggering the "ready" event only for page
// load and not for route changes.
this._.initialLoad = false;
// app entrance optionally set @ build-time
this._.appEntranceRI = null;
this._.emitter = new EventEmitter();
/**
* Provides configuration data of the generated SPA, which is originally set
* at build-time, by the user.
* See {@link api/#Docma~BuildConfiguration|build configuration} for more
* details on how these settings take affect.
* @name DocmaWeb#app
* @type {Object}
*
* @property {String} title
* Document title for the main file of the generated app.
* (Value of the `<title/>` tag.)
* @property {Array} meta
* Array of arbitrary objects set for main document meta (tags).
* @property {String} base
* Base path of the generated web app.
* @property {String} entrance
* Name of the initial content displayed, when the web app is first
* loaded.
* @property {String|Object} routing
* Routing settings for the generated SPA.
* @property {String} server
* Server/host type of the generated SPA.
*/
Object.defineProperty(this, 'app', {
configurable: false,
get: function () {
return this._.app || null;
}
});
/**
* Hash-map of JSDoc documentation outputs.
* Each key is the name of an API (formed by grouped Javascript files).
* e.g. `docma.apis["some-api"]`
*
* Unnamed documentation data (consisting of ungrouped Javascript files) can be
* accessed via `docma.apis._def_`.
*
* Each value is an `Object` with the following signature:
* `{ documentation:Array, symbols:Array }`. `documentation` is the actual
* JSDoc data, and `symbols` is a flat array of symbol names.
*
* <blockquote>See {@link api/#Docma~BuildConfiguration|build configuration} for more
* details on how Javascript files can be grouped (and named) to form separate
* API documentations and SPA routes.</blockquote>
*
* @name DocmaWeb#apis
* @type {Object}
*
* @example <caption>Programmatic access to documentation data</caption>
* // output ungrouped (unnamed) API documentation data
* console.log(docma.apis._def_.documentation);
* console.log(docma.apis._def_.symbols); // flat list of symbol names
* // output one of the grouped (named) API documentation data
* console.log(docma.apis['my-scondary-api'].documentation);
*
* @example <caption>Usage in a Dust partial</caption>
* <!--
* Each API data is passed to the partial, according to the route.
* So you'll always use `documentation` within the partials.
* -->
* {#documentation}
* <h4>{longname}</h4>
* <p>{description}</p>
* <hr />
* {/documentation}
*/
Object.defineProperty(this, 'apis', {
configurable: false,
get: function () {
return this._.apis || {};
}
});
/**
* Array of available SPA routes of the documentation.
* This is created at build-time and defined via the `src` param of the
* {@link api/#Docma~BuildConfiguration|build configuration}.
*
* @name DocmaWeb#routes
* @type {Array}
*
* @see {@link #DocmaWeb.Route|`DocmaWeb.Route`}
*/
Object.defineProperty(this, 'routes', {
configurable: false,
get: function () {
return this._.routes || {};
}
});
/**
* Provides template specific configuration data.
* This is also useful within the Dust partials of the Docma template.
* @name DocmaWeb#template
* @type {Object}
*
* @property {Object} options - Docma template options. Defined at build-time,
* by the user.
* @property {String} name
* Name of the Docma template.
* @property {String} version
* Version of the Docma template.
* @property {String} author
* Author information for the Docma template.
* @property {String} license
* License information for the Docma template.
* @property {String} mainHTML
* Name of the main file of the template. i.e. `index.html`
*
* @example <caption>Usage in a Dust partial</caption>
* <div>
* {?template.options.someOption}
* <span>Displayed if someOption is true.</span>
* {/template.options.someOption}
* </div>
* <div class="footer">{template.name} by {template.author}</div>
*/
Object.defineProperty(this, 'template', {
configurable: false,
get: function () {
return this._.template || {};
}
});
// --------------------------------
// DOCMA-WEB STATE
// --------------------------------
/**
* Similar to `window.location` but with differences and additional
* information.
*
* @name DocmaWeb#location
* @type {Object}
* @readonly
*
* @property {String} origin
* Gets the protocol, hostname and port number of the current URL.
* @property {String} host
* Gets the hostname and port number of the current URL.
* @property {String} hostname
* Gets the domain name of the web host.
* @property {String} protocol
* Gets the web protocol used, without `:` suffix.
* @property {String} href
* Gets the href (URL) of the current location.
* @property {String} entrance
* Gets the application entrance route, which is set at Docma build-time.
* @property {String} base
* Gets the base path of the application URL, which is set at Docma build-time.
* @property {String} fullpath
* Gets the path and filename of the current URL.
* @property {String} pathname
* Gets the path and filename of the current URL, without the base.
* @property {String} path
* Gets the path, filename and query-string of the current URL, without the base.
* @property {String} hash
* Gets the anchor `#` of the current URL, without `#` prefix.
* @property {String} query
* Gets the querystring part of the current URL, without `?` prefix.
* @property {Function} getQuery()
* Gets the value of the given querystring parameter.
*/
Object.defineProperty(this, 'location', {
configurable: false,
get: function () {
var fullpath = Utils._ensureSlash(true, window.location.pathname, true),
base = Utils._ensureSlash(true, docma.app.base, true),
pathname = fullpath;
if (fullpath.slice(0, base.length) === base) {
pathname = fullpath.slice(base.length - 1, fullpath.length);
}
return {
host: window.location.host,
hostname: window.location.hostname,
origin: window.location.origin,
port: window.location.port,
protocol: (window.location.protocol || '').replace(/:$/, ''),
entrance: Utils._ensureSlash(true, docma.app.entrance, false),
base: base,
hash: (window.location.hash || '').replace(/^#/, ''),
query: (window.location.search || '').replace(/^\?/, ''),
href: window.location.href,
fullpath: fullpath,
pathname: pathname,
path: pathname + (window.location.search || ''),
getQuery: function (name, query) {
// Modified from http://stackoverflow.com/a/901144/112731
query = query === undefined ? (window.location.search || '') : query;
if (query.slice(0, 1) === '?') query = query.slice(1);
name = (name || '').replace(/[[\]]/g, '\\$&');
var regex = new RegExp('&?' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(query);
if (!results || !results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
};
}
});
/**
* Gets the route information for the current rendered content being
* displayed.
*
* @name DocmaWeb#currentRoute
* @type {DocmaWeb.Route}
* @readonly
*
* @property {String} type
* Type of the current route. If a generated JSDoc API
* documentation is being displayed, this is set to `"api"`.
* If any other HTML content (such as a converted markdown) is
* being displayed; this is set to `"content"`.
* @property {String} name
* Name of the current route. For `api` routes, this is the name
* of the grouped JS files parsed. If no name is given, this is
* set to `"_def_"` by default. For `content` routes, this is
* either the custom name given at build-time or, by default; the
* name of the generated HTML file; lower-cased, without the
* extension. e.g. `"README.md"` will have the route name
* `"readme"` after the build.
* @property {String} path
* Path of the current route.
*/
Object.defineProperty(this, 'currentRoute', {
configurable: false,
get: function () {
return this._.currentRoute || null;
}
});
/**
* JSDoc documentation data for the current API route.
* If current route is not an API route, this will be `null`.
*
* <blockquote>See {@link api/#Docma~BuildConfiguration|build configuration} for more
* details on how Javascript files can be grouped (and named) to form
* separate API documentations and SPA routes.</blockquote>
*
* @name DocmaWeb#documentation
* @type {Array}
*
* @example <caption>Output current API documentation data</caption>
* if (docma.currentRoute.type === 'api') {
* console.log(docma.documentation);
* }
*
* @example <caption>Usage in (Dust) partial</caption>
* {#documentation}
* <h4>{longname}</h4>
* <p>{description}</p>
* <hr />
* {/documentation}
*/
Object.defineProperty(this, 'documentation', {
configurable: false,
get: function () {
return this._.documentation || [];
}
});
/**
* A flat array of JSDoc documentation symbol names. This is useful for
* building menus, etc... If current route is not an API route, this will
* be `null`.
*
* <blockquote>See {@link api/docma#Docma~BuildConfiguration|build configuration} for more
* details on how Javascript files can be grouped (and named) to form
* separate API documentations and SPA routes.</blockquote>
*
* @name DocmaWeb#symbols
* @type {Array}
*
* @example <caption>Usage in (Dust) partial</caption>
* <ul class="menu">
* {#symbols}
* <li><a href="#{.}">{.}</a></li>
* {/symbols}
* </ul>
*/
Object.defineProperty(this, 'symbols', {
configurable: false,
get: function () {
return this._.symbols || [];
}
});
}
// --------------------------------
// EVENTS
// --------------------------------
/** @private */
DocmaWeb.prototype._trigger = function (eventName, args) {
this.info('Event:', eventName, args ? args[0] : '');
this._.emitter.trigger(eventName, args);
};
/**
* Fired when Docma is ready and the initial content is rendered.
* This is only fired once.
*
* @event DocmaWeb~event:ready
*
* @example
* docma.once('ready', function () {
* // do stuff...
* });
*/
/**
* Fired when page content (a Dust partial) is rendered. The emitted obeject is
* `currentRoute`. If the route does not exist (404), `currentRoute` will be
* `null`. This is fired after the `route` event.
*
* @event DocmaWeb~event:render
* @type {DocmaWeb.Route}
*
* @example
* docma.on('render', function (currentRoute) {
* if (currentRoute && currentRoute.type === docma.Route.Type.API) {
* // do stuff...
* }
* });
*/
/**
* Fired when SPA route is changed. The emitted obeject is `currentRoute`. If
* the route does not exist (404), `currentRoute` will be `null`. This is fired
* before the `render` event.
*
* @event DocmaWeb~event:route
* @type {DocmaWeb.Route}
*
* @example
* docma.on('route', function (currentRoute) {
* if (currentRoute && currentRoute.type === docma.Route.Type.API) {
* // do stuff...
* }
* });
*/
/**
* Fired either when the route is changed or navigated to a bookmark
* (i.e. on hash-change). If the route does not exist (404), `currentRoute`
* will be `null`.
*
* @event DocmaWeb~event:navigate
* @type {DocmaWeb.Route}
*
* @example
* docma.on('navigate', function (currentRoute) {
* if (currentRoute) {
* // do stuff...
* }
* });
*/
/**
* Docma SPA events enumeration.
* @enum {String}
*/
DocmaWeb.Event = {
/**
* Emitted when Docma is ready and the initial content is rendered.
* @type {String}
*/
Ready: 'ready',
/**
* Emitted when page content (a Dust partial) is rendered.
* @type {String}
*/
Render: 'render',
/**
* Emitted when SPA route is changed.
* @type {String}
*/
Route: 'route',
/**
* Emitted either when the route is changed or navigated to a
* bookmark (i.e. hashchange).
* @type {String}
*/
Navigate: 'navigate'
};
/**
* Adds a listener function to the specified event.
* Note that the listener will not be added if it is a duplicate.
* If the listener returns true then it will be removed after it is called.
* @name DocmaWeb#on
* @function
* @alias DocmaWeb#addListener
* @chainable
*
* @param {String} eventName
* Name of the event to attach the listener to.
* See {@link #DocmaWeb.Event|`DocmaWeb.Event`} enumeration.
* @param {Function} listener
* Function to be called when the event is emitted. If the function
* returns true then it will be removed after calling.
*
* @returns {DocmaWeb} - `DocmaWeb` instance for chaining.
*
* @example
* docma.on('render', function (currentRoute) {
* if (!currentRoute) {
* console.log('Not found!');
* return;
* }
* if (currentRoute.type === docma.Route.Type.API) {
* console.log('This is an API route.')
* }
* });
*/
DocmaWeb.prototype.on = function (eventName, listener) { // eslint-disable-line
this._.emitter.on.apply(this._.emitter, arguments);
return docma;
};
/**
* Adds a listener that will be automatically removed after its first
* execution.
* @name DocmaWeb#once
* @function
* @alias DocmaWeb#addOnceListener
* @chainable
*
* @param {String} eventName
* Name of the event to attach the listener to.
* See {@link #DocmaWeb.Event|`DocmaWeb.Event`} enumeration.
* @param {Function} listener
* Function to be called when the event is emitted.
*
* @returns {DocmaWeb} - `DocmaWeb` instance for chaining.
*
* @example
* docma.once('ready', function () {
* console.log('Docma is ready!');
* });
*/
DocmaWeb.prototype.once = function () {
this._.emitter.once.apply(this._.emitter, arguments);
return this;
};
/**
* Removes the given listener from the specified event.
* @name DocmaWeb#off
* @function
* @alias DocmaWeb#removeListener
* @chainable
*
* @param {String} eventName
* Name of the event to remove the listener from.
* See {@link #DocmaWeb.Event|`DocmaWeb.Event`} enumeration.
* @param {Function} listener
* Function to be removed from the event.
*
* @returns {DocmaWeb} - `DocmaWeb` instance for chaining.
*/
DocmaWeb.prototype.off = function () {
this._.emitter.off.apply(this._.emitter, arguments);
return this;
};
/**
* Alias for `DocmaWeb#on`
* @private
*/
DocmaWeb.prototype.addListener = DocmaWeb.prototype.on;
/**
* Alias for `DocmaWeb#once`
* @private
*/
DocmaWeb.prototype.addListenerOnce = DocmaWeb.prototype.once;
/**
* Alias for `DocmaWeb#off`
* @private
*/
DocmaWeb.prototype.removeListener = DocmaWeb.prototype.off;
// --------------------------------
// DEBUG / LOGS
// --------------------------------
/**
* Outputs a general log to the browser console. (Unlike `console.log()`) this
* method respects `debug` option of Docma build configuration.
* @param {...*} [args=""] - Arguments to be logged.
*/
DocmaWeb.prototype.log = function () {
if (!docma._.logsEnabled) return;
console.log.apply(console, arguments);
};
/**
* Outputs an informational log to the browser console. (Unlike
* `console.info()`) this method respects `debug` option of Docma build
* configuration.
* @param {...*} [args=""] - Arguments to be logged.
*/
DocmaWeb.prototype.info = function () {
if (!docma._.logsEnabled) return;
console.info.apply(console, arguments);
};
/**
* Outputs a warning log to the browser console. (Unlike `console.warn()`) this
* method respects `debug` option of Docma build configuration.
* @param {...*} [args=""] - Arguments to be logged.
*/
DocmaWeb.prototype.warn = function () {
if (!docma._.logsEnabled) return;
console.warn.apply(console, arguments);
};
/**
* Outputs an error log to the browser console. (Unlike `console.error()`) this
* method respects `debug` option of Docma build configuration.
* @param {...*} [args=""] - Arguments to be logged.
*/
DocmaWeb.prototype.error = function () {
if (!docma._.logsEnabled) return;
console.error.apply(console, arguments);
};
// --------------------------------
// DOM
// --------------------------------
/**
* Gets Docma main DOM element which the Dust templates will be rendered
* into.
*
* @returns {HTMLElement} - Docma main DOM element.
*/
DocmaWeb.prototype.getDocmaElem = function () {
var docmaElem = document.getElementById(this._.elementID);
if (!docmaElem) {
docmaElem = Utils.DOM.createChild(document.body, 'div', {
id: this._.elementID
});
}
return docmaElem;
};
/**
* Gets Docma content DOM element that the HTML content will be loaded
* into. This should be called for `docma-content` partial.
*
* @returns {HTMLElement} - Docma content DOM element.
*/
DocmaWeb.prototype.getContentElem = function () {
// docma-content template (should) have a
// <div id="docma-content"></div> element whithin.
var dContent = document.getElementById(this._.contentElementID);
if (!dContent) {
// this is fatal, so we always throw if invalid content partial
// TODO: this should be checked during build process
throw new Error('Partial ' + this._.partials.content + ' should have an element with id="' + this._.contentElementID + '".');
}
return dContent;
};
/**
* Loads the given HTML content into `docma-content` element. This is a
* low-level method. Typically you would not need to use this.
*
* @param {String} html - Content to be loaded.
*/
DocmaWeb.prototype.loadContent = function (html) {
var dContent = this.getContentElem();
dContent.innerHTML = html;
// If this is a parsed HTML file that is loaded as content; it might
// include some styles within the body. We'll move them to head. But
// first, remove if there are any previously moved styles in the head.
Utils.DOM._removePrevBodyStyles();
// now move the styles within the current rendered body.
Utils.DOM._moveBodyStylesToHead();
// this._fixAnchors();
Utils.DOM.scrollTo(); // top
};
/**
* Loads dust-compiled HTML content into `docma-main` element.
* @private
*
* @param {String} compiledHTML - Dust-compiled HTML content.
*/
DocmaWeb.prototype._loadCompiledContent = function (compiledHTML) {
// load compiled content into <div id="docma-main"></div>
var docmaElem = this.getDocmaElem();
docmaElem.innerHTML = compiledHTML;
// this._fixAnchors();
};
/**
* Fixes the base+hash issue. When base tag is set in the head of an HTML,
* bookmark anchors will navigate to the base URL with a hash; even with
* sub paths. This will fix that behaviour.
* @private
*
* @param {Function} cb - Callback.
*
* @returns {void}
*/
DocmaWeb.prototype._fixAnchors = function (cb) {
if (this.app.base) {
setTimeout(function () {
var i, el,
nodes = document.querySelectorAll('a[href^="#"]');
for (i = 0; i < nodes.length; i++) {
el = nodes[i];
var href = el.getAttribute('href');
if (href.slice(0, 1) === '#' && href.length > 1) {
href = window.location.pathname + (window.location.search || '') + href;
el.setAttribute('href', href);
}
}
if (typeof cb === 'function') cb();
}, 50);
}
};
// --------------------------------
// DUST FILTERS
// --------------------------------
/**
* Adds a new Dust filter.
* @chainable
* @see {@link templates/filters/|Existing Docma (Dust) filters}
* @see {@link http://www.dustjs.com/docs/filter-api|Dust Filter API}
*
* @param {String} name - Name of the filter to be added.
* @param {Function} fn - Filter function.
*
* @returns {DocmaWeb} - `DocmaWeb` instance for chaining.
* @throws {Error} - If a filter with the given name already exists.
*/
DocmaWeb.prototype.addFilter = function (name, fn) {
if (this.filterExists(name)) {
throw new Error('Filter "' + name + '" already exists.');
}
dust.filters[name] = fn;
return this;
};
/**
* Removes an existing Dust filter.
* @chainable
* @param {String} name - Name of the filter to be removed.
* @returns {DocmaWeb} - `DocmaWeb` instance for chaining.
*/
DocmaWeb.prototype.removeFilter = function (name) {
delete dust.filters[name];
return this;
};
/**
* Checks whether a Dust filter with the given name already exists.
* @param {String} name - Name of the filter to be checked.
* @returns {Boolean} -
*/
DocmaWeb.prototype.filterExists = function (name) {
return typeof dust.filters[name] === 'function';
};
// --------------------------------
// ROUTES
// --------------------------------
/**
* Creates a SPA route information object for the given route name and type.
*
* @param {String} name
* Name of the route.
* @param {String} type
* Type of the SPA route. See {@link #DocmaWeb.Route.Type|`DocmaWeb.Route.Type`}
* enumeration for possible values.
*
* @returns {DocmaWeb.Route} - Route instance.
*/
DocmaWeb.prototype.createRoute = function (name, type) {
return new DocmaWeb.Route(this, name, type);
};
/**
* Get route information object from the given route ID.
* @private
*
* @param {String} id - ID of the route (in `type:name` format).
*
* @returns {DocmaWeb.Route} - Route instance.
*/
DocmaWeb.prototype.createRouteFromID = function (id) {
if (typeof id !== 'string') {
this.warn('Route ID is not a string: ' + id);
return new DocmaWeb.Route(this, null);
}
var s = id.split(':');
return new DocmaWeb.Route(this, s[1], s[0]); // name, type
};
/**
* Get route information object from the given query-string.
* @private
*
* @param {String} querystring - Query-string.
*
* @returns {DocmaWeb.Route} - Route instance.
*/
DocmaWeb.prototype.createRouteFromQuery = function (querystring) {
if (!querystring) return new DocmaWeb.Route(null);
// get the first key=value pair
var query = querystring.split('&')[0].split('='),
routeType = query[0].toLowerCase(), // "api" or "content"
routeName = query[1];
return new DocmaWeb.Route(this, routeName, routeType);
};
// --------------------------------
// DUST / RENDER
// --------------------------------
/**
* Renders the given Dust template into the docma main element.
* @private
*
* @param {String} dustTemplateName
* Name of the Dust template.
* @param {Function} [callback]
* Function to be executed when the rendering is complete.
*/
DocmaWeb.prototype._render = function (dustTemplateName, callback) {
var self = this;
// render docma main template
dust.render(dustTemplateName, this, function (err, compiledHTML) {
if (err) {
self.warn('Could not load Docma partial:', dustTemplateName);
self.log('Compiled HTML: ', compiledHTML);
throw err;
}
self._loadCompiledContent(compiledHTML);
if (typeof callback === 'function') callback();
});
};
/**
* Triggers "render" event and checks if now is the time to also trigger
* "ready" event.
* @private
*/
DocmaWeb.prototype._triggerAfterRender = function () {
this._trigger(DocmaWeb.Event.Render, [docma.currentRoute]);
if (this._.initialLoad) {
this._trigger(DocmaWeb.Event.Ready);
this._.initialLoad = false;
}
};
/**
* Renders docma-404 partial. Used for not-found routes.
* @private
* @param {Object} routeInfo -
* @param {Function} statusCallback -
*/
DocmaWeb.prototype._render404 = function (routeInfo, statusCallback) {
this._.currentRoute = this.createRoute(null);
var self = this;
this._render(this._.partials.notFound, function () {
self._trigger(DocmaWeb.Event.Render, [null]);
Utils.DOM.scrollTo();
if (typeof statusCallback === 'function') return statusCallback(404);
// no callback, throw...
throw new Error('Page or content not found for route: ' + Utils._safeStringify(routeInfo));
});
};
/**
* Asynchronously fetches (text) content from the given URL via an
* `XmlHttpRequest`. Note that the URL has to be in the same-origin, for
* this to work.
*
* @param {String} url
* URL to be fetched.
* @param {Function} callback
* Function to be executed when the content is fetched; with the
* following signature: `function (status, responseText) { .. }`
*/
DocmaWeb.prototype.fetch = function (url, callback) {
var xhr = new XMLHttpRequest();
var self = this;
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
var text = xhr.status === 200 ? xhr.responseText : '';
self.log('XHR GET:', xhr.status, url);
return callback(xhr.status, text);
}
};
xhr.open('GET', url, true); // async
xhr.send();
};
/**
* Renders content into docma-main element, by the given route information.
*
* If the content is empty or `"api"`, we'll render the `docma-api` Dust
* template. Otherwise, (e.g. `"readme"`) we'll render `docma-content` Dust
* template, then fetch `content/readme.html` and load it in the `docma-main`
* element.
*
* <blockquote>Note that rendering and the callback will be cancelled if the given
* content is the latest content rendered.</blockquote>
*
* @param {DocmaWeb.Route} routeInfo - Route information of the page to be
* rendered.
* @param {Function} [callback] - Function to be executed when the rendering is
* complete. `function (httpStatus:Number) { .. }`
* @returns {void}
* @emits DocmaWeb~event:render
*/
DocmaWeb.prototype.render = function (routeInfo, callback) {
// if no route info, render not-found partial (docma-404)
if (!routeInfo || !routeInfo.exists()) return this._render404(routeInfo, callback);
// return if same route
if (routeInfo.isEqualTo(this.currentRoute)) return;
// set current route
this._.currentRoute = routeInfo;
var isCbFn = typeof callback === 'function';
var self = this;
if (routeInfo.type === DocmaWeb.Route.Type.API) {
this._render(this._.partials.api, function () {
self._triggerAfterRender();
if (isCbFn) callback(200);
self._fixAnchors(function () {
Utils.DOM.scrollTo();
});
});
} else { // if (routeInfo.type === Route.Type.CONTENT) {
docma.fetch(routeInfo.contentPath, function (status, html) {
if (status === 404) return self._render404(routeInfo, callback);
// rendering docma-content Dust template
self._render(self._.partials.content, function () {
self.loadContent(html);
self._triggerAfterRender();
if (isCbFn) callback(status);
self._fixAnchors(function () {
Utils.DOM.scrollTo();
});
});
});
}
};
// --------------------------------
// UTILS
// --------------------------------
/**
* Utilities for inspecting JSDoc documentation and symbols; and parsing
* documentation data into proper HTML.
* See {@link api/web/utils|`DocmaWeb.Utils` documentation}.
* @type {Object}
* @namespace
*/
DocmaWeb.Utils = Utils;