From 0c48e8481907f27ce024d473ff737ed5ed0d490d Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Thu, 20 Oct 2011 16:34:04 -0700 Subject: [PATCH 01/33] [refactor] Initial support for metadata.json for directory views. --- lib/generators/article.js | 14 ++++++++++++++ lib/generators/directories.js | 10 ++++++++++ lib/helpers.js | 6 +++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/generators/article.js b/lib/generators/article.js index b07bdef..5baa6ab 100644 --- a/lib/generators/article.js +++ b/lib/generators/article.js @@ -137,6 +137,20 @@ article.load = function (resolve) { // load all articles var articles = fs2.readDirSync(docs.src, true, function (p) { + if (path.existsSync(p) && path.basename(p) === "metadata.json") { + // try to read the path as though it's json + try { + var md = JSON.parse(fs.readFileSync(p).toString()); + // filter if generator is not article. + var thing = !(md.generator && md.generator !== "article"); + return thing; + } catch (e) { + console.log("error reading "+p+"; skipping."); + return false; + } + } + + // No directories. return !fs.statSync(p).isDirectory(); }); diff --git a/lib/generators/directories.js b/lib/generators/directories.js index 363718e..ddc5d05 100644 --- a/lib/generators/directories.js +++ b/lib/generators/directories.js @@ -98,15 +98,25 @@ dir.generate = function(output, dirs) { }; // This returns a hash of the form { path: [children] } +// TODO: port to use fs2.js dir.load = function() { + // TODO: use/test docs.src var _dir = findit.sync('./pages'); var dir = {}; // Grab all folders that *only* have more folders + // or explicitly have a metadata.json that specifies + // "directory" _dir.filter(function(dir){ if (fs.statSync(dir).isDirectory()) { + + // check to see if the option is explicitly set + if (path.existsSync(dir+"/metadata.json") && fs.statSync(dir+"/metadata.json").isFile()) { + return JSON.parse(fs.readFileSync(dir+"/metadata.json").toString()).generator === "directory"; + } + return fs.readdirSync(dir).every(function(p) { var isDir = fs.statSync(dir+"/"+p).isDirectory(); if (!isDir && path.extname(p).length === 0) { diff --git a/lib/helpers.js b/lib/helpers.js index b42f71c..edc067a 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -149,7 +149,11 @@ helpers.articlesToObject = function (src, files) { articles[pathName] = {}; } - articles[pathName].metadata = JSON.parse(files[f]); + try { + articles[pathName].metadata = JSON.parse(files[f]); + } catch (e) { + throw new Error("Failed to parse \""+files[f]+"\""); + } articles[pathName].metadata.link = pathName; articles[pathName].metadata.breadcrumb = pathName.replace(path.dirname(src), "").split("/"); From ce857feb0d0a5ffa5878c95d4b1e98ab79809ce9 Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Thu, 20 Oct 2011 17:01:21 -0700 Subject: [PATCH 02/33] [refactor] Front page now uses 'articles' generator --- README.md | 2 -- lib/docs.js | 1 - lib/generators/article.js | 11 ++++++++--- pages/article.md | 1 + pages/metadata.json | 14 ++++++++++++++ 5 files changed, 23 insertions(+), 6 deletions(-) create mode 120000 pages/article.md create mode 100644 pages/metadata.json diff --git a/README.md b/README.md index 1c4c6c4..104eee5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -# docs - http://docs.nodejitsu.com **community powered rocket-fuel for node.js** diff --git a/lib/docs.js b/lib/docs.js index f2dccb1..812cc6f 100644 --- a/lib/docs.js +++ b/lib/docs.js @@ -34,7 +34,6 @@ docs.generators = [ { theme : require('./generators/theme')}, { article : require('./generators/article')}, { directories : require('./generators/directories')}, - { home : require('./generators/home')}, { toc : require('./generators/toc')}, { errors : require('./generators/errors')} ]; diff --git a/lib/generators/article.js b/lib/generators/article.js index 5baa6ab..9a3f33f 100644 --- a/lib/generators/article.js +++ b/lib/generators/article.js @@ -64,8 +64,13 @@ article.weld = function(dom, articles) { //date semantics if ($(element).hasClass("date")) { - $(element).attr("datetime", (new Date(val)).toISOString()); - $(element).text(val); + var date = val ? new Date(val).toISOString() : undefined; + if (val) { + $(element).attr("datetime", new Date(val).toISOString()); + $(element).text(val); + } else { + //TODO: If there isn't any metadata, we shouldn't have the "by x on y" div. + } return false; } @@ -89,7 +94,7 @@ article.weld = function(dom, articles) { $('title', dom).html('node docs - ' + metadata.title); //Add some meta tags - $('meta[name=keywords]', dom).attr('content', metadata.tags.concat(docs.config.get("tags") || []).join(',')); + $('meta[name=keywords]', dom).attr('content', (metadata.tags || []).concat(docs.config.get("tags") || []).join(',')); // // Remark: perform code highlighting, convert only inside diff --git a/pages/article.md b/pages/article.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/pages/article.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/pages/metadata.json b/pages/metadata.json new file mode 100644 index 0000000..8c25895 --- /dev/null +++ b/pages/metadata.json @@ -0,0 +1,14 @@ +{ + "title":"Docs", + "date": "Thu Oct 20 16:56:11 PDT 2011", + "author": "Nodejitsu", + "difficulty": 1, + "index": false, + "order": { + "intermediate": -3, + "advanced": -2, + "cryptography": -1, + "getting-started": 0, + "javascript-conventions": 1 + } +} From 50fc121270bc9271604002a4707bb09609587f3c Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Fri, 21 Oct 2011 17:32:54 -0700 Subject: [PATCH 03/33] [refactor] Merged directory and content generators --- lib/docs.js | 3 +- lib/generators/article.js | 164 ------------------------- lib/generators/content.js | 222 ++++++++++++++++++++++++++++++++++ lib/generators/directories.js | 137 --------------------- lib/helpers.js | 89 +++++++++++++- 5 files changed, 311 insertions(+), 304 deletions(-) delete mode 100644 lib/generators/article.js create mode 100644 lib/generators/content.js delete mode 100644 lib/generators/directories.js diff --git a/lib/docs.js b/lib/docs.js index 812cc6f..159e5d3 100644 --- a/lib/docs.js +++ b/lib/docs.js @@ -32,8 +32,7 @@ docs.config = nconf; docs.generators = [ { authors : require('./generators/authors')}, { theme : require('./generators/theme')}, - { article : require('./generators/article')}, - { directories : require('./generators/directories')}, + { content : require('./generators/content')}, { toc : require('./generators/toc')}, { errors : require('./generators/errors')} ]; diff --git a/lib/generators/article.js b/lib/generators/article.js deleted file mode 100644 index 9a3f33f..0000000 --- a/lib/generators/article.js +++ /dev/null @@ -1,164 +0,0 @@ -var findit = require('findit'), - path = require('path'), - hl = require('../../vendor/highlight/lib/highlight').Highlight, - markdown = require('github-flavored-markdown'), - mkdirp = require('mkdirp'), - fs = require('fs'), - fs2 = require('../fs2'), - docs = require('../docs'), - weld = require('weld').weld, - helpers = require('../helpers'); - -var article = exports; - - -article.weld = function(dom, articles) { - - // - // Remark: Create a short-cut reference to the jQuery object - // - var $ = docs.window.$; - - - // Here, we build up the table of contents. - var toc = helpers.buildToc(docs.src); - - Object.keys(articles).forEach( function (i){ - var content; - try { - content = markdown.parse(articles[i].content.toString()); - } catch (err) { - content = err.message; - } - var metadata = articles[i].metadata; - //join the author metadata - metadata.author = docs.content.authors[metadata.author] || { name: metadata.author}; - - // This is a good place to try explicitly setting the doctype for the - // dom. However, validating the doctype before trying to assign it - // is hard, and it's basically a global setting because we weld all - // documents before writing them out. - dom.innerHTML = docs.content.theme['./theme/article.html'].toString(); - - var data = { - metadata: metadata, - content: content, - toc: toc - }; - - weld(dom, data, { - map: function(parent, element, key, val) { - // - // Create breadcrumb - // - if ($(element).hasClass('breadcrumb')) { - var crumb = ''; - $('.breadcrumb', parent).each(function(i,v){ - crumb += ('/' + $(v).html()); - }); - crumb += ('/' + val); - $(element).attr('href', crumb); - $(element).html(val); - return false - } - - //date semantics - if ($(element).hasClass("date")) { - var date = val ? new Date(val).toISOString() : undefined; - if (val) { - $(element).attr("datetime", new Date(val).toISOString()); - $(element).text(val); - } else { - //TODO: If there isn't any metadata, we shouldn't have the "by x on y" div. - } - return false; - } - - if ($(element).hasClass("github")) { - if (val) { - $(element).append( - $("").attr("href", "https://github.com/"+val).text("[github]") - ); - } - return false; - } - - element.innerHTML = val; - return false; - - } - - }); - - //Give the page a title - $('title', dom).html('node docs - ' + metadata.title); - - //Add some meta tags - $('meta[name=keywords]', dom).attr('content', (metadata.tags || []).concat(docs.config.get("tags") || []).join(',')); - - // - // Remark: perform code highlighting, convert only inside - // - // TODO: the current code highlighting is slow, very slow. - // - - // - // Remark: The hilighter tries to hilight ">" as - // - // `&gt;` - // - // There are probably a few other html identities that also get incorrectly highlighted (such as <). - // The easiest way to fix this, besides using a different highlighter, - // turns out to be running a greedy search/replace for the - // bungled highlight. - - dom.innerHTML = hl(dom.innerHTML, false, true) - .replace(new RegExp("&gt;", "g"), ">"); - - articles[i].content = dom.innerHTML; - - }); - return dom; -}; - -article.generate = function(output, articles) { - - // - // Generate all articles - // - Object.keys(articles).forEach(function(file){ - // TODO: Figure out why this is breaking!! - var newPath = file.replace(path.resolve(docs.src), path.resolve(docs.dst)); - newPath = path.dirname(newPath); - newPath = path.normalize(newPath + '/index.html'); - fs2.writeFile(newPath, articles[file].content, function(){}); - }); - - return articles; -}; - - -article.load = function (resolve) { - - // load all articles - var articles = fs2.readDirSync(docs.src, true, function (p) { - if (path.existsSync(p) && path.basename(p) === "metadata.json") { - // try to read the path as though it's json - try { - var md = JSON.parse(fs.readFileSync(p).toString()); - // filter if generator is not article. - var thing = !(md.generator && md.generator !== "article"); - return thing; - } catch (e) { - console.log("error reading "+p+"; skipping."); - return false; - } - } - - // No directories. - return !fs.statSync(p).isDirectory(); - }); - - return helpers.articlesToObject(docs.src, articles); - -}; diff --git a/lib/generators/content.js b/lib/generators/content.js new file mode 100644 index 0000000..528e8ac --- /dev/null +++ b/lib/generators/content.js @@ -0,0 +1,222 @@ +var findit = require('findit'), + path = require('path'), + hl = require('../../vendor/highlight/lib/highlight').Highlight, + markdown = require('github-flavored-markdown'), + mkdirp = require('mkdirp'), + fs = require('fs'), + fs2 = require('../fs2'), + docs = require('../docs'), + weld = require('weld').weld, + helpers = require('../helpers'); + +var articles = exports; + + +articles.weld = function(dom, pages) { + + // + // Remark: Create a short-cut reference to the jQuery object + // + var $ = docs.window.$; + + + // Here, we build up the table of contents. + var toc = helpers.buildToc(docs.src); + + Object.keys(pages).forEach( function (i){ + + // parse content if it exists + if (pages[i].content) { + var content; + try { + content = markdown.parse(pages[i].content.toString()); + } catch (err) { + content = err.message; + } + } + + var metadata = pages[i].metadata; + + //join the author metadata + if (metadata && metadata.author) { + metadata.author = docs.content.authors[metadata.author] || { name: metadata.author}; + } + + // Use article view if there's content + if (content) { + + dom.innerHTML = docs.content.theme['./theme/article.html'].toString(); + + var data = { + metadata: metadata, + content: content, + toc: toc + }; + + // something breaks here. + weld(dom, data, { + map: function(parent, element, key, val) { + + // + // Create breadcrumb + // + if ($(element).hasClass('breadcrumb')) { + var crumb = ''; + $('.breadcrumb', parent).each(function(i,v){ + crumb += ('/' + $(v).html()); + }); + crumb += ('/' + val); + $(element).attr('href', crumb); + $(element).html(val); + return false + } + + //Handle dates + if ($(element).hasClass("date")) { + var date = val ? new Date(val).toISOString() : undefined; + if (val) { + $(element).attr("datetime", new Date(val).toISOString()); + $(element).text(val); + } else { + //TODO: If there isn't any metadata, we shouldn't have the "by x on y" div. + } + return false; + } + + //Handle author metadata + if ($(element).hasClass("github")) { + if (val) { + $(element).append( + $("").attr("href", "https://github.com/"+val).text("[github]") + ); + } + return false; + } + + element.innerHTML = val; + return false; + + } + + }); + + // use directory view + } else { + dom.innerHTML = docs.content.theme['./theme/directory.html']; + + //set up the data + var data = { + pwd: helpers.unresolve(docs.src, i), + ls: pages[i].ls, + toc: toc, + metadata: metadata + }; + + weld(dom, data, { + map: function(parent, element, key, val) { + + if ($(element).attr("id") === "toc") { + element.innerHTML = val; + return false; + } + + if ($(element).hasClass("ls")) { + var title = val.split("/"); + title = title[title.length - 1].replace(/-/g, " "); + var listing = $("").attr("class", "ls").append( + $("").append( + $("").attr("href", val.replace("pages/", "")).text(title) + ) + ); + + $("tr", $(element)).replaceWith(listing); + return false; + } + + // + // Create breadcrumb + // + if ($(element).hasClass('breadcrumb')) { + var crumb = ''; + $('.breadcrumb', parent).each(function(i,v){ + crumb += ('/' + $(v).html()); + }); + crumb += ('/' + val); + $(element).attr('href', crumb); + $(element).html(val); + return false; + } + + return true; + + } + }); + } + + + + //Give the page a title + $('title', dom).html('node docs - ' + (metadata && metadata.title) || ""); + + //Add some meta tags + $('meta[name=keywords]', dom).attr('content', (metadata && metadata.tags || []).concat(docs.config.get("tags") || []).join(',')); + + // + // Remark: perform code highlighting, convert only inside + // + // TODO: the current code highlighting is slow, very slow. + // + + // + // Remark: The hilighter tries to hilight ">" as + // + // `&gt;` + // + // There are probably a few other html identities that also get incorrectly highlighted (such as <). + // The easiest way to fix this, besides using a different highlighter, + // turns out to be running a greedy search/replace for the + // bungled highlight. + + dom.innerHTML = hl(dom.innerHTML, false, true) + .replace(new RegExp("&gt;", "g"), ">"); + + pages[i].content = dom.innerHTML || docs.content.theme['./theme/article.html'].toString(); + + }); + return dom; +}; + +articles.generate = function(output, pages) { + + // + // Generate articles and directory pages + // + Object.keys(pages).forEach(function(file){ + var newPath = file.replace(path.resolve(docs.src), path.resolve(docs.dst)); + + //newPath = path.dirname(newPath); + newPath = path.normalize(newPath + '/index.html'); + fs2.writeFile(newPath, pages[file].content, function(){}); + }); + + return pages; +}; + + + +// Load all content with an fs2.readDirSync. It should be relatively indiscriminate. +articles.load = function () { + if (!docs.src) { + docs.src = "../../pages"; + } + + // load all the contents + // { "path": "content" } + var pages = fs2.readDirSync(docs.src, true); + + // do some conversion here + pages = helpers.dirToContent(docs.src, pages, true); + + return pages; + +}; diff --git a/lib/generators/directories.js b/lib/generators/directories.js deleted file mode 100644 index ddc5d05..0000000 --- a/lib/generators/directories.js +++ /dev/null @@ -1,137 +0,0 @@ -var docs = require('../docs'), - weld = require('weld').weld, - markdown = require('github-flavored-markdown'), - findit = require('findit'), - fs = require('fs'), - fs2 = require('../fs2'), - path = require('path'), - helpers = require('../helpers'); - -var dir = exports; - -dir.weld = function(dom, dirs) { - - var $ = docs.window.$; - - Object.keys(dirs).forEach(function(d) { - - //start with our theme - dom.innerHTML = docs.content.theme['./theme/directory.html'].toString(); - - // Here, we build up the table of contents. - var toc = helpers.buildToc(docs.src); - - - //set up the data - var data = { - pwd: d.replace("./pages", ""), - ls: dirs[d], - toc: toc, - metadata: { - breadcrumb: d.split("/").slice(2) - } - }; - - //weld it! - weld(dom, data, { - map: function(parent, element, key, val) { - - if ($(element).attr("id") === "toc") { - element.innerHTML = val; - return false; - } - - if ($(element).hasClass("ls")) { - var title = val.split("/"); - title = title[title.length - 1].replace(/-/g, " "); - var listing = $("").attr("class", "ls").append( - $("").append( - $("").attr("href", val.replace("pages/", "")).text(title) - ) - ); - - $("tr", $(element)).replaceWith(listing); - return false; - } - - // - // Create breadcrumb - // - if ($(element).hasClass('breadcrumb')) { - var crumb = ''; - $('.breadcrumb', parent).each(function(i,v){ - crumb += ('/' + $(v).html()); - }); - crumb += ('/' + val); - $(element).attr('href', crumb); - $(element).html(val); - return false; - } - - return true; - - } - }); - - //Attach the results to dirs - dirs[d].content = dom.innerHTML; - }); - - return dom; - -}; - - -dir.generate = function(output, dirs) { - - // - // Generate the dir views yo - // - Object.keys(dirs).forEach(function(file){ - var newPath = file.replace('./pages', './public'); - newPath = path.normalize(newPath + '/index.html'); - - fs2.writeFile(newPath, dirs[file].content, function(){}); - }); - - return dirs; -}; - -// This returns a hash of the form { path: [children] } -// TODO: port to use fs2.js -dir.load = function() { - // TODO: use/test docs.src - var _dir = findit.sync('./pages'); - - var dir = {}; - - // Grab all folders that *only* have more folders - // or explicitly have a metadata.json that specifies - // "directory" - _dir.filter(function(dir){ - - if (fs.statSync(dir).isDirectory()) { - - // check to see if the option is explicitly set - if (path.existsSync(dir+"/metadata.json") && fs.statSync(dir+"/metadata.json").isFile()) { - return JSON.parse(fs.readFileSync(dir+"/metadata.json").toString()).generator === "directory"; - } - - return fs.readdirSync(dir).every(function(p) { - var isDir = fs.statSync(dir+"/"+p).isDirectory(); - if (!isDir && path.extname(p).length === 0) { - } - return isDir; - }); - } else { - return false; - } - - }).forEach(function(d) { - dir[d] = fs.readdirSync(d).map(function(files) { - return path.resolve("/"+d+"/"+files).replace("./pages", ""); - }); - }); - - return dir; -}; diff --git a/lib/helpers.js b/lib/helpers.js index edc067a..c0dc800 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -4,7 +4,8 @@ var helpers = exports; -var fs = require('fs'), +var docs = require("./docs"), + fs = require('fs'), fs2 = require('./fs2'), findit = require('findit'), path = require('path'), @@ -177,3 +178,89 @@ helpers.articlesToObject = function (src, files) { return articles; } +// Takes a directory, does some slicing/dicing to consolidate information and handle defaults +helpers.dirToContent = function (src, files, resolve) { + var content = {}; + if (typeof resolve === "undefined") { + resolve = true; + } + + Object.keys(files).forEach(function (f) { + + // we want the paths to represent path-based resources, + // not individual files as before + var p = path.dirname(f); + if (!content[p]) { + content[p] = {}; + } + + // if it's a metadata.json, load the metadata. + if (path.basename(f) === "metadata.json") { + try { + content[p].metadata = JSON.parse(files[f]); + } catch (e) { + content[p].metadata = {}; + } + + // what is "link" used for? + content[p].metadata.link = p; + + // build up a "breadcrumb" + content[p].metadata.breadcrumb = p.replace(path.dirname(src), "").split("/"); + content[p].metadata.breadcrumb = content[p].metadata.breadcrumb.slice(2, content[p].metadata.breadcrumb.length); + + } // grab content---assumes content is in "article.md" for now + else if (path.basename(f) === "article.md") { + if (!content[p]) { + content[p] = {}; + } + + if (resolve) { + content[p].content = files[f].toString(); + } else { + content[p].content = ""; + } + + } else if (fs.statSync(f).isDirectory()) { + // Directory stuffs + if (!content[p]) { + content[p] = {}; + } + + content[p].ls = fs.readdirSync(p).map(function(file) { + return helpers.unresolve(docs.src, p+"/"+file).replace(/^\./, ''); + }); + + // build up a "breadcrumb" + // if (!content[p].metadata) { + content[p].metadata = {}; + + content[p].metadata.breadcrumb = p.replace(path.dirname(src), "").split("/"); + content[p].metadata.breadcrumb = content[p].metadata.breadcrumb.slice(2, content[p].metadata.breadcrumb.length); + + + + + } else if (fs.statSync(f).isFile()) { + // these are "other" files. + if (!content[p]) { + content[p] = {}; + } + + if (!content[p].files) { + content[p].files = {}; + } + + content[p].files[path.basename(f)] = Buffer.isBuffer(files[f]) ? files[f].toString() : files[f]; + try { + content[p].files[path.basename(f)] = JSON.parse(content[f].files[path.basename(f)]); + } catch (e) { + //don't care + } + } + + }); + + + return content; +} From b63d19d0188613398472e7f0840a78eba812be5f Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Tue, 25 Oct 2011 12:58:29 -0700 Subject: [PATCH 04/33] [fix] Parse error in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a532fa1..7c96bae 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "jsdom": "0.2.x", "weld": "0.2.1", "findit": "0.1.x", - "nconf": "0.4.3"," + "nconf": "0.4.3", "colors": "*", "optimist": "*", "wordwrap": "*" From f39681e3167e11ec88c75234338847f855c80205 Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Tue, 25 Oct 2011 13:16:53 -0700 Subject: [PATCH 05/33] [fix] article metadata now read properly --- lib/generators/content.js | 222 ++++++++++++++++++++------------------ lib/helpers.js | 5 +- 2 files changed, 121 insertions(+), 106 deletions(-) diff --git a/lib/generators/content.js b/lib/generators/content.js index 528e8ac..6beb4d6 100644 --- a/lib/generators/content.js +++ b/lib/generators/content.js @@ -9,10 +9,10 @@ var findit = require('findit'), weld = require('weld').weld, helpers = require('../helpers'); -var articles = exports; +var content = exports; -articles.weld = function(dom, pages) { +content.weld = function(dom, pages) { // // Remark: Create a short-cut reference to the jQuery object @@ -27,11 +27,11 @@ articles.weld = function(dom, pages) { // parse content if it exists if (pages[i].content) { - var content; + var md; try { - content = markdown.parse(pages[i].content.toString()); + md = markdown.parse(pages[i].content.toString()); } catch (err) { - content = err.message; + md = err.message; } } @@ -39,118 +39,128 @@ articles.weld = function(dom, pages) { //join the author metadata if (metadata && metadata.author) { - metadata.author = docs.content.authors[metadata.author] || { name: metadata.author}; + metadata.author = docs.content.authors[metadata.author] + || { name: metadata.author}; } + // Use article view if there's content - if (content) { - - dom.innerHTML = docs.content.theme['./theme/article.html'].toString(); - - var data = { - metadata: metadata, - content: content, - toc: toc - }; - - // something breaks here. - weld(dom, data, { - map: function(parent, element, key, val) { - - // - // Create breadcrumb - // - if ($(element).hasClass('breadcrumb')) { - var crumb = ''; - $('.breadcrumb', parent).each(function(i,v){ - crumb += ('/' + $(v).html()); - }); - crumb += ('/' + val); - $(element).attr('href', crumb); - $(element).html(val); - return false - } + if (md) { - //Handle dates - if ($(element).hasClass("date")) { - var date = val ? new Date(val).toISOString() : undefined; - if (val) { - $(element).attr("datetime", new Date(val).toISOString()); - $(element).text(val); - } else { - //TODO: If there isn't any metadata, we shouldn't have the "by x on y" div. - } - return false; - } + if (!metadata.title) { + console.log(i); + console.log(metadata); + } - //Handle author metadata - if ($(element).hasClass("github")) { - if (val) { - $(element).append( - $("").attr("href", "https://github.com/"+val).text("[github]") - ); - } - return false; - } + dom.innerHTML = docs.content.theme['./theme/article.html'].toString(); + + var data = { + metadata: metadata, + content: md, + toc: toc + }; + + // something breaks here. + weld(dom, data, { + map: function(parent, element, key, val) { + + // + // Create breadcrumb + // + if ($(element).hasClass('breadcrumb')) { + var crumb = ''; + $('.breadcrumb', parent).each(function(i,v){ + crumb += ('/' + $(v).html()); + }); + crumb += ('/' + val); + $(element).attr('href', crumb); + $(element).html(val); + return false + } - element.innerHTML = val; + //Handle dates + if ($(element).hasClass("date")) { + var date = val ? new Date(val).toISOString() : undefined; + if (val) { + $(element).attr("datetime", new Date(val).toISOString()); + $(element).text(val); + } else { + //TODO: If there isn't any metadata, we shouldn't have the + //"by x on y" div. + } return false; + } + //Handle author metadata + if ($(element).hasClass("github")) { + if (val) { + $(element).append( + $("") + .attr("href", "https://github.com/"+val) + .text("[github]") + ); + } + return false; } - }); + element.innerHTML = val; + return false; + + } + + }); // use directory view } else { - dom.innerHTML = docs.content.theme['./theme/directory.html']; - - //set up the data - var data = { - pwd: helpers.unresolve(docs.src, i), - ls: pages[i].ls, - toc: toc, - metadata: metadata - }; - - weld(dom, data, { - map: function(parent, element, key, val) { - - if ($(element).attr("id") === "toc") { - element.innerHTML = val; - return false; - } + dom.innerHTML = docs.content.theme['./theme/directory.html']; - if ($(element).hasClass("ls")) { - var title = val.split("/"); - title = title[title.length - 1].replace(/-/g, " "); - var listing = $("").attr("class", "ls").append( - $("").append( - $("").attr("href", val.replace("pages/", "")).text(title) - ) - ); + //set up the data + var data = { + pwd: helpers.unresolve(docs.src, i), + ls: pages[i].ls, + toc: toc, + metadata: metadata + }; - $("tr", $(element)).replaceWith(listing); - return false; - } + weld(dom, data, { + map: function(parent, element, key, val) { - // - // Create breadcrumb - // - if ($(element).hasClass('breadcrumb')) { - var crumb = ''; - $('.breadcrumb', parent).each(function(i,v){ - crumb += ('/' + $(v).html()); - }); - crumb += ('/' + val); - $(element).attr('href', crumb); - $(element).html(val); - return false; - } + if ($(element).attr("id") === "toc") { + element.innerHTML = val; + return false; + } - return true; + if ($(element).hasClass("ls")) { + var title = val.split("/"); + title = title[title.length - 1].replace(/-/g, " "); + var listing = $("").attr("class", "ls").append( + $("").append( + $("").attr("href", val.replace("pages/", "")).text(title) + ) + ); + $("tr", $(element)).replaceWith(listing); + return false; + } + + // + // Create breadcrumb + // + if ($(element).hasClass('breadcrumb')) { + var crumb = ''; + $('.breadcrumb', parent).each(function(i,v){ + crumb += ('/' + $(v).html()); + }); + crumb += ('/' + val); + $(element).attr('href', crumb); + $(element).html(val); + return false; } - }); + + return true; + + } + }); } @@ -172,21 +182,24 @@ articles.weld = function(dom, pages) { // // `&gt;` // - // There are probably a few other html identities that also get incorrectly highlighted (such as <). + // There are probably a few other html identities that also get incorrectly + // highlighted (such as <). // The easiest way to fix this, besides using a different highlighter, // turns out to be running a greedy search/replace for the // bungled highlight. dom.innerHTML = hl(dom.innerHTML, false, true) - .replace(new RegExp("&gt;", "g"), ">"); + .replace( new RegExp("&gt;", + "g"), ">"); - pages[i].content = dom.innerHTML || docs.content.theme['./theme/article.html'].toString(); + pages[i].content = dom.innerHTML + || docs.content.theme['./theme/article.html'].toString(); }); return dom; }; -articles.generate = function(output, pages) { +content.generate = function(output, pages) { // // Generate articles and directory pages @@ -204,8 +217,9 @@ articles.generate = function(output, pages) { -// Load all content with an fs2.readDirSync. It should be relatively indiscriminate. -articles.load = function () { +// Load all content with an fs2.readDirSync. It should be relatively +// indiscriminate. +content.load = function () { if (!docs.src) { docs.src = "../../pages"; } diff --git a/lib/helpers.js b/lib/helpers.js index c0dc800..3c10a98 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -232,8 +232,9 @@ helpers.dirToContent = function (src, files, resolve) { }); // build up a "breadcrumb" - // if (!content[p].metadata) { - content[p].metadata = {}; + if (!content[p].metadata) { + content[p].metadata = {}; + } content[p].metadata.breadcrumb = p.replace(path.dirname(src), "").split("/"); content[p].metadata.breadcrumb = content[p].metadata.breadcrumb.slice(2, content[p].metadata.breadcrumb.length); From eab43595cf9be79dc14935c76fa9407697bc98e7 Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Tue, 25 Oct 2011 13:23:48 -0700 Subject: [PATCH 06/33] [refactor] content.js now uses contains one weld for 'both' cases --- lib/generators/content.js | 141 ++++++++++++++------------------------ 1 file changed, 53 insertions(+), 88 deletions(-) diff --git a/lib/generators/content.js b/lib/generators/content.js index 6beb4d6..4f26a60 100644 --- a/lib/generators/content.js +++ b/lib/generators/content.js @@ -47,11 +47,6 @@ content.weld = function(dom, pages) { // Use article view if there's content if (md) { - if (!metadata.title) { - console.log(i); - console.log(metadata); - } - dom.innerHTML = docs.content.theme['./theme/article.html'].toString(); var data = { @@ -60,56 +55,6 @@ content.weld = function(dom, pages) { toc: toc }; - // something breaks here. - weld(dom, data, { - map: function(parent, element, key, val) { - - // - // Create breadcrumb - // - if ($(element).hasClass('breadcrumb')) { - var crumb = ''; - $('.breadcrumb', parent).each(function(i,v){ - crumb += ('/' + $(v).html()); - }); - crumb += ('/' + val); - $(element).attr('href', crumb); - $(element).html(val); - return false - } - - //Handle dates - if ($(element).hasClass("date")) { - var date = val ? new Date(val).toISOString() : undefined; - if (val) { - $(element).attr("datetime", new Date(val).toISOString()); - $(element).text(val); - } else { - //TODO: If there isn't any metadata, we shouldn't have the - //"by x on y" div. - } - return false; - } - - //Handle author metadata - if ($(element).hasClass("github")) { - if (val) { - $(element).append( - $("") - .attr("href", "https://github.com/"+val) - .text("[github]") - ); - } - return false; - } - - element.innerHTML = val; - return false; - - } - - }); - // use directory view } else { dom.innerHTML = docs.content.theme['./theme/directory.html']; @@ -121,48 +66,68 @@ content.weld = function(dom, pages) { toc: toc, metadata: metadata }; + } - weld(dom, data, { - map: function(parent, element, key, val) { + weld(dom, data, { + map: function(parent, element, key, val) { + + // + // Create breadcrumb + // + if ($(element).hasClass('breadcrumb')) { + var crumb = ''; + $('.breadcrumb', parent).each(function(i,v){ + crumb += ('/' + $(v).html()); + }); + crumb += ('/' + val); + $(element).attr('href', crumb); + $(element).html(val); + return false + } - if ($(element).attr("id") === "toc") { - element.innerHTML = val; - return false; - } + if ($(element).hasClass("ls")) { + var title = val.split("/"); + title = title[title.length - 1].replace(/-/g, " "); + var listing = $("").attr("class", "ls").append( + $("").append( + $("").attr("href", val.replace("pages/", "")).text(title) + ) + ); - if ($(element).hasClass("ls")) { - var title = val.split("/"); - title = title[title.length - 1].replace(/-/g, " "); - var listing = $("").attr("class", "ls").append( - $("").append( - $("").attr("href", val.replace("pages/", "")).text(title) - ) - ); + $("tr", $(element)).replaceWith(listing); + return false; + } - $("tr", $(element)).replaceWith(listing); - return false; + //Handle dates + if ($(element).hasClass("date")) { + var date = val ? new Date(val).toISOString() : undefined; + if (val) { + $(element).attr("datetime", new Date(val).toISOString()); + $(element).text(val); + } else { + //TODO: If there isn't any metadata, we shouldn't have the + //"by x on y" div. } + return false; + } - // - // Create breadcrumb - // - if ($(element).hasClass('breadcrumb')) { - var crumb = ''; - $('.breadcrumb', parent).each(function(i,v){ - crumb += ('/' + $(v).html()); - }); - crumb += ('/' + val); - $(element).attr('href', crumb); - $(element).html(val); - return false; + //Handle author metadata + if ($(element).hasClass("github")) { + if (val) { + $(element).append( + $("") + .attr("href", "https://github.com/"+val) + .text("[github]") + ); } - - return true; - + return false; } - }); - } + element.innerHTML = val; + return false; + } + + }); //Give the page a title From 2b3979c0e7e798c8ac19287c1eb7a6b4995cbef4 Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Tue, 25 Oct 2011 16:01:03 -0700 Subject: [PATCH 07/33] [api] A modified ToTree for ordered ToC --- lib/fs2.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lib/fs2.js b/lib/fs2.js index 7278e50..554709b 100644 --- a/lib/fs2.js +++ b/lib/fs2.js @@ -8,6 +8,7 @@ var fs = require('fs'), path = require('path'), mkdirp = require('mkdirp'), traverse = require('traverse'), + Hash = require('hashish'), findit = require('findit'); // @@ -64,6 +65,65 @@ fs2.filesToTree = function(files){ } +// I want to build up a directory tree to use for the table of contents. +fs2.dirToTree = function (dir) { + + //Very similar to filesToTree. Builds your basic nested deal. + var _tree = Object.keys(dir).reduce(function (acc, p) { + var ps = ("./"+path.normalize("./pages/"+p)).split("/"); + + traverse(acc).set(ps, {}); + return acc; + + }, {}); + + // we need some array in order to impose an ordering. + var ordered = function ordered (tree) { + var hash = Hash(tree).map(function (v) { + var files = []; + Object.keys(v).forEach( function (sv) { + // order-ify the next layer down + ordered(v[sv]).forEach(function(o) { + files.push(o); + }); + }); + return files; + }).items; + + var _tree = []; + Object.keys(hash).forEach(function(k) { + var obj = {}; + obj[k] = hash[k]; + _tree.push(obj); + }); + + return _tree; + } + + _tree = ordered(_tree); + + // rebuilding the filepath + _tree = traverse(_tree).map( function (e) { + if (this.isLeaf) { + var p = this.path.filter(function (i) { + return isNaN(Number(i)); + }).join("/"); + if (dir[p]) { + return dir[p]; + } else { + return p; + } + } + else { + return e; + } + }); + + + return _tree; +} + + // // Recursively read directory into flat JSON object // arguments: path, resolve, filter From 06ead8d47abdfe251763b42ccceeac4bfb786096 Mon Sep 17 00:00:00 2001 From: Joshua Holbrook Date: Tue, 25 Oct 2011 19:14:39 -0700 Subject: [PATCH 08/33] [refactor] Rebuilt table of contents rendering to enable sorting --- lib/fs2.js | 18 +++-- lib/generators/content.js | 20 +++++- lib/helpers.js | 147 ++++++++++++++++++-------------------- 3 files changed, 96 insertions(+), 89 deletions(-) diff --git a/lib/fs2.js b/lib/fs2.js index 554709b..3726236 100644 --- a/lib/fs2.js +++ b/lib/fs2.js @@ -70,7 +70,7 @@ fs2.dirToTree = function (dir) { //Very similar to filesToTree. Builds your basic nested deal. var _tree = Object.keys(dir).reduce(function (acc, p) { - var ps = ("./"+path.normalize("./pages/"+p)).split("/"); + var ps = (p).replace("//", "/").split("/"); traverse(acc).set(ps, {}); return acc; @@ -81,17 +81,21 @@ fs2.dirToTree = function (dir) { var ordered = function ordered (tree) { var hash = Hash(tree).map(function (v) { var files = []; - Object.keys(v).forEach( function (sv) { - // order-ify the next layer down - ordered(v[sv]).forEach(function(o) { - files.push(o); - }); + // order-ify the next layer down + ordered(v).forEach(function(o) { + files.push(o); }); return files; }).items; var _tree = []; - Object.keys(hash).forEach(function(k) { + Object.keys(hash).filter(function (k) { + if (k === "") { + console.log("One of the hash values is \"\". This should not be happening."); + return false; + } + return true; + }).forEach(function(k) { var obj = {}; obj[k] = hash[k]; _tree.push(obj); diff --git a/lib/generators/content.js b/lib/generators/content.js index 4f26a60..e5e2037 100644 --- a/lib/generators/content.js +++ b/lib/generators/content.js @@ -19,11 +19,22 @@ content.weld = function(dom, pages) { // var $ = docs.window.$; - // Here, we build up the table of contents. var toc = helpers.buildToc(docs.src); - Object.keys(pages).forEach( function (i){ + /*console.log((function () { + var sorts = {}; + Object.keys(pages).forEach( function (i) { + if (pages[i].metadata && pages[i].metadata.order) { + Object.keys(pages[i].metadata.order).forEach(function (j) { + sorts[i+"/"+j] = pages[i].metadata.order[j]; + }); + } + }); + return fs2.filesToTree(sorts); + })());*/ + + Object.keys(pages).forEach( function (i) { // parse content if it exists if (pages[i].content) { @@ -134,7 +145,10 @@ content.weld = function(dom, pages) { $('title', dom).html('node docs - ' + (metadata && metadata.title) || ""); //Add some meta tags - $('meta[name=keywords]', dom).attr('content', (metadata && metadata.tags || []).concat(docs.config.get("tags") || []).join(',')); + $('meta[name=keywords]', dom).attr('content', + (metadata && metadata.tags || []) + .concat(docs.config.get("tags") || []).join(',') + ); // // Remark: perform code highlighting, convert only inside diff --git a/lib/helpers.js b/lib/helpers.js index 3c10a98..f7f0f5a 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -17,28 +17,31 @@ helpers.unresolve = function (base, abs) { helpers.buildToc = function (src) { - var p = helpers.unresolve(path.resolve(__dirname + "/../pages"), src); + console.log(src); + var src = "./pages"; - var _articles = findit.sync(src); + // get list of directories. + var _articles = fs2.readDirSync(src, true, function (a) { + return path.extname(a).length === 0; + }); - // - // Filter out all non-markdown files - // - _articles = _articles.filter(function(a){ - a = path.resolve(a); - if(a.match(/\./)){ - return false; - } else { - return true; + Object.keys(_articles).forEach(function (i) { + if (_articles[i].isDirectory) { + _articles[i] = ""; } }); - _articles = _articles.map(function (a) { - return helpers.unresolve(src, a); - }); + // build up the tree. + var toc = fs2.dirToTree(_articles); + + // digs until there is a layer with >1 item in it. + while(toc.length === 1) { + var key = Object.keys(toc[0])[0]; + + toc = toc[0][key]; + } - // toc has an "undefined" value. - var toc = fs2.filesToTree(_articles); + //console.log(JSON.stringify(toc, true, 2)); return helpers.treeToHTML(toc); } @@ -51,83 +54,69 @@ var isElement = helpers.isElement = function(xs, e) { // // Function to use with sorting the ToC. -// ie. toc.sort(tocSort); +// ie. tocSort(xs, order); // -helpers.tocSort = function (a, b) { - // For future reference, here is the list from the top-level. - /*[ - 'cryptography', - 'advanced', - 'intermediate', - 'errors', - 'file-system', - 'javascript-conventions', - 'child-processes', - 'command-line', - 'REPL', - 'getting-started', - 'HTTP' ] - */ - - // These items should be listed *first*. - var first = [ - 'getting-started', - 'javascript-conventions' - ]; - - // These items should be listed *last*. - var last = [ - 'intermediate', - 'advanced', - 'cryptography' - ]; - - var aInFirst = isElement(first, a), - bInFirst = isElement(first, b), - aInLast = isElement(last,a), - bInLast = isElement(last, b); - - - // Handles the case where a and/or b is in the "first" list. - if ( aInFirst || bInFirst ) { - if (aInFirst && bInFirst) { - return first.indexOf(a)-first.indexOf(b); - } else { - return aInFirst ? -1 : 1; +helpers.tocSort = function (xs, order) { + + Object.keys(order).forEach(function (t) { + if (order[t] < 0) { + // This is so you can use negative indices, like python + order[t] = xs.length + order[t]; + } + }) + + return xs.sort( function (a, b) { + + //console.log(path.basename(a)); + //console.log(path.basename(b)); + var aHasOrder = order.hasOwnProperty(path.basename(a)), + bHasOrder = order.hasOwnProperty(path.basename(b)); + + if (aHasOrder && bHasOrder) { + return order[a]-order[b]; + } else if (aHasOrder) { + console.log("a: "+order[path.basename(a)]); + console.log("b: "+xs.indexOf(b)); + return order[path.basename(a)] - xs.indexOf(b) + } else if (bHasOrder) { + console.log(path.basename(a)+": "+xs.indexOf(a)); + console.log(path.basename(b)+": "+order[path.basename(b)]); + return order[path.basename(b)]; - xs.indexOf(a) } - } - if ( aInLast || bInLast ) { - if ( aInLast && bInLast) { - return last.indexOf(a) - last.indexOf(b); + if (a > b) { + return 1; + } else if (a < b) { + return -1; } else { - return aInLast ? 1 : -1; + return 0; } - } - - if (a > b) { - return 1; - } else if (a < b) { - return -1; - } else { - return 0; - } - + }); }; +// TODO: Use weld. helpers.treeToHTML = function(values, parent) { var str = '