Skip to content

Commit 65eda42

Browse files
authored
Merge pull request #9 from bit-docs/refactor
Generate nested TOC based on heading hierarchy
2 parents c5286fc + 70eac06 commit 65eda42

File tree

9 files changed

+225
-277
lines changed

9 files changed

+225
-277
lines changed

.editorconfig

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@
33
end_of_line = LF
44
indent_style = tab
55
trim_trailing_whitespace = false
6-
insert_final_newline = true
6+
insert_final_newline = true
7+
8+
[{package.json,*.yml}]
9+
indent_style = space
10+
indent_size = 2

.jshintrc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"curly": true,
3+
"eqeqeq": true,
4+
"freeze": true,
5+
"noarg": true,
6+
"undef": true,
7+
"unused": "vars",
8+
"trailing": true,
9+
"maxdepth": 4,
10+
"boss" : true,
11+
12+
"esnext": true,
13+
"eqnull": true,
14+
"evil": true,
15+
"loopfunc": true,
16+
"smarttabs": true,
17+
"maxerr" : 200,
18+
19+
"browser": true,
20+
"phantom": true,
21+
"node": true,
22+
23+
"globals": {
24+
"describe": true,
25+
"it": true
26+
}
27+
}

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: node_js
2+
node_js: node
3+
before_install:
4+
- "export DISPLAY=:99.0"
5+
- "sh -e /etc/init.d/xvfb start"

bit-docs.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
var path = require("path");
21
var tags = require("./tags");
32

4-
module.exports = function(bitDocs){
3+
module.exports = function(bitDocs) {
54
var pkg = require("./package.json");
5+
66
var dependencies = {};
77
dependencies[pkg.name] = pkg.version;
88

@@ -11,4 +11,4 @@ module.exports = function(bitDocs){
1111
});
1212

1313
bitDocs.register("tags", tags);
14-
}
14+
};

make-tree.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
module.exports = function makeTree(elements) {
2+
var treeData = makeTreeData(elements || []);
3+
var rootNode = { level: 0, children: [] };
4+
5+
var tree = rootNode.children;
6+
var lastNode;
7+
8+
treeData.forEach(function(node) {
9+
node.children = [];
10+
11+
if (!lastNode) {
12+
tree.push(node);
13+
}
14+
else if (node.level === lastNode.level) {
15+
findParentNode(lastNode, rootNode).children.push(node);
16+
}
17+
else if (node.level > lastNode.level) {
18+
lastNode.children.push(node);
19+
}
20+
else {
21+
findAncestorOfLevel(node.level, lastNode, rootNode).children.push(node);
22+
}
23+
24+
lastNode = node;
25+
});
26+
27+
return tree;
28+
};
29+
30+
function findParentNode(needle, haystack) {
31+
var parent;
32+
33+
if (haystack.children.indexOf(needle) !== -1) {
34+
return haystack;
35+
}
36+
37+
for (var i = 0; i < haystack.children.length; i += 1) {
38+
parent = findParentNode(needle, haystack.children[i]);
39+
if (parent) { break; }
40+
}
41+
42+
return parent;
43+
}
44+
45+
function findAncestorOfLevel(level, needle, haystack) {
46+
var parent = findParentNode(needle, haystack);
47+
48+
return (parent.level < level) ?
49+
parent :
50+
findAncestorOfLevel(level, parent, haystack);
51+
}
52+
53+
function makeHeadingId(text) {
54+
return (text || "")
55+
.replace(/\s/g, "-") // replace spaces with dashes
56+
.replace(/[^\w\-]/g, "") // remove punctuation
57+
.toLowerCase();
58+
}
59+
60+
function makeTreeData(elements) {
61+
var ids = {};
62+
var map = [].map;
63+
64+
return map.call(elements, function(element) {
65+
var text = element.textContent;
66+
var id = makeHeadingId(text);
67+
var level = getElementLevel(element);
68+
69+
// generate unique id for elements with same text
70+
var count = ids[id] || 0;
71+
var uniq = count > 0 ? id + "-" + count : id;
72+
ids[id] = count + 1;
73+
74+
return { id: uniq, text: text, level: level };
75+
});
76+
}
77+
78+
function getElementLevel(element) {
79+
var defaultLevel = 2;
80+
var tagName = element.tagName.toLowerCase();
81+
var headingTagNames = ["h1", "h2", "h3", "h4", "h5", "h6"];
82+
83+
if (headingTagNames.indexOf(tagName) !== -1) {
84+
return parseInt(tagName.slice(1), 10);
85+
}
86+
87+
// default value if not a heading tag
88+
return defaultLevel;
89+
}

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": "table of contents bit-docs plugin",
55
"main": "toc.js",
66
"scripts": {
7-
"test": "echo 'testing'",
7+
"jshint": "jshint *.js --config .jshintrc",
8+
"test": "npm run jshint && mocha --reporter spec",
89
"postversion": "git push --tags && git push",
910
"release:pre": "npm version prerelease && npm publish",
1011
"release:patch": "npm version patch && npm publish",
@@ -13,21 +14,23 @@
1314
},
1415
"repository": {
1516
"type": "git",
16-
"url": "git+https://github.com/bit-docs/bit-docs-prettify.git"
17+
"url": "git+https://github.com/bit-docs/bit-docs-html-toc.git"
1718
},
1819
"keywords": [
1920
"bit-docs"
2021
],
2122
"author": "Bitovi",
2223
"license": "MIT",
2324
"bugs": {
24-
"url": "https://github.com/bit-docs/bit-docs-prettify/issues"
25+
"url": "https://github.com/bit-docs/bit-docs-html-toc/issues"
2526
},
26-
"homepage": "https://github.com/bit-docs/bit-docs-prettify#readme",
27+
"homepage": "https://github.com/bit-docs/bit-docs-html-toc#readme",
2728
"system": {},
2829
"dependencies": {
2930
"can-control": "^3.0.0-pre.5",
30-
"can-stache": "^3.0.0-pre.11",
31-
"can-stache-bindings": "^3.0.0-pre.8"
31+
"can-stache": "^3.0.0-pre.11"
32+
},
33+
"devDependencies": {
34+
"mocha": "^3.1.2"
3235
}
3336
}

