From ae7f7b67223424acce8b8562504b3105d9d2092b Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Wed, 29 Jan 2014 14:45:40 -0800 Subject: [PATCH 1/7] Add pretest jshint of lib and test files with jshintConfig from yui-lint (modernized). --- package.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/package.json b/package.json index a14b40e0..3078c318 100644 --- a/package.json +++ b/package.json @@ -41,12 +41,14 @@ "express": "~3.1.2" }, "devDependencies": { + "jshint": "2.4.x", "ytestrunner": "~0.3.3", "yuitest": ">=0.7.3", "selleck": "~0.1", "istanbul": "~0.1.27" }, "scripts": { + "pretest": "jshint ./lib/*.js ./tests/*.js", "test": "istanbul cover --print=both --yui ytestrunner -- --include ./tests/options.js --include ./tests/builder.js --include ./tests/parser.js --include ./tests/parser_coffee.js --include ./tests/files.js" }, "preferGlobal": "true", @@ -60,6 +62,27 @@ "type":"git", "url":"http://github.com/yui/yuidoc.git" }, + "jshintConfig": { + "bitwise" : true, + "browser" : true, + "curly" : true, + "eqeqeq" : true, + "forin" : true, + "immed" : true, + "latedef" : "nofunc", + "laxbreak" : true, + "maxerr" : 500, + "maxlen" : 150, + "newcap" : true, + "noarg" : true, + "node" : true, + "noempty" : true, + "onevar" : true, + "trailing" : true, + "undef" : true, + "unused" : "vars", + "yui" : true + }, "yuidoc": { "name": "YUIDoc", "logo": "http://yuilibrary.com/img/yui-logo.png", From 5be91958e37bc00e323a38abda3d1673f2f0e6a4 Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Wed, 29 Jan 2014 16:02:55 -0800 Subject: [PATCH 2/7] Add tests/utils.js to the scripts.test command. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3078c318..9c4e54b0 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ }, "scripts": { "pretest": "jshint ./lib/*.js ./tests/*.js", - "test": "istanbul cover --print=both --yui ytestrunner -- --include ./tests/options.js --include ./tests/builder.js --include ./tests/parser.js --include ./tests/parser_coffee.js --include ./tests/files.js" + "test": "istanbul cover --print=both --yui ytestrunner -- --include ./tests/options.js --include ./tests/builder.js --include ./tests/parser.js --include ./tests/parser_coffee.js --include ./tests/files.js --include ./tests/utils.js" }, "preferGlobal": "true", "licenses":[ From 852db0b8f8563542d615e6559cdace99675141b1 Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Wed, 29 Jan 2014 14:48:45 -0800 Subject: [PATCH 3/7] Lint the world. --- lib/builder.js | 156 ++++++++++++++++++-------------- lib/cli.js | 8 +- lib/docparser.js | 198 ++++++++++++++++++++++------------------- lib/files.js | 78 ++++++++-------- lib/help.js | 2 +- lib/index.js | 3 +- lib/options.js | 3 +- lib/project.js | 1 + lib/server.js | 25 +++--- lib/utils.js | 9 +- lib/yuidoc.js | 20 ++--- tests/builder.js | 34 +++---- tests/files.js | 1 + tests/options.js | 18 ++-- tests/parser.js | 120 ++++++++++++++----------- tests/parser_coffee.js | 5 +- tests/utils.js | 5 +- 17 files changed, 376 insertions(+), 310 deletions(-) diff --git a/lib/builder.js b/lib/builder.js index 1b10ba5e..f3117e11 100644 --- a/lib/builder.js +++ b/lib/builder.js @@ -17,6 +17,7 @@ based templates to generate static HTML content */ YUI.add('doc-builder', function(Y) { + /*jshint onevar:false */ var fixType = Y.Lang.fixType, print = function(items) { @@ -38,11 +39,12 @@ YUI.add('doc-builder', function(Y) { return out; }; - Y.Handlebars.registerHelper('buildFileTree', function(items, fn) { + Y.Handlebars.registerHelper('buildFileTree', function (items) { return print(items); }); - var DEFAULT_THEME = themeDir = path.join(__dirname, '../', 'themes', 'default'); + var DEFAULT_THEME = path.join(__dirname, '../', 'themes', 'default'), + themeDir = DEFAULT_THEME; Y.DocBuilder = function(options, data) { this.options = options; @@ -80,17 +82,17 @@ YUI.add('doc-builder', function(Y) { Y.Handlebars.registerHelper('crossLinkModule', function(item, options) { var str = item; if (self.data.modules[item]) { - var content = options.fn(this); - if (content === "") { - content = item; - } + var content = options.fn(this); + if (content === "") { + content = item; + } str = '' + content + ''; } return str; }); - Y.Handlebars.registerHelper('crossLinkRaw', function(item, fn) { + Y.Handlebars.registerHelper('crossLinkRaw', function (item) { var str = ''; if (!item) { item = ''; @@ -112,7 +114,6 @@ YUI.add('doc-builder', function(Y) { if (options.cacheTemplates === false) { this.cacheTemplates = false; } - }; Y.DocBuilder.prototype = { @@ -141,7 +142,7 @@ YUI.add('doc-builder', function(Y) { * @return {HTML} The rendered HTML */ markdown: function(md) { - html = marked(md, this.options.markdown); + var html = marked(md, this.options.markdown); //Only reprocess if helpers were asked for if (this.options.helpers || (html.indexOf('{{#crossLink') > -1)) { //console.log('MD: ', html); @@ -171,7 +172,7 @@ YUI.add('doc-builder', function(Y) { _parseCrossLink: function(item, raw, content) { var self = this; var base = '../', - baseName = item, + baseItem, newWin = false, className = 'crosslink'; @@ -212,7 +213,7 @@ YUI.add('doc-builder', function(Y) { if (method.indexOf(':') > -1) { parts = method.split(':'); - method = parts[0], + method = parts[0]; type = parts[1]; if (type.indexOf('attr') === 0) { type = 'attribute'; @@ -257,12 +258,12 @@ YUI.add('doc-builder', function(Y) { } } if (link) { - if (content !== undefined) { - content = content.trim(); - } - if (!content) { - content = baseItem; - } + if (content !== undefined) { + content = content.trim(); + } + if (!content) { + content = baseItem; + } item = '' + content + ''; } return (raw) ? href : item; @@ -361,8 +362,8 @@ YUI.add('doc-builder', function(Y) { } }); } - if (item.return) { - item.return.type = fixType(item.return.type); + if (item["return"]) { + item["return"].type = fixType(item["return"].type); } self.data.classitems.push(item); }); @@ -468,7 +469,7 @@ YUI.add('doc-builder', function(Y) { Y.log('Loading theme from ' + theme, 'info', 'builder'); meta = Y.Files.getJSON(theme); } else if (DEFAULT_THEME !== themeDir) { - theme = path.join(DEFAULT_THEME, 'theme.json') + theme = path.join(DEFAULT_THEME, 'theme.json'); if (Y.Files.exists(theme)) { Y.log('Loading theme from ' + theme, 'info', 'builder'); meta = Y.Files.getJSON(theme); @@ -489,7 +490,7 @@ YUI.add('doc-builder', function(Y) { var key = k.substring(0, 1).toUpperCase() + k.substring(1, k.length); obj.meta['project' + key] = v; }); - return obj + return obj; }, /** * Populate the meta data for classes @@ -500,7 +501,7 @@ YUI.add('doc-builder', function(Y) { populateClasses: function(opts) { opts.meta.classes = []; Y.each(this.data.classes, function(v) { - if (v.external) { return } + if (v.external) { return; } opts.meta.classes.push({ displayName: v.name, name: v.name, @@ -524,21 +525,21 @@ YUI.add('doc-builder', function(Y) { opts.meta.modules = []; opts.meta.allModules = []; Y.each(this.data.modules, function(v) { - if (v.external) { return } + if (v.external) { return; } opts.meta.allModules.push({ displayName: v.displayName || v.name, name: self.filterFileName(v.name), description: v.description }); if (!v.is_submodule) { var o = { displayName: v.displayName || v.name, name: self.filterFileName(v.name) }; if (v.submodules) { o.submodules = []; Y.each(v.submodules, function(i, k) { - moddef = self.data.modules[k]; + var moddef = self.data.modules[k]; if (moddef) { o.submodules.push({ displayName: k, description: moddef.description }); - } else { - //Y.log('Submodule data missing: ' + k + ' for ' + v.name, 'warn', 'builder'); + // } else { + // Y.log('Submodule data missing: ' + k + ' for ' + v.name, 'warn', 'builder'); } }); o.submodules.sort(self.nameSort); @@ -560,14 +561,14 @@ YUI.add('doc-builder', function(Y) { var self = this; opts.meta.files = []; Y.each(this.data.files, function(v) { - if (v.external) { return } + if (v.external) { return; } opts.meta.files.push({ displayName: v.name, name: self.filterFileName(v.name), path: v.path || v.name }); }); var tree = {}; var files = []; Y.each(this.data.files, function(v) { - if (v.external) { return } + if (v.external) { return; } files.push(v.name); }); files.sort(); @@ -610,7 +611,7 @@ YUI.add('doc-builder', function(Y) { if (a.file && a.line && !self.options.nocode) { a.foundAt = '../files/' + self.filterFileName(a.file) + '.html#l' + a.line; if (a.path) { - a.foundAt = a.path + '#l' + a.line; + a.foundAt = a.path + '#l' + a.line; } } return a; @@ -724,14 +725,14 @@ YUI.add('doc-builder', function(Y) { }, - _resolveUrl: function(url, opts) { - if(!url) { + _resolveUrl: function(url, opts) { + if (!url) { return null; } if ( url.indexOf("://") >= 0 ) { return url; } - return path.join(opts.meta.projectRoot,url); + return path.join(opts.meta.projectRoot, url); }, /** @@ -781,9 +782,9 @@ YUI.add('doc-builder', function(Y) { render: function(source, view, layout, partials, callback) { var html = []; - function buffer(line) { - html.push(line); - } + // function buffer(line) { + // html.push(line); + // } // Allow callback as third or fourth param. if (typeof partials === 'function') { @@ -810,7 +811,7 @@ YUI.add('doc-builder', function(Y) { } else { _v[k] = view[k]; } - }; + } html = TEMPLATE(_v); //html = html.replace(/{{//g, '{{/'); @@ -864,7 +865,7 @@ YUI.add('doc-builder', function(Y) { Y.Files.writeFile(path.join(self.options.outdir, 'index.html'), html, stack.add(noop)); })); - stack.done(function(html, view) { + stack.done(function(/* html, view */) { Y.log('Writing index.html', 'info', 'builder'); cb(stack.html, stack.view); }); @@ -918,7 +919,7 @@ YUI.add('doc-builder', function(Y) { }); data.example = e; } else { - data.example = self._parseCode(self.markdown(i.example)); + data.example = self._parseCode(self.markdown(data.example)); } opts.meta.example = data.example; } @@ -945,7 +946,6 @@ YUI.add('doc-builder', function(Y) { stack.done(function() { cb(stack.html, stack.view); }); - }, /** * Generates the module files under "out"/modules/ @@ -967,12 +967,16 @@ YUI.add('doc-builder', function(Y) { }); Y.log('Rendering and writing ' + counter + ' modules pages.', 'info', 'builder'); Y.each(self.data.modules, function(v) { - if (v.external) { return } + if (v.external) { return; } self.renderModule(function(html, view) { stack.html.push(html); stack.view.push(view); if (self.options.dumpview) { - Y.Files.writeFile(path.join(self.options.outdir, 'json', 'module_' + v.name + '.json'), JSON.stringify(view), stack.add(noop)); + Y.Files.writeFile( + path.join(self.options.outdir, 'json', 'module_' + v.name + '.json'), + JSON.stringify(view), + stack.add(noop) + ); } Y.Files.writeFile(path.join(self.options.outdir, 'modules', v.name + '.html'), html, stack.add(noop)); }, v, layout); @@ -991,7 +995,7 @@ YUI.add('doc-builder', function(Y) { */ hasProperty: function(a, b) { var other = false; - var h = Y.some(a, function(i, k) { + Y.some(a, function (i, k) { if ((i.itemtype === b.itemtype) && (i.name === b.name)) { other = k; return true; @@ -1032,16 +1036,17 @@ YUI.add('doc-builder', function(Y) { //console.error(v.class, '==', info.extends); if (hasItems[v.class]) { if (!v.static) { - var override = self.hasProperty(classItems, v); + var q, + override = self.hasProperty(classItems, v); if (override === false) { //This method was extended from the parent class but not over written //console.error('Merging extends from', v.class, 'onto', info.name); - var q = Y.merge({}, v); + q = Y.merge({}, v); q.extended_from = v.class; classItems.push(q); } else { //This method was extended from the parent and overwritten in this class - var q = Y.merge({}, v); + q = Y.merge({}, v); q = self.augmentData(q); classItems[override].overwritten_from = q; } @@ -1120,10 +1125,10 @@ YUI.add('doc-builder', function(Y) { i = self.augmentData(i); i.paramsList = []; if (i.params) { - i.params.forEach(function(p, v) { + i.params.forEach(function(p) { var name = p.name; if (p.optional) { - name = '[' + name + ((p.optdefault) ? '=' + p.optdefault : '') + ']' + name = '[' + name + ((p.optdefault) ? '=' + p.optdefault : '') + ']'; } i.paramsList.push(name); }); @@ -1137,9 +1142,9 @@ YUI.add('doc-builder', function(Y) { i.paramsList = ' '; } i.returnType = ' '; - if (i.return) { + if (i["return"]) { i.hasReturn = true; - i.returnType = i.return.type; + i.returnType = i["return"].type; } //console.error(i); opts.meta.is_constructor = [i]; @@ -1157,15 +1162,16 @@ YUI.add('doc-builder', function(Y) { } classItems.forEach(function(i) { + var e; switch (i.itemtype) { case 'method': i = self.augmentData(i); i.paramsList = []; if (i.params && i.params.forEach) { - i.params.forEach(function(p, v) { + i.params.forEach(function(p) { var name = p.name; if (p.optional) { - name = '[' + name + ((p.optdefault) ? '=' + p.optdefault : '') + ']' + name = '[' + name + ((p.optdefault) ? '=' + p.optdefault : '') + ']'; } i.paramsList.push(name); }); @@ -1174,7 +1180,7 @@ YUI.add('doc-builder', function(Y) { i.methodDescription = self._parseCode(i.description); if (i.example && i.example.length) { if (i.example.forEach) { - var e = ''; + e = ''; i.example.forEach(function(v) { e += self._parseCode(self.markdown(v)); }); @@ -1191,9 +1197,9 @@ YUI.add('doc-builder', function(Y) { i.paramsList = ' '; } i.returnType = ' '; - if (i.return) { + if (i["return"]) { i.hasReturn = true; - i.returnType = i.return.type; + i.returnType = i["return"].type; } // If this item is provided by a module other @@ -1219,7 +1225,7 @@ YUI.add('doc-builder', function(Y) { } if (i.example && i.example.length) { if (i.example.forEach) { - var e = ''; + e = ''; i.example.forEach(function(v) { e += self._parseCode(self.markdown(v)); }); @@ -1258,7 +1264,7 @@ YUI.add('doc-builder', function(Y) { if (i.example && i.example.length) { if (i.example.forEach) { - var e = ''; + e = ''; i.example.forEach(function(v) { e += self._parseCode(self.markdown(v)); }); @@ -1286,7 +1292,7 @@ YUI.add('doc-builder', function(Y) { if (i.example && i.example.length) { if (i.example.forEach) { - var e = ''; + e = ''; i.example.forEach(function(v) { e += self._parseCode(self.markdown(v)); }); @@ -1362,12 +1368,16 @@ YUI.add('doc-builder', function(Y) { }); Y.log('Rendering and writing ' + counter + ' class pages.', 'info', 'builder'); Y.each(self.data.classes, function(v) { - if (v.external) { return } + if (v.external) { return; } self.renderClass(stack.add(function(html, view) { stack.html.push(html); stack.view.push(view); if (self.options.dumpview) { - Y.Files.writeFile(path.join(self.options.outdir, 'json', 'classes_' + v.name + '.json'), JSON.stringify(view), stack.add(noop)); + Y.Files.writeFile( + path.join(self.options.outdir, 'json', 'classes_' + v.name + '.json'), + JSON.stringify(view), + stack.add(noop) + ); } Y.Files.writeFile(path.join(self.options.outdir, 'classes', v.name + '.html'), html, stack.add(noop)); }), v, layout); @@ -1396,7 +1406,7 @@ YUI.add('doc-builder', function(Y) { ret = -1; } if (an > bn) { - ret = 1 + ret = 1; } return ret; }, @@ -1420,7 +1430,7 @@ YUI.add('doc-builder', function(Y) { }); Y.log('Rendering and writing ' + counter + ' source files.', 'info', 'builder'); Y.each(self.data.files, function(v) { - if (v.external) { return } + if (v.external) { return; } self.renderFile(stack.add(function(html, view, data) { if (!view || !data) { return; @@ -1428,9 +1438,17 @@ YUI.add('doc-builder', function(Y) { stack.html.push(html); stack.view.push(view); if (self.options.dumpview) { - Y.Files.writeFile(path.join(self.options.outdir, 'json', 'files_' + self.filterFileName(data.name) + '.json'), JSON.stringify(view), stack.add(noop)); + Y.Files.writeFile( + path.join(self.options.outdir, 'json', 'files_' + self.filterFileName(data.name) + '.json'), + JSON.stringify(view), + stack.add(noop) + ); } - Y.Files.writeFile(path.join(self.options.outdir, 'files', self.filterFileName(data.name) + '.html'), html, stack.add(noop)); + Y.Files.writeFile( + path.join(self.options.outdir, 'files', self.filterFileName(data.name) + '.html'), + html, + stack.add(noop) + ); }), v, layout); }); stack.done(function() { @@ -1513,7 +1531,7 @@ YUI.add('doc-builder', function(Y) { */ renderAPIMeta: function(cb) { - var opts = { meta: {} }, self = this; + var opts = { meta: {} }; opts = this.populateClasses(opts); opts = this.populateModules(opts); @@ -1564,7 +1582,13 @@ YUI.add('doc-builder', function(Y) { if (!Y.Files.isDirectory(path.join(self.options.outdir, 'assets'))) { fs.mkdirSync(path.join(self.options.outdir, 'assets'), 0777); } - Y.Files.copyAssets([path.join(DEFAULT_THEME, 'assets'), path.join(themeDir, 'assets')], path.join(self.options.outdir, 'assets'), false, function() { + Y.Files.copyAssets([ + path.join(DEFAULT_THEME, 'assets'), + path.join(themeDir, 'assets') + ], + path.join(self.options.outdir, 'assets'), + false, + function () { var cstack = new Y.Parallel(); self.writeModules(cstack.add(function() { @@ -1594,5 +1618,5 @@ YUI.add('doc-builder', function(Y) { }); }); } - } + }; }); diff --git a/lib/cli.js b/lib/cli.js index f6d63d82..dcde47c8 100755 --- a/lib/cli.js +++ b/lib/cli.js @@ -11,14 +11,14 @@ http://yuilibrary.com/license/ * @module yuidoc */ -var Y = require('./index'), - path = require('path'); +/*global Y:true */ +var Y = require('./index'); var options = Y.Options(Y.Array(process.argv, 2)); Y.log('Starting YUIDoc@' + Y.packageInfo.version + ' using YUI@' + Y.version + ' with NodeJS@' + process.versions.node, 'info', 'yuidoc'); -var starttime = (new Date).getTime(); +var starttime = (new Date()).getTime(); options = Y.Project.init(options); @@ -42,7 +42,7 @@ if (options.server) { if (!options.parseOnly) { var builder = new Y.DocBuilder(options, json); builder.compile(function() { - var endtime = (new Date).getTime(); + var endtime = (new Date()).getTime(); Y.log('Completed in ' + ((endtime - starttime) / 1000) + ' seconds' , 'info', 'yuidoc'); }); } diff --git a/lib/docparser.js b/lib/docparser.js index 435cfef5..37974571 100644 --- a/lib/docparser.js +++ b/lib/docparser.js @@ -7,7 +7,6 @@ YUI.add('docparser', function(Y) { var Lang = Y.Lang, trim = Lang.trim, - stringify = Y.JSON.stringify, fixType = Y.Lang.fixType, /** * Parses the JSON data and formats it into a nice log string for filename and line number: @@ -248,7 +247,7 @@ YUI.add('docparser', function(Y) { return; } - var type, name, optional, optdefault, parent, multiple, len, + var type, name, parts, optional, optdefault, parent, multiple, len, result, desc = implodeString(trim(value)), match = REGEX_TYPE.exec(desc), host = target.params; @@ -287,7 +286,7 @@ YUI.add('docparser', function(Y) { len = name.length - 1; - if (name.charAt(len) == '*') { + if (name.charAt(len) === '*') { multiple = true; name = name.substr(0, len); } @@ -304,10 +303,10 @@ YUI.add('docparser', function(Y) { name = parts[0]; optdefault = parts[1]; //Add some shortcuts for object/array defaults - if (optdefault.toLowerCase() == 'object') { + if (optdefault.toLowerCase() === 'object') { optdefault = '{}'; } - if (optdefault.toLowerCase() == 'array') { + if (optdefault.toLowerCase() === 'array') { optdefault = '[]'; } } @@ -319,7 +318,7 @@ YUI.add('docparser', function(Y) { match = name.split('.'); parent = trim(match[0]); Y.each(target.params, function(param) { - if (param.name == parent) { + if (param.name === parent) { param.props = param.props || []; host = param.props; match.shift(); @@ -364,9 +363,8 @@ YUI.add('docparser', function(Y) { if (multiple) { result.multiple = true; } - host.push(result); - + host.push(result); }, // @return {type} description // methods @@ -416,24 +414,22 @@ YUI.add('docparser', function(Y) { this.set(CURRENT_MODULE, value); var go = true; Y.some(block, function(o) { - if (trim(o.tag) == 'submodule') { + if (trim(o.tag) === 'submodule') { go = false; return true; } }); if (go) { if (!this.get(MAIN_MODULE)) { - var o = { + this.set(MAIN_MODULE, { tag: tagname, name: value, file: target.file, line: target.line, description: target.description - }; - this.set(MAIN_MODULE, o); + }); } - host = this.modules[value]; - return host; + return this.modules[value]; } return null; }, @@ -444,7 +440,7 @@ YUI.add('docparser', function(Y) { o.mainName = value; o.tag = tagname; o.itemtype = 'main'; - o._main = true; + o._main = true; this.set(MAIN_MODULE, o); }, @@ -477,7 +473,7 @@ YUI.add('docparser', function(Y) { // A key bock type for declaring classes, subsequent // member blocks will be assigned to this class 'class': function(tagname, value, target, block) { - var _namespace, _value = value; + var namespace, fullname, host, parent; block.forEach(function(def) { if (def.tag === 'namespace') { @@ -485,21 +481,22 @@ YUI.add('docparser', function(Y) { var name = trim(def.value) + '.' + value; if (value.indexOf(trim(def.value) + '.') === -1) { value = name; - _namespace = trim(def.value); + namespace = trim(def.value); } } }); - if (_namespace) { - this.set(CURRENT_NAMESPACE, _namespace); + if (namespace) { + this.set(CURRENT_NAMESPACE, namespace); } this.set(CURRENT_CLASS, value); - var fullname = this.get(CURRENT_CLASS); - var host = this.classes[fullname], - parent = this.get(CURRENT_MODULE); - if (_namespace) { - host.namespace = _namespace; + fullname = this.get(CURRENT_CLASS); + host = this.classes[fullname]; + parent = this.get(CURRENT_MODULE); + + if (namespace) { + host.namespace = namespace; } if (parent) { host.module = parent; @@ -521,17 +518,18 @@ YUI.add('docparser', function(Y) { 'const': function(tagname, value, target, block) { target.itemtype = 'property'; target.name = value; + /*jshint sub:true */ target['final'] = ''; }, // supported classitems 'property': function(tagname, value, target, block) { - var match, name, desc; + var match, name, desc, type; target.itemtype = tagname; target.name = value; if (!target.type) { - desc = implodeString(trim(value)), + desc = implodeString(trim(value)); match = REGEX_TYPE.exec(desc); // Extract {type} @@ -597,7 +595,7 @@ YUI.add('docparser', function(Y) { var e = value; block.forEach(function(v) { - if (v.tag == 'example') { + if (v.tag === 'example') { if (v.value.indexOf(value) > -1) { e = v.value; } @@ -609,7 +607,6 @@ YUI.add('docparser', function(Y) { 'url': 'todo', 'icon': 'todo', 'see': 'todo', - 'throws': 'todo', 'requires': 'todo', 'knownissue': 'todo', 'uses': 'todo', @@ -639,26 +636,30 @@ YUI.add('docparser', function(Y) { //Shortcut this if namespace is an empty string. return; } - var file = this.get(CURRENT_FILE); + var m, + mod, + name, + lastNS, + file = this.get(CURRENT_FILE); if (file) { this.files[file].namespaces[value] = 1; } - var mod = this.get(CURRENT_MODULE); + mod = this.get(CURRENT_MODULE); if (mod) { this.modules[mod].namespaces[value] = 1; } - var mod = this.get(CURRENT_SUBMODULE); + mod = this.get(CURRENT_SUBMODULE); if (mod) { this.modules[mod].namespaces[value] = 1; } - var mod = this.get(CURRENT_CLASS); + mod = this.get(CURRENT_CLASS); if (mod) { - var lastNS = this.get('lastnamespace'); + lastNS = this.get('lastnamespace'); if (lastNS && lastNS !== value && (value.indexOf(lastNS + '.') !== 0)) { if (this.classes[mod]) { - var m = this.classes[mod]; + m = this.classes[mod]; delete this.classes[mod]; mod = value + '.' + mod.replace(lastNS + '.', ''); m.name = mod; @@ -674,17 +675,17 @@ YUI.add('docparser', function(Y) { } if (mod.indexOf(value + '.') === -1) { if (mod.indexOf('.') === -1) { - var m = this.classes[mod]; + m = this.classes[mod]; delete this.classes[mod]; - var name = m.namespace + '.' + m.name; + name = m.namespace + '.' + m.name; m.name = name; this.classes[name] = m; this.set(CURRENT_CLASS, name); } else { if (mod.indexOf(this.classes[mod].namespace + '.') === -1) { - var m = this.classes[mod]; + m = this.classes[mod]; delete this.classes[mod]; - var name = m.namespace + '.' + m.shortname; + name = m.namespace + '.' + m.shortname; m.name = name; this.classes[name] = m; this.set(CURRENT_CLASS, name); @@ -698,22 +699,27 @@ YUI.add('docparser', function(Y) { // updates the current class only (doesn't create // a new class definition) 'for': function(tagname, value, target, block) { + var ns, file, mod; + value = this._resolveFor(value); this.set(CURRENT_CLASS, value); - var ns = ((this.classes[value]) ? this.classes[value].namespace : ''); + + ns = ((this.classes[value]) ? this.classes[value].namespace : ''); this.set(CURRENT_NAMESPACE, ns); - var file = this.get(CURRENT_FILE); + + file = this.get(CURRENT_FILE); if (file) { this.files[file].fors[value] = 1; } - var mod = this.get(CURRENT_MODULE); + + mod = this.get(CURRENT_MODULE); if (mod) { this.modules[mod].fors[value] = 1; } - var mod = this.get(CURRENT_SUBMODULE); + mod = this.get(CURRENT_SUBMODULE); if (mod) { - this.modules[mod].fors[value]; + this.modules[mod].fors[value] = 1; } } @@ -838,10 +844,10 @@ YUI.add('docparser', function(Y) { if (!o) { return; } - //console.log('Main Module Setter: ', o); + //console.log('Main Module Setter: ', o); var write = true, name = o.mainName || o.name; - if (this.get(CURRENT_MODULE) === name) { + if (this.get(CURRENT_MODULE) === name) { if (name in this.modules) { //console.log('In Global Modules', this.modules[name]); @@ -852,12 +858,12 @@ YUI.add('docparser', function(Y) { } } if (write) { - //console.log('Writing'); + //console.log('Writing'); this.modules[name] = Y.merge(this.modules[name], o); } } else { if (o._main) { - //console.log('Writing'); + //console.log('Writing'); this.modules[name] = o; } } @@ -875,13 +881,18 @@ YUI.add('docparser', function(Y) { return val; } val = trim(val); + + var modMain, clazz; + this.set(CURRENT_SUBMODULE, ''); this.set(CURRENT_NAMESPACE, ''); - var m = this.get(MAIN_MODULE); - if (m && m.name !== val) { - this.set(MAIN_MODULE, ''); - } - var clazz = this.classes[this.get(CURRENT_CLASS)]; + + modMain = this.get(MAIN_MODULE); + if (modMain && modMain.name !== val) { + this.set(MAIN_MODULE, ''); + } + + clazz = this.classes[this.get(CURRENT_CLASS)]; if (clazz) { //Handles case where @module comes after @class in a new directory of files if (clazz.module !== val) { @@ -903,6 +914,7 @@ YUI.add('docparser', function(Y) { } } } + if (!(val in this.modules)) { this.modules[val] = { name: val, @@ -912,6 +924,7 @@ YUI.add('docparser', function(Y) { namespaces: {} }; } + return val; } }, @@ -968,18 +981,22 @@ YUI.add('docparser', function(Y) { } this.set('lastclass', this.get(CURRENT_CLASS)); val = trim(val); + var name = val, + ns, clazz; if (!(val in this.classes)) { - var ns = this.get(CURRENT_NAMESPACE), - name = (ns && ns !== '' && (val.indexOf(ns + '.') !== 0)) ? ns + '.' + val : val, - clazz = this.classes[name] = { - name: name, - shortname: val, - classitems: [], - plugins: [], - extensions: [], - plugin_for: [], - extension_for: [] - }; + ns = this.get(CURRENT_NAMESPACE); + if (ns && ns !== '' && (val.indexOf(ns + '.') !== 0)) { + name = ns + '.' + name; + } + clazz = this.classes[name] = { + name: name, + shortname: val, + classitems: [], + plugins: [], + extensions: [], + plugin_for: [], + extension_for: [] + }; clazz.module = this.get(CURRENT_MODULE); if (this.get(CURRENT_SUBMODULE)) { clazz.submodule = this.get(CURRENT_SUBMODULE); @@ -1104,20 +1121,21 @@ YUI.add('docparser', function(Y) { var lines = comment.split(REGEX_LINES), len = lines.length, i, parts, part, peek, skip, + tag, value, results = [{tag: 'file', value: file}, {tag: 'line', value: line}], syntaxtype = this.get('syntaxtype'), lineHeadCharRegex = REGEX_LINE_HEAD_CHAR[syntaxtype], hasLineHeadChar = lines[0] && lineHeadCharRegex.test(lines[0]); -// trim leading line head char(star or harp) if there are any + // trim leading line head char(star or harp) if there are any if (hasLineHeadChar) { for (i = 0; i < len; i++) { lines[i] = lines[i].replace(lineHeadCharRegex, ''); } } -// reconsitute and tokenize the comment block + // reconsitute and tokenize the comment block comment = this.unindent(lines.join('\n')); parts = comment.split(/(?:^|\n)\s*(@\w*)/); len = parts.length; @@ -1129,7 +1147,7 @@ YUI.add('docparser', function(Y) { } skip = false; -// the first token may be the description, otherwise it should be a tag + // the first token may be the description, otherwise it should be a tag if (i === 0 && part.substr(0, 1) !== '@') { if (part) { tag = '@description'; @@ -1174,11 +1192,11 @@ YUI.add('docparser', function(Y) { extract: function(filemap, dirmap) { filemap = filemap || this.get('filemap'); dirmap = dirmap || this.get('dirmap'); - var syntaxtype = this.get('syntaxtype'); - var commentmap = {}; + var syntaxtype = this.get('syntaxtype'), + commentmap = {}; Y.each(filemap, function(code, filename) { - var commentlines, comment, + var commentlines, comment, line, lines = code.split(REGEX_LINES), len = lines.length, i, linenum; @@ -1219,17 +1237,15 @@ YUI.add('docparser', function(Y) { */ processblock: function(block) { var target = {}, - clazz, - module, - submodule, digestname, digester, host; + // Y.log(block); - Y.each(block, function(tag) { + Y.each(block, function (tag) { var name = trim(tag.tag), value = trim(tag.value), - parent, ret; + ret; //Convert empty values to a 1 for JSON data parsing later if (SHORT_TAGS[name] && value === '') { @@ -1266,7 +1282,6 @@ YUI.add('docparser', function(Y) { target[name] = value; } } - }, this); if (host) { @@ -1275,10 +1290,12 @@ YUI.add('docparser', function(Y) { this.classitems.push(target); target['class'] = this.get(CURRENT_CLASS); target.module = this.get(CURRENT_MODULE); + host = this.get(CURRENT_SUBMODULE); if (host) { target.submodule = host; } + host = this.get(CURRENT_NAMESPACE); if (host) { target.namespace = host; @@ -1300,28 +1317,29 @@ YUI.add('docparser', function(Y) { files = self.files = {}, modules = self.modules = {}, classes = self.classes = {}, - classitems = self.classitems = [], - data = self.data = { - project: project, - files: files, - modules: modules, - classes: classes, - classitems: classitems - }; + classitems = self.classitems = []; + + self.data = { + project: project, + files: files, + modules: modules, + classes: classes, + classitems: classitems + }; commentmap = commentmap || self.commentmap; // process - Y.each(commentmap, function(blocks, file) { + Y.each(commentmap, function (blocks, file) { //Y.log('transform: ' + file, 'info', 'docparser'); self.set(CURRENT_FILE, file); - Y.each(blocks, function(block) { + Y.each(blocks, function (block) { self.processblock(block); }); }); // cross reference - Y.each(modules, function(module, name) { + Y.each(modules, function (module, name) { if (module.file) { files[module.file].modules[name] = 1; } @@ -1333,7 +1351,7 @@ YUI.add('docparser', function(Y) { delete module._main; }); - Y.each(classes, function(clazz, name) { + Y.each(classes, function (clazz, name) { if (clazz.module) { modules[clazz.module].classes[name] = 1; } @@ -1369,7 +1387,7 @@ YUI.add('docparser', function(Y) { } }); - Y.each(classitems, function(v) { + Y.each(classitems, function (v) { if (!v.itemtype) { self.warnings.push({ message: 'Missing item type' + (v.description ? '\n' + v.description : ''), @@ -1390,7 +1408,7 @@ YUI.add('docparser', function(Y) { } }); - Y.each(modules, function(mod) { + Y.each(modules, function (mod) { if (!mod.file || !mod.line || !mod.name) { console.log('Failed to find lines for', mod); } @@ -1417,5 +1435,5 @@ YUI.add('docparser', function(Y) { Y.DocParser = DocParser; -}, '0.1.0', { requires: ['base-base', 'json-stringify'] }); +}, '0.1.0', { requires: ['base-base'] }); diff --git a/lib/files.js b/lib/files.js index a9a033b6..d27abf23 100644 --- a/lib/files.js +++ b/lib/files.js @@ -15,20 +15,18 @@ Copyright (c) 2011 Yahoo! Inc. Licensed under the BSD License. */ -var fs = require('graceful-fs'); - fsPath = require('path'), +var fs = require('graceful-fs'), + fsPath = require('path'), useFS = (fs.exists) ? fs : fsPath; - - -var exists = function(file, cb) { +function exists(file, cb) { if (cb) { useFS.exists(file, cb); } else { return useFS.existsSync(file); } -}; +} Y.Files.exists = exists; @@ -96,6 +94,7 @@ function copyDirectory(source, dest, overwrite, callback) { if (!pending) { return callback(); } while ((filename = files.shift())) { + /*jshint loopfunc:true */ copyPath(fsPath.join(source, filename), fsPath.join(dest, filename), overwrite, function (err) { if (err) { return callback(err); } @@ -177,8 +176,7 @@ if they already exist. @param {Error} callback.err **/ function copyPath(source, dest, overwrite, callback) { - var destStats = statSync(dest), - sourceStats = statSync(source); + var sourceStats = statSync(source); // Allow callback as third arg. if (typeof overwrite === 'function') { @@ -235,23 +233,25 @@ Check to see if this is a directory @return {Boolean} True if it is a directory **/ function isDirectory(path, link) { - var i = false; + var stat, + result = false; + link = (link === false) ? false : true; + try { - var stat = fs.lstatSync(path); - + stat = fs.lstatSync(path); if (stat) { if (stat.isSymbolicLink() && link) { stat = fs.statSync(path); } - i = stat.isDirectory(); + result = stat.isDirectory(); } } catch (e) { - i = false; + result = false; } - return i; -}; + return result; +} Y.Files.isDirectory = isDirectory; @@ -263,21 +263,22 @@ Check to see if this is a File @return {Boolean} True if it is a file **/ function isFile(path, link) { - var i = false; + var stat, + result = false; + try { - var stat = fs.lstatSync(path); - + stat = fs.lstatSync(path); if (stat) { if (stat.isSymbolicLink() && link) { stat = fs.statSync(path); } - i = stat.isFile(); + result = stat.isFile(); } } catch (e) { - i = false; + result = false; } - return i; + return result; } Y.Files.isFile = isFile; @@ -368,7 +369,7 @@ function copyAssets() { copyPath(from[0], to, true, function() { if (isDirectory(from[1])) { - copyPath(from[1], to, true, callback) + copyPath(from[1], to, true, callback); } else { callback(); } @@ -393,7 +394,7 @@ Y.Files.getJSON = function(filename) { var data = {}; if (exists(filename)) { data = JSON.parse(fs.readFileSync(filename, 'utf8')); - }; + } return data; }; @@ -405,46 +406,47 @@ Y.Files.getJSON = function(filename) { * @param {Callback} callback* */ -var writeFileTimer = 100, - readFileTimer = 100; +function writeFile(file, data, cb) { + var out, + args = arguments, + flags = { + flags: "w", encoding: Y.charset, mode: 0644 + }; -var writeFile = function(file, data, cb) { - var flags = { - flags: "w", encoding: Y.charset, mode: 0644 - } - var args = arguments; if (cb) { fs.writeFile(file, data, flags, function(err) { if (err && err.message.match(/^EMFILE, Too many open files/)) { Y.log('Writefile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files'); - writeFileTimer++; - Y.later(writeFileTimer, Y, writeFile, args); + writeFile.timer++; + Y.later(writeFile.timer, Y, writeFile, args); return; } cb(); }); } else { - var out = fs.createWriteStream(file, flags); + out = fs.createWriteStream(file, flags); out.write(data); out.end(); } -}; +} +writeFile.timer = 100; Y.Files.writeFile = writeFile; -var readFile = function(file, enc, cb) { +function readFile(file, enc, cb) { var args = arguments; fs.readFile(file, enc, function(err, data) { if (err && err.message.match(/^EMFILE, Too many open files/)) { Y.log('Readfile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files'); - readFileTimer++; - Y.later(readFileTimer, Y, readFile, args); + readFile.timer++; + Y.later(readFile.timer, Y, readFile, args); return; } cb(err, data); }); -}; +} +readFile.timer = 100; Y.Files.readFile = readFile; diff --git a/lib/help.js b/lib/help.js index 3de4dd5a..e2b3731a 100644 --- a/lib/help.js +++ b/lib/help.js @@ -74,5 +74,5 @@ YUI.add('help', function(Y) { Y.showHelp = function() { console.error(Y.renderHelp()); process.exit(0); //Shouldn't exit one on help - } + }; }); diff --git a/lib/index.js b/lib/index.js index c6bd6a49..9f676c53 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,6 +3,7 @@ Copyright (c) 2011, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://yuilibrary.com/license/ */ +/*global YUI:true, Y:true */ /** Module creates the YUI instance with the required modules, uses them and exports the **Y** to be used by the _CLI class_ or by extenders: `require('yuidocjs');` @@ -44,7 +45,7 @@ process.on('uncaughtException', function(msg) { inst.applyConfig({ debug: true, useColor: useColor - }); + }); inst.log('--------------------------------------------------------------------------', 'error'); inst.log('An uncaught YUIDoc error has occurred, stack trace given below', 'error'); diff --git a/lib/options.js b/lib/options.js index 1b262d43..dec5caca 100644 --- a/lib/options.js +++ b/lib/options.js @@ -15,7 +15,8 @@ YUI.add('options', function(Y) { * @return {Object} The config object */ Y.Options = function(args) { - + /*jshint onevar:false */ + var options = { port: 3000, nocode: false diff --git a/lib/project.js b/lib/project.js index c66f31d7..12300077 100644 --- a/lib/project.js +++ b/lib/project.js @@ -47,6 +47,7 @@ YUI.add('project', function(Y) { } if (typeof options.tabtospace === 'number') { + /*jshint onevar:false */ options.tabspace = ''; for (var s = 0; s < options.tabtospace; s++) { options.tabspace += ' '; diff --git a/lib/server.js b/lib/server.js index 907ea6b6..a47af3db 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,12 +1,12 @@ YUI.add('server', function(Y) { - - var path = require('path'); + + var path = require('path'), /** * Provides the `--server` server option for YUIDoc * @class Server * @module yuidoc */ - var Server = { + Server = { /** * Cache for external mixed in data. * @property _externalData @@ -81,8 +81,7 @@ YUI.add('server', function(Y) { app.get('*', function(req, res) { var type = req.url.split('/')[1], - oType = type; - var html = ['

Item Not Found in internal meta-data

']; + html = ['

Item Not Found in internal meta-data

']; if (type === 'class') { type = 'classes'; @@ -110,8 +109,8 @@ YUI.add('server', function(Y) { * @param {Response} res Express response object */ files: function(req, res, next) { - var fileName = req.params.file; - var data; + var fileName = req.params.file, + data; Object.keys(Server.builder.data.files).forEach(function(file) { if (fileName === Server.builder.filterFileName(file)) { data = Server.builder.data.files[file]; @@ -179,12 +178,13 @@ YUI.add('server', function(Y) { */ init: function() { var express = require('express'), - path = require('path'); + path = require('path'), + stat; Server.app = express(); //console.log(Server.options); - var stat = Server.options.themedir || path.join(__dirname, '../', 'themes', 'default'); + stat = Server.options.themedir || path.join(__dirname, '../', 'themes', 'default'); Server.app.use(express.static(stat)); Server.routes(); Server.app.listen(Server.options.port); @@ -209,9 +209,12 @@ YUI.add('server', function(Y) { if (Server.options.external) { Y.log('Fetching external data, this may take a minute', 'warn', 'server'); - var json = (new Y.YUIDoc(Server.options)).run(); + var json, builder; + + json = (new Y.YUIDoc(Server.options)).run(); Server.options = Y.Project.mix(json, Server.options); - var builder = new Y.DocBuilder(Server.options, json); + + builder = new Y.DocBuilder(Server.options, json); builder.mixExternal(function() { Y.log('External data fetched, launching server..', 'info', 'server'); Server._externalData = builder.options.externalData; diff --git a/lib/utils.js b/lib/utils.js index 251538e8..ec2850d7 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -3,8 +3,8 @@ Copyright (c) 2011, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://yuilibrary.com/license/ */ +/*jshint onevar:false */ var path = require('path'), - util = require('util'), minimatch = require('minimatch'), fs = require('graceful-fs'); @@ -143,9 +143,9 @@ Mix/merge/munge data into the template. @param {Object} callback.options Merged options. **/ function prepare(inDirs, options, callback) { - var compiled = {}, - meta = {}, - type = 'project'; + var layouts, + partials, + type = 'project'; if (options && options.skipLoad) { // Skip loading layouts, metadata, pages, and partials and assume that @@ -234,6 +234,7 @@ var getProjectData = function(dir) { // * there are no more dirs to process // * we abort due to failsafe while (dirs.length && !projectData) { + /*jshint loopfunc:true */ if (dirCount++ > 5000) { Y.log('Scanned ' + dirCount + ' directories looking for a yuidoc.json file, something is probably wrong here..', 'error', 'yuidoc'); process.exit(1); diff --git a/lib/yuidoc.js b/lib/yuidoc.js index ff7827c1..f923f199 100755 --- a/lib/yuidoc.js +++ b/lib/yuidoc.js @@ -156,7 +156,7 @@ YUI.add('yuidoc', function(Y) { } var allfiles = fs.readdirSync(dir), stats, files = [], fullpath, self = this; - + if (dir in self.options.excludes) { return; } @@ -188,7 +188,7 @@ YUI.add('yuidoc', function(Y) { */ parsefiles: function(dir, files) { var self = this; - files = files.sort(); + files = files.sort(); Y.each(files, function(filename) { var ext = path.extname(filename), text, fullpath; @@ -221,7 +221,7 @@ YUI.add('yuidoc', function(Y) { //Y.log('Checking for Selleck data: ' + comp, 'info', 'yuidoc'); if (Y.Files.exists(comp)) { try { - var json = JSON.parse(fs.readFileSync(comp, 'utf8')); + json = JSON.parse(fs.readFileSync(comp, 'utf8')); delete json.examples; //Remove the selleck example data, we only want the comp info self.selleck[fullpath] = json; } catch (e) { @@ -239,7 +239,9 @@ YUI.add('yuidoc', function(Y) { */ writeJSON: function(parser) { var self = this, - data; + data, + file, + out; data = parser.data; @@ -271,7 +273,7 @@ YUI.add('yuidoc', function(Y) { if (self.options.writeJSON) { // Y.log(Y.JSON.stringify(parser.data, null, 4)); - var file = path.join(self.options.outdir, 'data.json'), out; + file = path.join(self.options.outdir, 'data.json'); if (Y.Files.exists(self.options.outdir) && !self.options.nodeleteout) { Y.log('Found out dir, deleting: ' + self.options.outdir, 'warn', 'yuidoc'); rimraf.sync(self.options.outdir); @@ -322,15 +324,13 @@ YUI.add('yuidoc', function(Y) { Y.log('YUIDoc Starting from: ' + this.options.paths.join(','), 'info', 'yuidoc'); this.starttime = new Date().getTime(); - var self = this; - this._processConfig(); this.walk(); var json = this.writeJSON(new Y.DocParser({ - syntaxtype: self.options.syntaxtype, - filemap: self.filemap, - dirmap: self.dirmap + syntaxtype: this.options.syntaxtype, + filemap: this.filemap, + dirmap: this.dirmap }).parse()); if (this.options.lint) { diff --git a/tests/builder.js b/tests/builder.js index 628699dd..171f3582 100644 --- a/tests/builder.js +++ b/tests/builder.js @@ -1,6 +1,6 @@ +/*global Y:true */ var YUITest = require('yuitest'), Assert = YUITest.Assert, - ArrayAssert = YUITest.ArrayAssert, path = require('path'), fs = require('fs'), Y = require(path.join(__dirname, '../', 'lib', 'index')); @@ -23,8 +23,9 @@ var suite = new YUITest.TestSuite({ return ret; }, setUp: function() { - var test = this; - var options = { + var options, json, builder; + + options = { quiet: true, paths: [ 'input/' ], outdir: './out', @@ -35,12 +36,12 @@ var suite = new YUITest.TestSuite({ langPrefix: "language-" } }; - var json = (new Y.YUIDoc(options)).run(); + json = (new Y.YUIDoc(options)).run(); this.project = json.project; this.data = json; - var builder = new Y.DocBuilder(options, json); + builder = new Y.DocBuilder(options, json); builder.compile(function() { suite._setupComplete = true; }); @@ -62,8 +63,8 @@ suite.add(new YUITest.TestCase({ so I have to check it in the first test to make sure my async task in the setup is complete before I can test against it. */ - var test = this; - var timer = setInterval(function() { + var test = this, + timer = setInterval(function() { if (suite._setupComplete) { clearInterval(timer); test.resume(function() { @@ -91,40 +92,39 @@ suite.add(new YUITest.TestCase({ }, 'test: index.html': function() { var p = path.join(__dirname, 'out', 'index.html'); - Assert.isTrue(exists(p), 'Failed to find: ' + p) + Assert.isTrue(exists(p), 'Failed to find: ' + p); }, 'test: data.json': function() { var p = path.join(__dirname, 'out', 'data.json'); - Assert.isTrue(exists(p), 'Failed to find: ' + p) + Assert.isTrue(exists(p), 'Failed to find: ' + p); }, 'test: api.js': function() { var p = path.join(__dirname, 'out', 'api.js'); - Assert.isTrue(exists(p), 'Failed to find: ' + p) + Assert.isTrue(exists(p), 'Failed to find: ' + p); }, 'test: classes/JSON.html': function() { var p = path.join(__dirname, 'out', 'classes', 'JSON.html'); - Assert.isTrue(exists(p), 'Failed to find: ' + p) + Assert.isTrue(exists(p), 'Failed to find: ' + p); }, 'test: files name filter': function() { var dir = path.join(__dirname, 'out', 'files'); - var files = fs.readdirSync(dir); - files.forEach(function(file) { + fs.readdirSync(dir).forEach(function(file) { Assert.isTrue(((file.indexOf('input_') ===0) || file.indexOf('index.html') === 0), 'Filed to parse: ' + file); }); }, 'test: module files': function() { var mods = this.data.modules; Object.keys(mods).forEach(function(name) { - var m = mods[name]; - var p = path.join(__dirname, 'out', 'modules', m.name + '.html'); + var m = mods[name], + p = path.join(__dirname, 'out', 'modules', m.name + '.html'); Assert.isTrue(exists(p), 'Failed to render: ' + m.name + '.html'); }); }, 'test: class files': function() { var mods = this.data.classes; Object.keys(mods).forEach(function(name) { - var m = mods[name]; - var p = path.join(__dirname, 'out', 'classes', m.name + '.html'); + var m = mods[name], + p = path.join(__dirname, 'out', 'classes', m.name + '.html'); Assert.isTrue(exists(p), 'Failed to render: ' + m.name + '.html'); }); } diff --git a/tests/files.js b/tests/files.js index e243ff1d..df132eae 100644 --- a/tests/files.js +++ b/tests/files.js @@ -1,3 +1,4 @@ +/*global Y:true */ var YUITest = require('yuitest'), Assert = YUITest.Assert, path = require('path'), diff --git a/tests/options.js b/tests/options.js index 81cb8d38..996a2a97 100644 --- a/tests/options.js +++ b/tests/options.js @@ -1,8 +1,7 @@ +/*global Y:true */ var YUITest = require('yuitest'), Assert = YUITest.Assert, - ArrayAssert = YUITest.ArrayAssert, path = require('path'), - fs = require('fs'), Y = require(path.join(__dirname, '../', 'lib', 'index')); //Move to the test dir before running the tests. @@ -236,24 +235,21 @@ suite.add(new YUITest.TestCase({ '-T', 'simple' ]); - var p = path.join(__dirname, '../themes/simple'); - Assert.areEqual(p, options.themedir); + Assert.areEqual(path.join(__dirname, '../themes/simple'), options.themedir); }, 'test: --theme simple': function() { var options = Y.Options([ '--theme', 'simple' ]); - var p = path.join(__dirname, '../themes/simple'); - Assert.areEqual(p, options.themedir); + Assert.areEqual(path.join(__dirname, '../themes/simple'), options.themedir); }, 'test: --theme foobar': function() { var options = Y.Options([ '--theme', 'foobar' ]); - var p = path.join(__dirname, '../themes/foobar'); - Assert.areEqual(p, options.themedir); + Assert.areEqual(path.join(__dirname, '../themes/foobar'), options.themedir); }, 'test: -t ./foobar': function() { var options = Y.Options([ @@ -337,7 +333,7 @@ suite.add(new YUITest.TestCase({ }, 'test --debug': function() { Assert.isFalse(Y.config.debug); - var options = Y.Options([ + Y.Options([ '--debug' ]); Assert.isTrue(Y.config.debug); @@ -347,14 +343,14 @@ suite.add(new YUITest.TestCase({ }); }, 'test: --charset': function() { - var options = Y.Options([ + Y.Options([ '--charset' ]); Assert.areEqual('utf8', Y.charset); }, 'test: --charset foo': function() { - var options = Y.Options([ + Y.Options([ '--charset', 'foo' ]); diff --git a/tests/parser.js b/tests/parser.js index 8b682e2e..3ba3d51f 100644 --- a/tests/parser.js +++ b/tests/parser.js @@ -1,3 +1,4 @@ +/*global Y:true */ var YUITest = require('yuitest'), Assert = YUITest.Assert, ArrayAssert = YUITest.ArrayAssert, @@ -67,28 +68,29 @@ suite.add(new YUITest.TestCase({ Assert.areSame('http://two.url', this.project.url[1], 'URL #2 is wrong'); }, 'test: files parsing': function() { - var files = this.data.files; + var files = this.data.files, + one, two, three, four; // 1 module, 3 classes - var one = files[path.normalize('input/test/anim.js')]; + one = files[path.normalize('input/test/anim.js')]; Assert.isObject(one, 'Failed to parse input/test/anim.js'); Assert.areSame(1, Object.keys(one.modules).length, '1 module should be found'); Assert.areSame(3, Object.keys(one.classes).length, '3 classes should be found'); // 2 modules, 3 classes - var two = files[path.normalize('input/test/test.js')]; + two = files[path.normalize('input/test/test.js')]; Assert.isObject(two, 'Failed to parse input/test/test.js'); Assert.areSame(2, Object.keys(two.modules).length, '2 modules should be found'); Assert.areSame(3, Object.keys(two.classes).length, '3 classes should be found'); //Module -> class association - var three = files[path.normalize('input/test2/dump/dump.js')]; + three = files[path.normalize('input/test2/dump/dump.js')]; Assert.isObject(three, 'Failed to parse input/test2/dump/dump.js'); Assert.areSame(1, three.modules.dump, 'dump module not found'); Assert.areSame(1, three.classes['YUI~dump'], 'YUI~dump class not found'); //Module -> class association - var four = files[path.normalize('input/test2/oop/oop.js')]; + four = files[path.normalize('input/test2/oop/oop.js')]; Assert.isObject(four, 'Failed to parse input/test2/oop/oop.js'); Assert.areSame(1, four.modules.oop, 'oop module not found'); Assert.areSame(1, four.classes['YUI~oop'], 'YUI~oop class not found'); @@ -123,33 +125,35 @@ suite.add(new YUITest.TestCase({ Assert.areSame('main', mod.itemtype, 'ItemType should be main'); }, 'test: submodule parsing': function() { - var mods = this.data.modules; + var mods = this.data.modules, + m, desc; //anim-easing submodule - var m = mods['anim-easing']; + m = mods['anim-easing']; Assert.isObject(m, 'Failed to parse anim-easing module'); - var desc = 'The easing module provides methods for customizing\nhow an animation behaves during each run.'; + desc = 'The easing module provides methods for customizing\nhow an animation behaves during each run.'; Assert.areSame(desc, m.description, 'Failed to parse submodule description'); Assert.areSame(0, Object.keys(m.submodules).length, 'Should have 0 submodules'); Assert.areSame(1, Object.keys(m.classes).length, 'Should have 1 class'); - Assert.areSame(1, m['is_submodule'], 'Submodule association failed'); + Assert.areSame(1, m.is_submodule, 'Submodule association failed'); Assert.areSame('anim', m.module, 'Failed to associate module'); //anim-easing-foo submodule - var m = mods['anim-easing-foo']; + m = mods['anim-easing-foo']; Assert.isObject(m, 'Failed to parse anim-easing-foo module'); - var desc = 'FOO FOO FOO FOO FOO The easing module provides methods for customizing'; + desc = 'FOO FOO FOO FOO FOO The easing module provides methods for customizing'; Assert.areSame(desc, m.description, 'Failed to parse submodule description'); Assert.areSame(0, Object.keys(m.submodules).length, 'Should have 0 submodules'); Assert.areSame(1, Object.keys(m.classes).length, 'Should have 1 class'); - Assert.areSame(1, m['is_submodule'], 'Submodule association failed'); + Assert.areSame(1, m.is_submodule, 'Submodule association failed'); Assert.areSame('anim', m.module, 'Failed to associate module'); }, 'test: extra module data parsing': function() { - var mods = this.data.modules; + var mods = this.data.modules, + m; - var m = mods.mymodule; + m = mods.mymodule; Assert.isObject(m, 'Failed to parse mymodule module'); Assert.areSame(1, Object.keys(m.submodules).length, 'Should have 1 submodules'); Assert.areSame(3, Object.keys(m.classes).length, 'Should have 3 class'); @@ -158,58 +162,59 @@ suite.add(new YUITest.TestCase({ ArrayAssert.itemsAreSame(['one', 'two'], m.requires, 'Requires parsing failed'); ArrayAssert.itemsAreSame(['three', 'four'], m.uses, 'Uses parsing failed'); - var m = mods.mysubmodule; + m = mods.mysubmodule; Assert.isObject(m, 'Failed to parse mysubmodule module'); Assert.areSame(0, Object.keys(m.submodules).length, 'Should have 0 submodules'); Assert.areSame(3, Object.keys(m.classes).length, 'Should have 3 class'); - Assert.areSame(1, m['is_submodule'], 'Submodule association failed'); + Assert.areSame(1, m.is_submodule, 'Submodule association failed'); ArrayAssert.itemsAreSame(['three', 'four'], m.category, 'Category parsing failed'); //Testing modules with slashes in them - var m = mods['myapp/views/index']; + m = mods['myapp/views/index']; Assert.isObject(m, 'Failed to parse myapp/views/index module'); Assert.areSame(1, Object.keys(m.classes).length, 'Should have 1 class'); - var m = mods['P.storage']; + m = mods['P.storage']; Assert.isObject(m, 'Failed to parse P.storage module'); ArrayAssert.itemsAreSame(['P.storage.Store', 'P.storage.LocalStore', 'P.storage'], Object.keys(m.classes), 'Failed to parse classes'); ArrayAssert.itemsAreSame(['P.storage', 'P'], Object.keys(m.namespaces), 'Namespace failed to parse'); }, 'test: class parsing': function() { - var cl = this.data.classes; + var cl = this.data.classes, + anim, easing, my, other, m; - var anim = cl.Anim; + anim = cl.Anim; Assert.isObject(anim, 'Failed to find Anim class'); Assert.areSame('Anim', anim.name, 'Failed to set name'); Assert.areSame('Anim', anim.shortname, 'Failed to set shortname'); Assert.areSame('anim', anim.module, 'Failed to test module.'); - var easing = cl.Easing; + easing = cl.Easing; Assert.isObject(easing, 'Failed to find Easing class'); Assert.areSame('Easing', easing.name, 'Failed to set name'); Assert.areSame('Easing', easing.shortname, 'Failed to set shortname'); Assert.areSame('anim', easing.module, 'Failed to test module.'); Assert.areSame('anim-easing', easing.submodule, 'Failed to test submodule.'); - var my = cl.myclass; + my = cl.myclass; Assert.isObject(my, 'Failed to find myclass class'); Assert.areSame('myclass', my.name, 'Failed to set name'); Assert.areSame('myclass', my.shortname, 'Failed to set shortname'); Assert.areSame('mymodule', my.module, 'Failed to test module.'); Assert.areSame('mysubmodule', my.submodule, 'Failed to test submodule.'); - Assert.areSame(1, my['is_constructor'], 'Failed to register constructor.'); + Assert.areSame(1, my.is_constructor, 'Failed to register constructor.'); - var other = cl.OtherClass; + other = cl.OtherClass; Assert.isObject(other, 'Failed to find myclass class'); Assert.areSame('OtherClass', other.name, 'Failed to set name'); Assert.areSame('OtherClass', other.shortname, 'Failed to set shortname'); Assert.areSame('mymodule', other.module, 'Failed to test module.'); Assert.areSame('mysubmodule', other.submodule, 'Failed to test submodule.'); - Assert.areSame(1, Object.keys(other['extension_for']).length, 'Failed to assign extension_for'); - Assert.areSame('myclass', other['extension_for'][0], 'Failed to assign extension_for'); + Assert.areSame(1, Object.keys(other.extension_for).length, 'Failed to assign extension_for'); + Assert.areSame('myclass', other.extension_for[0], 'Failed to assign extension_for'); - var m = cl['P.storage.P.storage']; + m = cl['P.storage.P.storage']; Assert.isUndefined(m, 'Should not have double namespaces'); Assert.isNotUndefined(cl['P.storage'], 'Should not have double namespaces'); @@ -218,15 +223,30 @@ suite.add(new YUITest.TestCase({ }, 'test: classitems parsing': function() { Assert.isArray(this.data.classitems, 'Failed to populate classitems array'); - - var item = this.findByName('testoptional', 'myclass'); + var keys, item, item2; + + item = this.findByName('testoptional', 'myclass'); Assert.areSame('testoptional', item.name, 'Failed to find item: testoptional'); Assert.areSame('myclass', item.class, 'Failed to find class: testoptional'); Assert.areSame('mymodule', item.module, 'Failed to find module: testoptional'); Assert.areSame('mysubmodule', item.submodule, 'Failed to find submodule: testoptional'); Assert.areSame('method', item.itemtype, 'Should be a method'); - var keys = [ 'file', 'line', 'description', 'itemtype', 'name', 'params', 'evil', 'injects', 'return', 'example', 'class', 'module', 'submodule' ]; + keys = [ + 'file', + 'line', + 'description', + 'itemtype', + 'name', + 'params', + 'evil', + 'injects', + 'return', + 'example', + 'class', + 'module', + 'submodule' + ]; ArrayAssert.itemsAreSame(keys, Object.keys(item), 'Item missing from output'); @@ -236,11 +256,12 @@ suite.add(new YUITest.TestCase({ Assert.isUndefined(item.return.type, 'Type should be missing'); Assert.areSame(2, item.example.length, 'Should have 2 example snippets'); - var item2 = this.findByName('testobjectparam', 'myclass'); + item2 = this.findByName('testobjectparam', 'myclass'); Assert.areSame('String', item2.return.type, 'Type should not be missing'); }, 'test: parameter parsing': function() { - var item = this.findByName('testoptional', 'myclass'); + var item, item2; + item = this.findByName('testoptional', 'myclass'); Assert.isArray(item.params, 'Params should be an array'); Assert.areSame(5, item.params.length, 'Failed to parse all 5 parameters'); @@ -259,14 +280,14 @@ suite.add(new YUITest.TestCase({ Assert.isTrue(item.params[4].optional, 'Parameter should be optional'); Assert.areSame('"defaultval"', item.params[4].optdefault, 'Optional Default value is incorrect'); - var item2 = this.findByName('test0ton', 'myclass'); + item2 = this.findByName('test0ton', 'myclass'); Assert.isArray(item2.params, 'Params should be an array'); Assert.areSame(1, item2.params.length, 'Failed to parse all 5 parameters'); Assert.isTrue(item2.params[0].optional, 'Optional not set'); Assert.isTrue(item2.params[0].multiple, 'Multiple not set'); Assert.isUndefined(item2.return.type, 'Type should be missing'); - var item2 = this.findByName('test1ton', 'myclass'); + item2 = this.findByName('test1ton', 'myclass'); Assert.isArray(item2.params, 'Params should be an array'); Assert.areSame(1, item2.params.length, 'Failed to parse all 5 parameters'); Assert.isUndefined(item2.params[0].optional, 'Optional should not be set'); @@ -275,8 +296,9 @@ suite.add(new YUITest.TestCase({ }, 'test: object parameters': function() { - var item = this.findByName('testobjectparam', 'myclass'); + var item, props; + item = this.findByName('testobjectparam', 'myclass'); Assert.areSame('testobjectparam', item.name, 'Failed to find item: testobjectparam'); Assert.areSame('myclass', item.class, 'Failed to find class: testobjectparam'); Assert.areSame('mymodule', item.module, 'Failed to find module: testobjectparam'); @@ -284,7 +306,7 @@ suite.add(new YUITest.TestCase({ Assert.areSame('method', item.itemtype, 'Should be a method'); Assert.areSame(1, item.params.length, 'More than one param found'); - var props = item.params[0].props; + props = item.params[0].props; Assert.areSame(2, props.length, 'First param should have props'); Assert.areSame('prop1', props[0].name, 'Invalid item'); Assert.areSame('prop1', props[0].description, 'Invalid item'); @@ -307,13 +329,11 @@ suite.add(new YUITest.TestCase({ item = this.findByName('crashTest', 'OtherClass2'); Assert.isObject(item, 'failed to find item'); Assert.areEqual(1, item.params.length, 'Failed to replace params with param'); - - }, 'test: double namespaces': function() { - var cls = this.data.classes; - var mod_bad = cls['Foo.Bar.Foo.Bar']; - var mod_good = cls['Foo.Bar']; + var cls = this.data.classes, + mod_bad = cls['Foo.Bar.Foo.Bar'], + mod_good = cls['Foo.Bar']; Assert.isUndefined(mod_bad, 'Found class Foo.Bar.Foo.Bar'); Assert.isObject(mod_good, 'Failed to parse Foo.Bar namespace'); }, @@ -353,15 +373,15 @@ suite.add(new YUITest.TestCase({ 'test: event with optional items': function() { var item = this.findByName('changeWithOptional', 'OtherClass2'); Assert.isObject(item, 'Failed to locate event object'); - var params = item.params; - Assert.isArray(params); - var ev = params[0]; - Assert.areSame(ev.name, 'ev'); - Assert.areSame(ev.type, 'EventFacade'); - var props = ev.props; - Assert.isArray(props); - Assert.areSame(props[0].name, 'name'); - Assert.isTrue(props[0].optional); + + Assert.isArray(item.params); + + Assert.areSame(item.params[0].name, 'ev'); + Assert.areSame(item.params[0].type, 'EventFacade'); + + Assert.isArray(item.params[0].props); + Assert.areSame(item.params[0].props[0].name, 'name'); + Assert.isTrue( item.params[0].props[0].optional); } })); diff --git a/tests/parser_coffee.js b/tests/parser_coffee.js index 9e778bfa..b33e8d19 100644 --- a/tests/parser_coffee.js +++ b/tests/parser_coffee.js @@ -1,15 +1,12 @@ +/*global Y:true */ var YUITest = require('yuitest'), Assert = YUITest.Assert, - ArrayAssert = YUITest.ArrayAssert, path = require('path'), - fs = require('fs'), Y = require(path.join(__dirname, '../', 'lib', 'index')); //Move to the test dir before running the tests. process.chdir(__dirname); -var existsSync = fs.existsSync || path.existsSync; - var suite = new YUITest.TestSuite({ name: 'Coffee Parser Test Suite', setUp: function() { diff --git a/tests/utils.js b/tests/utils.js index 4f146437..211a864a 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,6 +1,7 @@ +/*global Y:true */ var YUITest = require('yuitest'), Assert = YUITest.Assert, - path = require('path') + path = require('path'), Y = require(path.join(__dirname, '../', 'lib', 'index')) ; @@ -9,7 +10,7 @@ var suite = new YUITest.TestSuite({ }); suite.add(new YUITest.TestCase({ - name: 'getProjectData Folder Priority', + name: 'getProjectData Folder Priority', 'test: Nearest Folder Priority': function() { var d = Y.getProjectData('input/folders1'); Assert.areEqual('yuidoc-root', d.name, 'must use nearest yuidoc.json first'); From 3eda08eefe4a6b83918e3eba7bfe4e77c96c46d0 Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Wed, 29 Jan 2014 15:55:04 -0800 Subject: [PATCH 4/7] Beautify the world. --- lib/builder.js | 1056 +++++++++++++++-------------- lib/cli.js | 14 +- lib/docparser.js | 1468 ++++++++++++++++++++-------------------- lib/docview.js | 31 +- lib/files.js | 781 ++++++++++----------- lib/help.js | 42 +- lib/index.js | 4 +- lib/options.js | 282 ++++---- lib/project.js | 15 +- lib/server.js | 411 ++++++----- lib/utils.js | 737 ++++++++++---------- lib/yuidoc.js | 83 ++- tests/builder.js | 78 +-- tests/options.js | 103 ++- tests/parser.js | 86 +-- tests/parser_coffee.js | 12 +- tests/utils.js | 65 +- 17 files changed, 2673 insertions(+), 2595 deletions(-) diff --git a/lib/builder.js b/lib/builder.js index f3117e11..7c7af1d2 100644 --- a/lib/builder.js +++ b/lib/builder.js @@ -3,9 +3,9 @@ Copyright (c) 2011, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://yuilibrary.com/license/ */ -var marked = require("marked"), +var marked = require('marked'), fs = require('graceful-fs'), - noop = function() {}, + noop = function () {}, path = require('path'), TEMPLATE; @@ -16,28 +16,28 @@ based templates to generate static HTML content * @module yuidoc */ -YUI.add('doc-builder', function(Y) { +YUI.add('doc-builder', function (Y) { /*jshint onevar:false */ var fixType = Y.Lang.fixType, - print = function(items) { - var out = '
    '; - - Y.each(items, function(i, k) { - out += '
  • '; - if (Y.Lang.isObject(i)) { - if (!i.path) { - out += k + '/' + print(i); - } else { - out += '' + k + ''; + print = function (items) { + var out = '
      '; + + Y.each(items, function (i, k) { + out += '
    • '; + if (Y.Lang.isObject(i)) { + if (!i.path) { + out += k + '/' + print(i); + } else { + out += '' + k + ''; + } } - } - out += '
    • '; - }); + out += ''; + }); - out += '
    '; - return out; - }; + out += '
'; + return out; + }; Y.Handlebars.registerHelper('buildFileTree', function (items) { return print(items); @@ -46,7 +46,7 @@ YUI.add('doc-builder', function(Y) { var DEFAULT_THEME = path.join(__dirname, '../', 'themes', 'default'), themeDir = DEFAULT_THEME; - Y.DocBuilder = function(options, data) { + Y.DocBuilder = function (options, data) { this.options = options; if (options.helpers) { this._addHelpers(options.helpers); @@ -60,7 +60,7 @@ YUI.add('doc-builder', function(Y) { this.files = 0; var self = this; - Y.Handlebars.registerHelper('crossLink', function(item, options) { + Y.Handlebars.registerHelper('crossLink', function (item, options) { var str = ''; if (!item) { item = ''; @@ -68,8 +68,8 @@ YUI.add('doc-builder', function(Y) { //console.log('CrossLink:', item); if (item.indexOf('|') > 0) { var parts = item.split('|'), - p = []; - Y.each(parts, function(i) { + p = []; + Y.each(parts, function (i) { p.push(self._parseCrossLink.call(self, i)); }); str = p.join(' | '); @@ -79,7 +79,7 @@ YUI.add('doc-builder', function(Y) { return str; }); - Y.Handlebars.registerHelper('crossLinkModule', function(item, options) { + Y.Handlebars.registerHelper('crossLinkModule', function (item, options) { var str = item; if (self.data.modules[item]) { var content = options.fn(this); @@ -87,7 +87,7 @@ YUI.add('doc-builder', function(Y) { content = item; } str = '' + content + ''; + '.html">' + content + ''; } return str; }); @@ -99,8 +99,8 @@ YUI.add('doc-builder', function(Y) { } if (item.indexOf('|') > 0) { var parts = item.split('|'), - p = []; - Y.each(parts, function(i) { + p = []; + Y.each(parts, function (i) { p.push(self._parseCrossLink.call(self, i, true)); }); str = p.join(' | '); @@ -118,30 +118,30 @@ YUI.add('doc-builder', function(Y) { Y.DocBuilder.prototype = { /** - * Register a `Y.Handlebars` helper method - * @method _addHelpers - * @param {Object} helpers Object containing a hash of names and functions - */ - _addHelpers: function(helpers) { + * Register a `Y.Handlebars` helper method + * @method _addHelpers + * @param {Object} helpers Object containing a hash of names and functions + */ + _addHelpers: function (helpers) { Y.log('Importing helpers: ' + helpers, 'info', 'builder'); - helpers.forEach(function(imp) { + helpers.forEach(function (imp) { if (!Y.Files.exists(imp) || Y.Files.exists(path.join(process.cwd(), imp))) { - imp= path.join(process.cwd(), imp); + imp = path.join(process.cwd(), imp); } var h = require(imp); - Object.keys(h).forEach(function(name) { + Object.keys(h).forEach(function (name) { Y.Handlebars.registerHelper(name, h[name]); }); }); }, /** - * Wrapper around the Markdown parser so it can be normalized or even side stepped - * @method markdown - * @private - * @param {String} md The Markdown string to parse - * @return {HTML} The rendered HTML - */ - markdown: function(md) { + * Wrapper around the Markdown parser so it can be normalized or even side stepped + * @method markdown + * @private + * @param {String} md The Markdown string to parse + * @return {HTML} The rendered HTML + */ + markdown: function (md) { var html = marked(md, this.options.markdown); //Only reprocess if helpers were asked for if (this.options.helpers || (html.indexOf('{{#crossLink') > -1)) { @@ -160,16 +160,16 @@ YUI.add('doc-builder', function(Y) { } return html; }, - + /** - * Parse the item to be cross linked and return an HREF linked to the item - * @method _parseCrossLink - * @private - * @param {String} item The item to crossLink - * @param {Boolean} [raw=false] Do not wrap it in HTML - * @param {String} [content] crossLink helper content - */ - _parseCrossLink: function(item, raw, content) { + * Parse the item to be cross linked and return an HREF linked to the item + * @method _parseCrossLink + * @private + * @param {String} item The item to crossLink + * @param {Boolean} [raw=false] Do not wrap it in HTML + * @param {String} [content] crossLink helper content + */ + _parseCrossLink: function (item, raw, content) { var self = this; var base = '../', baseItem, @@ -222,7 +222,7 @@ YUI.add('doc-builder', function(Y) { if (cls && method) { if (self.data.classes[cls]) { - self.data.classitems.forEach(function(i) { + self.data.classitems.forEach(function (i) { if (i.itemtype === type && i.name === method && i.class === cls) { link = true; baseItem = method; @@ -269,10 +269,10 @@ YUI.add('doc-builder', function(Y) { return (raw) ? href : item; }, /** - * List of native types to cross link to MDN - * @property NATIVES - * @type Object - */ + * List of native types to cross link to MDN + * @property NATIVES + * @type Object + */ NATIVES: { 'Array': 1, 'Boolean': 1, @@ -302,44 +302,44 @@ YUI.add('doc-builder', function(Y) { 'TypeError': 1, 'undefined': 1, 'URIError': 1, - 'HTMLElement': 'https:/'+'/developer.mozilla.org/en/Document_Object_Model_(DOM)/', - 'HTMLCollection': 'https:/'+'/developer.mozilla.org/en/Document_Object_Model_(DOM)/', - 'DocumentFragment': 'https:/'+'/developer.mozilla.org/en/Document_Object_Model_(DOM)/', - 'HTMLDocument': 'https:/'+'/developer.mozilla.org/en/Document_Object_Model_(DOM)/' + 'HTMLElement': 'https:/' + '/developer.mozilla.org/en/Document_Object_Model_(DOM)/', + 'HTMLCollection': 'https:/' + '/developer.mozilla.org/en/Document_Object_Model_(DOM)/', + 'DocumentFragment': 'https:/' + '/developer.mozilla.org/en/Document_Object_Model_(DOM)/', + 'HTMLDocument': 'https:/' + '/developer.mozilla.org/en/Document_Object_Model_(DOM)/' }, /** - * Function to link an external type uses `NATIVES` object - * @method NATIVES_LINKER - * @private - * @param {String} name The name of the type to link - * @return {String} The combined URL - */ - NATIVES_LINKER: function(name) { - var url = 'https:/'+'/developer.mozilla.org/en/JavaScript/Reference/Global_Objects/'; + * Function to link an external type uses `NATIVES` object + * @method NATIVES_LINKER + * @private + * @param {String} name The name of the type to link + * @return {String} The combined URL + */ + NATIVES_LINKER: function (name) { + var url = 'https:/' + '/developer.mozilla.org/en/JavaScript/Reference/Global_Objects/'; if (this.NATIVES[name] !== 1) { url = this.NATIVES[name]; } return url + name; }, /** - * Mixes the various external data soures together into the local data, augmenting - * it with flags. - * @method _mixExternal - * @private - */ - _mixExternal: function() { + * Mixes the various external data soures together into the local data, augmenting + * it with flags. + * @method _mixExternal + * @private + */ + _mixExternal: function () { var self = this; Y.log('External data received, mixing', 'info', 'builder'); - self.options.externalData.forEach(function(exData) { - - ['files', 'classes', 'modules'].forEach(function(k) { - Y.each(exData[k], function(item, key) { + self.options.externalData.forEach(function (exData) { + + ['files', 'classes', 'modules'].forEach(function (k) { + Y.each(exData[k], function (item, key) { item.external = true; var file = item.name; if (!item.file) { file = self.filterFileName(item.name); } - + if (item.type) { item.type = fixType(item.type); } @@ -349,14 +349,14 @@ YUI.add('doc-builder', function(Y) { self.data[k][key] = item; }); }); - Y.each(exData.classitems, function(item) { + Y.each(exData.classitems, function (item) { item.external = true; item.path = exData.base + path.join('files', self.filterFileName(item.file) + '.html'); if (item.type) { item.type = fixType(item.type); } if (item.params) { - item.params.forEach(function(p) { + item.params.forEach(function (p) { if (p.type) { p.type = fixType(p.type); } @@ -370,12 +370,12 @@ YUI.add('doc-builder', function(Y) { }); }, /** - * Fetches the remote data and fires the callback when it's all complete - * @method mixExternal - * @param {Callback} cb The callback to execute when complete - * @async - */ - mixExternal: function(cb) { + * Fetches the remote data and fires the callback when it's all complete + * @method mixExternal + * @param {Callback} cb The callback to execute when complete + * @async + */ + mixExternal: function (cb) { var self = this, info = self.options.external; @@ -392,20 +392,20 @@ YUI.add('doc-builder', function(Y) { return; } if (!Y.Lang.isArray(info.data)) { - info.data = [ info.data ]; + info.data = [info.data]; } Y.log('Importing external documentation data.', 'info', 'builder'); - + var stack = new Y.Parallel(); - info.data.forEach(function(i) { + info.data.forEach(function (i) { var base; if (i.match(/^https?:\/\//)) { base = i.replace('data.json', ''); - Y.use('io-base', stack.add(function() { + Y.use('io-base', stack.add(function () { Y.log('Fetching: ' + i, 'info', 'builder'); Y.io(i, { on: { - complete: stack.add(function(id, e) { + complete: stack.add(function (id, e) { Y.log('Received: ' + i, 'info', 'builder'); var data = JSON.parse(e.responseText); data.base = base; @@ -430,31 +430,31 @@ YUI.add('doc-builder', function(Y) { } }); - stack.done(function() { + stack.done(function () { Y.log('Finished fetching remote data', 'info', 'builder'); self._mixExternal(); cb(); }); }, /** - * File counter - * @property files - * @type Number - */ + * File counter + * @property files + * @type Number + */ files: null, /** - * Holder for project meta data - * @property _meta - * @type Object - * @private - */ + * Holder for project meta data + * @property _meta + * @type Object + * @private + */ _meta: null, /** - * Prep the meta data to be fed to Selleck - * @method getProjectMeta - * @return {Object} The project metadata - */ - getProjectMeta: function() { + * Prep the meta data to be fed to Selleck + * @method getProjectMeta + * @return {Object} The project metadata + */ + getProjectMeta: function () { var obj = { meta: { yuiSeedUrl: 'http://yui.yahooapis.com/3.5.0/build/yui/yui-min.js', @@ -486,22 +486,24 @@ YUI.add('doc-builder', function(Y) { } else { obj.meta = this._meta; } - Y.each(this.data.project, function(v, k) { + Y.each(this.data.project, function (v, k) { var key = k.substring(0, 1).toUpperCase() + k.substring(1, k.length); obj.meta['project' + key] = v; }); return obj; }, /** - * Populate the meta data for classes - * @method populateClasses - * @param {Object} opts The original options - * @return {Object} The modified options - */ - populateClasses: function(opts) { + * Populate the meta data for classes + * @method populateClasses + * @param {Object} opts The original options + * @return {Object} The modified options + */ + populateClasses: function (opts) { opts.meta.classes = []; - Y.each(this.data.classes, function(v) { - if (v.external) { return; } + Y.each(this.data.classes, function (v) { + if (v.external) { + return; + } opts.meta.classes.push({ displayName: v.name, name: v.name, @@ -515,31 +517,40 @@ YUI.add('doc-builder', function(Y) { return opts; }, /** - * Populate the meta data for modules - * @method populateModules - * @param {Object} opts The original options - * @return {Object} The modified options - */ - populateModules: function(opts) { + * Populate the meta data for modules + * @method populateModules + * @param {Object} opts The original options + * @return {Object} The modified options + */ + populateModules: function (opts) { var self = this; opts.meta.modules = []; opts.meta.allModules = []; - Y.each(this.data.modules, function(v) { - if (v.external) { return; } - opts.meta.allModules.push({ displayName: v.displayName || v.name, name: self.filterFileName(v.name), description: v.description }); + Y.each(this.data.modules, function (v) { + if (v.external) { + return; + } + opts.meta.allModules.push({ + displayName: v.displayName || v.name, + name: self.filterFileName(v.name), + description: v.description + }); if (!v.is_submodule) { - var o = { displayName: v.displayName || v.name, name: self.filterFileName(v.name) }; + var o = { + displayName: v.displayName || v.name, + name: self.filterFileName(v.name) + }; if (v.submodules) { o.submodules = []; - Y.each(v.submodules, function(i, k) { + Y.each(v.submodules, function (i, k) { var moddef = self.data.modules[k]; if (moddef) { o.submodules.push({ displayName: k, description: moddef.description }); - // } else { - // Y.log('Submodule data missing: ' + k + ' for ' + v.name, 'warn', 'builder'); + // } else { + // Y.log('Submodule data missing: ' + k + ' for ' + v.name, 'warn', 'builder'); } }); o.submodules.sort(self.nameSort); @@ -552,30 +563,38 @@ YUI.add('doc-builder', function(Y) { return opts; }, /** - * Populate the meta data for files - * @method populateFiles - * @param {Object} opts The original options - * @return {Object} The modified options - */ - populateFiles: function(opts) { + * Populate the meta data for files + * @method populateFiles + * @param {Object} opts The original options + * @return {Object} The modified options + */ + populateFiles: function (opts) { var self = this; opts.meta.files = []; - Y.each(this.data.files, function(v) { - if (v.external) { return; } - opts.meta.files.push({ displayName: v.name, name: self.filterFileName(v.name), path: v.path || v.name }); + Y.each(this.data.files, function (v) { + if (v.external) { + return; + } + opts.meta.files.push({ + displayName: v.name, + name: self.filterFileName(v.name), + path: v.path || v.name + }); }); var tree = {}; var files = []; - Y.each(this.data.files, function(v) { - if (v.external) { return; } + Y.each(this.data.files, function (v) { + if (v.external) { + return; + } files.push(v.name); }); files.sort(); - Y.each(files, function(v) { + Y.each(files, function (v) { var p = v.split('/'), par; - p.forEach(function(i, k) { + p.forEach(function (i, k) { if (!par) { if (!tree[i]) { tree[i] = {}; @@ -601,12 +620,12 @@ YUI.add('doc-builder', function(Y) { return opts; }, /** - * Parses file and line number from an item object and build's an HREF - * @method addFoundAt - * @param {Object} a The item to parse - * @return {String} The parsed HREF - */ - addFoundAt: function(a) { + * Parses file and line number from an item object and build's an HREF + * @method addFoundAt + * @param {Object} a The item to parse + * @return {String} The parsed HREF + */ + addFoundAt: function (a) { var self = this; if (a.file && a.line && !self.options.nocode) { a.foundAt = '../files/' + self.filterFileName(a.file) + '.html#l' + a.line; @@ -617,18 +636,18 @@ YUI.add('doc-builder', function(Y) { return a; }, /** - * Augments the **DocParser** meta data to provide default values for certain keys as well as parses all descriptions - * with the `Markdown Parser` - * @method augmentData - * @param {Object} o The object to recurse and augment - * @return {Object} The augmented object - */ - augmentData: function(o) { + * Augments the **DocParser** meta data to provide default values for certain keys as well as parses all descriptions + * with the `Markdown Parser` + * @method augmentData + * @param {Object} o The object to recurse and augment + * @return {Object} The augmented object + */ + augmentData: function (o) { var self = this; o = self.addFoundAt(o); - Y.each(o, function(i, k1) { + Y.each(o, function (i, k1) { if (i && i.forEach) { - Y.each(i, function(a, k) { + Y.each(i, function (a, k) { if (!(a instanceof Object)) { return; } @@ -649,7 +668,7 @@ YUI.add('doc-builder', function(Y) { } a = self.addFoundAt(a); - Y.each(a, function(c, d) { + Y.each(a, function (c, d) { if (c.forEach || (c instanceof Object)) { c = self.augmentData(c); a[d] = c; @@ -660,7 +679,7 @@ YUI.add('doc-builder', function(Y) { }); } else if (i instanceof Object) { i = self.addFoundAt(i); - Y.each(i, function(v, k) { + Y.each(i, function (v, k) { if (k === 'final') { o[k1][k] = true; } @@ -683,18 +702,18 @@ YUI.add('doc-builder', function(Y) { return o; }, /** - * Makes the default directories needed - * @method makeDirs - * @param {Callback} cb The callback to execute after it's completed - */ - makeDirs: function(cb) { + * Makes the default directories needed + * @method makeDirs + * @param {Callback} cb The callback to execute after it's completed + */ + makeDirs: function (cb) { var self = this; var dirs = ['classes', 'modules', 'files']; if (self.options.dumpview) { dirs.push('json'); } - var writeRedirect = function(dir, file, cb) { - Y.Files.exists(file, function(x) { + var writeRedirect = function (dir, file, cb) { + Y.Files.exists(file, function (x) { if (x) { var out = path.join(dir, 'index.html'); fs.createReadStream(file).pipe(fs.createWriteStream(out)); @@ -705,11 +724,11 @@ YUI.add('doc-builder', function(Y) { var defaultIndex = path.join(themeDir, 'assets', 'index.html'); var stack = new Y.Parallel(); Y.log('Making default directories: ' + dirs.join(','), 'info', 'builder'); - dirs.forEach(function(d) { + dirs.forEach(function (d) { var dir = path.join(self.options.outdir, d); - Y.Files.exists(dir, stack.add(function(x) { + Y.Files.exists(dir, stack.add(function (x) { if (!x) { - fs.mkdir(dir, 0777, stack.add(function() { + fs.mkdir(dir, 0777, stack.add(function () { writeRedirect(dir, defaultIndex, stack.add(noop)); })); } else { @@ -717,7 +736,7 @@ YUI.add('doc-builder', function(Y) { } })); }); - stack.done(function() { + stack.done(function () { if (cb) { cb(); } @@ -725,23 +744,23 @@ YUI.add('doc-builder', function(Y) { }, - _resolveUrl: function(url, opts) { + _resolveUrl: function (url, opts) { if (!url) { - return null; + return null; } - if ( url.indexOf("://") >= 0 ) { + if (url.indexOf("://") >= 0) { return url; } return path.join(opts.meta.projectRoot, url); }, /** - * Parses `
` tags and adds the __prettyprint__ `className` to them
-        * @method _parseCode
-        * @private
-        * @param {HTML} html The HTML to parse
-        * @return {HTML} The parsed HTML
-        */
+         * Parses `
` tags and adds the __prettyprint__ `className` to them
+         * @method _parseCode
+         * @private
+         * @param {HTML} html The HTML to parse
+         * @return {HTML} The parsed HTML
+         */
         _parseCode: function (html) {
             html = html || '';
             //html = html.replace(/
/g, '
');
@@ -756,7 +775,7 @@ YUI.add('doc-builder', function(Y) {
         * @param {HTML} html The HTML to parse
         * @return {HTML} The parsed HTML
         */
-        _inlineCode: function(html) {
+        _inlineCode: function (html) {
             html = html.replace(/\\`/g, '__{{SELLECK_BACKTICK}}__');
 
             html = html.replace(/`(.+?)`/g, function (match, code) {
@@ -779,7 +798,7 @@ YUI.add('doc-builder', function(Y) {
         * @param {Error} callback.err
         * @param {HTML} callback.html The assembled template markup
         */
-        render: function(source, view, layout, partials, callback) {
+        render: function (source, view, layout, partials, callback) {
             var html = [];
 
             // function buffer(line) {
@@ -794,8 +813,10 @@ YUI.add('doc-builder', function(Y) {
                 callback = layout;
                 layout = null;
             }
-            var parts = Y.merge(partials || {}, { layout_content: source });
-            Y.each(parts, function(source, name) {
+            var parts = Y.merge(partials || {}, {
+                layout_content: source
+            });
+            Y.each(parts, function (source, name) {
                 Y.Handlebars.registerPartial(name, source);
             });
 
@@ -815,23 +836,23 @@ YUI.add('doc-builder', function(Y) {
             html = TEMPLATE(_v);
             //html = html.replace(/{{//g, '{{/');
 
-            
+
             //html = (Y.Handlebars.compile(html))({});
 
             html = this._inlineCode(html);
             callback(null, html);
         },
         /**
-        * Render the index file
-        * @method renderIndex
-        * @param {Function} cb The callback fired when complete
-        * @param {String} cb.html The HTML to render this view
-        * @param {Object} cv.view The View Data
-        */
-        renderIndex: function(cb) {
+         * Render the index file
+         * @method renderIndex
+         * @param {Function} cb The callback fired when complete
+         * @param {String} cb.html The HTML to render this view
+         * @param {Object} cv.view The View Data
+         */
+        renderIndex: function (cb) {
             var self = this;
 
-            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function(err, opts) {
+            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function (err, opts) {
                 opts.meta.title = self.data.project.name;
                 opts.meta.projectRoot = './';
                 opts.meta.projectAssets = './assets';
@@ -839,24 +860,24 @@ YUI.add('doc-builder', function(Y) {
                 opts = self.populateClasses(opts);
                 opts = self.populateModules(opts);
 
-                var view   = new Y.DocView(opts.meta);
-                self.render('{{>index}}', view, opts.layouts.main, opts.partials, function(err, html) {
+                var view = new Y.DocView(opts.meta);
+                self.render('{{>index}}', view, opts.layouts.main, opts.partials, function (err, html) {
                     self.files++;
                     cb(html, view);
                 });
             });
         },
         /**
-        * Generates the index.html file
-        * @method writeIndex
-        * @param {Callback} cb The callback to execute after it's completed
-        */
-        writeIndex: function(cb) {
+         * Generates the index.html file
+         * @method writeIndex
+         * @param {Callback} cb The callback to execute after it's completed
+         */
+        writeIndex: function (cb) {
             var self = this,
                 stack = new Y.Parallel();
 
             Y.log('Preparing index.html', 'info', 'builder');
-            self.renderIndex(stack.add(function(html, view) {
+            self.renderIndex(stack.add(function (html, view) {
                 stack.html = html;
                 stack.view = view;
                 if (self.options.dumpview) {
@@ -865,25 +886,25 @@ YUI.add('doc-builder', function(Y) {
                 Y.Files.writeFile(path.join(self.options.outdir, 'index.html'), html, stack.add(noop));
             }));
 
-            stack.done(function(/* html, view */) {
+            stack.done(function ( /* html, view */ ) {
                 Y.log('Writing index.html', 'info', 'builder');
                 cb(stack.html, stack.view);
             });
         },
         /**
-        * Render a module 
-        * @method renderModule
-        * @param {Function} cb The callback fired when complete
-        * @param {String} cb.html The HTML to render this view
-        * @param {Object} cv.view The View Data
-        */
-        renderModule: function(cb, data, layout) {
+         * Render a module
+         * @method renderModule
+         * @param {Function} cb The callback fired when complete
+         * @param {String} cb.html The HTML to render this view
+         * @param {Object} cv.view The View Data
+         */
+        renderModule: function (cb, data, layout) {
             var self = this;
             var stack = new Y.Parallel();
 
             data.displayName = data.name;
             data.name = self.filterFileName(data.name);
-            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function(err, opts) {
+            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function (err, opts) {
                 opts.meta = Y.merge(opts.meta, data);
 
                 //opts.meta.htmlTitle = v.name + ': ' + self.data.project.name;
@@ -903,10 +924,13 @@ YUI.add('doc-builder', function(Y) {
 
                 if (data.classes && Object.keys(data.classes).length) {
                     opts.meta.moduleClasses = [];
-                    Y.each(Object.keys(data.classes), function(name) {
+                    Y.each(Object.keys(data.classes), function (name) {
                         var i = self.data.classes[name];
                         if (i) {
-                            opts.meta.moduleClasses.push({ name: i.name, displayName: i.name });
+                            opts.meta.moduleClasses.push({
+                                name: i.name,
+                                displayName: i.name
+                            });
                         }
                     });
                     opts.meta.moduleClasses.sort(self.nameSort);
@@ -914,7 +938,7 @@ YUI.add('doc-builder', function(Y) {
                 if (data.example && data.example.length) {
                     if (data.example.forEach) {
                         var e = '';
-                        data.example.forEach(function(v) {
+                        data.example.forEach(function (v) {
                             e += self._parseCode(self.markdown(v));
                         });
                         data.example = e;
@@ -925,50 +949,56 @@ YUI.add('doc-builder', function(Y) {
                 }
                 if (data.submodules && Object.keys(data.submodules).length) {
                     opts.meta.subModules = [];
-                    Y.each(Object.keys(data.submodules), function(name) {
+                    Y.each(Object.keys(data.submodules), function (name) {
                         var i = self.data.modules[name];
                         if (i) {
-                            opts.meta.subModules.push({ name: i.name, displayName: i.name, description: i.description });
+                            opts.meta.subModules.push({
+                                name: i.name,
+                                displayName: i.name,
+                                description: i.description
+                            });
                         }
                     });
                     opts.meta.subModules.sort(self.nameSort);
                 }
 
-                var view   = new Y.DocView(opts.meta);
+                var view = new Y.DocView(opts.meta);
                 var mainLayout = opts.layouts[layout];
-                self.render('{{>module}}', view, mainLayout, opts.partials, stack.add(function(err, html) {
+                self.render('{{>module}}', view, mainLayout, opts.partials, stack.add(function (err, html) {
                     self.files++;
                     stack.html = html;
                     stack.view = view;
                 }));
             });
 
-            stack.done(function() {
+            stack.done(function () {
                 cb(stack.html, stack.view);
             });
         },
         /**
-        * Generates the module files under "out"/modules/
-        * @method writeModules
-        * @param {Callback} cb The callback to execute after it's completed
-        */
-        writeModules: function(cb, layout) {
+         * Generates the module files under "out"/modules/
+         * @method writeModules
+         * @param {Callback} cb The callback to execute after it's completed
+         */
+        writeModules: function (cb, layout) {
             layout = layout || 'main';
             var self = this,
                 stack = new Y.Parallel();
             stack.html = [];
             stack.view = [];
-            
+
             var counter = 0;
-                Object.keys(self.data.modules).forEach(function(k) {
-                    if (!self.data.modules[k].external) {
-                        counter++;
-                    }
-                });
+            Object.keys(self.data.modules).forEach(function (k) {
+                if (!self.data.modules[k].external) {
+                    counter++;
+                }
+            });
             Y.log('Rendering and writing ' + counter + ' modules pages.', 'info', 'builder');
-            Y.each(self.data.modules, function(v) {
-                if (v.external) { return; }
-                self.renderModule(function(html, view) {
+            Y.each(self.data.modules, function (v) {
+                if (v.external) {
+                    return;
+                }
+                self.renderModule(function (html, view) {
                     stack.html.push(html);
                     stack.view.push(view);
                     if (self.options.dumpview) {
@@ -981,19 +1011,19 @@ YUI.add('doc-builder', function(Y) {
                     Y.Files.writeFile(path.join(self.options.outdir, 'modules', v.name + '.html'), html, stack.add(noop));
                 }, v, layout);
             });
-            stack.done(function() {
+            stack.done(function () {
                 Y.log('Finished writing module files', 'info', 'builder');
                 cb(stack.html, stack.view);
             });
         },
         /**
-        * Checks an array of items (class items) to see if an item is in that list
-        * @method hasProperty
-        * @param {Array} a The Array of items to check
-        * @param {Object} b The object to find
-        * @return Boolean
-        */
-        hasProperty: function(a, b) {
+         * Checks an array of items (class items) to see if an item is in that list
+         * @method hasProperty
+         * @param {Array} a The Array of items to check
+         * @param {Object} b The object to find
+         * @return Boolean
+         */
+        hasProperty: function (a, b) {
             var other = false;
             Y.some(a, function (i, k) {
                 if ((i.itemtype === b.itemtype) && (i.name === b.name)) {
@@ -1004,35 +1034,35 @@ YUI.add('doc-builder', function(Y) {
             return other;
         },
         /**
-        * Counter for stepping into merges
-        * @private
-        * @property _mergeCounter
-        * @type Number
-        */
+         * Counter for stepping into merges
+         * @private
+         * @property _mergeCounter
+         * @type Number
+         */
         _mergeCounter: null,
         /**
-        * Merge superclass data into a child class
-        * @method mergeExtends
-        * @param {Object} info The item to extend
-        * @param {Array} classItems The list of items to merge in
-        * @param {Boolean} first Set for the first call
-        */
-        mergeExtends: function(info, classItems, first) {
+         * Merge superclass data into a child class
+         * @method mergeExtends
+         * @param {Object} info The item to extend
+         * @param {Array} classItems The list of items to merge in
+         * @param {Boolean} first Set for the first call
+         */
+        mergeExtends: function (info, classItems, first) {
             var self = this;
             self._mergeCounter = (first) ? 0 : (self._mergeCounter + 1);
 
             if (self._mergeCounter === 100) {
-                throw('YUIDoc detected a loop extending class ' + info.name);
+                throw ('YUIDoc detected a loop extending class ' + info.name);
             }
             if (info.extends || info.uses) {
                 var hasItems = {};
                 hasItems[info.extends] = 1;
                 if (info.uses) {
-                    info.uses.forEach(function(v) {
+                    info.uses.forEach(function (v) {
                         hasItems[v] = 1;
                     });
                 }
-                self.data.classitems.forEach(function(v) {
+                self.data.classitems.forEach(function (v) {
                     //console.error(v.class, '==', info.extends);
                     if (hasItems[v.class]) {
                         if (!v.static) {
@@ -1063,17 +1093,17 @@ YUI.add('doc-builder', function(Y) {
             return classItems;
         },
         /**
-        * Render the class file
-        * @method renderClass
-        * @param {Function} cb The callback fired when complete
-        * @param {String} cb.html The HTML to render this view
-        * @param {Object} cv.view The View Data
-        */
-        renderClass: function(cb, data, layout) {
+         * Render the class file
+         * @method renderClass
+         * @param {Function} cb The callback fired when complete
+         * @param {String} cb.html The HTML to render this view
+         * @param {Object} cv.view The View Data
+         */
+        renderClass: function (cb, data, layout) {
             var self = this;
             var stack = new Y.Parallel();
 
-            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function(err, opts) {
+            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function (err, opts) {
                 //console.log(opts);
                 if (err) {
                     console.log(err);
@@ -1112,7 +1142,7 @@ YUI.add('doc-builder', function(Y) {
                 }
 
                 var classItems = [];
-                self.data.classitems.forEach(function(i) {
+                self.data.classitems.forEach(function (i) {
                     if (i.class === data.name) {
                         classItems.push(i);
                     }
@@ -1125,7 +1155,7 @@ YUI.add('doc-builder', function(Y) {
                     i = self.augmentData(i);
                     i.paramsList = [];
                     if (i.params) {
-                        i.params.forEach(function(p) {
+                        i.params.forEach(function (p) {
                             var name = p.name;
                             if (p.optional) {
                                 name = '[' + name + ((p.optdefault) ? '=' + p.optdefault : '') + ']';
@@ -1151,7 +1181,7 @@ YUI.add('doc-builder', function(Y) {
                     if (i.example && i.example.length) {
                         if (i.example.forEach) {
                             var e = '';
-                            i.example.forEach(function(v) {
+                            i.example.forEach(function (v) {
                                 e += self._parseCode(self.markdown(v));
                             });
                             i.example = e;
@@ -1161,158 +1191,158 @@ YUI.add('doc-builder', function(Y) {
                     }
                 }
 
-                classItems.forEach(function(i) {
+                classItems.forEach(function (i) {
                     var e;
                     switch (i.itemtype) {
-                        case 'method':
-                            i = self.augmentData(i);
-                            i.paramsList = [];
-                            if (i.params && i.params.forEach) {
-                                i.params.forEach(function(p) {
-                                    var name = p.name;
-                                    if (p.optional) {
-                                        name = '[' + name + ((p.optdefault) ? '=' + p.optdefault : '') + ']';
-                                    }
-                                    i.paramsList.push(name);
-                                });
-                            }
-                            //i.methodDescription = self._parseCode(markdown(i.description || ''));
-                            i.methodDescription = self._parseCode(i.description);
-                            if (i.example && i.example.length) {
-                                if (i.example.forEach) {
-                                    e = '';
-                                    i.example.forEach(function(v) {
-                                        e += self._parseCode(self.markdown(v));
-                                    });
-                                    i.example = e;
-                                } else {
-                                    i.example = self._parseCode(self.markdown(i.example));
+                    case 'method':
+                        i = self.augmentData(i);
+                        i.paramsList = [];
+                        if (i.params && i.params.forEach) {
+                            i.params.forEach(function (p) {
+                                var name = p.name;
+                                if (p.optional) {
+                                    name = '[' + name + ((p.optdefault) ? '=' + p.optdefault : '') + ']';
                                 }
-                            }
-                            i.hasAccessType = i.access;
-                            i.hasParams = i.paramsList.length;
-                            if (i.paramsList.length) {
-                                i.paramsList = i.paramsList.join(', ');
+                                i.paramsList.push(name);
+                            });
+                        }
+                        //i.methodDescription = self._parseCode(markdown(i.description || ''));
+                        i.methodDescription = self._parseCode(i.description);
+                        if (i.example && i.example.length) {
+                            if (i.example.forEach) {
+                                e = '';
+                                i.example.forEach(function (v) {
+                                    e += self._parseCode(self.markdown(v));
+                                });
+                                i.example = e;
                             } else {
-                                i.paramsList = ' ';
-                            }
-                            i.returnType = ' ';
-                            if (i["return"]) {
-                                i.hasReturn = true;
-                                i.returnType = i["return"].type;
+                                i.example = self._parseCode(self.markdown(i.example));
                             }
+                        }
+                        i.hasAccessType = i.access;
+                        i.hasParams = i.paramsList.length;
+                        if (i.paramsList.length) {
+                            i.paramsList = i.paramsList.join(', ');
+                        } else {
+                            i.paramsList = ' ';
+                        }
+                        i.returnType = ' ';
+                        if (i["return"]) {
+                            i.hasReturn = true;
+                            i.returnType = i["return"].type;
+                        }
 
-                            // If this item is provided by a module other
-                            // than the module that provided the original
-                            // class, add the original module name to the
-                            // item's `providedBy` property so we can
-                            // indicate the relationship.
-                            if ((i.submodule || i.module) !== (data.submodule || data.module)) {
-                                i.providedBy = (i.submodule || i.module);
-                            }
+                        // If this item is provided by a module other
+                        // than the module that provided the original
+                        // class, add the original module name to the
+                        // item's `providedBy` property so we can
+                        // indicate the relationship.
+                        if ((i.submodule || i.module) !== (data.submodule || data.module)) {
+                            i.providedBy = (i.submodule || i.module);
+                        }
 
-                            opts.meta.methods.push(i);
-                            break;
-                        case 'property':
-                            i = self.augmentData(i);
-                            //i.propertyDescription = self._parseCode(markdown(i.description || ''));
-                            i.propertyDescription = self._parseCode(i.description);
-                            if (!i.type) {
-                                i.type = 'unknown';
-                            }
-                            if (i.final === '') {
-                                i.final = true;
-                            }
-                            if (i.example && i.example.length) {
-                                if (i.example.forEach) {
-                                    e = '';
-                                    i.example.forEach(function(v) {
-                                        e += self._parseCode(self.markdown(v));
-                                    });
-                                    i.example = e;
-                                } else {
-                                    i.example = self._parseCode(self.markdown(i.example));
-                                }
+                        opts.meta.methods.push(i);
+                        break;
+                    case 'property':
+                        i = self.augmentData(i);
+                        //i.propertyDescription = self._parseCode(markdown(i.description || ''));
+                        i.propertyDescription = self._parseCode(i.description);
+                        if (!i.type) {
+                            i.type = 'unknown';
+                        }
+                        if (i.final === '') {
+                            i.final = true;
+                        }
+                        if (i.example && i.example.length) {
+                            if (i.example.forEach) {
+                                e = '';
+                                i.example.forEach(function (v) {
+                                    e += self._parseCode(self.markdown(v));
+                                });
+                                i.example = e;
+                            } else {
+                                i.example = self._parseCode(self.markdown(i.example));
                             }
+                        }
 
-                            // If this item is provided by a module other
-                            // than the module that provided the original
-                            // class, add the original module name to the
-                            // item's `providedBy` property so we can
-                            // indicate the relationship.
-                            if ((i.submodule || i.module) !== (data.submodule || data.module)) {
-                                i.providedBy = (i.submodule || i.module);
-                            }
+                        // If this item is provided by a module other
+                        // than the module that provided the original
+                        // class, add the original module name to the
+                        // item's `providedBy` property so we can
+                        // indicate the relationship.
+                        if ((i.submodule || i.module) !== (data.submodule || data.module)) {
+                            i.providedBy = (i.submodule || i.module);
+                        }
 
-                            opts.meta.properties.push(i);
-                            break;
+                        opts.meta.properties.push(i);
+                        break;
 
-                        case 'attribute': // fallthru
-                        case 'config':
-                            i = self.augmentData(i);
-                            //i.attrDescription = self._parseCode(markdown(i.description || ''));
-                            i.attrDescription = self._parseCode(i.description);
+                    case 'attribute': // fallthru
+                    case 'config':
+                        i = self.augmentData(i);
+                        //i.attrDescription = self._parseCode(markdown(i.description || ''));
+                        i.attrDescription = self._parseCode(i.description);
 
-                            if (i.itemtype === 'config') {
-                                i.config = true;
-                            } else {
-                                i.emit = self.options.attributesEmit;
-                            }
-                            if (i.readonly === '') {
-                                i.readonly = true;
-                            }
+                        if (i.itemtype === 'config') {
+                            i.config = true;
+                        } else {
+                            i.emit = self.options.attributesEmit;
+                        }
+                        if (i.readonly === '') {
+                            i.readonly = true;
+                        }
 
-                            if (i.example && i.example.length) {
-                                if (i.example.forEach) {
-                                    e = '';
-                                    i.example.forEach(function(v) {
-                                        e += self._parseCode(self.markdown(v));
-                                    });
-                                    i.example = e;
-                                } else {
-                                    i.example = self._parseCode(self.markdown(i.example));
-                                }
+                        if (i.example && i.example.length) {
+                            if (i.example.forEach) {
+                                e = '';
+                                i.example.forEach(function (v) {
+                                    e += self._parseCode(self.markdown(v));
+                                });
+                                i.example = e;
+                            } else {
+                                i.example = self._parseCode(self.markdown(i.example));
                             }
+                        }
 
-                            // If this item is provided by a module other
-                            // than the module that provided the original
-                            // class, add the original module name to the
-                            // item's `providedBy` property so we can
-                            // indicate the relationship.
-                            if ((i.submodule || i.module) !== (data.submodule || data.module)) {
-                                i.providedBy = (i.submodule || i.module);
-                            }
+                        // If this item is provided by a module other
+                        // than the module that provided the original
+                        // class, add the original module name to the
+                        // item's `providedBy` property so we can
+                        // indicate the relationship.
+                        if ((i.submodule || i.module) !== (data.submodule || data.module)) {
+                            i.providedBy = (i.submodule || i.module);
+                        }
 
-                            opts.meta.attrs.push(i);
-                            break;
-                        case 'event':
-                            i = self.augmentData(i);
-                            //i.eventDescription = self._parseCode(markdown(i.description || ''));
-                            i.eventDescription = self._parseCode(i.description);
-
-                            if (i.example && i.example.length) {
-                                if (i.example.forEach) {
-                                    e = '';
-                                    i.example.forEach(function(v) {
-                                        e += self._parseCode(self.markdown(v));
-                                    });
-                                    i.example = e;
-                                } else {
-                                    i.example = self._parseCode(self.markdown(i.example));
-                                }
+                        opts.meta.attrs.push(i);
+                        break;
+                    case 'event':
+                        i = self.augmentData(i);
+                        //i.eventDescription = self._parseCode(markdown(i.description || ''));
+                        i.eventDescription = self._parseCode(i.description);
+
+                        if (i.example && i.example.length) {
+                            if (i.example.forEach) {
+                                e = '';
+                                i.example.forEach(function (v) {
+                                    e += self._parseCode(self.markdown(v));
+                                });
+                                i.example = e;
+                            } else {
+                                i.example = self._parseCode(self.markdown(i.example));
                             }
+                        }
 
-                            // If this item is provided by a module other
-                            // than the module that provided the original
-                            // class, add the original module name to the
-                            // item's `providedBy` property so we can
-                            // indicate the relationship.
-                            if ((i.submodule || i.module) !== (data.submodule || data.module)) {
-                                i.providedBy = (i.submodule || i.module);
-                            }
+                        // If this item is provided by a module other
+                        // than the module that provided the original
+                        // class, add the original module name to the
+                        // item's `providedBy` property so we can
+                        // indicate the relationship.
+                        if ((i.submodule || i.module) !== (data.submodule || data.module)) {
+                            i.providedBy = (i.submodule || i.module);
+                        }
 
-                            opts.meta.events.push(i);
-                            break;
+                        opts.meta.events.push(i);
+                        break;
                     }
                 });
 
@@ -1334,9 +1364,9 @@ YUI.add('doc-builder', function(Y) {
                     delete opts.meta.events;
                 }
 
-                var view   = new Y.DocView(opts.meta);
+                var view = new Y.DocView(opts.meta);
                 var mainLayout = opts.layouts[layout];
-                self.render('{{>classes}}', view, mainLayout, opts.partials, stack.add(function(err, html) {
+                self.render('{{>classes}}', view, mainLayout, opts.partials, stack.add(function (err, html) {
                     self.files++;
                     stack.html = html;
                     stack.view = view;
@@ -1344,16 +1374,16 @@ YUI.add('doc-builder', function(Y) {
                 }));
             });
 
-            stack.done(function() {
+            stack.done(function () {
                 cb(stack.html, stack.view, stack.opts);
             });
         },
         /**
-        * Generates the class files under "out"/classes/
-        * @method writeClasses
-        * @param {Callback} cb The callback to execute after it's completed
-        */
-        writeClasses: function(cb, layout) {
+         * Generates the class files under "out"/classes/
+         * @method writeClasses
+         * @param {Callback} cb The callback to execute after it's completed
+         */
+        writeClasses: function (cb, layout) {
             layout = layout || 'main';
             var self = this,
                 stack = new Y.Parallel();
@@ -1361,15 +1391,17 @@ YUI.add('doc-builder', function(Y) {
             stack.view = [];
 
             var counter = 0;
-                Object.keys(self.data.classes).forEach(function(k) {
-                    if (!self.data.classes[k].external) {
-                        counter++;
-                    }
-                });
+            Object.keys(self.data.classes).forEach(function (k) {
+                if (!self.data.classes[k].external) {
+                    counter++;
+                }
+            });
             Y.log('Rendering and writing ' + counter + ' class pages.', 'info', 'builder');
-            Y.each(self.data.classes, function(v) {
-                if (v.external) { return; }
-                self.renderClass(stack.add(function(html, view) {
+            Y.each(self.data.classes, function (v) {
+                if (v.external) {
+                    return;
+                }
+                self.renderClass(stack.add(function (html, view) {
                     stack.html.push(html);
                     stack.view.push(view);
                     if (self.options.dumpview) {
@@ -1382,19 +1414,19 @@ YUI.add('doc-builder', function(Y) {
                     Y.Files.writeFile(path.join(self.options.outdir, 'classes', v.name + '.html'), html, stack.add(noop));
                 }), v, layout);
             });
-            stack.done(function() {
+            stack.done(function () {
                 Y.log('Finished writing class files', 'info', 'builder');
                 cb(stack.html, stack.view);
             });
         },
         /**
-        * Sort method of array of objects with a property called __name__
-        * @method nameSort
-        * @param {Object} a First object to compare
-        * @param {Object} b Second object to compare
-        * @return {Number} 1, -1 or 0 for sorting.
-        */
-        nameSort: function(a, b) {
+         * Sort method of array of objects with a property called __name__
+         * @method nameSort
+         * @param {Object} a First object to compare
+         * @param {Object} b Second object to compare
+         * @return {Number} 1, -1 or 0 for sorting.
+         */
+        nameSort: function (a, b) {
             if (!a.name || !b.name) {
                 return 0;
             }
@@ -1406,16 +1438,16 @@ YUI.add('doc-builder', function(Y) {
                 ret = -1;
             }
             if (an > bn) {
-                ret =  1;
+                ret = 1;
             }
             return ret;
         },
         /**
-        * Generates the syntax files under `"out"/files/`
-        * @method writeFiles
-        * @param {Callback} cb The callback to execute after it's completed
-        */
-        writeFiles: function(cb, layout) {
+         * Generates the syntax files under `"out"/files/`
+         * @method writeFiles
+         * @param {Callback} cb The callback to execute after it's completed
+         */
+        writeFiles: function (cb, layout) {
             layout = layout || 'main';
             var self = this,
                 stack = new Y.Parallel();
@@ -1423,15 +1455,17 @@ YUI.add('doc-builder', function(Y) {
             stack.view = [];
 
             var counter = 0;
-                Object.keys(self.data.files).forEach(function(k) {
-                    if (!self.data.files[k].external) {
-                        counter++;
-                    }
-                });
+            Object.keys(self.data.files).forEach(function (k) {
+                if (!self.data.files[k].external) {
+                    counter++;
+                }
+            });
             Y.log('Rendering and writing ' + counter + ' source files.', 'info', 'builder');
-            Y.each(self.data.files, function(v) {
-                if (v.external) { return; }
-                self.renderFile(stack.add(function(html, view, data) {
+            Y.each(self.data.files, function (v) {
+                if (v.external) {
+                    return;
+                }
+                self.renderFile(stack.add(function (html, view, data) {
                     if (!view || !data) {
                         return;
                     }
@@ -1451,22 +1485,22 @@ YUI.add('doc-builder', function(Y) {
                     );
                 }), v, layout);
             });
-            stack.done(function() {
+            stack.done(function () {
                 Y.log('Finished writing source files', 'info', 'builder');
                 cb(stack.html, stack.view);
             });
         },
         /**
-        * Render the source file
-        * @method renderFile
-        * @param {Function} cb The callback fired when complete
-        * @param {String} cb.html The HTML to render this view
-        * @param {Object} cv.view The View Data
-        */
-        renderFile: function(cb, data, layout) {
+         * Render the source file
+         * @method renderFile
+         * @param {Function} cb The callback fired when complete
+         * @param {String} cb.html The HTML to render this view
+         * @param {Object} cv.view The View Data
+         */
+        renderFile: function (cb, data, layout) {
             var self = this;
-            
-            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function(err, opts) {
+
+            Y.prepare([DEFAULT_THEME, themeDir], self.getProjectMeta(), function (err, opts) {
                 if (err) {
                     console.log(err);
                 }
@@ -1487,7 +1521,7 @@ YUI.add('doc-builder', function(Y) {
                 opts = self.populateFiles(opts);
 
                 opts.meta.fileName = data.name;
-                fs.readFile(opts.meta.fileName, Y.charset, Y.rbind(function(err, str, opts, data) {
+                fs.readFile(opts.meta.fileName, Y.charset, Y.rbind(function (err, str, opts, data) {
                     if (err) {
                         Y.log(err, 'error', 'builder');
                         cb(err);
@@ -1497,11 +1531,11 @@ YUI.add('doc-builder', function(Y) {
                     if (typeof self.options.tabspace === 'string') {
                         str = str.replace(/\t/g, self.options.tabspace);
                     }
-                    
+
                     opts.meta.fileData = str;
                     var view = new Y.DocView(opts.meta, 'index');
                     var mainLayout = opts.layouts[layout];
-                    self.render('{{>files}}', view, mainLayout, opts.partials, function(err, html) {
+                    self.render('{{>files}}', view, mainLayout, opts.partials, function (err, html) {
                         self.files++;
                         cb(html, view, data);
                     });
@@ -1511,42 +1545,44 @@ YUI.add('doc-builder', function(Y) {
 
         },
         /**
-        * Write the API meta data used for the AutoComplete widget
-        * @method writeAPIMeta
-        * @param {Callback} cb The callback to execute when complete
-        * @async
-        */
-        writeAPIMeta: function(cb) {
+         * Write the API meta data used for the AutoComplete widget
+         * @method writeAPIMeta
+         * @param {Callback} cb The callback to execute when complete
+         * @async
+         */
+        writeAPIMeta: function (cb) {
             Y.log('Writing API Meta Data', 'info', 'builder');
             var self = this;
-            this.renderAPIMeta(function(js) {
+            this.renderAPIMeta(function (js) {
                 fs.writeFile(path.join(self.options.outdir, 'api.js'), js, Y.charset, cb);
             });
         },
         /**
-        * Render the API meta and return the Javascript
-        * @method renderAPIMeta
-        * @param {Callback} cb The callback
-        * @async
-        */
-        renderAPIMeta: function(cb) {
-
-            var opts = { meta: {} };
+         * Render the API meta and return the Javascript
+         * @method renderAPIMeta
+         * @param {Callback} cb The callback
+         * @async
+         */
+        renderAPIMeta: function (cb) {
+
+            var opts = {
+                meta: {}
+            };
             opts = this.populateClasses(opts);
             opts = this.populateModules(opts);
 
-            ['classes', 'modules'].forEach(function(id) {
-                opts.meta[id].forEach(function(v, k) {
+            ['classes', 'modules'].forEach(function (id) {
+                opts.meta[id].forEach(function (v, k) {
                     opts.meta[id][k] = v.name;
                     if (v.submodules) {
-                        v.submodules.forEach(function(s) {
+                        v.submodules.forEach(function (s) {
                             opts.meta[id].push(s.displayName);
                         });
                     }
                 });
                 opts.meta[id].sort();
             });
-            
+
             var apijs = 'YUI.add("yuidoc-meta", function(Y) {\n' +
                 '   Y.YUIDoc = { meta: ' + JSON.stringify(opts.meta, null, 4) + ' };\n' +
                 '});';
@@ -1554,67 +1590,69 @@ YUI.add('doc-builder', function(Y) {
             cb(apijs);
         },
         /**
-        * Normalizes a file path to a writable filename:
-        *
-        *    var path = 'lib/file.js';
-        *    returns 'lib_file.js';
-        *
-        * @method filterFileName
-        * @param {String} f The filename to normalize
-        * @return {String} The filtered file path
-        */
-        filterFileName: function(f) {
+         * Normalizes a file path to a writable filename:
+         *
+         *    var path = 'lib/file.js';
+         *    returns 'lib_file.js';
+         *
+         * @method filterFileName
+         * @param {String} f The filename to normalize
+         * @return {String} The filtered file path
+         */
+        filterFileName: function (f) {
             return f.replace(/[\/\\]/g, '_');
         },
         /**
-        * Compiles the templates from the meta-data provided by DocParser
-        * @method compile
-        * @param {Callback} cb The callback to execute after it's completed
-        */
-        compile: function(cb) {
+         * Compiles the templates from the meta-data provided by DocParser
+         * @method compile
+         * @param {Callback} cb The callback to execute after it's completed
+         */
+        compile: function (cb) {
             var self = this;
             var starttime = (new Date()).getTime();
             Y.log('Compiling Templates', 'info', 'builder');
 
-            this.mixExternal(function() {
-                self.makeDirs(function() {
+            this.mixExternal(function () {
+                self.makeDirs(function () {
                     Y.log('Copying Assets', 'info', 'builder');
                     if (!Y.Files.isDirectory(path.join(self.options.outdir, 'assets'))) {
                         fs.mkdirSync(path.join(self.options.outdir, 'assets'), 0777);
                     }
                     Y.Files.copyAssets([
-                        path.join(DEFAULT_THEME, 'assets'),
-                        path.join(themeDir, 'assets')
-                    ],
-                    path.join(self.options.outdir, 'assets'),
-                    false,
-                    function () {
-                        var cstack = new Y.Parallel();
-                        
-                        self.writeModules(cstack.add(function() {
-                            self.writeClasses(cstack.add(function() {
-                                if (!self.options.nocode) {
-                                    self.writeFiles(cstack.add(noop));
-                                }
+                            path.join(DEFAULT_THEME, 'assets'),
+                            path.join(themeDir, 'assets')
+                        ],
+                        path.join(self.options.outdir, 'assets'),
+                        false,
+                        function () {
+                            var cstack = new Y.Parallel();
+
+                            self.writeModules(cstack.add(function () {
+                                self.writeClasses(cstack.add(function () {
+                                    if (!self.options.nocode) {
+                                        self.writeFiles(cstack.add(noop));
+                                    }
+                                }));
                             }));
-                        }));
-                        /*
+                            /*
                         self.writeModules(cstack.add(noop));
                         self.writeClasses(cstack.add(noop));
                         if (!self.options.nocode) {
                             self.writeFiles(cstack.add(noop));
                         }
                         */
-                        self.writeIndex(cstack.add(noop));
-                        self.writeAPIMeta(cstack.add(noop));
-
-                        cstack.done(function() {
-                            var endtime = (new Date()).getTime();
-                            var timer = ((endtime - starttime) / 1000) + ' seconds';
-                            Y.log('Finished writing ' + self.files + ' files in ' + timer, 'info', 'builder');
-                            if (cb) { cb(); }
+                            self.writeIndex(cstack.add(noop));
+                            self.writeAPIMeta(cstack.add(noop));
+
+                            cstack.done(function () {
+                                var endtime = (new Date()).getTime();
+                                var timer = ((endtime - starttime) / 1000) + ' seconds';
+                                Y.log('Finished writing ' + self.files + ' files in ' + timer, 'info', 'builder');
+                                if (cb) {
+                                    cb();
+                                }
+                            });
                         });
-                    });
                 });
             });
         }
diff --git a/lib/cli.js b/lib/cli.js
index dcde47c8..aacf3cd9 100755
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -1,4 +1,5 @@
 #!/usr/bin/env node
+
 /*
 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
 Code licensed under the BSD License:
@@ -6,10 +7,10 @@ http://yuilibrary.com/license/
 */
 
 /**
-* Parses the arguments, creates the options and passes them to `Y.YUIDoc` and then `Y.DocBuilder`.
-* @class CLI
-* @module yuidoc
-*/
+ * Parses the arguments, creates the options and passes them to `Y.YUIDoc` and then `Y.DocBuilder`.
+ * @class CLI
+ * @module yuidoc
+ */
 
 /*global Y:true */
 var Y = require('./index');
@@ -32,7 +33,6 @@ Y.log(opts, 'info', 'yuidoc');
 if (options.server) {
     Y.Server.start(options);
 } else {
-
     var json = (new Y.YUIDoc(options)).run();
     if (json === null) {
         return;
@@ -41,9 +41,9 @@ if (options.server) {
 
     if (!options.parseOnly) {
         var builder = new Y.DocBuilder(options, json);
-        builder.compile(function() {
+        builder.compile(function () {
             var endtime = (new Date()).getTime();
-            Y.log('Completed in ' + ((endtime - starttime) / 1000) + ' seconds' , 'info', 'yuidoc');
+            Y.log('Completed in ' + ((endtime - starttime) / 1000) + ' seconds', 'info', 'yuidoc');
         });
     }
 }
diff --git a/lib/docparser.js b/lib/docparser.js
index 37974571..21463ad4 100644
--- a/lib/docparser.js
+++ b/lib/docparser.js
@@ -3,743 +3,742 @@ Copyright (c) 2011, Yahoo! Inc. All rights reserved.
 Code licensed under the BSD License:
 http://yuilibrary.com/license/
 */
-YUI.add('docparser', function(Y) {
+YUI.add('docparser', function (Y) {
 
     var Lang = Y.Lang,
-    trim = Lang.trim,
-    fixType = Y.Lang.fixType,
-    /**
-    * Parses the JSON data and formats it into a nice log string for filename and line number:
-    `/file/name.js:123`
-    * @method stringlog
-    * @private
-    * @param {Object} data The data block from the parser
-    * @return {String} The formatted string.
-    * @for DocParser
-    */
-    stringlog = function(data) {
-        var line, file;
-        
-        if (data.file && data.line) {
-            file = data.file;
-            line = data.line;
-        } else {
-            data.forEach(function(d) {
-                if (d.tag === 'file') {
-                    file = d.value;
-                }
-                if (d.tag === 'line') {
-                    line = d.value;
-                }
-            });
-        }
-        return ' ' + file + ':' + line;
-    },
-    /*
-    * Flatted a string, remove all line breaks and replace them with a token
-    * @method implodeString
-    * @private
-    * @param {String} str The string to operate on
-    * @return {String} The modified string
-    */
-    implodeString = function(str) {
-        return str.replace(REGEX_GLOBAL_LINES, '!~YUIDOC_LINE~!');
-    },
-    /*
-    * Un-flatted a string, replace tokens injected with `implodeString`
-    * @method implodeString
-    * @private
-    * @param {String} str The string to operate on
-    * @return {String} The modified string
-    */
-    explodeString = function(str) {
-        return str.replace(/!~YUIDOC_LINE~!/g, '\n');
-    },
-    CURRENT_NAMESPACE = 'currentnamespace',
-    CURRENT_MODULE = 'currentmodule',
-    MAIN_MODULE = 'mainmodule',
-    CURRENT_SUBMODULE = 'currentsubmodule',
-    CURRENT_FILE = 'currentfile',
-    CURRENT_CLASS = 'currentclass',
-
-    REGEX_TYPE = /(.*?)\{(.*?)\}(.*)/,
-    REGEX_FIRSTWORD = /^\s*?([^\s]+)(.*)/,
-    REGEX_OPTIONAL = /\[(.*?)\]/,
-    REGEX_START_COMMENT = {
-      js: /^\s*\/\*\*/,
-      coffee: /^\s*###\*/
-    },
-    REGEX_END_COMMENT = {
-      js: /\*\/\s*$/,
-      coffee: /###\s*$/
-    },
-    REGEX_LINE_HEAD_CHAR = {
-      js: /^\s*\*/,
-      coffee: /^\s*#/
-    },
-    REGEX_LINES = /\r\n|\n/,
-    REGEX_GLOBAL_LINES = /\r\n|\n/g,
-
-    SHORT_TAGS = {
-        'async': 1,
-        'beta': 1,
-        'chainable': 1,
-        'extends': 1,
-        'final': 1,
-        'static': 1,
-        'optional': 1,
-        'required': 1
-    },
-
-    /**
-     * A list of known tags.  This populates a member variable
-     * during initialization, and will be updated if additional
-     * digesters are added.
-     * @property TAGLIST
-     * @type Array
-     * @final
-     * @for DocParser
-     */
-    TAGLIST = [
-        "async",  // bool, custom events can fire the listeners in a setTimeout
-        "author", // author best for projects and modules, but can be used anywhere // multi
-        "attribute", // YUI attributes -- get/set with change notification, etc
-        "beta",  // module maturity identifier
-        "broadcast",  // bool, events
-        "bubbles", // custom events that bubble
-        "category", // modules can be in multiple categories
-        "chainable", // methods that return the host object
-        "class", // pseudo class
-        "conditional", // conditional module
-        "config", // a config param (not an attribute, so no change events)
-        "const", // not standardized yet, converts to final property
-        "constructs", // factory methods (not yet used)
-        "constructor", // this is a constructor
-        "contributor", // like author
-        "default", // property/attribute default value
-        "deprecated", // please specify what to use instead
-        "description", // can also be free text at the beginning of a comment is
-        "emitfacade",  // bool, YUI custom event can have a dom-like event facade
-        "event", // YUI custom event
-        "evil", // uses eval
-        "extension", // this is an extension for [entity]
-        "extensionfor", // this is an extension for [entity]
-        "extension_for", // this is an extension for [entity]
-        "example", // 0..n code snippets.  snippets can also be embedded in the desc
-        "experimental",  // module maturity identifier
-        "extends", // pseudo inheritance
-        "file", // file name (used by the parser)
-        "final", // not meant to be changed
-        "fireonce",  // bool, YUI custom event config allows
-        "for", // used to change class context
-        "global", // declare your globals
-        "icon", // project icon(s)
-        "in", // indicates module this lives in (obsolete now)
-        "initonly", // attribute writeonce value
-        "injects",  // injects {HTML|script|CSS}
-        "knownissue", // 0..n known issues for your consumption
-        "line", // line number for the comment block (used by the parser)
-        "method", // a method
-        "module", // YUI module name
-        "main", // Description for the module
-        "namespace", // Y.namespace, used to fully qualify class names
-        "optional", // For optional attributes
-        "required", // For required attributes
-        "param", // member param
-        "plugin", // this is a plugin for [entityl]
-        "preventable", // YUI custom events can be preventable ala DOM events
-        "private", // > access
-        "project", // project definition, one per source tree allowed
-        "property", // a regular-ole property
-        "protected", // > access
-        "public", // > access
-        "queuable",  // bool, events
-        "readonly", // YUI attribute config
-        "requires", // YUI module requirements
-        "return", // {type} return desc -- returns is converted to this
-        "see", // 0..n things to look at
-        "since", // when it was introduced
-        "static",  // static
-        "submodule", // YUI submodule
-        "throws", // {execption type} description
-        "title", // this should be something for the project description
-        "todo", // 0..n things to revisit eventually (hopefully)
-        "type", // the var type
-        "url", // project url(s)
-        "uses", // 0..n compents mixed (usually, via augment) into the prototype
-        "value", // the value of a constant
-        "writeonce" // YUI attribute config
-    ],
-
-    /**
-     * Common errors will get scrubbed instead of being ignored.
-     * @property CORRECTIONS
-     * @type Object
-     * @final
-     * @for DocParser
-     */
-    CORRECTIONS = {
-        'augments': 'uses', // YUI convention for prototype mixins
-        'depreciated': 'deprecated', // subtle difference
-        'desciption': 'description', // shouldn't need the @description tag at all
-        'extend': 'extends', // typo
-        'function': 'method', // we may want standalone inner functions at some point
-        'member': 'method', // probably meant method
-        'parm': 'param', // typo
-        'params': 'param', // typo
-        'pamra': 'param', // typo
-        'parma': 'param', // typo
-        'propery': 'property', // typo
-        'prop': 'property', // probably meant property
-        'returns': 'return' // need to standardize on one or the other
-    },
-
-    /**
-     * A map of the default tag processors, keyed by the
-     * tag name.  Multiple tags can use the same digester
-     * by supplying the string name that points to the
-     * implementation rather than a function.
-     * @property DIGESTERS
-     * @type Object
-     * @final
-     * @for DocParser
-     */
-    DIGESTERS = {
-
-        // "params": [
-        // {
-        //   "name": "optionalandmultiple",
-        //   "description": "my desc",
-        //   "type": "string",
-        //   "optional": true, // [surroundedbybrackets]
-        //   "optdefault": "if specified, this is always string to avoid syntax errors @TODO",
-        //   "multiple": true // endswith*
-        // }
-        // ],
-        // @param {type} name description    -or-
-        // @param name {type} description
-        // #2173362 optional w/ or w/o default
-        // @param {type} [optvar=default] description
-        // #12 document config objects
-        // @param {object|config} config description
-        // @param {type} config.prop1 description
-        // @param {type} config.prop2 description
-        // #11 document callback argument signature
-        // @param {callback|function} callback description
-        // @param {type} callback.arg1 description
-        // @param {type} callback.arg2 description
-        // #2173362 document event facade decorations for custom events
-        // @param {event} event description
-        // @param {type}  event.child description
-        // @param {type}  event.index description
-        // @param name* {type} 1..n description
-        // @param [name]* {type} 0..n description
-        'param': function(tagname, value, target, block) {
-            // Y.log('param digester' + value);
-            target.params = target.params || [];
-
-            if (!value) {
-                this.warnings.push({
-                    message: 'param name/type/descript missing',
-                    line: stringlog(block)
+        trim = Lang.trim,
+        fixType = Y.Lang.fixType,
+        /**
+         * Parses the JSON data and formats it into a nice log string for
+         * filename and line number: `/file/name.js:123`
+         * @method stringlog
+         * @private
+         * @param {Object} data The data block from the parser
+         * @return {String} The formatted string.
+         * @for DocParser
+         */
+        stringlog = function (data) {
+            var line, file;
+
+            if (data.file && data.line) {
+                file = data.file;
+                line = data.line;
+            } else {
+                data.forEach(function (d) {
+                    if (d.tag === 'file') {
+                        file = d.value;
+                    }
+                    if (d.tag === 'line') {
+                        line = d.value;
+                    }
                 });
-                Y.log('param name/type/descript missing: ' + stringlog(block), 'warn', 'docparser');
-                return;
             }
+            return ' ' + file + ':' + line;
+        },
+        /**
+         * Flatten a string, remove all line breaks and replace them with a token
+         * @method implodeString
+         * @private
+         * @param {String} str The string to operate on
+         * @return {String} The modified string
+         */
+        implodeString = function (str) {
+            return str.replace(REGEX_GLOBAL_LINES, '!~YUIDOC_LINE~!');
+        },
+        /**
+         * Un-flatten a string, replace tokens injected with `implodeString`
+         * @method implodeString
+         * @private
+         * @param {String} str The string to operate on
+         * @return {String} The modified string
+         */
+        explodeString = function (str) {
+            return str.replace(/!~YUIDOC_LINE~!/g, '\n');
+        },
+        CURRENT_NAMESPACE = 'currentnamespace',
+        CURRENT_MODULE = 'currentmodule',
+        MAIN_MODULE = 'mainmodule',
+        CURRENT_SUBMODULE = 'currentsubmodule',
+        CURRENT_FILE = 'currentfile',
+        CURRENT_CLASS = 'currentclass',
+
+        REGEX_TYPE = /(.*?)\{(.*?)\}(.*)/,
+        REGEX_FIRSTWORD = /^\s*?([^\s]+)(.*)/,
+        REGEX_OPTIONAL = /\[(.*?)\]/,
+        REGEX_START_COMMENT = {
+            js: /^\s*\/\*\*/,
+            coffee: /^\s*###\*/
+        },
+        REGEX_END_COMMENT = {
+            js: /\*\/\s*$/,
+            coffee: /###\s*$/
+        },
+        REGEX_LINE_HEAD_CHAR = {
+            js: /^\s*\*/,
+            coffee: /^\s*#/
+        },
+        REGEX_LINES = /\r\n|\n/,
+        REGEX_GLOBAL_LINES = /\r\n|\n/g,
+
+        SHORT_TAGS = {
+            'async': 1,
+            'beta': 1,
+            'chainable': 1,
+            'extends': 1,
+            'final': 1,
+            'static': 1,
+            'optional': 1,
+            'required': 1
+        },
 
-            var type, name, parts, optional, optdefault, parent, multiple, len, result,
-                desc = implodeString(trim(value)),
-                match = REGEX_TYPE.exec(desc),
-                host = target.params;
-
-            // Extract {type}
-            if (match) {
-                type = fixType(trim(match[2]));
-                desc = trim(match[1] + match[3]);
-            }
+        /**
+         * A list of known tags.  This populates a member variable
+         * during initialization, and will be updated if additional
+         * digesters are added.
+         * @property TAGLIST
+         * @type Array
+         * @final
+         * @for DocParser
+         */
+        TAGLIST = [
+            "async",        // bool, custom events can fire the listeners in a setTimeout
+            "author",       // author best for projects and modules, but can be used anywhere // multi
+            "attribute",    // YUI attributes -- get/set with change notification, etc
+            "beta",         // module maturity identifier
+            "broadcast",    // bool, events
+            "bubbles",      // custom events that bubble
+            "category",     // modules can be in multiple categories
+            "chainable",    // methods that return the host object
+            "class",        // pseudo class
+            "conditional",  // conditional module
+            "config",       // a config param (not an attribute, so no change events)
+            "const",        // not standardized yet, converts to final property
+            "constructs",   // factory methods (not yet used)
+            "constructor",  // this is a constructor
+            "contributor",  // like author
+            "default",      // property/attribute default value
+            "deprecated",   // please specify what to use instead
+            "description",  // can also be free text at the beginning of a comment is
+            "emitfacade",   // bool, YUI custom event can have a dom-like event facade
+            "event",        // YUI custom event
+            "evil",         // uses eval
+            "extension",    // this is an extension for [entity]
+            "extensionfor", // this is an extension for [entity]
+            "extension_for",// this is an extension for [entity]
+            "example",      // 0..n code snippets.  snippets can also be embedded in the desc
+            "experimental", // module maturity identifier
+            "extends",      // pseudo inheritance
+            "file",         // file name (used by the parser)
+            "final",        // not meant to be changed
+            "fireonce",     // bool, YUI custom event config allows
+            "for",          // used to change class context
+            "global",       // declare your globals
+            "icon",         // project icon(s)
+            "in",           // indicates module this lives in (obsolete now)
+            "initonly",     // attribute writeonce value
+            "injects",      // injects {HTML|script|CSS}
+            "knownissue",   // 0..n known issues for your consumption
+            "line",         // line number for the comment block (used by the parser)
+            "method",       // a method
+            "module",       // YUI module name
+            "main",         // Description for the module
+            "namespace",    // Y.namespace, used to fully qualify class names
+            "optional",     // For optional attributes
+            "required",     // For required attributes
+            "param",        // member param
+            "plugin",       // this is a plugin for [entityl]
+            "preventable",  // YUI custom events can be preventable ala DOM events
+            "private",      // > access
+            "project",      // project definition, one per source tree allowed
+            "property",     // a regular-ole property
+            "protected",    // > access
+            "public",       // > access
+            "queuable",     // bool, events
+            "readonly",     // YUI attribute config
+            "requires",     // YUI module requirements
+            "return",       // {type} return desc -- returns is converted to this
+            "see",          // 0..n things to look at
+            "since",        // when it was introduced
+            "static",       // static
+            "submodule",    // YUI submodule
+            "throws",       // {execption type} description
+            "title",        // this should be something for the project description
+            "todo",         // 0..n things to revisit eventually (hopefully)
+            "type",         // the var type
+            "url",          // project url(s)
+            "uses",         // 0..n compents mixed (usually, via augment) into the prototype
+            "value",        // the value of a constant
+            "writeonce"     // YUI attribute config
+        ],
 
-            // extract the first word, this is the param name
-            match = REGEX_FIRSTWORD.exec(desc);
-            if (match) {
-                name = trim(match[1]);
-                desc = trim(match[2]);
-            }
+        /**
+         * Common errors will get scrubbed instead of being ignored.
+         * @property CORRECTIONS
+         * @type Object
+         * @final
+         * @for DocParser
+         */
+        CORRECTIONS = {
+            'augments': 'uses', // YUI convention for prototype mixins
+            'depreciated': 'deprecated', // subtle difference
+            'desciption': 'description', // shouldn't need the @description tag at all
+            'extend': 'extends', // typo
+            'function': 'method', // we may want standalone inner functions at some point
+            'member': 'method', // probably meant method
+            'parm': 'param', // typo
+            'params': 'param', // typo
+            'pamra': 'param', // typo
+            'parma': 'param', // typo
+            'propery': 'property', // typo
+            'prop': 'property', // probably meant property
+            'returns': 'return' // need to standardize on one or the other
+        },
 
-            if (!name) {
-                if (value && value.match(/callback/i)) {
-                    this.warnings.push({
-                        message: 'Fixing missing name for callback',
-                        line: stringlog(block)
-                    });
-                    Y.log('Fixing missing name for callback:' + stringlog(block), 'warn', 'docparser');
-                    name = 'callback';
-                    type = 'Callback';
-                } else {
+        /**
+         * A map of the default tag processors, keyed by the
+         * tag name.  Multiple tags can use the same digester
+         * by supplying the string name that points to the
+         * implementation rather than a function.
+         * @property DIGESTERS
+         * @type Object
+         * @final
+         * @for DocParser
+         */
+        DIGESTERS = {
+            // "params": [
+            // {
+            //   "name": "optionalandmultiple",
+            //   "description": "my desc",
+            //   "type": "string",
+            //   "optional": true, // [surroundedbybrackets]
+            //   "optdefault": "if specified, this is always string to avoid syntax errors @TODO",
+            //   "multiple": true // endswith*
+            // }
+            // ],
+            // @param {type} name description    -or-
+            // @param name {type} description
+            // #2173362 optional w/ or w/o default
+            // @param {type} [optvar=default] description
+            // #12 document config objects
+            // @param {object|config} config description
+            // @param {type} config.prop1 description
+            // @param {type} config.prop2 description
+            // #11 document callback argument signature
+            // @param {callback|function} callback description
+            // @param {type} callback.arg1 description
+            // @param {type} callback.arg2 description
+            // #2173362 document event facade decorations for custom events
+            // @param {event} event description
+            // @param {type}  event.child description
+            // @param {type}  event.index description
+            // @param name* {type} 1..n description
+            // @param [name]* {type} 0..n description
+            'param': function (tagname, value, target, block) {
+                // Y.log('param digester' + value);
+                target.params = target.params || [];
+
+                if (!value) {
                     this.warnings.push({
-                        message: 'param name missing: ' + value,
+                        message: 'param name/type/descript missing',
                         line: stringlog(block)
                     });
-                    Y.log('param name missing: ' + value + ':' + stringlog(block), 'warn', 'docparser');
-                    name = 'UNKNOWN';
+                    Y.log('param name/type/descript missing: ' + stringlog(block), 'warn', 'docparser');
+                    return;
                 }
-            }
 
-            len = name.length - 1;
+                var type, name, parts, optional, optdefault, parent, multiple, len, result,
+                    desc = implodeString(trim(value)),
+                    match = REGEX_TYPE.exec(desc),
+                    host = target.params;
 
-            if (name.charAt(len) === '*') {
-                multiple = true;
-                name = name.substr(0, len);
-            }
+                // Extract {type}
+                if (match) {
+                    type = fixType(trim(match[2]));
+                    desc = trim(match[1] + match[3]);
+                }
 
-            // extract [name], optional param
-            if (name.indexOf('[') > -1) {
-                match = REGEX_OPTIONAL.exec(name);
+                // extract the first word, this is the param name
+                match = REGEX_FIRSTWORD.exec(desc);
                 if (match) {
-                    optional = true;
                     name = trim(match[1]);
-                    // extract optional=defaultvalue
-                    parts = name.split('=');
-                    if (parts.length > 1) {
-                        name = parts[0];
-                        optdefault = parts[1];
-                        //Add some shortcuts for object/array defaults
-                        if (optdefault.toLowerCase() === 'object') {
-                            optdefault = '{}';
-                        }
-                        if (optdefault.toLowerCase() === 'array') {
-                            optdefault = '[]';
-                        }
-                    }
+                    desc = trim(match[2]);
                 }
-            }
 
-            // parse object.prop, indicating a child property for object
-            if (name.indexOf('.') > -1) {
-                match = name.split('.');
-                parent = trim(match[0]);
-                Y.each(target.params, function(param) {
-                    if (param.name === parent) {
-                        param.props = param.props || [];
-                        host = param.props;
-                        match.shift();
-                        name = trim(match.join('.'));
-                        if (match.length > 1) {
-                            var pname = name.split('.')[0],
-                            par;
-                            Y.each(param.props, function(o) {
-                                if (o.name === pname) {
-                                    par = o;
-                                }
-                            });
-                            if (par) {
-                                match = name.split('.');
-                                match.shift();
-                                name = match.join('.');
-                                par.props = par.props || [];
-                                host = par.props;
-                            }
-                        }
+                if (!name) {
+                    if (value && value.match(/callback/i)) {
+                        this.warnings.push({
+                            message: 'Fixing missing name for callback',
+                            line: stringlog(block)
+                        });
+                        Y.log('Fixing missing name for callback:' + stringlog(block), 'warn', 'docparser');
+                        name = 'callback';
+                        type = 'Callback';
+                    } else {
+                        this.warnings.push({
+                            message: 'param name missing: ' + value,
+                            line: stringlog(block)
+                        });
+                        Y.log('param name missing: ' + value + ':' + stringlog(block), 'warn', 'docparser');
+                        name = 'UNKNOWN';
                     }
-                });
-
-            }
+                }
 
-            result = {
-                name: name,
-                description: explodeString(desc)
-            };
+                len = name.length - 1;
 
-            if (type) {
-                result.type = type;
-            }
+                if (name.charAt(len) === '*') {
+                    multiple = true;
+                    name = name.substr(0, len);
+                }
 
-            if (optional) {
-                result.optional = true;
-                if (optdefault) {
-                    result.optdefault = optdefault;
+                // extract [name], optional param
+                if (name.indexOf('[') > -1) {
+                    match = REGEX_OPTIONAL.exec(name);
+                    if (match) {
+                        optional = true;
+                        name = trim(match[1]);
+                        // extract optional=defaultvalue
+                        parts = name.split('=');
+                        if (parts.length > 1) {
+                            name = parts[0];
+                            optdefault = parts[1];
+                            //Add some shortcuts for object/array defaults
+                            if (optdefault.toLowerCase() === 'object') {
+                                optdefault = '{}';
+                            }
+                            if (optdefault.toLowerCase() === 'array') {
+                                optdefault = '[]';
+                            }
+                        }
+                    }
                 }
-            }
 
-            if (multiple) {
-                result.multiple = true;
-            }
+                // parse object.prop, indicating a child property for object
+                if (name.indexOf('.') > -1) {
+                    match = name.split('.');
+                    parent = trim(match[0]);
+                    Y.each(target.params, function (param) {
+                        if (param.name === parent) {
+                            param.props = param.props || [];
+                            host = param.props;
+                            match.shift();
+                            name = trim(match.join('.'));
+                            if (match.length > 1) {
+                                var pname = name.split('.')[0],
+                                    par;
+                                Y.each(param.props, function (o) {
+                                    if (o.name === pname) {
+                                        par = o;
+                                    }
+                                });
+                                if (par) {
+                                    match = name.split('.');
+                                    match.shift();
+                                    name = match.join('.');
+                                    par.props = par.props || [];
+                                    host = par.props;
+                                }
+                            }
+                        }
+                    });
 
-            host.push(result);
-        },
+                }
 
-        // @return {type} description // methods
-        // @returns {type} description // methods
-        // @throws {type} an error #2173342
-        // @injects {HTML|CSS|script} description
-        // can be used by anthing that has an optional {type} and a description
-        'return': function(tagname, value, target, block) {
-
-            var desc = implodeString(trim(value)), type,
-                match = REGEX_TYPE.exec(desc),
-                result = {};
-            if (match) {
-                type = fixType(trim(match[2]));
-                desc = trim(match[1] + match[3]);
-            }
+                result = {
+                    name: name,
+                    description: explodeString(desc)
+                };
 
-            result = {
-                description: explodeString(desc)
-            };
+                if (type) {
+                    result.type = type;
+                }
 
-            if (type) {
-                result.type = type;
-            }
+                if (optional) {
+                    result.optional = true;
+                    if (optdefault) {
+                        result.optdefault = optdefault;
+                    }
+                }
 
-            target[tagname] = result;
+                if (multiple) {
+                    result.multiple = true;
+                }
 
-        },
-        'throws': 'return',
-        'injects': 'return',
+                host.push(result);
+            },
 
-        // trying to overwrite the constructor value is a bad idea
-        'constructor': function(tagname, value, target, block) {
-            target.is_constructor = 1;
-        },
+            // @return {type} description // methods
+            // @returns {type} description // methods
+            // @throws {type} an error #2173342
+            // @injects {HTML|CSS|script} description
+            // can be used by anthing that has an optional {type} and a description
+            'return': function (tagname, value, target, block) {
 
-        // @author {twitter: @arthurdent | github: ArthurDent}
-        //    Arthur Dent adent@h2g2.earth #23, multiple // modules/class/method
-        // 'author': function(tagname, value, target, block) {
-        //     // Y.log('author digester');
-        // },
-
-        // A key bock type for declaring modules and submodules
-        // subsequent class and member blocks will be assigned
-        // to this module.
-        'module': function(tagname, value, target, block) {
-            this.set(CURRENT_MODULE, value);
-            var go = true;
-            Y.some(block, function(o) {
-                if (trim(o.tag) === 'submodule') {
-                    go = false;
-                    return true;
-                }
-            });
-            if (go) {
-                if (!this.get(MAIN_MODULE)) {
-                    this.set(MAIN_MODULE, {
-                        tag: tagname,
-                        name: value,
-                        file: target.file,
-                        line: target.line,
-                        description: target.description
-                    });
+                var desc = implodeString(trim(value)),
+                    type,
+                    match = REGEX_TYPE.exec(desc),
+                    result = {};
+                if (match) {
+                    type = fixType(trim(match[2]));
+                    desc = trim(match[1] + match[3]);
                 }
-                return this.modules[value];
-            }
-            return null;
-        },
 
-        //Setting the description for the module..
-        'main': function(tagname, value, target, block) {
-            var o = target;
-            o.mainName = value;
-            o.tag = tagname;
-            o.itemtype = 'main';
-            o._main = true;
-            this.set(MAIN_MODULE, o);
-        },
+                result = {
+                    description: explodeString(desc)
+                };
 
-        // accepts a single project definition for the source tree
-        'project': function(tagname, value, target, block) {
-            return this.project;
-        },
+                if (type) {
+                    result.type = type;
+                }
 
-        // A key bock type for declaring submodules.  subsequent class and
-        // member blocks will be assigned to this submodule.
-        'submodule': function(tagname, value, target, block) {
-            //console.log('Setting current submodule: ', value, 'on class');
-            this.set(CURRENT_SUBMODULE, value);
-            var host = this.modules[value],
-                clazz = this.get(CURRENT_CLASS),
-                parent = this.get(CURRENT_MODULE);
+                target[tagname] = result;
+
+            },
+            'throws': 'return',
+            'injects': 'return',
+
+            // trying to overwrite the constructor value is a bad idea
+            'constructor': function (tagname, value, target, block) {
+                target.is_constructor = 1;
+            },
+
+            // @author {twitter: @arthurdent | github: ArthurDent}
+            //    Arthur Dent adent@h2g2.earth #23, multiple // modules/class/method
+            // 'author': function(tagname, value, target, block) {
+            //     // Y.log('author digester');
+            // },
+
+            // A key bock type for declaring modules and submodules
+            // subsequent class and member blocks will be assigned
+            // to this module.
+            'module': function (tagname, value, target, block) {
+                this.set(CURRENT_MODULE, value);
+                var go = true;
+                Y.some(block, function (o) {
+                    if (trim(o.tag) === 'submodule') {
+                        go = false;
+                        return true;
+                    }
+                });
+                if (go) {
+                    if (!this.get(MAIN_MODULE)) {
+                        this.set(MAIN_MODULE, {
+                            tag: tagname,
+                            name: value,
+                            file: target.file,
+                            line: target.line,
+                            description: target.description
+                        });
+                    }
+                    return this.modules[value];
+                }
+                return null;
+            },
+
+            //Setting the description for the module..
+            'main': function (tagname, value, target, block) {
+                var o = target;
+                o.mainName = value;
+                o.tag = tagname;
+                o.itemtype = 'main';
+                o._main = true;
+                this.set(MAIN_MODULE, o);
+            },
+
+            // accepts a single project definition for the source tree
+            'project': function (tagname, value, target, block) {
+                return this.project;
+            },
+
+            // A key bock type for declaring submodules.  subsequent class and
+            // member blocks will be assigned to this submodule.
+            'submodule': function (tagname, value, target, block) {
+                //console.log('Setting current submodule: ', value, 'on class');
+                this.set(CURRENT_SUBMODULE, value);
+                var host = this.modules[value],
+                    clazz = this.get(CURRENT_CLASS),
+                    parent = this.get(CURRENT_MODULE);
                 if (parent) {
                     host.module = parent;
                 }
                 if (clazz && this.classes[clazz]) {
                     //console.log('Adding submodule', value , 'to class', clazz, ' it has submodule', this.classes[clazz].submodule);
                     //if (!this.classes[clazz].submodule) {
-                        //console.log('REALLY Adding submodule', value , 'to class', clazz);
-                        this.classes[clazz].submodule = value;
+                    //console.log('REALLY Adding submodule', value , 'to class', clazz);
+                    this.classes[clazz].submodule = value;
                     //}
                 }
-            return host;
-        },
-
-        // A key bock type for declaring classes, subsequent
-        // member blocks will be assigned to this class
-        'class': function(tagname, value, target, block) {
-            var namespace, fullname, host, parent;
-
-            block.forEach(function(def) {
-                if (def.tag === 'namespace') {
-                    //We have a namespace, augment the name
-                    var name = trim(def.value) + '.' + value;
-                    if (value.indexOf(trim(def.value) + '.') === -1) {
-                        value = name;
-                        namespace = trim(def.value);
+                return host;
+            },
+
+            // A key bock type for declaring classes, subsequent
+            // member blocks will be assigned to this class
+            'class': function (tagname, value, target, block) {
+                var namespace, fullname, host, parent;
+
+                block.forEach(function (def) {
+                    if (def.tag === 'namespace') {
+                        //We have a namespace, augment the name
+                        var name = trim(def.value) + '.' + value;
+                        if (value.indexOf(trim(def.value) + '.') === -1) {
+                            value = name;
+                            namespace = trim(def.value);
+                        }
                     }
-                }
-            });
-
-            if (namespace) {
-                this.set(CURRENT_NAMESPACE, namespace);
-            }
-            this.set(CURRENT_CLASS, value);
-
-            fullname = this.get(CURRENT_CLASS);
-            host = this.classes[fullname];
-            parent = this.get(CURRENT_MODULE);
-
-            if (namespace) {
-                host.namespace = namespace;
-            }
-            if (parent) {
-                host.module = parent;
-            }
+                });
 
-            //Merge host and target in case the class was defined in a "for" tag
-            //before it was defined in a "class" tag
-            host = Y.merge(host, target);
-            this.classes[fullname] = host;
-            parent = this.get(CURRENT_SUBMODULE);
-            if (parent) {
-                //this.set(CURRENT_SUBMODULE, parent);
-                host.submodule = parent;
-            }
-            return host;
-        },
+                if (namespace) {
+                    this.set(CURRENT_NAMESPACE, namespace);
+                }
+                this.set(CURRENT_CLASS, value);
 
-        // change 'const' to final property
-        'const': function(tagname, value, target, block) {
-            target.itemtype = 'property';
-            target.name = value;
-            /*jshint sub:true */
-            target['final'] = '';
-        },
+                fullname = this.get(CURRENT_CLASS);
+                host = this.classes[fullname];
+                parent = this.get(CURRENT_MODULE);
 
-        // supported classitems
-        'property': function(tagname, value, target, block) {
-            var match, name, desc, type;
+                if (namespace) {
+                    host.namespace = namespace;
+                }
+                if (parent) {
+                    host.module = parent;
+                }
 
-            target.itemtype = tagname;
-            target.name = value;
-            if (!target.type) {
-                desc = implodeString(trim(value));
-                match = REGEX_TYPE.exec(desc);
-                
-                // Extract {type}
-                if (match) {
-                    type = fixType(trim(match[2]));
-                    name = trim(match[1] + match[3]);
-                    target.type = type;
-                    target.name = name;
+                //Merge host and target in case the class was defined in a "for" tag
+                //before it was defined in a "class" tag
+                host = Y.merge(host, target);
+                this.classes[fullname] = host;
+                parent = this.get(CURRENT_SUBMODULE);
+                if (parent) {
+                    //this.set(CURRENT_SUBMODULE, parent);
+                    host.submodule = parent;
                 }
-                
-            }
-            if (target.type && target.type.toLowerCase() === 'object') {
-                block.forEach(function(i, k) {
-                    if (i.tag === 'property') {
-                        i.value = trim(i.value);
-                        i.tag = 'param';
-                        block[k] = i;
+                return host;
+            },
+
+            // change 'const' to final property
+            'const': function (tagname, value, target, block) {
+                target.itemtype = 'property';
+                target.name = value;
+                /*jshint sub:true */
+                target['final'] = '';
+            },
+
+            // supported classitems
+            'property': function (tagname, value, target, block) {
+                var match, name, desc, type;
+
+                target.itemtype = tagname;
+                target.name = value;
+                if (!target.type) {
+                    desc = implodeString(trim(value));
+                    match = REGEX_TYPE.exec(desc);
+
+                    // Extract {type}
+                    if (match) {
+                        type = fixType(trim(match[2]));
+                        name = trim(match[1] + match[3]);
+                        target.type = type;
+                        target.name = name;
                     }
-                });
-            }
-        },
-        'method': 'property',
-        'attribute': 'property',
-        'config': 'property',
-        'event': 'property',
-
-        // access fields
-        'public': function(tagname, value, target, block) {
-            target.access = tagname;
-            target.tagname = value;
-        },
-        'private': 'public',
-        'protected': 'public',
-        'inner': 'public',
-
-        // tags that can have multiple occurances in a single block
-        'todo': function(tagname, value, target, block) {
-            if (!Lang.isArray(target[tagname])) {
-                target[tagname] = [];
-            }
-            //If the item is @tag one,two
-            if (value.indexOf(',') > -1) {
-                value = value.split(',');
-            } else {
-                value = [value];
-            }
-            
-            value.forEach(function(v) {
-                v = trim(v);
-                target[tagname].push(v);
-            });
-        },
-        'extension_for': 'extensionfor',
-        'extensionfor': function(tagname, value, target, block) {
-            if (this.classes[this.get(CURRENT_CLASS)]) {
-                this.classes[this.get(CURRENT_CLASS)].extension_for.push(value);
-            }
-        },
-        'example': function(tagname, value, target, block) {
-            if (!Lang.isArray(target[tagname])) {
-                target[tagname] = [];
-            }
 
-            var e = value;
-            block.forEach(function(v) {
-                if (v.tag === 'example') {
-                    if (v.value.indexOf(value) > -1) {
-                        e = v.value;
-                    }
                 }
-            });
-
-            target[tagname].push(e);
-        },
-        'url': 'todo',
-        'icon': 'todo',
-        'see': 'todo',
-        'requires': 'todo',
-        'knownissue': 'todo',
-        'uses': 'todo',
-        'category': 'todo',
-        'unimplemented': 'todo',
-
-        genericValueTag: function (tagname, value, target, block) {
-            target[tagname] = value;
-        },
-
-        'author'     : 'genericValueTag',
-        'contributor': 'genericValueTag',
-        'since'      : 'genericValueTag',
+                if (target.type && target.type.toLowerCase() === 'object') {
+                    block.forEach(function (i, k) {
+                        if (i.tag === 'property') {
+                            i.value = trim(i.value);
+                            i.tag = 'param';
+                            block[k] = i;
+                        }
+                    });
+                }
+            },
+            'method': 'property',
+            'attribute': 'property',
+            'config': 'property',
+            'event': 'property',
+
+            // access fields
+            'public': function (tagname, value, target, block) {
+                target.access = tagname;
+                target.tagname = value;
+            },
+            'private': 'public',
+            'protected': 'public',
+            'inner': 'public',
+
+            // tags that can have multiple occurances in a single block
+            'todo': function (tagname, value, target, block) {
+                if (!Lang.isArray(target[tagname])) {
+                    target[tagname] = [];
+                }
+                //If the item is @tag one,two
+                if (value.indexOf(',') > -1) {
+                    value = value.split(',');
+                } else {
+                    value = [value];
+                }
 
-        'deprecated': function (tagname, value, target, block) {
-            target.deprecated = true;
+                value.forEach(function (v) {
+                    v = trim(v);
+                    target[tagname].push(v);
+                });
+            },
+            'extension_for': 'extensionfor',
+            'extensionfor': function (tagname, value, target, block) {
+                if (this.classes[this.get(CURRENT_CLASS)]) {
+                    this.classes[this.get(CURRENT_CLASS)].extension_for.push(value);
+                }
+            },
+            'example': function (tagname, value, target, block) {
+                if (!Lang.isArray(target[tagname])) {
+                    target[tagname] = [];
+                }
 
-            if (typeof value === 'string' && value.length) {
-                target.deprecationMessage = value;
-            }
-        },
+                var e = value;
+                block.forEach(function (v) {
+                    if (v.tag === 'example') {
+                        if (v.value.indexOf(value) > -1) {
+                            e = v.value;
+                        }
+                    }
+                });
 
-        // updates the current namespace
-        'namespace': function(tagname, value, target, block) {
-            this.set(CURRENT_NAMESPACE, value);
-            if (value === '') {
-                //Shortcut this if namespace is an empty string.
-                return;
-            }
-            var m,
-                mod,
-                name,
-                lastNS,
-                file = this.get(CURRENT_FILE);
-            if (file) {
-                this.files[file].namespaces[value] = 1;
-            }
-            mod = this.get(CURRENT_MODULE);
-            if (mod) {
-                this.modules[mod].namespaces[value] = 1;
-            }
+                target[tagname].push(e);
+            },
+            'url': 'todo',
+            'icon': 'todo',
+            'see': 'todo',
+            'requires': 'todo',
+            'knownissue': 'todo',
+            'uses': 'todo',
+            'category': 'todo',
+            'unimplemented': 'todo',
+
+            genericValueTag: function (tagname, value, target, block) {
+                target[tagname] = value;
+            },
+
+            'author': 'genericValueTag',
+            'contributor': 'genericValueTag',
+            'since': 'genericValueTag',
+
+            'deprecated': function (tagname, value, target, block) {
+                target.deprecated = true;
+
+                if (typeof value === 'string' && value.length) {
+                    target.deprecationMessage = value;
+                }
+            },
 
-            mod = this.get(CURRENT_SUBMODULE);
-            if (mod) {
-                this.modules[mod].namespaces[value] = 1;
-            }
+            // updates the current namespace
+            'namespace': function (tagname, value, target, block) {
+                this.set(CURRENT_NAMESPACE, value);
+                if (value === '') {
+                    //Shortcut this if namespace is an empty string.
+                    return;
+                }
+                var m,
+                    mod,
+                    name,
+                    lastNS,
+                    file = this.get(CURRENT_FILE);
+                if (file) {
+                    this.files[file].namespaces[value] = 1;
+                }
+                mod = this.get(CURRENT_MODULE);
+                if (mod) {
+                    this.modules[mod].namespaces[value] = 1;
+                }
 
-            mod = this.get(CURRENT_CLASS);
-            if (mod) {
-                lastNS = this.get('lastnamespace');
-                if (lastNS && lastNS !== value && (value.indexOf(lastNS + '.') !== 0)) {
-                    if (this.classes[mod]) {
-                        m = this.classes[mod];
-                        delete this.classes[mod];
-                        mod = value + '.' + mod.replace(lastNS + '.', '');
-                        m.name = mod;
-                        m.namespace = value;
-                        this.classes[mod] = m;
-                        this.set(CURRENT_CLASS, m.name);
-                    }
+                mod = this.get(CURRENT_SUBMODULE);
+                if (mod) {
+                    this.modules[mod].namespaces[value] = 1;
                 }
-                if (this.classes[mod]) {
-                    this.classes[mod].namespace = value;
-                    if (mod === value) {
-                        return;
-                    }
-                    if (mod.indexOf(value + '.') === -1) {
-                        if (mod.indexOf('.') === -1) {
+
+                mod = this.get(CURRENT_CLASS);
+                if (mod) {
+                    lastNS = this.get('lastnamespace');
+                    if (lastNS && lastNS !== value && (value.indexOf(lastNS + '.') !== 0)) {
+                        if (this.classes[mod]) {
                             m = this.classes[mod];
                             delete this.classes[mod];
-                            name = m.namespace + '.' + m.name;
-                            m.name = name;
-                            this.classes[name] = m;
-                            this.set(CURRENT_CLASS, name);
-                        } else {
-                            if (mod.indexOf(this.classes[mod].namespace + '.') === -1) {
+                            mod = value + '.' + mod.replace(lastNS + '.', '');
+                            m.name = mod;
+                            m.namespace = value;
+                            this.classes[mod] = m;
+                            this.set(CURRENT_CLASS, m.name);
+                        }
+                    }
+                    if (this.classes[mod]) {
+                        this.classes[mod].namespace = value;
+                        if (mod === value) {
+                            return;
+                        }
+                        if (mod.indexOf(value + '.') === -1) {
+                            if (mod.indexOf('.') === -1) {
                                 m = this.classes[mod];
                                 delete this.classes[mod];
-                                name = m.namespace + '.' + m.shortname;
+                                name = m.namespace + '.' + m.name;
                                 m.name = name;
                                 this.classes[name] = m;
                                 this.set(CURRENT_CLASS, name);
+                            } else {
+                                if (mod.indexOf(this.classes[mod].namespace + '.') === -1) {
+                                    m = this.classes[mod];
+                                    delete this.classes[mod];
+                                    name = m.namespace + '.' + m.shortname;
+                                    m.name = name;
+                                    this.classes[name] = m;
+                                    this.set(CURRENT_CLASS, name);
+                                }
                             }
                         }
                     }
                 }
-            }
-        },
+            },
 
-        // updates the current class only (doesn't create
-        // a new class definition)
-        'for': function(tagname, value, target, block) {
-            var ns, file, mod;
+            // updates the current class only (doesn't create
+            // a new class definition)
+            'for': function (tagname, value, target, block) {
+                var ns, file, mod;
 
-            value = this._resolveFor(value);
-            this.set(CURRENT_CLASS, value);
+                value = this._resolveFor(value);
+                this.set(CURRENT_CLASS, value);
 
-            ns = ((this.classes[value]) ? this.classes[value].namespace : '');
-            this.set(CURRENT_NAMESPACE, ns);
+                ns = ((this.classes[value]) ? this.classes[value].namespace : '');
+                this.set(CURRENT_NAMESPACE, ns);
 
-            file = this.get(CURRENT_FILE);
-            if (file) {
-                this.files[file].fors[value] = 1;
-            }
+                file = this.get(CURRENT_FILE);
+                if (file) {
+                    this.files[file].fors[value] = 1;
+                }
 
-            mod = this.get(CURRENT_MODULE);
-            if (mod) {
-                this.modules[mod].fors[value] = 1;
-            }
+                mod = this.get(CURRENT_MODULE);
+                if (mod) {
+                    this.modules[mod].fors[value] = 1;
+                }
 
-            mod = this.get(CURRENT_SUBMODULE);
-            if (mod) {
-                this.modules[mod].fors[value] = 1;
+                mod = this.get(CURRENT_SUBMODULE);
+                if (mod) {
+                    this.modules[mod].fors[value] = 1;
+                }
             }
-        }
+        },
 
-    },
-
-    /**
-     * The doc parser accepts a **map** of files to file content.
-     * Once `parse()` is called, various properties will be populated
-     * with the parsers data (aggregated in the `'data'` property).
-     * @class DocParser
-     * @extends Base
-     * @constructor
-     * @param {Object} o the config object
-     * @module yuidoc
-     */
-    DocParser = function(o) {
-        this.digesters = Y.merge(DocParser.DIGESTERS);
-        this.knowntags = Y.Array.hash(DocParser.TAGLIST);
-        DocParser.superclass.constructor.apply(this, arguments);
-    };
+        /**
+         * The doc parser accepts a **map** of files to file content.
+         * Once `parse()` is called, various properties will be populated
+         * with the parsers data (aggregated in the `'data'` property).
+         * @class DocParser
+         * @extends Base
+         * @constructor
+         * @param {Object} o the config object
+         * @module yuidoc
+         */
+        DocParser = function (o) {
+            this.digesters = Y.merge(DocParser.DIGESTERS);
+            this.knowntags = Y.Array.hash(DocParser.TAGLIST);
+            DocParser.superclass.constructor.apply(this, arguments);
+        };
 
     DocParser.NAME = 'DocParser';
 
@@ -748,7 +747,7 @@ YUI.add('docparser', function(Y) {
     DocParser.CORRECTIONS = CORRECTIONS;
 
     DocParser.ATTRS = {
-        
+
         lint: {
             value: false
         },
@@ -768,7 +767,7 @@ YUI.add('docparser', function(Y) {
          * @attribute digesters
          */
         digesters: {
-            setter: function(val) {
+            setter: function (val) {
                 Y.mix(this.digesters, val, true);
                 Y.mix(this.knowntags, val, true);
                 return val;
@@ -781,13 +780,13 @@ YUI.add('docparser', function(Y) {
          * @attribute emitters
          */
         emitters: {
-            setter: function(val) {
+            setter: function (val) {
                 Y.mix(this.emitters, val, true);
             }
         },
 
         /**
-         * Comment syntax type. 
+         * Comment syntax type.
          * @attribute syntaxtype
          * @type String
          */
@@ -800,7 +799,7 @@ YUI.add('docparser', function(Y) {
          * @attribute filemap
          */
         filemap: {
-            writeOnce : true
+            writeOnce: true
         },
 
         /**
@@ -810,7 +809,7 @@ YUI.add('docparser', function(Y) {
          * @attribute dirmap
          */
         dirmap: {
-            writeOnce : true
+            writeOnce: true
         },
 
         /**
@@ -819,7 +818,7 @@ YUI.add('docparser', function(Y) {
          * @type String
          */
         currentfile: {
-            setter: function(val) {
+            setter: function (val) {
                 val = trim(val);
                 // this.set(CURRENT_NAMESPACE, '');
                 if (!(val in this.files)) {
@@ -835,12 +834,12 @@ YUI.add('docparser', function(Y) {
             }
         },
         /**
-        * The main documentation block for the module itself.
-        * @attribute mainmodule
+         * The main documentation block for the module itself.
+         * @attribute mainmodule
          * @type String
-        */
+         */
         mainmodule: {
-            setter: function(o) {
+            setter: function (o) {
                 if (!o) {
                     return;
                 }
@@ -876,7 +875,7 @@ YUI.add('docparser', function(Y) {
          * @type String
          */
         currentmodule: {
-            setter: function(val) {
+            setter: function (val) {
                 if (!val) {
                     return val;
                 }
@@ -935,7 +934,7 @@ YUI.add('docparser', function(Y) {
          * @type String
          */
         currentsubmodule: {
-            setter: function(val) {
+            setter: function (val) {
                 if (!val) {
                     return val;
                 }
@@ -958,7 +957,7 @@ YUI.add('docparser', function(Y) {
             }
         },
         currentnamespace: {
-            setter: function(val) {
+            setter: function (val) {
                 this.set('lastnamespace', this.get(CURRENT_NAMESPACE));
                 return val;
             }
@@ -975,7 +974,7 @@ YUI.add('docparser', function(Y) {
          * @type String
          */
         currentclass: {
-            setter: function(val) {
+            setter: function (val) {
                 if (!val) {
                     return val;
                 }
@@ -1010,15 +1009,15 @@ YUI.add('docparser', function(Y) {
 
     Y.extend(DocParser, Y.Base, {
         /**
-        * Takes a non-namespaced classname and resolves it to a namespace (to support `@for`)
-        * @private
-        * @method _resolveFor
-        * @param {String} value The classname to resolve
-        * @return {String} The resolved namespace + classname
-        */
-        _resolveFor: function(value) {
+         * Takes a non-namespaced classname and resolves it to a namespace (to support `@for`)
+         * @private
+         * @method _resolveFor
+         * @param {String} value The classname to resolve
+         * @return {String} The resolved namespace + classname
+         */
+        _resolveFor: function (value) {
             if (value.indexOf('.') === -1) {
-                Y.each(this.classes, function(i) {
+                Y.each(this.classes, function (i) {
                     if (i.shortname === value) {
                         if (i.namespace) {
                             value = i.namespace + '.' + i.shortname;
@@ -1029,37 +1028,38 @@ YUI.add('docparser', function(Y) {
             return value;
         },
 
-        initializer: function() {
-
+        initializer: function () {
             this.warnings = [];
 
             var self = this;
-            self.after('currentfileChange', function(e) {
+            self.after('currentfileChange', function (e) {
                 /*
-                * File changed, so we reset class and submodule.
-                * You should use @for if you want to reference another class
-                * in different file.
-                */
+                 * File changed, so we reset class and submodule.
+                 * You should use @for if you want to reference another class
+                 * in different file.
+                 */
                 self.set(CURRENT_SUBMODULE, '');
                 self.set(CURRENT_CLASS, '');
             });
 
-            self.after('currentmoduleChange', function(e) {
-                var mod = e.newVal, classes = self.classes;
-                Y.each(classes, function(clazz) {
+            self.after('currentmoduleChange', function (e) {
+                var mod = e.newVal,
+                    classes = self.classes;
+                Y.each(classes, function (clazz) {
                     if (!(clazz.module)) {
                         clazz.module = mod;
                     }
                 });
             });
 
-            self.after('currentsubmoduleChange', function(e) {
-                var mod = e.newVal, classes = self.classes,
+            self.after('currentsubmoduleChange', function (e) {
+                var mod = e.newVal,
+                    classes = self.classes,
                     parent;
 
                 if (mod) {
                     parent = self.modules[mod].module;
-                    Y.each(classes, function(clazz) {
+                    Y.each(classes, function (clazz) {
                         if (!(clazz.submodule)) {
                             //if ((!clazz.module) || clazz.module == parent) {
                             if (!clazz.module) {
@@ -1071,17 +1071,17 @@ YUI.add('docparser', function(Y) {
                 }
             });
 
-            self.after('currentclassChange', function(e) {
+            self.after('currentclassChange', function (e) {
                 var clazz = e.newVal;
-                Y.each(self.classitems, function(item) {
+                Y.each(self.classitems, function (item) {
                     if (!(item["class"])) {
                         item["class"] = clazz;
                     }
                 });
                 // Y.log(self.classitems);
             });
-
         },
+
         /**
         Normalizes the initial indentation of the given _content_ so that the first line
         is unindented, and all other lines are unindented to the same degree as the
@@ -1093,8 +1093,7 @@ YUI.add('docparser', function(Y) {
         @return {String} Unindented text.
         @private
         **/
-
-        unindent: function(content) {
+        unindent: function (content) {
             var indent = content.match(/^(\s+)/);
 
             if (indent) {
@@ -1105,28 +1104,32 @@ YUI.add('docparser', function(Y) {
         },
 
         /**
-         Transforms a JavaDoc style comment block (less the start
-         and end of it) into a list
-         of tag/text pairs.  The leading space and '*' are removed,
-         but the remaining whitespace is preserved so that the
-         output should be friendly for both markdown and html
-         parsers.
-
-         @method handlecomment
-         @param {String} comment The comment to parse
-         @param {String} file The file it was parsed from
-         @param {String} line The line number it was found on
-         */
-        handlecomment: function(comment, file, line) {
+        Transforms a JavaDoc style comment block (less the start and end of it)
+        into a list of tag/text pairs. The leading space and '*' are removed,
+        but the remaining whitespace is preserved so that the output should be
+        friendly for both markdown and html parsers.
+
+        @method handlecomment
+        @param {String} comment The comment to parse
+        @param {String} file The file it was parsed from
+        @param {String} line The line number it was found on
+        **/
+        handlecomment: function (comment, file, line) {
             var lines = comment.split(REGEX_LINES),
-                len = lines.length, i,
+                len = lines.length,
+                i,
                 parts, part, peek, skip,
                 tag, value,
-                results = [{tag: 'file', value: file},
-                           {tag: 'line', value: line}],
+                results = [{
+                    tag: 'file',
+                    value: file
+                }, {
+                    tag: 'line',
+                    value: line
+                }],
                 syntaxtype = this.get('syntaxtype'),
                 lineHeadCharRegex = REGEX_LINE_HEAD_CHAR[syntaxtype],
-                hasLineHeadChar  = lines[0] && lineHeadCharRegex.test(lines[0]);
+                hasLineHeadChar = lines[0] && lineHeadCharRegex.test(lines[0]);
 
             // trim leading line head char(star or harp) if there are any
             if (hasLineHeadChar) {
@@ -1189,20 +1192,21 @@ YUI.add('docparser', function(Y) {
          * @return {Object} A map of filenames to an array of extracted
          * comment text.
          */
-        extract: function(filemap, dirmap) {
+        extract: function (filemap, dirmap) {
             filemap = filemap || this.get('filemap');
             dirmap = dirmap || this.get('dirmap');
             var syntaxtype = this.get('syntaxtype'),
                 commentmap = {};
-            Y.each(filemap, function(code, filename) {
+            Y.each(filemap, function (code, filename) {
 
                 var commentlines, comment, line,
                     lines = code.split(REGEX_LINES),
-                    len = lines.length, i, linenum;
+                    len = lines.length,
+                    i, linenum;
 
                 for (i = 0; i < len; i++) {
                     line = lines[i];
-                    if(REGEX_START_COMMENT[syntaxtype].test(line)) {
+                    if (REGEX_START_COMMENT[syntaxtype].test(line)) {
                         commentlines = [];
 
                         linenum = i + 1;
@@ -1221,7 +1225,7 @@ YUI.add('docparser', function(Y) {
                         comment = commentlines.join('\n');
                         commentmap[filename] = commentmap[filename] || [];
                         commentmap[filename]
-                          .push(this.handlecomment(comment, filename, linenum));
+                            .push(this.handlecomment(comment, filename, linenum));
                     }
                 }
             }, this);
@@ -1235,7 +1239,7 @@ YUI.add('docparser', function(Y) {
          * @method processblock
          * @param {Array} an array of the tag/text pairs
          */
-        processblock: function(block) {
+        processblock: function (block) {
             var target = {},
                 digestname,
                 digester,
@@ -1253,7 +1257,6 @@ YUI.add('docparser', function(Y) {
                 }
 
                 if (tag && tag.tag) {
-
                     if (!(name in this.knowntags)) {
                         if (name in CORRECTIONS) {
                             this.warnings.push({
@@ -1270,6 +1273,7 @@ YUI.add('docparser', function(Y) {
                             Y.log('unknown tag: ' + name + ',' + stringlog(block), 'warn', 'docparser');
                         }
                     }
+
                     digestname = name;
                     if (digestname in this.digesters) {
                         digester = this.digesters[digestname];
@@ -1311,7 +1315,7 @@ YUI.add('docparser', function(Y) {
          * @param {object} commentmap The hash of files and parsed comment blocks
          * @return {object} The transformed data for the project
          */
-        transform: function(commentmap) {
+        transform: function (commentmap) {
             var self = this,
                 project = self.project = {},
                 files = self.files = {},
@@ -1378,7 +1382,7 @@ YUI.add('docparser', function(Y) {
                     }
                 }
                 if (clazz.uses && clazz.uses.length) {
-                    clazz.uses.forEach(function(u) {
+                    clazz.uses.forEach(function (u) {
                         var c = classes[u];
                         if (c) {
                             c.extension_for.push(clazz.name);
@@ -1391,7 +1395,7 @@ YUI.add('docparser', function(Y) {
                 if (!v.itemtype) {
                     self.warnings.push({
                         message: 'Missing item type' + (v.description ? '\n' + v.description : ''),
-                        line:  stringlog(v)
+                        line: stringlog(v)
                     });
                     Y.log('Missing item type: ' + stringlog(v), 'warn', 'DocParser');
                     if (v.description) {
@@ -1400,7 +1404,7 @@ YUI.add('docparser', function(Y) {
                 }
                 if (v.itemtype === 'property' && v.params) {
                     v.subprops = v.params;
-                    v.subprops.forEach(function(i) {
+                    v.subprops.forEach(function (i) {
                         //Remove top level prop name from sub props (should have been done in the @param parser
                         i.name = i.name.replace(v.name + '.', '');
                     });
@@ -1425,15 +1429,15 @@ YUI.add('docparser', function(Y) {
          * @return {DocParser} this parser instance.  The total results
          * are available in parser.data.
          */
-        parse: function(filemap, dirmap) {
+        parse: function (filemap, dirmap) {
             filemap = filemap || this.get('filemap');
             dirmap = dirmap || this.get('dirmap');
             return this.transform(this.extract(filemap, dirmap));
         }
-
     });
 
     Y.DocParser = DocParser;
 
-}, '0.1.0', { requires: ['base-base'] });
-
+}, '0.1.0', {
+    requires: ['base-base']
+});
diff --git a/lib/docview.js b/lib/docview.js
index 0b34316c..adcbd4bf 100644
--- a/lib/docview.js
+++ b/lib/docview.js
@@ -3,14 +3,13 @@ Copyright (c) 2011, Yahoo! Inc. All rights reserved.
 Code licensed under the BSD License:
 http://yuilibrary.com/license/
 */
-YUI.add('docview', function(Y) {
+YUI.add('docview', function (Y) {
 
-
-/*
-Selleck
-Copyright (c) 2011 Yahoo! Inc.
-Licensed under the BSD License.
-*/
+    /*
+    Selleck
+    Copyright (c) 2011 Yahoo! Inc.
+    Licensed under the BSD License.
+    */
 
     /**
     View class borrowed from [Selleck](https://github.com/rgrove/selleck)  
@@ -19,7 +18,7 @@ Licensed under the BSD License.
     @constructor
     @param {Object} data Meta data to use in this template
     @param {String} templateName The name of the template file to render.
-    */
+    **/
     function DocView(data, templateName) {
         this.templateName = templateName;
         Y.mix(this, data);
@@ -27,11 +26,11 @@ Licensed under the BSD License.
 
     DocView.prototype = {
         /**
-        * **Mustache** `lambda` method for setting the HTML title
-        * @method htmlTitle
-        */
+         * **Mustache** `lambda` method for setting the HTML title
+         * @method htmlTitle
+         */
         htmlTitle: function () {
-            var name  = this.displayName || this.name,
+            var name = this.displayName || this.name,
                 title = name;
 
             if (title) {
@@ -46,11 +45,11 @@ Licensed under the BSD License.
         },
 
         /**
-        * **Mustache** `lambda` method for setting the title
-        * @method title
-        */
+         * **Mustache** `lambda` method for setting the title
+         * @method title
+         */
         title: function () {
-            var name  = this.displayName || this.name,
+            var name = this.displayName || this.name,
                 title = this.projectName;
 
             if (name) {
diff --git a/lib/files.js b/lib/files.js
index d27abf23..4bc93929 100644
--- a/lib/files.js
+++ b/lib/files.js
@@ -1,453 +1,480 @@
-YUI.add('files', function(Y) {
+YUI.add('files', function (Y) {
 
-/**
-* Ported fileutils methods from [Selleck](http://github.com/rgrove/selleck)
-* @class Files
-* @module yuidoc
-*/
+    /**
+     * Ported fileutils methods from [Selleck](http://github.com/rgrove/selleck)
+     * @class Files
+     * @module yuidoc
+     */
 
-Y.Files = {};
+    Y.Files = {};
 
+    /*
+    Selleck
+    Copyright (c) 2011 Yahoo! Inc.
+    Licensed under the BSD License.
+    */
 
-/*
-Selleck
-Copyright (c) 2011 Yahoo! Inc.
-Licensed under the BSD License.
-*/
+    var fs = require('graceful-fs'),
+        fsPath = require('path'),
+        useFS = (fs.exists) ? fs : fsPath;
 
-var fs = require('graceful-fs'),
-    fsPath = require('path'),
-    useFS = (fs.exists) ? fs : fsPath;
 
-
-function exists(file, cb) {
-    if (cb) {
-        useFS.exists(file, cb);
-    } else {
-        return useFS.existsSync(file);
-    }
-}
-
-Y.Files.exists = exists;
-
-
-/**
-* Copy a directory from one location to another
-* @method copyDirectory
-* @param {Path} source The source directory
-* @param {Path} dest The destination directory
-* @param {Boolean} [overwrite=false] Whether or not to overwrite destination files
-  if they already exist.
-* @param {Function} callback The callback to be executed when complete.
-**/
-function copyDirectory(source, dest, overwrite, callback) {
-    // Allow callback as third arg.
-    if (typeof overwrite === 'function') {
-        callback = overwrite;
-        overwrite = null;
+    function exists(file, cb) {
+        if (cb) {
+            useFS.exists(file, cb);
+        } else {
+            return useFS.existsSync(file);
+        }
     }
+    Y.Files.exists = exists;
+
+
+    /**
+    Copy a directory from one location to another
+    @method copyDirectory
+    @param {Path} source The source directory
+    @param {Path} dest The destination directory
+    @param {Boolean} [overwrite=false] Whether or not to overwrite destination files
+        if they already exist.
+    @param {Function} callback The callback to be executed when complete.
+    **/
+    function copyDirectory(source, dest, overwrite, callback) {
+        // Allow callback as third arg.
+        if (typeof overwrite === 'function') {
+            callback = overwrite;
+            overwrite = null;
+        }
 
-    fs.stat(source, afterSourceStat);
+        fs.stat(source, afterSourceStat);
 
-    function afterSourceStat(err, stats) {
-        if (err) { return callback(err); }
+        function afterSourceStat(err, stats) {
+            if (err) {
+                return callback(err);
+            }
+
+            if (!stats.isDirectory()) {
+                return callback(new Error("Source is not a directory: " + source));
+            }
 
-        if (!stats.isDirectory()) {
-            return callback(new Error("Source is not a directory: " + source));
+            fs.lstat(dest, afterDestStat);
         }
 
-        fs.lstat(dest, afterDestStat);
-    }
+        function afterDestStat(err, stats) {
+            if (err && err.code !== 'ENOENT') {
+                return callback(err);
+            }
 
-    function afterDestStat(err, stats) {
-        if (err && err.code !== 'ENOENT') { return callback(err); }
-
-        if (stats) {
-            // If the destination is a file or a link, either delete it or 
-            // bubble an error if overwrite isn't true.
-            if (stats.isFile() || stats.isSymbolicLink()) {
-                if (overwrite) {
-                    deletePath(dest); // TODO: make this async
-                } else {
-                    callback(new Error("Destination already exists: " + dest));
-                    return;
+            if (stats) {
+                // If the destination is a file or a link, either delete it or 
+                // bubble an error if overwrite isn't true.
+                if (stats.isFile() || stats.isSymbolicLink()) {
+                    if (overwrite) {
+                        deletePath(dest); // TODO: make this async
+                    } else {
+                        callback(new Error("Destination already exists: " + dest));
+                        return;
+                    }
                 }
-            }
 
-            afterMkDir();
-        } else {
-            fs.mkdir(dest, 0755, afterMkDir);
+                afterMkDir();
+            } else {
+                fs.mkdir(dest, 0755, afterMkDir);
+            }
         }
-    }
 
-    function afterMkDir(err) {
-        if (err && err.code !== 'EEXIST') { return callback(err); }
-        fs.readdir(source, afterReadDir);
-    }
+        function afterMkDir(err) {
+            if (err && err.code !== 'EEXIST') {
+                return callback(err);
+            }
+            fs.readdir(source, afterReadDir);
+        }
 
-    function afterReadDir(err, files) {
-        if (err) { return callback(err); }
+        function afterReadDir(err, files) {
+            if (err) {
+                return callback(err);
+            }
 
-        var pending = files.length,
-            filename;
+            var pending = files.length,
+                filename;
 
-        if (!pending) { return callback(); }
+            if (!pending) {
+                return callback();
+            }
 
-        while ((filename = files.shift())) {
-            /*jshint loopfunc:true */
-            copyPath(fsPath.join(source, filename), fsPath.join(dest, filename), overwrite, function (err) {
-                if (err) { return callback(err); }
+            while ((filename = files.shift())) {
+                /*jshint loopfunc:true */
+                copyPath(fsPath.join(source, filename), fsPath.join(dest, filename), overwrite, function (err) {
+                    if (err) {
+                        return callback(err);
+                    }
 
-                pending -= 1;
+                    pending -= 1;
 
-                if (!pending) {
-                    callback();
-                }
-            });
+                    if (!pending) {
+                        callback();
+                    }
+                });
+            }
         }
     }
-}
-Y.Files.copyDirectory = copyDirectory;
-
-/**
-* Copy a file from one location to another
-* @method copyFile
-* @param {Path} source The source file
-* @param {Path} dest The destination file
-* @param {Boolean} [overwrite=false] Whether or not to overwrite destination files
-  if they already exist.
-* @param {Callback} callback The callback to be executed when complete.
-* @param {Error} callback.err The Error returned from Node
-**/
-function copyFile(source, dest, overwrite, callback) {
-    // Allow callback as third arg.
-    if (typeof overwrite === 'function') {
-        callback = overwrite;
-        overwrite = null;
-    }
+    Y.Files.copyDirectory = copyDirectory;
+
+    /**
+    Copy a file from one location to another
+    @method copyFile
+    @param {Path} source The source file
+    @param {Path} dest The destination file
+    @param {Boolean} [overwrite=false] Whether or not to overwrite destination files
+        if they already exist.
+    @param {Callback} callback The callback to be executed when complete.
+    @param {Error} callback.err The Error returned from Node
+    **/
+    function copyFile(source, dest, overwrite, callback) {
+        // Allow callback as third arg.
+        if (typeof overwrite === 'function') {
+            callback = overwrite;
+            overwrite = null;
+        }
 
-    fs.lstat(source, function (err, sourceStats) {
-        if (err) { return callback(err); }
+        fs.lstat(source, function (err, sourceStats) {
+            if (err) {
+                return callback(err);
+            }
 
-        if (!sourceStats.isFile()) {
-            return callback(new Error("Source is not a file: " + source));
-        }
+            if (!sourceStats.isFile()) {
+                return callback(new Error("Source is not a file: " + source));
+            }
 
-        fs.lstat(dest, function (err, destStats) {
-            var rs;
+            fs.lstat(dest, function (err, destStats) {
+                var rs;
 
-            if (err && err.code !== 'ENOENT') { return callback(err); }
+                if (err && err.code !== 'ENOENT') {
+                    return callback(err);
+                }
 
-            if (destStats) {
-                if (overwrite) {
-                    deletePath(dest); // TODO: make this async
-                } else {
-                    callback(new Error("Destination already exists: " + dest));
-                    return;
+                if (destStats) {
+                    if (overwrite) {
+                        deletePath(dest); // TODO: make this async
+                    } else {
+                        callback(new Error("Destination already exists: " + dest));
+                        return;
+                    }
                 }
-            }
 
-            rs = fs.createReadStream(source);
-            rs.pipe(fs.createWriteStream(dest, {mode: 0655}));
-            rs.on('end', callback);
+                rs = fs.createReadStream(source);
+                rs.pipe(fs.createWriteStream(dest, {
+                    mode: 0655
+                }));
+                rs.on('end', callback);
+            });
         });
-    });
-}
-Y.Files.copyFile = copyFile;
-
-/**
-If _source_ is a file, copies it to _dest_. If it's a directory, recursively
-copies it and all files and directories it contains to _dest_.
-
-Note that when attempting to copy a file into a directory, you should specify
-the full path to the new file (including the new filename). Otherwise, it will
-be interpreted as an attempt to copy the _source_ file *over* the _dest_
-directory instead of *into* it.
-
-Known issues:
-- Doesn't preserve ownership or permissions on copied files/directories.
-
-@method copyPath
-@param {String} source Source path.
-@param {String} dest Destination path.
-@param {Boolean} [overwrite=false] Whether or not to overwrite destination files
-if they already exist.
-@param {Callback} callback The callback to execute when completed.
-@param {Error} callback.err
-**/
-function copyPath(source, dest, overwrite, callback) {
-    var sourceStats = statSync(source);
-
-    // Allow callback as third arg.
-    if (typeof overwrite === 'function') {
-        callback = overwrite;
-        overwrite = null;
     }
+    Y.Files.copyFile = copyFile;
+
+
+    /**
+    If _source_ is a file, copies it to _dest_. If it's a directory, recursively
+    copies it and all files and directories it contains to _dest_.
+
+    Note that when attempting to copy a file into a directory, you should specify
+    the full path to the new file (including the new filename). Otherwise, it will
+    be interpreted as an attempt to copy the _source_ file *over* the _dest_
+    directory instead of *into* it.
+
+    Known issues:
+    - Doesn't preserve ownership or permissions on copied files/directories.
+
+    @method copyPath
+    @param {String} source Source path.
+    @param {String} dest Destination path.
+    @param {Boolean} [overwrite=false] Whether or not to overwrite destination files
+    if they already exist.
+    @param {Callback} callback The callback to execute when completed.
+    @param {Error} callback.err
+    **/
+    function copyPath(source, dest, overwrite, callback) {
+        var sourceStats = statSync(source);
+
+        // Allow callback as third arg.
+        if (typeof overwrite === 'function') {
+            callback = overwrite;
+            overwrite = null;
+        }
 
-    if (!sourceStats) {
-        callback(new Error("Source not found: " + source));
-        return;
-    }
+        if (!sourceStats) {
+            callback(new Error("Source not found: " + source));
+            return;
+        }
 
-    if (sourceStats.isFile()) {
-        copyFile(source, dest, overwrite, callback);
-    } else if (sourceStats.isDirectory()) {
-        copyDirectory(source, dest, overwrite, callback);
-    } else {
-        callback(new Error("Source is neither a file nor a directory: " + source));
+        if (sourceStats.isFile()) {
+            copyFile(source, dest, overwrite, callback);
+        } else if (sourceStats.isDirectory()) {
+            copyDirectory(source, dest, overwrite, callback);
+        } else {
+            callback(new Error("Source is neither a file nor a directory: " + source));
+        }
     }
-}
-Y.Files.copyPath = copyPath;
+    Y.Files.copyPath = copyPath;
 
-// TODO: copySymbolicLink()?
+    // TODO: copySymbolicLink()?
 
-/**
-If _path_ is a file, deletes it. If _path_ is a directory, recursively deletes
-it and all files and directories it contains.
+    /**
+    If _path_ is a file, deletes it. If _path_ is a directory, recursively deletes
+    it and all files and directories it contains.
 
-This method is synchronous.
+    This method is synchronous.
 
-@method deletePath
-@param {String} path File or directory to delete.
-**/
-function deletePath(path) {
-    var stats = fs.lstatSync(path);
+    @method deletePath
+    @param {String} path File or directory to delete.
+    **/
+    function deletePath(path) {
+        var stats = fs.lstatSync(path);
 
-    if (stats.isFile() || stats.isSymbolicLink()) {
-        fs.unlinkSync(path);
-    } else if (stats.isDirectory()) {
-        fs.readdirSync(path).forEach(function (filename) {
-            deletePath(fsPath.join(path, filename));
-        });
+        if (stats.isFile() || stats.isSymbolicLink()) {
+            fs.unlinkSync(path);
+        } else if (stats.isDirectory()) {
+            fs.readdirSync(path).forEach(function (filename) {
+                deletePath(fsPath.join(path, filename));
+            });
 
-        fs.rmdirSync(path);
+            fs.rmdirSync(path);
+        }
     }
-}
-Y.Files.deletePath = deletePath;
-
-/**
-Check to see if this is a directory
-@method isDirectory
-@param {Path} path The path to check
-@param {Boolean} [link=false] Also validate a symlink
-@return {Boolean} True if it is a directory
-**/
-function isDirectory(path, link) {
-    var stat,
-        result = false;
-
-    link = (link === false) ? false : true;
-
-    try {
-        stat = fs.lstatSync(path);
-        if (stat) {
-            if (stat.isSymbolicLink() && link) {
-                stat = fs.statSync(path);
+    Y.Files.deletePath = deletePath;
+
+
+    /**
+    Check to see if this is a directory
+
+    @method isDirectory
+    @param {Path} path The path to check
+    @param {Boolean} [link=false] Also validate a symlink
+    @return {Boolean} True if it is a directory
+    **/
+    function isDirectory(path, link) {
+        var stat,
+            result = false;
+
+        link = (link === false) ? false : true;
+
+        try {
+            stat = fs.lstatSync(path);
+            if (stat) {
+                if (stat.isSymbolicLink() && link) {
+                    stat = fs.statSync(path);
+                }
+                result = stat.isDirectory();
             }
-            result = stat.isDirectory();
+        } catch (e) {
+            result = false;
         }
-    } catch (e) {
-        result = false;
-    }
 
-    return result;
-}
-
-Y.Files.isDirectory = isDirectory;
-
-/**
-Check to see if this is a File
-@method isFile
-@param {Path} path The path to check
-@param {Boolean} [link=false] Also validate a symlink
-@return {Boolean} True if it is a file
-**/
-function isFile(path, link) {
-    var stat,
-        result = false;
-
-    try {
-        stat = fs.lstatSync(path);
-        if (stat) {
-            if (stat.isSymbolicLink() && link) {
-                stat = fs.statSync(path);
+        return result;
+    }
+    Y.Files.isDirectory = isDirectory;
+
+
+    /**
+    Check to see if this is a File
+
+    @method isFile
+    @param {Path} path The path to check
+    @param {Boolean} [link=false] Also validate a symlink
+    @return {Boolean} True if it is a file
+    **/
+    function isFile(path, link) {
+        var stat,
+            result = false;
+
+        try {
+            stat = fs.lstatSync(path);
+            if (stat) {
+                if (stat.isSymbolicLink() && link) {
+                    stat = fs.statSync(path);
+                }
+                result = stat.isFile();
             }
-            result = stat.isFile();
+        } catch (e) {
+            result = false;
         }
-    } catch (e) {
-        result = false;
+
+        return result;
     }
+    Y.Files.isFile = isFile;
 
-    return result;
-}
-Y.Files.isFile = isFile;
-
-/**
-Check to see if this is a SymLink
-@method isSymbolicLink
-@param {Path} path The path to check
-@return {Boolean} True if it is a link
-**/
-function isSymbolicLink(path) {
-    var stats = lstatSync(path);
-    return stats ? stats.isSymbolicLink() : false;
-}
-Y.Files.isSymbolicLink = isSymbolicLink;
-
-/**
-Like `fs.lstatSync()`, but returns `null` instead of throwing when _path_
-doesn't exist. Will still throw on other types of errors.
-
-@method lstatSync
-@param {String} path Path to stat.
-@return {fs.Stats|null} `fs.Stats` object, or `null` if _path_ doesn't exist.
-**/
-function lstatSync(path) {
-    try {
-        return fs.lstatSync(path);
-    } catch (ex) {
-        if (ex.code === 'ENOENT') {
-            return null;
-        }
 
-        throw ex;
+    /**
+    Check to see if this is a SymLink
+
+    @method isSymbolicLink
+    @param {Path} path The path to check
+    @return {Boolean} True if it is a link
+    **/
+    function isSymbolicLink(path) {
+        var stats = lstatSync(path);
+        return stats ? stats.isSymbolicLink() : false;
     }
-}
-Y.Files.lstatSync = lstatSync;
-
-/**
-Like `fs.statSync()`, but returns `null` instead of throwing when _path_
-doesn't exist. Will still throw on other types of errors.
-
-@method statSync
-@param {String} path Path to stat.
-@return {fs.Stats|null} `fs.Stats` object, or `null` if _path_ doesn't exist.
-**/
-function statSync(path) {
-    try {
-        return fs.statSync(path);
-    } catch (ex) {
-        if (ex.code === 'ENOENT') {
-            return null;
-        }
+    Y.Files.isSymbolicLink = isSymbolicLink;
+
+
+    /**
+    Like `fs.lstatSync()`, but returns `null` instead of throwing when _path_
+    doesn't exist. Will still throw on other types of errors.
+
+    @method lstatSync
+    @param {String} path Path to stat.
+    @return {fs.Stats|null} `fs.Stats` object, or `null` if _path_ doesn't exist.
+    **/
+    function lstatSync(path) {
+        try {
+            return fs.lstatSync(path);
+        } catch (ex) {
+            if (ex.code === 'ENOENT') {
+                return null;
+            }
 
-        throw ex;
+            throw ex;
+        }
     }
-}
-Y.Files.statSync = statSync;
-
-/**
-Copy the theme assets directory
-@method copyAssets
-@param {Path} from The source directory
-@param {Path} dest The destination directory
-@param {Boolean} deleteFirst Should the directory be deleted if it exists
-@param {Function} callback The callback to be executed
-*/
-function copyAssets() {
-    var args        = Array.prototype.slice.call(arguments),
-        callback    = args.pop(),
-        from        = args.shift(),
-        to          = args.shift(),
-        deleteFirst = args.shift();
-    
-    if (from[0] === from[1]) {
-        if (isDirectory(from[0])) {
-            if (deleteFirst && isDirectory(to)) {
-                deletePath(to);
+    Y.Files.lstatSync = lstatSync;
+
+
+    /**
+    Like `fs.statSync()`, but returns `null` instead of throwing when _path_
+    doesn't exist. Will still throw on other types of errors.
+
+    @method statSync
+    @param {String} path Path to stat.
+    @return {fs.Stats|null} `fs.Stats` object, or `null` if _path_ doesn't exist.
+    **/
+    function statSync(path) {
+        try {
+            return fs.statSync(path);
+        } catch (ex) {
+            if (ex.code === 'ENOENT') {
+                return null;
             }
 
-            copyPath(from[0], to, true, callback);
-        } else {
-            callback();
+            throw ex;
         }
-    } else {
-        if (isDirectory(from[0])) {
-            if (deleteFirst && isDirectory(to)) {
-                deletePath(to);
+    }
+    Y.Files.statSync = statSync;
+
+    /**
+    Copy the theme assets directory
+
+    @method copyAssets
+    @param {Path} from The source directory
+    @param {Path} dest The destination directory
+    @param {Boolean} deleteFirst Should the directory be deleted if it exists
+    @param {Function} callback The callback to be executed
+    */
+    function copyAssets() {
+        var args = Array.prototype.slice.call(arguments),
+            callback = args.pop(),
+            from = args.shift(),
+            to = args.shift(),
+            deleteFirst = args.shift();
+
+        if (from[0] === from[1]) {
+            if (isDirectory(from[0])) {
+                if (deleteFirst && isDirectory(to)) {
+                    deletePath(to);
+                }
+
+                copyPath(from[0], to, true, callback);
+            } else {
+                callback();
+            }
+        } else {
+            if (isDirectory(from[0])) {
+                if (deleteFirst && isDirectory(to)) {
+                    deletePath(to);
+                }
+
+                copyPath(from[0], to, true, function () {
+                    if (isDirectory(from[1])) {
+                        copyPath(from[1], to, true, callback);
+                    } else {
+                        callback();
+                    }
+                });
+            } else {
+                callback();
             }
 
-            copyPath(from[0], to, true, function() {
-                if (isDirectory(from[1])) {
-                    copyPath(from[1], to, true, callback);
-                } else {
-                    callback();
+        }
+    }
+    Y.Files.copyAssets = copyAssets;
+
+
+    /**
+    Helper method for getting JSON data from a local file
+
+    @method getJSON
+    @param {Path} filename The filename to parse JSON from
+    @return {Object} The JSON data
+    **/
+    Y.Files.getJSON = function (filename) {
+        var data = {};
+        if (exists(filename)) {
+            data = JSON.parse(fs.readFileSync(filename, 'utf8'));
+        }
+        return data;
+    };
+
+    /**
+    Helper method for writing files to disk. It wraps the NodeJS file API
+
+    @method writeFile
+    @param {Path} file The filename to write to
+    @param {String} data The data to write
+    @param {Callback} callback*
+    **/
+    function writeFile(file, data, cb) {
+        var out,
+            args = arguments,
+            flags = {
+                flags: "w",
+                encoding: Y.charset,
+                mode: 0644
+            };
+
+        if (cb) {
+            fs.writeFile(file, data, flags, function (err) {
+                if (err && err.message.match(/^EMFILE, Too many open files/)) {
+                    Y.log('Writefile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files');
+                    writeFile.timer++;
+                    Y.later(writeFile.timer, Y, writeFile, args);
+                    return;
                 }
+                cb();
             });
         } else {
-            callback();
+            out = fs.createWriteStream(file, flags);
+            out.write(data);
+            out.end();
         }
-        
     }
-}
+    writeFile.timer = 100;
 
-Y.Files.copyAssets = copyAssets;
+    Y.Files.writeFile = writeFile;
 
 
-/**
-* Helper method for getting JSON data from a local file
-* @method getJSON
-* @param {Path} filename The filename to parse JSON from
-* @return {Object} The JSON data 
-*/
-Y.Files.getJSON = function(filename) {
-    var data = {};
-    if (exists(filename)) {
-        data = JSON.parse(fs.readFileSync(filename, 'utf8'));
-    }
-    return data;
-};
-
-/**
-* Helper method for writing files to disk. It wraps the NodeJS file API
-* @method writeFile
-* @param {Path} file The filename to write to
-* @param {String} data The data to write
-* @param {Callback} callback*
-*/
-
-function writeFile(file, data, cb) {
-    var out,
-        args = arguments,
-        flags = {
-            flags: "w", encoding: Y.charset, mode: 0644
-        };
-
-    if (cb) {
-        fs.writeFile(file, data, flags, function(err) {
+    function readFile(file, enc, cb) {
+        var args = arguments;
+        fs.readFile(file, enc, function (err, data) {
             if (err && err.message.match(/^EMFILE, Too many open files/)) {
-                Y.log('Writefile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files');
-                writeFile.timer++;
-                Y.later(writeFile.timer, Y, writeFile, args);
+                Y.log('Readfile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files');
+                readFile.timer++;
+                Y.later(readFile.timer, Y, readFile, args);
                 return;
             }
-            cb();
+            cb(err, data);
         });
-    } else {
-        out = fs.createWriteStream(file, flags);
-        out.write(data);
-        out.end();
     }
-}
-writeFile.timer = 100;
-
-Y.Files.writeFile = writeFile;
-
-
-function readFile(file, enc, cb) {
-    var args = arguments;
-    fs.readFile(file, enc, function(err, data) {
-        if (err && err.message.match(/^EMFILE, Too many open files/)) {
-            Y.log('Readfile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files');
-            readFile.timer++;
-            Y.later(readFile.timer, Y, readFile, args);
-            return;
-        }
-        cb(err, data);
-    });
-}
-readFile.timer = 100;
+    readFile.timer = 100;
 
-Y.Files.readFile = readFile;
+    Y.Files.readFile = readFile;
 
 });
diff --git a/lib/help.js b/lib/help.js
index e2b3731a..6e59133b 100644
--- a/lib/help.js
+++ b/lib/help.js
@@ -3,20 +3,20 @@ Copyright (c) 2011, Yahoo! Inc. All rights reserved.
 Code licensed under the BSD License:
 http://yuilibrary.com/license/
 */
-YUI.add('help', function(Y) {
-    
+YUI.add('help', function (Y) {
+
     /**
-    * Shows the help text
-    * @module yuidoc
-    * @class Help
-    */
+     * Shows the help text
+     * @module yuidoc
+     * @class Help
+     */
 
     /**
-    * The help text to display
-    * @private
-    * @property help
-    * @type Array
-    */
+     * The help text to display
+     * @private
+     * @property help
+     * @type Array
+     */
     var help = [
         "",
         "YUI Doc generates API documentation from a modified JavaDoc syntax.",
@@ -56,22 +56,22 @@ YUI.add('help', function(Y) {
         "   Supply a list of paths (shell globbing is handy here)",
         "",
     ].join('\n');
-    
+
     /**
-    * Render the help message as a string
-    * @method renderHelp
-    * @return {String} The help screen to display
-    */
-    Y.renderHelp = function() {
+     * Render the help message as a string
+     * @method renderHelp
+     * @return {String} The help screen to display
+     */
+    Y.renderHelp = function () {
         return Y.Lang.sub(help, {
             VERSION: Y.packageInfo.version
         });
     };
     /**
-    * Display the help message, write it to the screen and exit
-    * @method showHelp
-    */
-    Y.showHelp = function() {
+     * Display the help message, write it to the screen and exit
+     * @method showHelp
+     */
+    Y.showHelp = function () {
         console.error(Y.renderHelp());
         process.exit(0); //Shouldn't exit one on help
     };
diff --git a/lib/index.js b/lib/index.js
index 9f676c53..3fc62548 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -25,7 +25,7 @@ You can use it like this:
 var args = process.argv.slice(2);
 var debug = false;
 
-args.forEach(function(item) {
+args.forEach(function (item) {
     if (item.toLowerCase() === '--debug') {
         debug = true;
     }
@@ -37,7 +37,7 @@ var YUI = require('yui' + (debug ? '/debug' : '')).YUI,
     metaPath = path.join(__dirname, '../', 'package.json');
 
 
-process.on('uncaughtException', function(msg) {
+process.on('uncaughtException', function (msg) {
     var meta = JSON.parse(fs.readFileSync(metaPath)),
         inst = YUI(),
         useColor = (Y ? Y.config.useColor : false);
diff --git a/lib/options.js b/lib/options.js
index dec5caca..39a89ec7 100644
--- a/lib/options.js
+++ b/lib/options.js
@@ -1,20 +1,20 @@
-YUI.add('options', function(Y) {
-    
+YUI.add('options', function (Y) {
+
     var path = require('path');
 
     /**
-    * Handles argument parsing
-    * @module yuidoc
-    * @class Options
-    */
+     * Handles argument parsing
+     * @module yuidoc
+     * @class Options
+     */
 
     /**
-    * Parses arguments and returns an Object of config options
-    * @method Options
-    * @param {Array} args Arguments to parse
-    * @return {Object} The config object
-    */
-    Y.Options = function(args) {
+     * Parses arguments and returns an Object of config options
+     * @method Options
+     * @param {Array} args Arguments to parse
+     * @return {Object} The config object
+     */
+    Y.Options = function (args) {
         /*jshint onevar:false */
 
         var options = {
@@ -26,141 +26,145 @@ YUI.add('options', function(Y) {
             var v = args.shift();
             // options.* defined in ./builder.js
             switch (v) {
-                case '--lint':
-                    options.lint = true;
-                    options.parseOnly = true;
-                    options.writeJSON = false;
-                    options.quiet = true;
-                    break;
-                case "--debug":
-                    Y.applyConfig({ debug: true, filter: 'debug' });
-                    break;
-                case "--charset":
-                    Y.charset = args.shift() || 'utf8';
-                    Y.log('Setting default charset to ' + Y.charset, 'yuidoc', 'warn');
-                    break;
-                case "-c":
-                case "--config":
-                case "--configfile":
-                    options.configfile = args.shift();
-                    break;
-                case "-e":
-                case "--extension":
-                    options.extension = args.shift();
-                    break;
-                case "-x":
-                case "--exclude":
-                    options.exclude = args.shift();
-                    break;
-                case "-v":
-                case "--version":
-                    console.error(Y.packageInfo.version);
-                    process.exit(1);
-                    break;
-                case "--project-version":
-                    options.version = args.shift();
-                    break;
-                case "-N":
-                case "--no-color":
-                    Y.config.useColor = false;
-                    options.nocolor = true;
-                    break;
-                case "-D":
-                case "--no-delete-out":
-                    options.nodeleteout = true;
-                    break;
-                case "-C":
-                case "--no-code":
-                    options.nocode = true;
-                    break;
-                case "-n":
-                case "--norecurse":
-                    options.norecurse = true;
-                    break;
-                case "-S":
-                case "--selleck":
-                    options.selleck = true;
-                    break;
-                case "-V":
-                case "--view":
-                    options.dumpview = true;
-                    break;
-                case "-p":
-                case "--parse-only":
-                    options.parseOnly = true;
-                    break;
-                case "-o":
-                case "--outdir":
-                    options.outdir = args.shift();
-                    break;
-                case "-t":
-                case "--themedir":
-                    options.themedir = args.shift();
-                    break;
-                case "--server":
-                    options.server = true;
-                    var a = args.shift();
-                    var p = parseInt(a, 10);
-                    if (isNaN(p) || !p) {
-                        if (a) {
-                            args.unshift(a);
-                        }
-                        Y.log('Failed to extract port, setting to the default :3000', 'warn', 'yuidoc');
-                    } else {
-                        options.port = p;
-                    }
-                    break;
-                case "-h":
-                case "--help":
-                    Y.showHelp();
-                    break;
-                case "-H":
-                case "--helpers":
-                    var list = args.shift();
-                    if (list) {
-                        options.helpers = list.split(',');
-                    } else {
-                        throw('Failed to pass a helper file.');
-                    }
-                    break;
-                case "-T":
-                case "--theme":
-                    var theme = args.shift();
-                    options.themedir = path.join(__dirname, '../', 'themes', theme);
-                    break;
-                case "-q":
-                case "--quiet":
-                    options.quiet = true;
-                    break;
-                case "--syntaxtype":
-                    options.syntaxtype = args.shift();
-                    break;
-                case "--tab-to-space":
-                    options.tabtospace = parseInt(args.shift(), 10);
-                    if (typeof options.tabtospace === 'number') {
-                        options.tabspace = '';
-                        for (var s = 0; s < options.tabtospace; s++) {
-                            options.tabspace += ' ';
-                        }
+            case '--lint':
+                options.lint = true;
+                options.parseOnly = true;
+                options.writeJSON = false;
+                options.quiet = true;
+                break;
+            case "--debug":
+                Y.applyConfig({
+                    debug: true,
+                    filter: 'debug'
+                });
+                break;
+            case "--charset":
+                Y.charset = args.shift() || 'utf8';
+                Y.log('Setting default charset to ' + Y.charset, 'yuidoc', 'warn');
+                break;
+            case "-c":
+            case "--config":
+            case "--configfile":
+                options.configfile = args.shift();
+                break;
+            case "-e":
+            case "--extension":
+                options.extension = args.shift();
+                break;
+            case "-x":
+            case "--exclude":
+                options.exclude = args.shift();
+                break;
+            case "-v":
+            case "--version":
+                console.error(Y.packageInfo.version);
+                process.exit(1);
+                break;
+            case "--project-version":
+                options.version = args.shift();
+                break;
+            case "-N":
+            case "--no-color":
+                Y.config.useColor = false;
+                options.nocolor = true;
+                break;
+            case "-D":
+            case "--no-delete-out":
+                options.nodeleteout = true;
+                break;
+            case "-C":
+            case "--no-code":
+                options.nocode = true;
+                break;
+            case "-n":
+            case "--norecurse":
+                options.norecurse = true;
+                break;
+            case "-S":
+            case "--selleck":
+                options.selleck = true;
+                break;
+            case "-V":
+            case "--view":
+                options.dumpview = true;
+                break;
+            case "-p":
+            case "--parse-only":
+                options.parseOnly = true;
+                break;
+            case "-o":
+            case "--outdir":
+                options.outdir = args.shift();
+                break;
+            case "-t":
+            case "--themedir":
+                options.themedir = args.shift();
+                break;
+            case "--server":
+                options.server = true;
+                var a = args.shift();
+                var p = parseInt(a, 10);
+                if (isNaN(p) || !p) {
+                    if (a) {
+                        args.unshift(a);
                     }
-                    break;
-                default:
-                    if (!options.paths) {
-                        options.paths = [];
+                    Y.log('Failed to extract port, setting to the default :3000', 'warn', 'yuidoc');
+                } else {
+                    options.port = p;
+                }
+                break;
+            case "-h":
+            case "--help":
+                Y.showHelp();
+                break;
+            case "-H":
+            case "--helpers":
+                var list = args.shift();
+                if (list) {
+                    options.helpers = list.split(',');
+                } else {
+                    throw ('Failed to pass a helper file.');
+                }
+                break;
+            case "-T":
+            case "--theme":
+                var theme = args.shift();
+                options.themedir = path.join(__dirname, '../', 'themes', theme);
+                break;
+            case "-q":
+            case "--quiet":
+                options.quiet = true;
+                break;
+            case "--syntaxtype":
+                options.syntaxtype = args.shift();
+                break;
+            case "--tab-to-space":
+                options.tabtospace = parseInt(args.shift(), 10);
+                if (typeof options.tabtospace === 'number') {
+                    options.tabspace = '';
+                    for (var s = 0; s < options.tabtospace; s++) {
+                        options.tabspace += ' ';
                     }
-                    if (v && v.indexOf('-') === 0) {
-                        throw('Unknown option: ' + v);
-                    }
-                    options.paths.push(v);
+                }
+                break;
+            default:
+                if (!options.paths) {
+                    options.paths = [];
+                }
+                if (v && v.indexOf('-') === 0) {
+                    throw ('Unknown option: ' + v);
+                }
+                options.paths.push(v);
             }
         }
 
         if (options.quiet) {
-            Y.applyConfig({ debug: false });
+            Y.applyConfig({
+                debug: false
+            });
         }
-    
-        return options;
 
+        return options;
     };
 
 });
diff --git a/lib/project.js b/lib/project.js
index 12300077..0eabb7fe 100644
--- a/lib/project.js
+++ b/lib/project.js
@@ -1,16 +1,18 @@
-YUI.add('project', function(Y) {
-    
+YUI.add('project', function (Y) {
+
     Y.Project = {
-        mix: function(json, options) {
+        mix: function (json, options) {
             if (json.project) {
                 options = Y.merge(options, json.project);
             }
+
             if (options.title && !options.name) {
                 options.name = options.title;
             }
+
             return options;
         },
-        init: function(options) {
+        init: function (options) {
             var project = {};
             if (options.configfile) {
                 project = Y.Files.getJSON(options.configfile);
@@ -22,7 +24,6 @@ YUI.add('project', function(Y) {
                 }
             }
 
-
             if (project.options && Object.keys(project.options).length) {
                 options = Y.merge(project.options, options);
                 delete project.options;
@@ -45,7 +46,7 @@ YUI.add('project', function(Y) {
                 Y.showHelp();
                 process.exit(1);
             }
-            
+
             if (typeof options.tabtospace === 'number') {
                 /*jshint onevar:false */
                 options.tabspace = '';
@@ -55,7 +56,7 @@ YUI.add('project', function(Y) {
             }
 
             return options;
-            
         }
     };
+
 });
diff --git a/lib/server.js b/lib/server.js
index a47af3db..90e765f5 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -1,231 +1,230 @@
-YUI.add('server', function(Y) {
+YUI.add('server', function (Y) {
 
     var path = require('path'),
-    /**
-    * Provides the `--server` server option for YUIDoc
-    * @class Server
-    * @module yuidoc
-    */
-    Server = {
         /**
-        * Cache for external mixed in data.
-        * @property _externalData
-        * @private
-        * @type Object
-        */
-        _externalData: null,
-        /**
-        * Middleware to parse the API docs per request
-        * @method parse
-        * @param {Request} req Express request object
-        * @param {Response} res Express response object
-        * @param {Function} next Express next callback
-        */
-        parse: function(req, res, next) {
-            var json = (new Y.YUIDoc(Server.options)).run();
-            Server.options = Y.Project.mix(json, Server.options);
-            Server.builder = new Y.DocBuilder(Server.options, json);
-            if (Server._externalData) {
-                Server.options.externalData = Server._externalData;
-                Server.builder._mixExternal();
-            }
+         * Provides the `--server` server option for YUIDoc
+         * @class Server
+         * @module yuidoc
+         */
+        Server = {
+            /**
+             * Cache for external mixed in data.
+             * @property _externalData
+             * @private
+             * @type Object
+             */
+            _externalData: null,
+            /**
+             * Middleware to parse the API docs per request
+             * @method parse
+             * @param {Request} req Express request object
+             * @param {Response} res Express response object
+             * @param {Function} next Express next callback
+             */
+            parse: function (req, res, next) {
+                var json = (new Y.YUIDoc(Server.options)).run();
+                Server.options = Y.Project.mix(json, Server.options);
+                Server.builder = new Y.DocBuilder(Server.options, json);
+                if (Server._externalData) {
+                    Server.options.externalData = Server._externalData;
+                    Server.builder._mixExternal();
+                }
 
-            next();
-        },
-        /**
-        * Create the routes used to serve YUIDoc files dynamically
-        * @method routes
-        */
-        routes: function() {
-            var app = Server.app;
-
-            app.get('/', Server.parse, function(req, res) {
-                Server.home(req, res);
-            });
-            app.get('/api.js', function(req, res) {
-                Server.builder.renderAPIMeta(function(js) {
-                    res.contentType('.js');
-                    res.send(js);
+                next();
+            },
+            /**
+             * Create the routes used to serve YUIDoc files dynamically
+             * @method routes
+             */
+            routes: function () {
+                var app = Server.app;
+
+                app.get('/', Server.parse, function (req, res) {
+                    Server.home(req, res);
+                });
+                app.get('/api.js', function (req, res) {
+                    Server.builder.renderAPIMeta(function (js) {
+                        res.contentType('.js');
+                        res.send(js);
+                    });
                 });
-            });
-
 
-            app.get('/classes/:class.html', Server.parse, function(req, res, next) {
-                Server.clazz(req, res, next);
-            });
 
-            app.get('/modules/:module.html', Server.parse, function(req, res, next) {
-                Server.module(req, res, next);
-            });
+                app.get('/classes/:class.html', Server.parse, function (req, res, next) {
+                    Server.clazz(req, res, next);
+                });
 
-            app.get('/files/:file.html', Server.parse, function(req, res, next) {
-                Server.files(req, res, next);
-            });
-            
-            //These routes are special catch routes..
+                app.get('/modules/:module.html', Server.parse, function (req, res, next) {
+                    Server.module(req, res, next);
+                });
 
-            app.get('//api.js', function(req, res) {
-                res.redirect('/api.js');
-            });
-            app.get('//classes/:class.html', Server.parse, function(req, res, next) {
-                Server.clazz(req, res, next);
-            });
+                app.get('/files/:file.html', Server.parse, function (req, res, next) {
+                    Server.files(req, res, next);
+                });
 
-            app.get('//modules/:module.html', Server.parse, function(req, res, next) {
-                Server.module(req, res, next);
-            });
+                //These routes are special catch routes..
 
-            app.get('//files/:file.html', Server.parse, function(req, res, next) {
-                Server.files(req, res, next);
-            });
+                app.get('//api.js', function (req, res) {
+                    res.redirect('/api.js');
+                });
+                app.get('//classes/:class.html', Server.parse, function (req, res, next) {
+                    Server.clazz(req, res, next);
+                });
 
-            app.get('*', function(req, res) {
-                var type = req.url.split('/')[1],
-                    html = ['

Item Not Found in internal meta-data

']; + app.get('//modules/:module.html', Server.parse, function (req, res, next) { + Server.module(req, res, next); + }); - if (type === 'class') { - type = 'classes'; - } - if (Server.builder && Server.builder.data && Server.builder.data[type]) { - if (Object.keys(Server.builder.data[type]).length) { - html.push('

But I know about these? Misname your module?

'); - html.push('
    '); - Object.keys(Server.builder.data[type]).forEach(function(item) { - html.push('
  • ' + item + '
  • '); - }); - html.push('
'); - } - } + app.get('//files/:file.html', Server.parse, function (req, res, next) { + Server.files(req, res, next); + }); + app.get('*', function (req, res) { + var type = req.url.split('/')[1], + html = ['

Item Not Found in internal meta-data

']; - res.send(html.join('\n'), 404); - }); + if (type === 'class') { + type = 'classes'; + } + if (Server.builder && Server.builder.data && Server.builder.data[type]) { + if (Object.keys(Server.builder.data[type]).length) { + html.push('

But I know about these? Misname your module?

'); + html.push('
    '); + Object.keys(Server.builder.data[type]).forEach(function (item) { + html.push('
  • ' + item + '
  • '); + }); + html.push('
'); + } + } - }, - /** - * `/files` endpoint - * @method files - * @param {Request} req Express request object - * @param {Response} res Express response object - */ - files: function(req, res, next) { - var fileName = req.params.file, - data; - Object.keys(Server.builder.data.files).forEach(function(file) { - if (fileName === Server.builder.filterFileName(file)) { - data = Server.builder.data.files[file]; - } - }); - - if (!data) { - return next(); - } - Y.log('Serving /files/' + data.name, 'info', 'server'); + res.send(html.join('\n'), 404); + }); + }, + /** + * `/files` endpoint + * @method files + * @param {Request} req Express request object + * @param {Response} res Express response object + */ + files: function (req, res, next) { + var fileName = req.params.file, + data; + Object.keys(Server.builder.data.files).forEach(function (file) { + if (fileName === Server.builder.filterFileName(file)) { + data = Server.builder.data.files[file]; + } + }); - Server.builder.renderFile(function(html) { - res.send(html); - }, data, (req.xhr ? 'xhr' : 'main')); - }, - /** - * `/classes` endpoint - * @method clazz - * @param {Request} req Express request object - * @param {Response} res Express response object - */ - clazz: function(req, res, next) { - var className = req.params['class']; - Y.log('Serving /classes/' + className + '.html', 'info', 'server'); - if (!Server.builder.data.classes[className]) { - return next(); - } - Server.builder.renderClass(function(html) { - res.send(html); - }, Server.builder.data.classes[className], (req.xhr ? 'xhr' : 'main')); - }, - /** - * `/modules` endpoint - * @method modules - * @param {Request} req Express request object - * @param {Response} res Express response object - */ - module: function(req, res, next) { - var modName = req.params.module; - Y.log('Serving /modules/' + modName + '.html', 'info', 'server'); - if (!Server.builder.data.modules[modName]) { - return next(); - } - Server.builder.renderModule(function(html) { - res.send(html); - }, Server.builder.data.modules[modName], (req.xhr ? 'xhr' : 'main')); - }, - /** - * `/` endpoint - * @method home - * @param {Request} req Express request object - * @param {Response} res Express response object - */ - home: function(req, res, next) { - Y.log('Serving index.html', 'info', 'server'); - Server.builder.renderIndex(function(html) { - res.send(html); - }); - }, - /** - * Creates the Express server and prep's YUI for serving - * @method init - */ - init: function() { - var express = require('express'), - path = require('path'), - stat; - - - Server.app = express(); - //console.log(Server.options); - stat = Server.options.themedir || path.join(__dirname, '../', 'themes', 'default'); - Server.app.use(express.static(stat)); - Server.routes(); - Server.app.listen(Server.options.port); - - Y.log('Starting server: http:/'+'/127.0.0.1:' + Server.options.port, 'info', 'server'); - }, - /** - * Start the server with the supplied options. - * @method start - * @param {Object} options Server options - */ - start: function(options) { - options = Y.Project.init(options); - Server.options = options; - - Server.options.cacheTemplates = false; //Don't cache the Handlebars templates - Server.options.writeJSON = false; //Don't write the JSON file out - - Y.config.logExclude.yuidoc = true; - Y.config.logExclude.docparser = true; - Y.config.logExclude.builder = true; - - if (Server.options.external) { - Y.log('Fetching external data, this may take a minute', 'warn', 'server'); - var json, builder; - - json = (new Y.YUIDoc(Server.options)).run(); - Server.options = Y.Project.mix(json, Server.options); + if (!data) { + return next(); + } - builder = new Y.DocBuilder(Server.options, json); - builder.mixExternal(function() { - Y.log('External data fetched, launching server..', 'info', 'server'); - Server._externalData = builder.options.externalData; - Server.init(); + Y.log('Serving /files/' + data.name, 'info', 'server'); + + + Server.builder.renderFile(function (html) { + res.send(html); + }, data, (req.xhr ? 'xhr' : 'main')); + }, + /** + * `/classes` endpoint + * @method clazz + * @param {Request} req Express request object + * @param {Response} res Express response object + */ + clazz: function (req, res, next) { + var className = req.params['class']; + Y.log('Serving /classes/' + className + '.html', 'info', 'server'); + if (!Server.builder.data.classes[className]) { + return next(); + } + Server.builder.renderClass(function (html) { + res.send(html); + }, Server.builder.data.classes[className], (req.xhr ? 'xhr' : 'main')); + }, + /** + * `/modules` endpoint + * @method modules + * @param {Request} req Express request object + * @param {Response} res Express response object + */ + module: function (req, res, next) { + var modName = req.params.module; + Y.log('Serving /modules/' + modName + '.html', 'info', 'server'); + if (!Server.builder.data.modules[modName]) { + return next(); + } + Server.builder.renderModule(function (html) { + res.send(html); + }, Server.builder.data.modules[modName], (req.xhr ? 'xhr' : 'main')); + }, + /** + * `/` endpoint + * @method home + * @param {Request} req Express request object + * @param {Response} res Express response object + */ + home: function (req, res, next) { + Y.log('Serving index.html', 'info', 'server'); + Server.builder.renderIndex(function (html) { + res.send(html); }); - - } else { - Server.init(); + }, + /** + * Creates the Express server and prep's YUI for serving + * @method init + */ + init: function () { + var express = require('express'), + path = require('path'), + stat; + + Server.app = express(); + //console.log(Server.options); + stat = Server.options.themedir || path.join(__dirname, '../', 'themes', 'default'); + Server.app.use(express.static(stat)); + Server.routes(); + Server.app.listen(Server.options.port); + + Y.log('Starting server: http:/' + '/127.0.0.1:' + Server.options.port, 'info', 'server'); + }, + /** + * Start the server with the supplied options. + * @method start + * @param {Object} options Server options + */ + start: function (options) { + options = Y.Project.init(options); + Server.options = options; + + Server.options.cacheTemplates = false; //Don't cache the Handlebars templates + Server.options.writeJSON = false; //Don't write the JSON file out + + Y.config.logExclude.yuidoc = true; + Y.config.logExclude.docparser = true; + Y.config.logExclude.builder = true; + + if (Server.options.external) { + Y.log('Fetching external data, this may take a minute', 'warn', 'server'); + var json, builder; + + json = (new Y.YUIDoc(Server.options)).run(); + Server.options = Y.Project.mix(json, Server.options); + + builder = new Y.DocBuilder(Server.options, json); + builder.mixExternal(function () { + Y.log('External data fetched, launching server..', 'info', 'server'); + Server._externalData = builder.options.externalData; + Server.init(); + }); + + } else { + Server.init(); + } } - } - }; + }; Y.Server = Server; }); diff --git a/lib/utils.js b/lib/utils.js index ec2850d7..9976dded 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -9,24 +9,24 @@ var path = require('path'), fs = require('graceful-fs'); /** -* Utilities Class -* @class Utils -* @module yuidoc -*/ + * Utilities Class + * @class Utils + * @module yuidoc + */ + +YUI.add('utils', function (Y) { -YUI.add('utils', function(Y) { - Y.charset = 'utf8'; -var HTML_CHARS = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '/': '/', - '`': '`' -}; + var HTML_CHARS = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + '`': '`' + }; /** Escapes HTML characters in _html_. @@ -35,7 +35,7 @@ var HTML_CHARS = { @param {String} html String to escape. @return {String} Escaped string. **/ - Y.escapeHTML = function(html) { + Y.escapeHTML = function (html) { return html.replace(/[&<>"'\/`]/g, function (match) { return HTML_CHARS[match]; }); @@ -52,7 +52,7 @@ var HTML_CHARS = { @return {String} Unindented text. @private **/ - Y.unindent = function(content) { + Y.unindent = function (content) { var indent = content.match(/^(\s+)/); if (indent) { @@ -61,411 +61,416 @@ var HTML_CHARS = { return content; }; - -/** -Like `getPages()`, but returns only the files under the `layout/` subdirectory -of the specified _dir_. - -@method getLayouts -@param {String} dir Directory path. -@return {Object} Mapping of layout names to layout content. -**/ -function getLayouts(dir) { - return getPages(path.join(dir, 'layouts')); -} -Y.getLayouts = getLayouts; -/** -Loads and returns the content of the specified page file. - -@method getPage -@param {String} pagePath Path to a single `.handlebars` page. -@return {String|null} Page content, or `null` if not found. -**/ -function getPage(pagePath) { - if (!Y.Files.isFile(pagePath)) { return null; } - return fs.readFileSync(pagePath, Y.charset); -} -Y.getPage = getPage; + /** + Like `getPages()`, but returns only the files under the `layout/` subdirectory + of the specified _dir_. -/** -Loads pages (files with a `.handlebars` extension) in the specified directory and -returns an object containing a mapping of page names (the part of the filename) -preceding the `.handlebars` extension) to page content. - -@method getPages -@param {String} dir Directory path. -@return {Object} Mapping of page names to page content. -**/ -var cache = {}; -function getPages(dir) { - if (cache[dir]) { - return cache[dir]; + @method getLayouts + @param {String} dir Directory path. + @return {Object} Mapping of layout names to layout content. + **/ + function getLayouts(dir) { + return getPages(path.join(dir, 'layouts')); } - var pages = {}; + Y.getLayouts = getLayouts; - if (!Y.Files.isDirectory(dir)) { return pages; } - - fs.readdirSync(dir).forEach(function (filename) { - var filePath = path.join(dir, filename); + /** + Loads and returns the content of the specified page file. - if (path.extname(filename) === '.handlebars' && Y.Files.isFile(filePath)) { - pages[path.basename(filename, '.handlebars')] = fs.readFileSync(filePath, Y.charset); + @method getPage + @param {String} pagePath Path to a single `.handlebars` page. + @return {String|null} Page content, or `null` if not found. + **/ + function getPage(pagePath) { + if (!Y.Files.isFile(pagePath)) { + return null; } - }); - cache[dir] = pages; + return fs.readFileSync(pagePath, Y.charset); + } + Y.getPage = getPage; - return pages; -} -Y.getPages = getPages; + /** + Loads pages (files with a `.handlebars` extension) in the specified directory and + returns an object containing a mapping of page names (the part of the filename) + preceding the `.handlebars` extension) to page content. -/** -Like `getPages()`, but returns only the files under the `partial/` subdirectory -of the specified _dir_. + @method getPages + @param {String} dir Directory path. + @return {Object} Mapping of page names to page content. + **/ + var cache = {}; + function getPages(dir) { + if (cache[dir]) { + return cache[dir]; + } + var pages = {}; -@method getPartials -@param {String} dir Directory path. -@return {Object} Mapping of partial names to partial content. -**/ -function getPartials(dir) { - return getPages(path.join(dir, 'partials')); -} -Y.getPartials = getPartials; + if (!Y.Files.isDirectory(dir)) { + return pages; + } + fs.readdirSync(dir).forEach(function (filename) { + var filePath = path.join(dir, filename); -/** -Mix/merge/munge data into the template. -@method prepare -@param {String} inDir The starting directory -@param {Object} options The `options` for the meta data. -@param {callback} callback The callback to excecute when complete -@param {Error} callback.err -@param {Object} callback.options Merged options. -**/ -function prepare(inDirs, options, callback) { - var layouts, - partials, - type = 'project'; - - if (options && options.skipLoad) { - // Skip loading layouts, metadata, pages, and partials and assume that - // the caller has provided them if they want them. - options = Y.merge({ - layouts : {}, - meta : {}, - pages : {}, - partials : {}, - viewClass: Y.DocView - }, options); - } else { - - // Gather layouts, metadata, pages, and partials from the specified - // input directory, then merge them into the provided options (if any). - // - // Gathered data will override provided data if there are conflicts, in - // order to support a use case where global data are provided by the - // caller and overridden by more specific component-level data gathered - // from the input directory. - // - // The metadata inheritance chain looks like this: - // - // - override metadata specified via CLI (highest precedence) - // - component metadata (if this is a component) - // - project-level component default metadata (if specified and this is a component) - // - theme-level component default metadata (if specified and this is a component) - // - project metadata - // - theme metadata (lowest precedence) - - try { - if (inDirs[0] === inDirs[1]) { - layouts = getLayouts(inDirs[0]); - partials = getPartials(inDirs[0]); - } else { - layouts = Y.merge(getLayouts(inDirs[0]), getLayouts(inDirs[1])); - partials = Y.merge(getPartials(inDirs[0]), getPartials(inDirs[1])); + if (path.extname(filename) === '.handlebars' && Y.Files.isFile(filePath)) { + pages[path.basename(filename, '.handlebars')] = fs.readFileSync(filePath, Y.charset); } - options = Y.merge( - {viewClass: Y.DocView}, - options || {}, - { - layouts : layouts, - meta : options.meta, - partials: partials - } - ); - } catch (ex) { - return callback(ex); - } + }); + cache[dir] = pages; + + return pages; } + Y.getPages = getPages; - // Mix in the override metadata, if any. It takes precedence over everything - // else. - Y.mix(options.meta, options.overrideMeta); + /** + Like `getPages()`, but returns only the files under the `partial/` subdirectory + of the specified _dir_. - // Set a default asset path if one isn't specified in the metadata. - if (!options.meta.projectAssets) { - options.meta.projectAssets = options.component ? '../assets' : 'assets'; + @method getPartials + @param {String} dir Directory path. + @return {Object} Mapping of partial names to partial content. + **/ + function getPartials(dir) { + return getPages(path.join(dir, 'partials')); } + Y.getPartials = getPartials; - if (!options.meta.componentAssets && options.component) { - options.meta.componentAssets = '../assets/' + options.meta.name; - } - if (typeof options.meta.layout === 'undefined') { - options.meta.layout = options.layouts[type] ? type : 'main'; - } - - callback(null, options); -} + /** + Mix/merge/munge data into the template. + + @method prepare + @param {String} inDir The starting directory + @param {Object} options The `options` for the meta data. + @param {callback} callback The callback to excecute when complete + @param {Error} callback.err + @param {Object} callback.options Merged options. + **/ + function prepare(inDirs, options, callback) { + var layouts, + partials, + type = 'project'; + + if (options && options.skipLoad) { + // Skip loading layouts, metadata, pages, and partials and assume that + // the caller has provided them if they want them. + options = Y.merge({ + layouts: {}, + meta: {}, + pages: {}, + partials: {}, + viewClass: Y.DocView + }, options); + } else { + // Gather layouts, metadata, pages, and partials from the specified + // input directory, then merge them into the provided options (if any). + // + // Gathered data will override provided data if there are conflicts, in + // order to support a use case where global data are provided by the + // caller and overridden by more specific component-level data gathered + // from the input directory. + // + // The metadata inheritance chain looks like this: + // + // - override metadata specified via CLI (highest precedence) + // - component metadata (if this is a component) + // - project-level component default metadata (if specified and this is a component) + // - theme-level component default metadata (if specified and this is a component) + // - project metadata + // - theme metadata (lowest precedence) + try { + if (inDirs[0] === inDirs[1]) { + layouts = getLayouts(inDirs[0]); + partials = getPartials(inDirs[0]); + } else { + layouts = Y.merge(getLayouts(inDirs[0]), getLayouts(inDirs[1])); + partials = Y.merge(getPartials(inDirs[0]), getPartials(inDirs[1])); + } + options = Y.merge({ + viewClass: Y.DocView + }, + options || {}, { + layouts: layouts, + meta: options.meta, + partials: partials + } + ); + } catch (ex) { + return callback(ex); + } + } -Y.prepare = prepare; + // Mix in the override metadata, if any. It takes precedence over everything + // else. + Y.mix(options.meta, options.overrideMeta); -/** -* Walk the directory tree to locate the yuidoc.json file. -* @method getProjectData -* @param {Path} [dir=process.cwd()] The directory to start from -*/ -var getProjectData = function(dir) { - var dirs = [dir || process.cwd()]; - var projectData, packageData; - var dirCount = 0; - // keep looping until - // * data is found - // * there are no more dirs to process - // * we abort due to failsafe - while (dirs.length && !projectData) { - /*jshint loopfunc:true */ - if (dirCount++ > 5000) { - Y.log('Scanned ' + dirCount + ' directories looking for a yuidoc.json file, something is probably wrong here..', 'error', 'yuidoc'); - process.exit(1); + // Set a default asset path if one isn't specified in the metadata. + if (!options.meta.projectAssets) { + options.meta.projectAssets = options.component ? '../assets' : 'assets'; + } + + if (!options.meta.componentAssets && options.component) { + options.meta.componentAssets = '../assets/' + options.meta.name; } - // accumulator for directories at this level - var childDirs = []; - // for each directory at the previous level - dirs.forEach(function(dir) { - // abort iterating if we have project data - if (projectData) { - return; + + if (typeof options.meta.layout === 'undefined') { + options.meta.layout = options.layouts[type] ? type : 'main'; + } + + callback(null, options); + } + + Y.prepare = prepare; + + /** + * Walk the directory tree to locate the yuidoc.json file. + * @method getProjectData + * @param {Path} [dir=process.cwd()] The directory to start from + */ + var getProjectData = function (dir) { + var dirs = [dir || process.cwd()]; + var projectData, packageData; + var dirCount = 0; + // keep looping until + // * data is found + // * there are no more dirs to process + // * we abort due to failsafe + while (dirs.length && !projectData) { + /*jshint loopfunc:true */ + if (dirCount++ > 5000) { + Y.log('Scanned ' + dirCount + ' directories looking for a yuidoc.json file, something is probably wrong here..', 'error', 'yuidoc'); + process.exit(1); } - // squelch (but log) any complaints about this particular directory - try { - // for each item in this directory - var names = fs.readdirSync(dir); - names.forEach(function(name) { - // abort iterating a folder if we have found both data - if (projectData && packageData) { - return; - } - // build a full path - var p = path.join(dir, name); - // acquire project data from this item if possible - if (Y.Files.isFile(p)) { - projectData = getFileData(p, name, 'yuidoc.json'); - // 'package.json' is used for auxilliary configuration - // if it's found. Formerly, it was only found if it - // came _before_'yuidoc.json' in the folder tree - // (never in the same folder). - // This code will find 'package.json' in the same - // folder as 'yuidoc.json'. - // If there is no 'yuidoc.json', former algorithm would - // use the deepest 'package.json' it can find, this one - // will use the first (most shallow) one. - packageData = packageData || getFileData(p, name, 'package.json'); - } - // if we are a folder, but not ., .., or node_modules, - // then add to directory accumulator - if (Y.Files.isDirectory(p)) { - if (name.indexOf('.') === 0) { + // accumulator for directories at this level + var childDirs = []; + // for each directory at the previous level + dirs.forEach(function (dir) { + // abort iterating if we have project data + if (projectData) { + return; + } + // squelch (but log) any complaints about this particular directory + try { + // for each item in this directory + var names = fs.readdirSync(dir); + names.forEach(function (name) { + // abort iterating a folder if we have found both data + if (projectData && packageData) { return; } - if (name === 'node_modules') { - Y.log('Skipping node_modules directory while scanning for yuidoc.json', 'warn', 'yuidoc'); - return; + // build a full path + var p = path.join(dir, name); + // acquire project data from this item if possible + if (Y.Files.isFile(p)) { + projectData = getFileData(p, name, 'yuidoc.json'); + // 'package.json' is used for auxilliary configuration + // if it's found. Formerly, it was only found if it + // came _before_'yuidoc.json' in the folder tree + // (never in the same folder). + // This code will find 'package.json' in the same + // folder as 'yuidoc.json'. + // If there is no 'yuidoc.json', former algorithm would + // use the deepest 'package.json' it can find, this one + // will use the first (most shallow) one. + packageData = packageData || getFileData(p, name, 'package.json'); } - childDirs.push(p); - } - }); - } catch (dirPerm) { - Y.log('Accessing dir (' + dir + ') threw an error', 'warn', 'yuidoc'); + // if we are a folder, but not ., .., or node_modules, + // then add to directory accumulator + if (Y.Files.isDirectory(p)) { + if (name.indexOf('.') === 0) { + return; + } + if (name === 'node_modules') { + Y.log('Skipping node_modules directory while scanning for yuidoc.json', 'warn', 'yuidoc'); + return; + } + childDirs.push(p); + } + }); + } catch (dirPerm) { + Y.log('Accessing dir (' + dir + ') threw an error', 'warn', 'yuidoc'); + } + }); + // iterate over new set of folders + dirs = childDirs; + } + if ((packageData && projectData) || (packageData && packageData.yuidoc)) { + projectData = mergeData(packageData, projectData); + } + return projectData; + }; + + var getFileData = function (p, name, file) { + if (name === file) { + Y.log('Loading ' + name + ' data from: ' + p, 'info', 'yuidoc'); + try { + return Y.Files.getJSON(p); + } catch (e) { + var err = 'Failed to parse ' + name + ' file, please make sure it is valid JSON'; + Y.log(err, 'error', 'yuidoc'); + throw (e + ''); } - }); - // iterate over new set of folders - dirs = childDirs; - } - if ((packageData && projectData) || (packageData && packageData.yuidoc)) { - projectData = mergeData(packageData, projectData); - } - return projectData; -}; - -var getFileData = function(p, name, file) { - if (name === file) { - Y.log('Loading ' + name + ' data from: ' + p, 'info', 'yuidoc'); - try { - return Y.Files.getJSON(p); - } catch (e) { - var err = 'Failed to parse ' + name + ' file, please make sure it is valid JSON'; - Y.log(err, 'error', 'yuidoc'); - throw(e+''); } - } -}; + }; -var mergeData = function(pack, project) { - project = project || {}; + var mergeData = function (pack, project) { + project = project || {}; - if (pack.yuidoc) { - Object.keys(pack.yuidoc).forEach(function(key) { - if (!project[key]) { - project[key] = pack.yuidoc[key]; + if (pack.yuidoc) { + Object.keys(pack.yuidoc).forEach(function (key) { + if (!project[key]) { + project[key] = pack.yuidoc[key]; + } + }); + } + + ['name', 'description', 'version', 'url'].forEach(function (key) { + if (pack[key] && !project[key]) { + project[key] = pack[key]; } }); - } - ['name', 'description', 'version', 'url'].forEach(function(key) { - if (pack[key] && !project[key]) { - project[key] = pack[key]; - } - }); + return project; + }; - return project; -}; + Y.getProjectData = getProjectData; -Y.getProjectData = getProjectData; + /** + * Walks the tree from this dir and returns all the subdirs + * @method getDirs + * @param {String} dir The dir to begin at + * @return {Array} The array of directories.. + */ + var getDirs = function (dir) { + var dirs = fs.readdirSync(dir), + paths = []; + + dirs.forEach(function (d) { + var _dir = path.join(dir, d), + stat = fs.lstatSync(_dir); + + if (stat.isDirectory()) { + if (_dir.indexOf('.') !== 0) { + paths = [].concat(paths, _dir, getDirs(_dir)); + } + } + }); -/** -* Walks the tree from this dir and returns all the subdirs -* @method getDirs -* @param {String} dir The dir to begin at -* @return {Array} The array of directories.. -*/ + return paths; + }; -var getDirs = function(dir) { - var dirs = fs.readdirSync(dir), - paths = []; + Y.getDirs = getDirs; - dirs.forEach(function(d) { - var _dir = path.join(dir, d), - stat = fs.lstatSync(_dir); + /** + * Make sure all the paths passed are directories and that they are not in the ignore list. + * @method validatePaths + * @param {Array} paths The array of paths to validate + * @param {String} [ignore=false] A string to call `.indexOf` on a path to determine if it should be ignored + */ + var validatePaths = function (paths, ignore) { + var newpaths = []; + //Shortcut the *, . & ./ shortcuts that shall globbing fixes for us + if (paths === '*' || paths === '.' || paths === './') { + paths = [process.cwd()]; + } - if (stat.isDirectory()) { - if (_dir.indexOf('.') !== 0) { - paths = [].concat(paths, _dir, getDirs(_dir)); - } + // Ensure that we always have an array of some kind. + paths = paths || []; + if (!Y.Lang.isArray(paths)) { + paths = [paths]; } - }); + paths.forEach(function (path) { + var glob = path || ''; - return paths; -}; + if (process.platform === 'win32') { + glob = path.replace(/\//g, '\\\\'); + } -Y.getDirs = getDirs; + var glob_paths = getDirs('.'), + is_globbed = false; -/** -* Make sure all the paths passed are directories and that they are not in the ignore list. -* @method validatePaths -* @param {Array} paths The array of paths to validate -* @param {String} [ignore=false] A string to call `.indexOf` on a path to determine if it should be ignored -*/ + glob_paths.forEach(function (dir) { + //Don't scan things in node_modules + if (dir.indexOf('node_modules') > -1) { + return; + } + if (minimatch(dir, glob, { + period: true + })) { + newpaths.push(dir); + is_globbed = true; + } + }); -var validatePaths = function(paths, ignore) { - var newpaths = []; - //Shortcut the *, . & ./ shortcuts that shall globbing fixes for us - if (paths === '*' || paths === '.' || paths === './') { - paths = [process.cwd()]; - } + if (!is_globbed && (Y.Files.isDirectory(glob))) { + //If minimatch fails, check to see if it's a relative directory + // if it is, add it directly + newpaths.push(glob); + } + }); - // Ensure that we always have an array of some kind. - paths = paths || []; - if (!Y.Lang.isArray(paths)) { - paths = [ paths ]; - } - paths.forEach(function(path) { - var glob = path || ''; + paths = newpaths; + paths.forEach(function (p) { + try { + if (!Y.Files.isDirectory(p)) { + throw ('Path not a directory: ' + p); + } + } catch (e) {} + }); - if (process.platform === 'win32') { - glob = path.replace(/\//g, '\\\\'); + if (!paths || !paths.forEach) { + throw ('Paths should be an array of paths'); } - var glob_paths = getDirs('.'), is_globbed = false; - glob_paths.forEach(function(dir) { - //Don't scan things in node_modules - if (dir.indexOf('node_modules') > -1) { - return; + if (ignore) { + if (!(ignore instanceof Array)) { + ignore = [ignore]; } - if (minimatch(dir, glob, { period: true })) { - newpaths.push(dir); - is_globbed = true; - } - }); - if (!is_globbed && (Y.Files.isDirectory(glob))) { - //If minimatch fails, check to see if it's a relative directory - // if it is, add it directly - newpaths.push(glob); - } - }); - - paths = newpaths; - paths.forEach(function(p) { - try { - if (!Y.Files.isDirectory(p)) { - throw('Path not a directory: ' + p); - } - } catch (e) {} - }); - if (!paths || !paths.forEach) { - throw('Paths should be an array of paths'); - } - if (ignore) { - if (!(ignore instanceof Array)) { - ignore = [ignore]; - } - var p = [], - shouldIgnore = false; - - paths.forEach(function(v) { - shouldIgnore = false; - ignore.forEach(function(i) { - if (!shouldIgnore && v.indexOf(i) !== -1) { - shouldIgnore = true; + var p = [], + shouldIgnore = false; + + paths.forEach(function (v) { + shouldIgnore = false; + ignore.forEach(function (i) { + if (!shouldIgnore && v.indexOf(i) !== -1) { + shouldIgnore = true; + } + }); + if (!shouldIgnore) { + p.push(v); } }); - if (!shouldIgnore) { - p.push(v); - } - }); - paths = p; - } - paths = paths.sort(); - return paths; -}; - -Y.validatePaths = validatePaths; + paths = p; + } + paths = paths.sort(); + return paths; + }; + Y.validatePaths = validatePaths; -/** -* Takes a type string and converts it to a "First letter upper cased" type. e.g. `(string -> String, object -> Object)` -* @method fixType -* @param {String} t The type string to convert -* @return {String} The fixed string -*/ -var fixType = function(t) { - - if (t && t.indexOf('.') === -1) { - t = t.replace(/{/g, '').replace(/}/g, ''); - var firstChar = t.charAt(0), - upperFirstChar = firstChar.toUpperCase(); - - if (firstChar !== upperFirstChar) { - return upperFirstChar + t.substring(1); + /** + * Takes a type string and converts it to a "First letter upper cased" type. e.g. `(string -> String, object -> Object)` + * @method fixType + * @param {String} t The type string to convert + * @return {String} The fixed string + */ + var fixType = function (t) { + if (t && t.indexOf('.') === -1) { + t = t.replace(/{/g, '').replace(/}/g, ''); + var firstChar = t.charAt(0), + upperFirstChar = firstChar.toUpperCase(); + + if (firstChar !== upperFirstChar) { + return upperFirstChar + t.substring(1); + } } - } - return t; -}; + return t; + }; -Y.Lang.fixType = fixType; + Y.Lang.fixType = fixType; }); - diff --git a/lib/yuidoc.js b/lib/yuidoc.js index f923f199..3d354047 100755 --- a/lib/yuidoc.js +++ b/lib/yuidoc.js @@ -3,9 +3,9 @@ Copyright (c) 2011, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://yuilibrary.com/license/ */ -var fs = require("fs"), +var fs = require('graceful-fs'), rimraf = require('rimraf'), - path = require("path"); + path = require('path'); /** This is the __module__ description for the `YUIDoc` module. @@ -23,7 +23,7 @@ This is the __module__ description for the `YUIDoc` module. */ -YUI.add('yuidoc', function(Y) { +YUI.add('yuidoc', function (Y) { /** @@ -33,17 +33,16 @@ YUI.add('yuidoc', function(Y) { * @final * @for YUIDoc */ - var OPTIONS = { quiet: false, writeJSON: true, - outdir: path.join(process.cwd(), 'out'), + outdir: path.join(process.cwd(), 'out'), extension: '.js', exclude: '.DS_Store,.svn,CVS,.git,build_rollup_tmp,build_tmp,node_modules', norecurse: false, version: '0.1.0', paths: [], - themedir: path.join(__dirname, 'themes', 'default'), + themedir: path.join(__dirname, 'themes', 'default'), syntaxtype: 'js' }; @@ -63,7 +62,7 @@ YUI.add('yuidoc', function(Y) { * @constructor * @param config The config object */ - Y.YUIDoc = function(config) { + Y.YUIDoc = function (config) { /** * Holds the number of files that we are processing. * @property filecount @@ -115,54 +114,61 @@ YUI.add('yuidoc', function(Y) { * @method _setDefaultExcludes * @private */ - _setDefaultExcludes: function() { + _setDefaultExcludes: function () { //These should always be excluded var excludes = '.DS_Store,.svn,CVS,.git,build_rollup_tmp,build_tmp,node_modules'.split(','), self = this; - excludes.forEach(function(item) { + excludes.forEach(function (item) { self.options.excludes[item] = true; }); }, + /** * Does post process on self.options. * @method _processConfig * @private */ - _processConfig: function() { + _processConfig: function () { this.options.extensions = Y.Array.hash(this.options.extension.split(',')); this.options.excludes = Y.Array.hash(this.options.exclude.split(',')); this._setDefaultExcludes(); }, + /** * Walks the paths and parses the directory contents * @method walk * @private */ - walk: function() { - Y.each(this.options.paths, function(dir) { + walk: function () { + Y.each(this.options.paths, function (dir) { this.parsedir(dir); }, this); }, + /** * Walks the passed directory and grabs all the files recursively. * @method parsedir * @param {String} dir The directory to parse the contents of. * @private */ - parsedir: function(dir) { + parsedir: function (dir) { if (!Y.Files.isDirectory(dir)) { - throw('Can not find directory: ' + dir); + throw ('Can not find directory: ' + dir); } - var allfiles = fs.readdirSync(dir), stats, - files = [], fullpath, self = this; + + var allfiles = fs.readdirSync(dir), + stats, + files = [], + fullpath, self = this; if (dir in self.options.excludes) { return; } + allfiles = allfiles.sort(); - Y.each(allfiles, function(filename) { + Y.each(allfiles, function (filename) { if (!(filename in self.options.excludes)) { fullpath = path.join(dir, filename); @@ -175,10 +181,12 @@ YUI.add('yuidoc', function(Y) { } } }); + if (!(dir in self.options.excludes)) { this.parsefiles(dir, files); } }, + /** * Gathers all the file data and populates the filemap and dirmap hashes. * @method parsefiles @@ -186,11 +194,14 @@ YUI.add('yuidoc', function(Y) { * @param {Array} files List of files to parse. * @private */ - parsefiles: function(dir, files) { + parsefiles: function (dir, files) { var self = this; files = files.sort(); - Y.each(files, function(filename) { - var ext = path.extname(filename), text, fullpath; + + Y.each(files, function (filename) { + var ext = path.extname(filename), + text, + fullpath; if (ext) { if (ext in self.options.extensions) { @@ -211,9 +222,12 @@ YUI.add('yuidoc', function(Y) { } }); }, - getSelleck: function(fullpath) { + + getSelleck: function (fullpath) { var self = this, - base, comp, json; + base, + comp, + json; if (self.options.selleck) { base = path.dirname(fullpath); @@ -230,6 +244,7 @@ YUI.add('yuidoc', function(Y) { } } }, + /** * Writes the parser JSON data to disk. * @method writeJSON @@ -237,7 +252,7 @@ YUI.add('yuidoc', function(Y) { * @private * @return {Object} The JSON data returned from the DocParser */ - writeJSON: function(parser) { + writeJSON: function (parser) { var self = this, data, file, @@ -248,12 +263,12 @@ YUI.add('yuidoc', function(Y) { data.warnings = parser.warnings; if (self.selleck && self.options.selleck && data.files && data.modules) { - Object.keys(self.selleck).forEach(function(file) { - Object.keys(data.files).forEach(function(f) { + Object.keys(self.selleck).forEach(function (file) { + Object.keys(data.files).forEach(function (f) { if (file === f) { var mods = data.files[f].modules; if (mods) { - Object.keys(mods).forEach(function(mod) { + Object.keys(mods).forEach(function (mod) { if (data.modules[mod]) { if (!data.modules[mod].extra) { data.modules[mod].extra = {}; @@ -274,10 +289,12 @@ YUI.add('yuidoc', function(Y) { if (self.options.writeJSON) { // Y.log(Y.JSON.stringify(parser.data, null, 4)); file = path.join(self.options.outdir, 'data.json'); + if (Y.Files.exists(self.options.outdir) && !self.options.nodeleteout) { Y.log('Found out dir, deleting: ' + self.options.outdir, 'warn', 'yuidoc'); rimraf.sync(self.options.outdir); } + if (!Y.Files.exists(self.options.outdir)) { Y.log('Making out dir: ' + self.options.outdir, 'info', 'yuidoc'); try { @@ -286,9 +303,11 @@ YUI.add('yuidoc', function(Y) { Y.log('Outdir creation failed', 'warn', 'yuidoc'); } } - + out = fs.createWriteStream(file, { - flags: "w", encoding: Y.charset, mode: 0644 + flags: "w", + encoding: Y.charset, + mode: 0644 }); out.write(JSON.stringify(data, null, 4)); out.end(); @@ -296,26 +315,28 @@ YUI.add('yuidoc', function(Y) { return data; }, - lint: function(warnings) { + + lint: function (warnings) { var code = 0, count = 0; if (warnings && warnings.length) { code = 1; console.log('YUIDoc found', warnings.length, 'lint errors in your docs'); - warnings.forEach(function(item) { + warnings.forEach(function (item) { count++; console.log('#' + count, item.message, item.line + '\n'); }); process.exit(code); } }, + /** * Process the config, walk the file tree and write out the JSON data. * @method run * @return {Object} The JSON data returned from the DocParser */ - run: function() { + run: function () { /** * Timestamp holder so we know when YUIDoc started the parse process. * @property starttime diff --git a/tests/builder.js b/tests/builder.js index 171f3582..1de640e3 100644 --- a/tests/builder.js +++ b/tests/builder.js @@ -10,11 +10,11 @@ process.chdir(__dirname); var suite = new YUITest.TestSuite({ name: 'Builder Test Suite', - findByName: function(name, cl) { + findByName: function (name, cl) { var items = this.data.classitems, ret; - items.forEach(function(i) { + items.forEach(function (i) { if (i.name === name && i.class === cl) { ret = i; } @@ -22,12 +22,12 @@ var suite = new YUITest.TestSuite({ return ret; }, - setUp: function() { + setUp: function () { var options, json, builder; options = { quiet: true, - paths: [ 'input/' ], + paths: ['input/'], outdir: './out', helpers: [ path.join(__dirname, 'lib/davglass.js') @@ -40,9 +40,9 @@ var suite = new YUITest.TestSuite({ this.project = json.project; this.data = json; - + builder = new Y.DocBuilder(options, json); - builder.compile(function() { + builder.compile(function () { suite._setupComplete = true; }); this.builder = builder; @@ -54,7 +54,7 @@ var exists = fs.existsSync || path.existsSync; suite.add(new YUITest.TestCase({ name: 'Builder setup', - 'test: prep': function() { + 'test: prep': function () { this.project = suite.project; this.data = suite.data; /* @@ -64,65 +64,65 @@ suite.add(new YUITest.TestCase({ async task in the setup is complete before I can test against it. */ var test = this, - timer = setInterval(function() { - if (suite._setupComplete) { - clearInterval(timer); - test.resume(function() { - Assert.isTrue(suite._setupComplete); - }); - } - },10); + timer = setInterval(function () { + if (suite._setupComplete) { + clearInterval(timer); + test.resume(function () { + Assert.isTrue(suite._setupComplete); + }); + } + }, 10); this.wait(); }, - 'test: Directories': function() { + 'test: Directories': function () { var dirs = ['assets', 'classes', 'files', 'modules']; - dirs.forEach(function(d) { + dirs.forEach(function (d) { var p = path.join(__dirname, 'out', d); Assert.isTrue(exists(p), 'Failed to find: ' + p); }); }, - 'test: Assets Directories': function() { + 'test: Assets Directories': function () { var dirs = ['css', 'js', 'img', 'vendor', 'index.html']; - dirs.forEach(function(d) { + dirs.forEach(function (d) { var p = path.join(__dirname, 'out', 'assets', d); Assert.isTrue(exists(p), 'Failed to find: ' + p); }); }, - 'test: index.html': function() { + 'test: index.html': function () { var p = path.join(__dirname, 'out', 'index.html'); Assert.isTrue(exists(p), 'Failed to find: ' + p); }, - 'test: data.json': function() { + 'test: data.json': function () { var p = path.join(__dirname, 'out', 'data.json'); Assert.isTrue(exists(p), 'Failed to find: ' + p); }, - 'test: api.js': function() { + 'test: api.js': function () { var p = path.join(__dirname, 'out', 'api.js'); Assert.isTrue(exists(p), 'Failed to find: ' + p); }, - 'test: classes/JSON.html': function() { + 'test: classes/JSON.html': function () { var p = path.join(__dirname, 'out', 'classes', 'JSON.html'); Assert.isTrue(exists(p), 'Failed to find: ' + p); }, - 'test: files name filter': function() { + 'test: files name filter': function () { var dir = path.join(__dirname, 'out', 'files'); - fs.readdirSync(dir).forEach(function(file) { - Assert.isTrue(((file.indexOf('input_') ===0) || file.indexOf('index.html') === 0), 'Filed to parse: ' + file); + fs.readdirSync(dir).forEach(function (file) { + Assert.isTrue(((file.indexOf('input_') === 0) || file.indexOf('index.html') === 0), 'Filed to parse: ' + file); }); }, - 'test: module files': function() { + 'test: module files': function () { var mods = this.data.modules; - Object.keys(mods).forEach(function(name) { + Object.keys(mods).forEach(function (name) { var m = mods[name], p = path.join(__dirname, 'out', 'modules', m.name + '.html'); Assert.isTrue(exists(p), 'Failed to render: ' + m.name + '.html'); }); }, - 'test: class files': function() { + 'test: class files': function () { var mods = this.data.classes; - Object.keys(mods).forEach(function(name) { + Object.keys(mods).forEach(function (name) { var m = mods[name], p = path.join(__dirname, 'out', 'classes', m.name + '.html'); Assert.isTrue(exists(p), 'Failed to render: ' + m.name + '.html'); @@ -132,12 +132,12 @@ suite.add(new YUITest.TestCase({ suite.add(new YUITest.TestCase({ name: 'Builder Augmentation Tests', - 'test: inherited methods': function() { + 'test: inherited methods': function () { var item = suite.data.classes['mywidget.SubWidget']; Assert.isObject(item, 'Failed to parse class'); - suite.builder.renderClass(function(html, view, opts) { + suite.builder.renderClass(function (html, view, opts) { var method; - opts.meta.methods.forEach(function(i) { + opts.meta.methods.forEach(function (i) { if (i.name === 'myMethod' && i.class === 'mywidget.SubWidget') { method = i; } @@ -147,12 +147,12 @@ suite.add(new YUITest.TestCase({ Assert.isObject(method.overwritten_from, 'Failed to find overwritten data'); }, item); }, - 'test: helper methods': function() { + 'test: helper methods': function () { var item = suite.data.classes['mywidget.SuperWidget']; Assert.isObject(item, 'Failed to parse class'); - suite.builder.renderClass(function(html, view, opts) { + suite.builder.renderClass(function (html, view, opts) { var method; - opts.meta.methods.forEach(function(i) { + opts.meta.methods.forEach(function (i) { if (i.name === 'getTargets2' && i.class === 'mywidget.SuperWidget') { method = i; } @@ -163,12 +163,12 @@ suite.add(new YUITest.TestCase({ }, item); }, - 'test: markdown options': function() { + 'test: markdown options': function () { var item = suite.data.classes['mywidget.SuperWidget']; Assert.isObject(item, 'Failed to parse class'); - suite.builder.renderClass(function(html, view, opts) { + suite.builder.renderClass(function (html, view, opts) { var method; - opts.meta.methods.forEach(function(i) { + opts.meta.methods.forEach(function (i) { if (i.name === 'getTargets3' && i.class === 'mywidget.SuperWidget') { method = i; } diff --git a/tests/options.js b/tests/options.js index 996a2a97..4190cce2 100644 --- a/tests/options.js +++ b/tests/options.js @@ -11,7 +11,7 @@ var suite = new YUITest.TestSuite('Options Test Suite'); suite.add(new YUITest.TestCase({ name: "Server Options", - 'test: server': function() { + 'test: server': function () { var options = Y.Options([ '--server' ]); @@ -19,7 +19,7 @@ suite.add(new YUITest.TestCase({ Assert.isTrue(options.server, 'Failed to set server option'); Assert.areSame(3000, options.port, 'Failed to set default port'); }, - 'test: server with port': function() { + 'test: server with port': function () { var options = Y.Options([ '--server', '5000' @@ -28,7 +28,7 @@ suite.add(new YUITest.TestCase({ Assert.isTrue(options.server, 'Failed to set server option'); Assert.areSame(5000, options.port, 'Failed to set port'); }, - 'test: server with default port and following argument': function() { + 'test: server with default port and following argument': function () { var options = Y.Options([ '--server', './foo' @@ -39,7 +39,7 @@ suite.add(new YUITest.TestCase({ Assert.isArray(options.paths, 'Failed to set path'); Assert.areSame('./foo', options.paths[0], 'Failed to set path after empty --server'); }, - 'test: tab-to-space': function() { + 'test: tab-to-space': function () { var options, value; // Test that --tab-to-space gives the correct number. @@ -94,235 +94,217 @@ suite.add(new YUITest.TestCase({ suite.add(new YUITest.TestCase({ name: "Various Options", - "test: long quiet option": function() { + "test: long quiet option": function () { var options = Y.Options([ '--quiet' ]); - Assert.isTrue(options.quiet, 'Failed to set long quiet'); }, - "test: short quiet option": function() { + "test: short quiet option": function () { var options = Y.Options([ '-q' ]); - Assert.isTrue(options.quiet, 'Failed to set short quiet'); }, - "test: short config": function() { + "test: short config": function () { var options = Y.Options([ '-c', './foo.json' ]); - Assert.areSame('./foo.json', options.configfile, 'Failed to set config'); }, - 'test: --config': function() { + 'test: --config': function () { var options = Y.Options([ '--config', './foo.json' ]); - Assert.areSame('./foo.json', options.configfile, 'Failed to set config'); }, - 'test: --configfile': function() { + 'test: --configfile': function () { var options = Y.Options([ '--configfile', './foo.json' ]); - Assert.areSame('./foo.json', options.configfile, 'Failed to set config'); }, - 'test: -e': function() { + 'test: -e': function () { var options = Y.Options([ '-e', '.foo' ]); - Assert.areSame('.foo', options.extension, 'Failed to set extension'); }, - 'test: --extension': function() { + 'test: --extension': function () { var options = Y.Options([ '--extension', '.foo' ]); - Assert.areSame('.foo', options.extension, 'Failed to set extension'); }, - 'test: -x': function() { + 'test: -x': function () { var options = Y.Options([ '-x', 'foo,bar,baz' ]); - Assert.areSame('foo,bar,baz', options.exclude, 'Failed to set exclude'); }, - 'test: --exclude': function() { + 'test: --exclude': function () { var options = Y.Options([ '--exclude', 'foo,bar,baz' ]); - Assert.areSame('foo,bar,baz', options.exclude, 'Failed to set exclude'); }, - 'test: --project-version': function() { + 'test: --project-version': function () { var options = Y.Options([ '--project-version', '6.6.6' ]); - Assert.areSame('6.6.6', options.version, 'Failed to set version'); }, - 'test: --no-color': function() { + 'test: --no-color': function () { var options = Y.Options([ '--no-color', ]); - Assert.isTrue(options.nocolor, 'Failed to set nocolor'); Assert.isFalse(Y.config.useColor, 'Failed to set Y.config.useColor'); }, - 'test: -N': function() { + 'test: -N': function () { var options = Y.Options([ '-N', ]); - Assert.isTrue(options.nocolor, 'Failed to set nocolor'); Assert.isFalse(Y.config.useColor, 'Failed to set Y.config.useColor'); }, - 'test: --no-code': function() { + 'test: --no-code': function () { var options = Y.Options([ '--no-code', ]); - Assert.isTrue(options.nocode, 'Failed to set nocode'); }, - 'test: -C': function() { + 'test: -C': function () { var options = Y.Options([ '-C', ]); - Assert.isTrue(options.nocode, 'Failed to set nocode'); }, - 'test: --norecurse': function() { + 'test: --norecurse': function () { var options = Y.Options([ '--norecurse', ]); - Assert.isTrue(options.norecurse, 'Failed to set norecurse'); }, - 'test: -n': function() { + 'test: -n': function () { var options = Y.Options([ '-n', ]); - Assert.isTrue(options.norecurse, 'Failed to set norecurse'); }, - 'test: --selleck': function() { + 'test: --selleck': function () { var options = Y.Options([ '--selleck', ]); - Assert.isTrue(options.selleck, 'Failed to set selleck'); }, - 'test: -S': function() { + 'test: -S': function () { var options = Y.Options([ '-S', ]); - Assert.isTrue(options.selleck, 'Failed to set selleck'); }, - 'test: -T simple': function() { + 'test: -T simple': function () { var options = Y.Options([ '-T', 'simple' ]); Assert.areEqual(path.join(__dirname, '../themes/simple'), options.themedir); }, - 'test: --theme simple': function() { + 'test: --theme simple': function () { var options = Y.Options([ '--theme', 'simple' ]); Assert.areEqual(path.join(__dirname, '../themes/simple'), options.themedir); }, - 'test: --theme foobar': function() { + 'test: --theme foobar': function () { var options = Y.Options([ '--theme', 'foobar' ]); Assert.areEqual(path.join(__dirname, '../themes/foobar'), options.themedir); }, - 'test: -t ./foobar': function() { + 'test: -t ./foobar': function () { var options = Y.Options([ '-t', './foobar' ]); Assert.areEqual('./foobar', options.themedir); }, - 'test: --themedir ./foobar': function() { + 'test: --themedir ./foobar': function () { var options = Y.Options([ '--themedir', './foobar' ]); Assert.areEqual('./foobar', options.themedir); }, - 'test: --syntaxtype coffee': function() { + 'test: --syntaxtype coffee': function () { var options = Y.Options([ '--syntaxtype', 'coffee' ]); Assert.areEqual('coffee', options.syntaxtype); }, - 'test: --view': function() { + 'test: --view': function () { var options = Y.Options([ '--view' ]); Assert.isTrue(options.dumpview); }, - 'test: -V': function() { + 'test: -V': function () { var options = Y.Options([ '-V' ]); Assert.isTrue(options.dumpview); }, - 'test: -p': function() { + 'test: -p': function () { var options = Y.Options([ '-p' ]); Assert.isTrue(options.parseOnly); }, - 'test: --parse-only': function() { + 'test: --parse-only': function () { var options = Y.Options([ '--parse-only' ]); Assert.isTrue(options.parseOnly); }, - 'test: -o ': function() { + 'test: -o ': function () { var options = Y.Options([ '-o', '/foo/bar' ]); Assert.areEqual('/foo/bar', options.outdir); }, - 'test: --outdir ': function() { + 'test: --outdir ': function () { var options = Y.Options([ '--outdir', '/foo/bar' ]); Assert.areEqual('/foo/bar', options.outdir); }, - 'test: -D': function() { + 'test: -D': function () { var options = Y.Options([ '-D' ]); Assert.isTrue(options.nodeleteout); }, - 'test: --no-delete-out': function() { + 'test: --no-delete-out': function () { var options = Y.Options([ '--no-delete-out' ]); Assert.isTrue(options.nodeleteout); }, - 'test: --lint': function() { + 'test: --lint': function () { var options = Y.Options([ '--lint' ]); @@ -331,7 +313,7 @@ suite.add(new YUITest.TestCase({ Assert.isTrue(options.quiet); Assert.isFalse(options.writeJSON); }, - 'test --debug': function() { + 'test --debug': function () { Assert.isFalse(Y.config.debug); Y.Options([ '--debug' @@ -342,14 +324,14 @@ suite.add(new YUITest.TestCase({ debug: false }); }, - 'test: --charset': function() { + 'test: --charset': function () { Y.Options([ '--charset' ]); Assert.areEqual('utf8', Y.charset); - + }, - 'test: --charset foo': function() { + 'test: --charset foo': function () { Y.Options([ '--charset', 'foo' @@ -357,7 +339,7 @@ suite.add(new YUITest.TestCase({ Assert.areEqual('foo', Y.charset); Y.charset = 'utf8'; }, - 'test: --tab-to-space 8': function() { + 'test: --tab-to-space 8': function () { var options = Y.Options([ '--tab-to-space', '8' @@ -369,4 +351,3 @@ suite.add(new YUITest.TestCase({ YUITest.TestRunner.add(suite); - diff --git a/tests/parser.js b/tests/parser.js index 3ba3d51f..e149bde4 100644 --- a/tests/parser.js +++ b/tests/parser.js @@ -13,10 +13,10 @@ var existsSync = fs.existsSync || path.existsSync; var suite = new YUITest.TestSuite({ name: 'Parser Test Suite', - setUp: function() { + setUp: function () { var json = (new Y.YUIDoc({ quiet: true, - paths: [ 'input/' ], + paths: ['input/'], outdir: './out' })).run(); @@ -27,15 +27,15 @@ var suite = new YUITest.TestSuite({ suite.add(new YUITest.TestCase({ name: "Project Data", - setUp: function() { + setUp: function () { this.project = suite.project; this.data = suite.data; }, - findByName: function(name, cl) { + findByName: function (name, cl) { var items = this.data.classitems, ret; - items.forEach(function(i) { + items.forEach(function (i) { if (i.name === name && i.class === cl) { ret = i; } @@ -43,18 +43,18 @@ suite.add(new YUITest.TestCase({ return ret; }, - 'test: out directory': function() { + 'test: out directory': function () { Assert.isTrue(existsSync(path.join(__dirname, 'out')), 'Out directory was not created'); }, - 'test: data.json creation': function() { + 'test: data.json creation': function () { Assert.isTrue(existsSync(path.join(__dirname, 'out', 'data.json')), 'data.json file was not created'); }, - 'test: parser': function() { + 'test: parser': function () { var keys = Object.keys(this.data); Assert.areEqual(6, keys.length, 'Failed to populate all fields'); - ArrayAssert.itemsAreSame([ 'project', 'files', 'modules', 'classes', 'classitems', 'warnings' ], keys, 'Object keys are wrong'); + ArrayAssert.itemsAreSame(['project', 'files', 'modules', 'classes', 'classitems', 'warnings'], keys, 'Object keys are wrong'); }, - 'test: project data': function() { + 'test: project data': function () { Assert.areSame(path.normalize('input/test/test.js'), this.project.file, 'Project data loaded from wrong file'); Assert.areSame(2, this.project.line, 'Line number is off'); Assert.areSame('The test project', this.project.description, 'Description not set properly'); @@ -67,10 +67,10 @@ suite.add(new YUITest.TestCase({ Assert.areSame('http://one.url', this.project.url[0], 'URL #1 is wrong'); Assert.areSame('http://two.url', this.project.url[1], 'URL #2 is wrong'); }, - 'test: files parsing': function() { + 'test: files parsing': function () { var files = this.data.files, one, two, three, four; - + // 1 module, 3 classes one = files[path.normalize('input/test/anim.js')]; Assert.isObject(one, 'Failed to parse input/test/anim.js'); @@ -96,7 +96,7 @@ suite.add(new YUITest.TestCase({ Assert.areSame(1, four.classes['YUI~oop'], 'YUI~oop class not found'); }, - 'test: namespace parsing': function() { + 'test: namespace parsing': function () { var item = this.data.files[path.normalize('input/test2/namespace.js')]; Assert.isObject(item, 'Failed to parse input/test2/namespace.js'); Assert.areSame(3, Object.keys(item.classes).length, 'Failed to parse all classes'); @@ -104,7 +104,7 @@ suite.add(new YUITest.TestCase({ ArrayAssert.itemsAreSame(['P.storage', 'P'], Object.keys(item.namespaces), 'Namespace failed to parse'); ArrayAssert.itemsAreSame(['P.storage.Store', 'P.storage.LocalStore', 'P.storage'], Object.keys(item.classes), 'Classes failed to parse'); }, - 'test: module parsing': function() { + 'test: module parsing': function () { var mods = this.data.modules; //anim Module @@ -115,7 +115,7 @@ suite.add(new YUITest.TestCase({ Assert.areSame('main', mods.anim.itemtype, 'Failed to parse @main itemtype'); Assert.areSame('module', mods.anim.tag, 'Tag parse failed'); }, - 'test: main module association': function() { + 'test: main module association': function () { var mod = this.data.modules.charts, d = 'The Charts widget provides an api for displaying data\ngraphically.'; @@ -124,7 +124,7 @@ suite.add(new YUITest.TestCase({ Assert.areSame('main', mod.tag, 'Tagname is not main'); Assert.areSame('main', mod.itemtype, 'ItemType should be main'); }, - 'test: submodule parsing': function() { + 'test: submodule parsing': function () { var mods = this.data.modules, m, desc; @@ -149,10 +149,10 @@ suite.add(new YUITest.TestCase({ Assert.areSame('anim', m.module, 'Failed to associate module'); }, - 'test: extra module data parsing': function() { + 'test: extra module data parsing': function () { var mods = this.data.modules, m; - + m = mods.mymodule; Assert.isObject(m, 'Failed to parse mymodule module'); Assert.areSame(1, Object.keys(m.submodules).length, 'Should have 1 submodules'); @@ -173,14 +173,14 @@ suite.add(new YUITest.TestCase({ m = mods['myapp/views/index']; Assert.isObject(m, 'Failed to parse myapp/views/index module'); Assert.areSame(1, Object.keys(m.classes).length, 'Should have 1 class'); - + m = mods['P.storage']; Assert.isObject(m, 'Failed to parse P.storage module'); ArrayAssert.itemsAreSame(['P.storage.Store', 'P.storage.LocalStore', 'P.storage'], Object.keys(m.classes), 'Failed to parse classes'); ArrayAssert.itemsAreSame(['P.storage', 'P'], Object.keys(m.namespaces), 'Namespace failed to parse'); - + }, - 'test: class parsing': function() { + 'test: class parsing': function () { var cl = this.data.classes, anim, easing, my, other, m; @@ -213,7 +213,7 @@ suite.add(new YUITest.TestCase({ Assert.areSame('mysubmodule', other.submodule, 'Failed to test submodule.'); Assert.areSame(1, Object.keys(other.extension_for).length, 'Failed to assign extension_for'); Assert.areSame('myclass', other.extension_for[0], 'Failed to assign extension_for'); - + m = cl['P.storage.P.storage']; Assert.isUndefined(m, 'Should not have double namespaces'); @@ -221,7 +221,7 @@ suite.add(new YUITest.TestCase({ Assert.isNotUndefined(cl['P.storage.Store'], 'Should not have double namespaces'); Assert.isNotUndefined(cl['P.storage.LocalStore'], 'Should not have double namespaces'); }, - 'test: classitems parsing': function() { + 'test: classitems parsing': function () { Assert.isArray(this.data.classitems, 'Failed to populate classitems array'); var keys, item, item2; @@ -247,19 +247,19 @@ suite.add(new YUITest.TestCase({ 'module', 'submodule' ]; - + ArrayAssert.itemsAreSame(keys, Object.keys(item), 'Item missing from output'); Assert.areSame('', item.evil, 'Single tag not found'); Assert.areSame('HTML', item.injects.type, 'Injection type not found'); - Assert.isUndefined(item.return.type, 'Type should be missing'); + Assert.isUndefined(item["return"].type, 'Type should be missing'); Assert.areSame(2, item.example.length, 'Should have 2 example snippets'); item2 = this.findByName('testobjectparam', 'myclass'); - Assert.areSame('String', item2.return.type, 'Type should not be missing'); + Assert.areSame('String', item2["return"].type, 'Type should not be missing'); }, - 'test: parameter parsing': function() { + 'test: parameter parsing': function () { var item, item2; item = this.findByName('testoptional', 'myclass'); Assert.isArray(item.params, 'Params should be an array'); @@ -285,17 +285,17 @@ suite.add(new YUITest.TestCase({ Assert.areSame(1, item2.params.length, 'Failed to parse all 5 parameters'); Assert.isTrue(item2.params[0].optional, 'Optional not set'); Assert.isTrue(item2.params[0].multiple, 'Multiple not set'); - Assert.isUndefined(item2.return.type, 'Type should be missing'); + Assert.isUndefined(item2["return"].type, 'Type should be missing'); item2 = this.findByName('test1ton', 'myclass'); Assert.isArray(item2.params, 'Params should be an array'); Assert.areSame(1, item2.params.length, 'Failed to parse all 5 parameters'); Assert.isUndefined(item2.params[0].optional, 'Optional should not be set'); Assert.isTrue(item2.params[0].multiple, 'Multiple not set'); - Assert.isUndefined(item2.return.type, 'Type should be missing'); + Assert.isUndefined(item2["return"].type, 'Type should be missing'); }, - 'test: object parameters': function() { + 'test: object parameters': function () { var item, props; item = this.findByName('testobjectparam', 'myclass'); @@ -316,11 +316,11 @@ suite.add(new YUITest.TestCase({ Assert.areSame('prop2', props[1].description, 'Invalid item'); Assert.areSame('Bool', props[1].type, 'Invalid item'); }, - 'test: tag fixing': function() { + 'test: tag fixing': function () { var item = this.findByName('testoptional', 'myclass'); - + Assert.isObject(item, 'failed to find item'); - Assert.isNotUndefined(item.return, 'Failed to replace returns with return'); + Assert.isNotUndefined(item["return"], 'Failed to replace returns with return'); item = this.findByName('_positionChangeHandler', 'Axis'); Assert.isObject(item, 'failed to find item'); @@ -330,47 +330,47 @@ suite.add(new YUITest.TestCase({ Assert.isObject(item, 'failed to find item'); Assert.areEqual(1, item.params.length, 'Failed to replace params with param'); }, - 'test: double namespaces': function() { + 'test: double namespaces': function () { var cls = this.data.classes, mod_bad = cls['Foo.Bar.Foo.Bar'], mod_good = cls['Foo.Bar']; Assert.isUndefined(mod_bad, 'Found class Foo.Bar.Foo.Bar'); Assert.isObject(mod_good, 'Failed to parse Foo.Bar namespace'); }, - 'test: inherited methods': function() { + 'test: inherited methods': function () { var item = this.findByName('myMethod', 'mywidget.SubWidget'); Assert.isObject(item, 'Failed to parse second method'); }, - 'test: case tags': function() { + 'test: case tags': function () { var item = this.findByName('testMethod', 'OtherClass2'); Assert.isObject(item, 'Failed to parse second method'); Assert.areSame('method', item.itemtype, 'Failed to parse Cased Method tag'); Assert.isArray(item.params, 'Failed to parse Cased Params'); Assert.areSame(1, item.params.length, 'Failed to parse number of cased params'); }, - 'test: required attribute': function() { + 'test: required attribute': function () { var item = this.findByName('requiredAttr', 'OtherClass2'); Assert.isObject(item, 'Failed to parse attribute'); Assert.areSame('attribute', item.itemtype, 'Failed to parse itemtype'); Assert.areSame(1, item.required, 'Failed to find required short tag'); }, - 'test: optional attribute': function() { + 'test: optional attribute': function () { var item = this.findByName('optionalAttr', 'OtherClass2'); Assert.isObject(item, 'Failed to parse attribute'); Assert.areSame('attribute', item.itemtype, 'Failed to parse itemtype'); Assert.areSame(1, item.optional, 'Failed to find optional short tag'); }, - 'test: module with example meta': function() { + 'test: module with example meta': function () { var item = this.data.modules.ExampleModule; Assert.isObject(item, 'Failed to parse module'); Assert.isArray(item.example, 'Failed to parse module example data'); }, - 'test: class with example meta': function() { + 'test: class with example meta': function () { var item = this.data.classes['mywidget.SuperWidget']; Assert.isObject(item, 'Failed to parse class'); Assert.isArray(item.example, 'Failed to parse class example data'); }, - 'test: event with optional items': function() { + 'test: event with optional items': function () { var item = this.findByName('changeWithOptional', 'OtherClass2'); Assert.isObject(item, 'Failed to locate event object'); @@ -381,8 +381,8 @@ suite.add(new YUITest.TestCase({ Assert.isArray(item.params[0].props); Assert.areSame(item.params[0].props[0].name, 'name'); - Assert.isTrue( item.params[0].props[0].optional); - + Assert.isTrue(item.params[0].props[0].optional); + } })); diff --git a/tests/parser_coffee.js b/tests/parser_coffee.js index b33e8d19..08d96795 100644 --- a/tests/parser_coffee.js +++ b/tests/parser_coffee.js @@ -9,10 +9,10 @@ process.chdir(__dirname); var suite = new YUITest.TestSuite({ name: 'Coffee Parser Test Suite', - setUp: function() { + setUp: function () { var json = (new Y.YUIDoc({ quiet: true, - paths: [ 'input/' ], + paths: ['input/'], outdir: './out', extension: '.coffee', syntaxtype: 'coffee' @@ -25,15 +25,15 @@ var suite = new YUITest.TestSuite({ suite.add(new YUITest.TestCase({ name: "Project Data", - setUp: function() { + setUp: function () { this.project = suite.project; this.data = suite.data; }, - findByName: function(name, cl) { + findByName: function (name, cl) { var items = this.data.classitems, ret; - items.forEach(function(i) { + items.forEach(function (i) { if (i.name === name && i.class === cl) { ret = i; } @@ -41,7 +41,7 @@ suite.add(new YUITest.TestCase({ return ret; }, - 'test: project data': function() { + 'test: project data': function () { Assert.areSame(path.normalize('input/coffee/test.coffee'), this.project.file, 'Project data loaded from wrong file'); Assert.areSame(2, this.project.line, 'Line number is off'); Assert.areSame('The test project', this.project.description, 'Description not set properly'); diff --git a/tests/utils.js b/tests/utils.js index 211a864a..981bd88d 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,45 +1,44 @@ /*global Y:true */ var YUITest = require('yuitest'), - Assert = YUITest.Assert, - path = require('path'), - Y = require(path.join(__dirname, '../', 'lib', 'index')) - ; + Assert = YUITest.Assert, + path = require('path'), + Y = require(path.join(__dirname, '../', 'lib', 'index')); var suite = new YUITest.TestSuite({ - name: 'Utils Test Suite' + name: 'Utils Test Suite' }); suite.add(new YUITest.TestCase({ - name: 'getProjectData Folder Priority', - 'test: Nearest Folder Priority': function() { - var d = Y.getProjectData('input/folders1'); - Assert.areEqual('yuidoc-root', d.name, 'must use nearest yuidoc.json first'); - }, - 'test: Finds package.json': function() { - var d = Y.getProjectData('input/folders2'); - Assert.areEqual('yuidoc-two', d.name, 'used deep yuidoc.json'); - Assert.areEqual('package-root', d.description, 'used shallow package.json'); - }, - 'test: Finds package.json in same folder as yuidoc.json': function() { - var d = Y.getProjectData('input/folders3'); - Assert.areEqual('yuidoc-root', d.name, 'used deep yuidoc.json'); - Assert.areEqual('package-root', d.description, 'used deep package.json'); - }, - 'test: Ignores package.json in deeper folder than yuidoc.json': function() { - var d = Y.getProjectData('input/folders4'); - Assert.areEqual('yuidoc-one', d.name, 'used deep yuidoc.json'); - Assert.isUndefined(d.description, 'used deep package.json'); - }, - 'test: Must be breadth-first': function() { - var d = Y.getProjectData('input/folders5'); - Assert.areEqual('yuidoc-two', d.name, 'used wrong yuidoc.json'); - Assert.areEqual('package-two', d.description, 'used wrong package.json'); - } + name: 'getProjectData Folder Priority', + 'test: Nearest Folder Priority': function () { + var d = Y.getProjectData('input/folders1'); + Assert.areEqual('yuidoc-root', d.name, 'must use nearest yuidoc.json first'); + }, + 'test: Finds package.json': function () { + var d = Y.getProjectData('input/folders2'); + Assert.areEqual('yuidoc-two', d.name, 'used deep yuidoc.json'); + Assert.areEqual('package-root', d.description, 'used shallow package.json'); + }, + 'test: Finds package.json in same folder as yuidoc.json': function () { + var d = Y.getProjectData('input/folders3'); + Assert.areEqual('yuidoc-root', d.name, 'used deep yuidoc.json'); + Assert.areEqual('package-root', d.description, 'used deep package.json'); + }, + 'test: Ignores package.json in deeper folder than yuidoc.json': function () { + var d = Y.getProjectData('input/folders4'); + Assert.areEqual('yuidoc-one', d.name, 'used deep yuidoc.json'); + Assert.isUndefined(d.description, 'used deep package.json'); + }, + 'test: Must be breadth-first': function () { + var d = Y.getProjectData('input/folders5'); + Assert.areEqual('yuidoc-two', d.name, 'used wrong yuidoc.json'); + Assert.areEqual('package-two', d.description, 'used wrong package.json'); + } })); suite.add(new YUITest.TestCase({ - name: 'validatePaths', - 'test: path globs': function() { + name: 'validatePaths', + 'test: path globs': function () { var options; process.chdir(path.join(__dirname, 'input/globbing')); @@ -74,7 +73,7 @@ suite.add(new YUITest.TestCase({ Assert.isArray(options.paths, 'Failed to set path'); Assert.areSame(3, options.paths.length, 'Failed to retrieve all path options'); - } + } })); YUITest.TestRunner.add(suite); From 5f75d58be1f428e6db8f6594dc9709d22208e120 Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Wed, 29 Jan 2014 17:39:36 -0800 Subject: [PATCH 5/7] Remove tests/utils.js from scripts.test until node v0.8 issue is figured out. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9c4e54b0..3078c318 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ }, "scripts": { "pretest": "jshint ./lib/*.js ./tests/*.js", - "test": "istanbul cover --print=both --yui ytestrunner -- --include ./tests/options.js --include ./tests/builder.js --include ./tests/parser.js --include ./tests/parser_coffee.js --include ./tests/files.js --include ./tests/utils.js" + "test": "istanbul cover --print=both --yui ytestrunner -- --include ./tests/options.js --include ./tests/builder.js --include ./tests/parser.js --include ./tests/parser_coffee.js --include ./tests/files.js" }, "preferGlobal": "true", "licenses":[ From 4c1c9605a1816936f6498b3ec7d48d2bee353e31 Mon Sep 17 00:00:00 2001 From: Daniel Stockman Date: Fri, 31 Jan 2014 14:28:47 -0800 Subject: [PATCH 6/7] Use consistent variable in currentclass setter. Using `name` interchangeably was indeed confusing. --- lib/docparser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docparser.js b/lib/docparser.js index 21463ad4..68835398 100644 --- a/lib/docparser.js +++ b/lib/docparser.js @@ -985,7 +985,7 @@ YUI.add('docparser', function (Y) { if (!(val in this.classes)) { ns = this.get(CURRENT_NAMESPACE); if (ns && ns !== '' && (val.indexOf(ns + '.') !== 0)) { - name = ns + '.' + name; + name = ns + '.' + val; } clazz = this.classes[name] = { name: name, From c46f13f8a0de0e37516ffaf4cb8d0ddbd086365c Mon Sep 17 00:00:00 2001 From: yyjdelete Date: Tue, 18 Feb 2014 10:10:49 +0800 Subject: [PATCH 7/7] Fix issue #192 --- themes/default/assets/js/apidocs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/themes/default/assets/js/apidocs.js b/themes/default/assets/js/apidocs.js index c64bb463..e00591c1 100644 --- a/themes/default/assets/js/apidocs.js +++ b/themes/default/assets/js/apidocs.js @@ -284,7 +284,7 @@ pjax.handleClasses = function (req, res, next) { var status = res.ioResponse.status; // Handles success and local filesystem XHRs. - if (!status || (status >= 200 && status < 300)) { + if (res.ioResponse.readyState === 4 && (!status || (status >= 200 && status < 300))) { pjax.initClassTabView(); } @@ -295,7 +295,7 @@ pjax.handleFiles = function (req, res, next) { var status = res.ioResponse.status; // Handles success and local filesystem XHRs. - if (!status || (status >= 200 && status < 300)) { + if (res.ioResponse.readyState === 4 && (!status || (status >= 200 && status < 300))) { pjax.initLineNumbers(); }