From e8cc85f733a49ca53e8cda5a96bbaacc9a20ac7e Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Thu, 17 Oct 2013 14:16:32 -0700 Subject: chore(docs): generate header ids for better linking - generate ids for all headers - collect defined anchors - check broken links (even if the page exists, but the anchor/id does not) --- docs/src/dom.js | 73 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 17 deletions(-) (limited to 'docs/src/dom.js') diff --git a/docs/src/dom.js b/docs/src/dom.js index 897a1831..e696faf4 100644 --- a/docs/src/dom.js +++ b/docs/src/dom.js @@ -4,6 +4,7 @@ exports.DOM = DOM; exports.htmlEscape = htmlEscape; +exports.normalizeHeaderToId = normalizeHeaderToId; ////////////////////////////////////////////////////////// @@ -16,10 +17,36 @@ function htmlEscape(text){ .replace(/\}\}/g, '}}'); } +function nonEmpty(header) { + return !!header; +} + +function idFromCurrentHeaders(headers) { + if (headers.length === 1) return headers[0]; + // Do not include the first level title, as that's the title of the page. + return headers.slice(1).filter(nonEmpty).join('_'); +} + +function normalizeHeaderToId(header) { + if (typeof header !== 'string') { + return ''; + } + + return header.toLowerCase() + .replace(/<.*>/g, '') // html tags + .replace(/[\!\?\:\.\']/g, '') // special characters + .replace(/&#\d\d;/g, '') // html entities + .replace(/\(.*\)/mg, '') // stuff in parenthesis + .replace(/\s$/, '') // trailing spaces + .replace(/\s+/g, '-'); // replace whitespaces with dashes +} + function DOM() { this.out = []; this.headingDepth = 0; + this.currentHeaders = []; + this.anchors = []; } var INLINE_TAGS = { @@ -44,17 +71,28 @@ DOM.prototype = { }, html: function(html) { - if (html) { - var headingDepth = this.headingDepth; - for ( var i = 10; i > 0; --i) { - html = html - .replace(new RegExp('([\\s\\S]+)<\/h' + i +'>', 'gm'), function(_, attrs, content){ - var tag = 'h' + (i + headingDepth); - return '<' + tag + attrs + '>' + content + ''; - }); - } - this.out.push(html); - } + if (!html) return; + + var self = this; + // rewrite header levels, add ids and collect the ids + html = html.replace(/([\s\S]+?)<\/h\1>/gm, function(_, level, attrs, content) { + level = parseInt(level, 10) + self.headingDepth; // change header level based on the context + + self.currentHeaders[level - 1] = normalizeHeaderToId(content); + self.currentHeaders.length = level; + + var id = idFromCurrentHeaders(self.currentHeaders); + self.anchors.push(id); + return '' + content + ''; + }); + + // collect anchors + html = html.replace(//g, function(match, anchor) { + self.anchors.push(anchor); + return match; + }); + + this.out.push(html); }, tag: function(name, attr, text) { @@ -85,17 +123,18 @@ DOM.prototype = { h: function(heading, content, fn){ if (content==undefined || (content instanceof Array && content.length == 0)) return; + this.headingDepth++; + this.currentHeaders[this.headingDepth - 1] = normalizeHeaderToId(heading); + this.currentHeaders.length = this.headingDepth; + var className = null, anchor = null; if (typeof heading == 'string') { - var id = heading. - replace(/\(.*\)/mg, ''). - replace(/[^\d\w\$]/mg, '.'). - replace(/-+/gm, '-'). - replace(/-*$/gm, ''); + var id = idFromCurrentHeaders(this.currentHeaders); + this.anchors.push(id); anchor = {'id': id}; - var classNameValue = id.toLowerCase().replace(/[._]/mg, '-'); + var classNameValue = this.currentHeaders[this.headingDepth - 1] if(classNameValue == 'hide') classNameValue = ''; className = {'class': classNameValue}; } -- cgit v1.2.3