test.js

Lines changed: 0 additions & 92 deletions
This file was deleted.

test/make-tree-test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
var assert = require("assert");
2+
var makeTree = require("../make-tree");
3+
4+
describe("makeTree", function() {
5+
it("makes flat trees based on heading tag names", function() {
6+
var elements = [
7+
{ tagName: "h2", textContent: "Usage" },
8+
{ tagName: "h2", textContent: "Install" },
9+
{ tagName: "h2", textContent: "Configure" },
10+
{ tagName: "h2", textContent: "Configure" }
11+
];
12+
13+
assert.deepEqual(makeTree(elements), [
14+
{ id: "usage", level: 2, text: "Usage", children: [] },
15+
{ id: "install", level: 2, text: "Install", children: [] },
16+
{ id: "configure", level: 2, text: "Configure", children: [] },
17+
{ id: "configure-1", level: 2, text: "Configure", children: [] }
18+
]);
19+
});
20+
21+
it("nests trees based on headings hierarchy", function() {
22+
var elements = [
23+
{ tagName: "h2", textContent: "Bower" },
24+
{ tagName: "h3", textContent: "Install" },
25+
{ tagName: "h2", textContent: "NPM" },
26+
{ tagName: "h3", textContent: "Install" },
27+
{ tagName: "h4", textContent: "Specifying components folder" },
28+
{ tagName: "h2", textContent: "Writing Modules" }
29+
];
30+
31+
assert.deepEqual(makeTree(elements), [
32+
{ id: "bower",
33+
level: 2,
34+
text: "Bower",
35+
children: [
36+
{ id: "install", level: 3, text: "Install", children: [] }
37+
]
38+
},
39+
{ id: "npm",
40+
level: 2,
41+
text: "NPM",
42+
children: [
43+
{ id: "install-1", level: 3, text: "Install",
44+
children: [
45+
{ id: "specifying-components-folder",
46+
level: 4,
47+
text: "Specifying components folder",
48+
children: []
49+
}
50+
]
51+
}
52+
]
53+
},
54+
{ id: "writing-modules", level: 2, text: "Writing Modules", children: [] }
55+
]);
56+
});
57+
});

0 commit comments

Comments
 (0)