diff --git a/lib/builder.js b/lib/builder.js index 1b10ba5e..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,35 +16,37 @@ 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 = ''; + 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) { + Y.DocBuilder = function (options, data) { this.options = options; if (options.helpers) { this._addHelpers(options.helpers); @@ -58,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 = ''; @@ -66,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(' | '); @@ -77,28 +79,28 @@ 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); - if (content === "") { - content = item; - } + var content = options.fn(this); + if (content === "") { + content = item; + } str = '' + content + ''; + '.html">' + content + ''; } return str; }); - Y.Handlebars.registerHelper('crossLinkRaw', function(item, fn) { + Y.Handlebars.registerHelper('crossLinkRaw', function (item) { var str = ''; if (!item) { 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, true)); }); str = p.join(' | '); @@ -112,36 +114,35 @@ YUI.add('doc-builder', function(Y) { if (options.cacheTemplates === false) { this.cacheTemplates = false; } - }; 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) { - html = marked(md, this.options.markdown); + * 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)) { //console.log('MD: ', html); @@ -159,19 +160,19 @@ 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 = '../', - 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'; @@ -221,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; @@ -257,21 +258,21 @@ 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; }, /** - * 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, @@ -301,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); } @@ -348,33 +349,33 @@ 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); } }); } - 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); }); }); }, /** - * 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; @@ -391,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; @@ -429,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', @@ -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); @@ -485,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 + 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, @@ -514,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) { - moddef = self.data.modules[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); @@ -551,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] = {}; @@ -600,34 +620,34 @@ 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; if (a.path) { - a.foundAt = a.path + '#l' + a.line; + a.foundAt = a.path + '#l' + a.line; } } 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; } @@ -648,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; @@ -659,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; } @@ -682,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)); @@ -704,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 { @@ -716,7 +736,7 @@ YUI.add('doc-builder', function(Y) { } })); }); - stack.done(function() { + stack.done(function () { if (cb) { cb(); } @@ -724,23 +744,23 @@ YUI.add('doc-builder', function(Y) { }, - _resolveUrl: function(url, opts) { - if(!url) { - return null; + _resolveUrl: function (url, opts) { + if (!url) { + return null; } - if ( url.indexOf("://") >= 0 ) { + if (url.indexOf("://") >= 0) { return url; } - return path.join(opts.meta.projectRoot,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, '
');
@@ -755,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) {
@@ -778,12 +798,12 @@ 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) {
-                html.push(line);
-            }
+            // function buffer(line) {
+            //     html.push(line);
+            // }
 
             // Allow callback as third or fourth param.
             if (typeof partials === 'function') {
@@ -793,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);
             });
 
@@ -810,27 +832,27 @@ YUI.add('doc-builder', function(Y) {
                 } else {
                     _v[k] = view[k];
                 }
-            };
+            }
             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';
@@ -838,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) {
@@ -864,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;
@@ -902,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);
@@ -913,85 +938,94 @@ 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;
                     } else {
-                        data.example = self._parseCode(self.markdown(i.example));
+                        data.example = self._parseCode(self.markdown(data.example));
                     }
                     opts.meta.example = data.example;
                 }
                 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) {
-                        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);
             });
-            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;
-            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;
@@ -1000,48 +1034,49 @@ 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) {
-                            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;
                             }
@@ -1058,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);
@@ -1107,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);
                     }
@@ -1120,10 +1155,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,16 +1172,16 @@ 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];
                     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;
@@ -1156,157 +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, v) {
-                                    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) {
-                                    var 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) {
-                                    var 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) {
-                                    var 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) {
-                                    var 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;
                     }
                 });
 
@@ -1328,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;
@@ -1338,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();
@@ -1355,36 +1391,42 @@ 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) {
-                        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);
             });
-            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;
             }
@@ -1396,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();
@@ -1413,42 +1455,52 @@ 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;
                     }
                     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() {
+            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);
                 }
@@ -1469,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);
@@ -1479,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);
                     });
@@ -1493,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: {} }, self = this;
+         * 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' +
                 '});';
@@ -1536,63 +1590,71 @@ 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));
-                                }
+                    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));
+                                    }
+                                }));
                             }));
-                        }));
-                        /*
+                            /*
                         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 f6d63d82..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,19 +7,19 @@ 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
+ */
 
-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);
 
@@ -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() {
-            var endtime = (new Date).getTime();
-            Y.log('Completed in ' + ((endtime - starttime) / 1000) + ' seconds' , 'info', 'yuidoc');
+        builder.compile(function () {
+            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..68835398 100644
--- a/lib/docparser.js
+++ b/lib/docparser.js
@@ -3,737 +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,
-    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:
-    `/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, optional, optdefault, parent, multiple, len,
-                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)
-            };
+                }
 
-            if (type) {
-                result.type = type;
-            }
+                len = name.length - 1;
 
-            if (optional) {
-                result.optional = true;
-                if (optdefault) {
-                    result.optdefault = optdefault;
+                if (name.charAt(len) === '*') {
+                    multiple = true;
+                    name = name.substr(0, len);
                 }
-            }
 
-            if (multiple) {
-                result.multiple = true;
-            }
-            host.push(result);
+                // 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 = '[]';
+                            }
+                        }
+                    }
+                }
 
+                // 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;
+                                }
+                            }
+                        }
+                    });
 
-        },
+                }
 
-        // @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)) {
-                    var o = {
-                        tag: tagname,
-                        name: value,
-                        file: target.file,
-                        line: target.line,
-                        description: target.description
-                    };
-                    this.set(MAIN_MODULE, o);
+                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]);
                 }
-                host = this.modules[value];
-                return host;
-            }
-            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, _value = value;
-
-            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);
 
-            if (_namespace) {
-                this.set(CURRENT_NAMESPACE, _namespace);
-            }
-            this.set(CURRENT_CLASS, value);
-            var fullname = this.get(CURRENT_CLASS);
-            var host = this.classes[fullname],
+                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;
-        },
-
-        // change 'const' to final property
-        'const': function(tagname, value, target, block) {
-            target.itemtype = 'property';
-            target.name = value;
-            target['final'] = '';
-        },
-
-        // supported classitems
-        'property': function(tagname, value, target, block) {
-            var match, name, desc;
+                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',
-        'throws': '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 file = this.get(CURRENT_FILE);
-            if (file) {
-                this.files[file].namespaces[value] = 1;
-            }
-            var 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;
+                }
+            },
 
-            var 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;
+                }
 
-            var mod = this.get(CURRENT_CLASS);
-            if (mod) {
-                var lastNS = this.get('lastnamespace');
-                if (lastNS && lastNS !== value && (value.indexOf(lastNS + '.') !== 0)) {
-                    if (this.classes[mod]) {
-                        var 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) {
-                            var m = this.classes[mod];
+
+                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];
-                            var 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];
+                            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];
-                                var 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) {
-            value = this._resolveFor(value);
-            this.set(CURRENT_CLASS, value);
-            var ns = ((this.classes[value]) ? this.classes[value].namespace : '');
-            this.set(CURRENT_NAMESPACE, ns);
-            var file = this.get(CURRENT_FILE);
-            if (file) {
-                this.files[file].fors[value] = 1;
-            }
-            var mod = this.get(CURRENT_MODULE);
-            if (mod) {
-                this.modules[mod].fors[value] = 1;
-            }
+            // 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 mod = this.get(CURRENT_SUBMODULE);
-            if (mod) {
-                this.modules[mod].fors[value];
+                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;
+                }
+
+                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;
+                }
             }
-        }
+        },
 
-    },
-
-    /**
-     * 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';
 
@@ -742,7 +747,7 @@ YUI.add('docparser', function(Y) {
     DocParser.CORRECTIONS = CORRECTIONS;
 
     DocParser.ATTRS = {
-        
+
         lint: {
             value: false
         },
@@ -762,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;
@@ -775,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
          */
@@ -794,7 +799,7 @@ YUI.add('docparser', function(Y) {
          * @attribute filemap
          */
         filemap: {
-            writeOnce : true
+            writeOnce: true
         },
 
         /**
@@ -804,7 +809,7 @@ YUI.add('docparser', function(Y) {
          * @attribute dirmap
          */
         dirmap: {
-            writeOnce : true
+            writeOnce: true
         },
 
         /**
@@ -813,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)) {
@@ -829,19 +834,19 @@ 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;
                 }
-		        //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 +857,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;
                         }
                     }
@@ -870,18 +875,23 @@ YUI.add('docparser', function(Y) {
          * @type String
          */
         currentmodule: {
-            setter: function(val) {
+            setter: function (val) {
                 if (!val) {
                     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 +913,7 @@ YUI.add('docparser', function(Y) {
                         }
                     }
                 }
+
                 if (!(val in this.modules)) {
                     this.modules[val] = {
                         name: val,
@@ -912,6 +923,7 @@ YUI.add('docparser', function(Y) {
                         namespaces: {}
                     };
                 }
+
                 return val;
             }
         },
@@ -922,7 +934,7 @@ YUI.add('docparser', function(Y) {
          * @type String
          */
         currentsubmodule: {
-            setter: function(val) {
+            setter: function (val) {
                 if (!val) {
                     return val;
                 }
@@ -945,7 +957,7 @@ YUI.add('docparser', function(Y) {
             }
         },
         currentnamespace: {
-            setter: function(val) {
+            setter: function (val) {
                 this.set('lastnamespace', this.get(CURRENT_NAMESPACE));
                 return val;
             }
@@ -962,24 +974,28 @@ YUI.add('docparser', function(Y) {
          * @type String
          */
         currentclass: {
-            setter: function(val) {
+            setter: function (val) {
                 if (!val) {
                     return val;
                 }
                 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 + '.' + val;
+                    }
+                    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);
@@ -993,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;
@@ -1012,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) {
@@ -1054,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
@@ -1076,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) {
@@ -1088,36 +1104,41 @@ 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,
-                results = [{tag: 'file', value: file},
-                           {tag: 'line', value: line}],
+                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]);
+                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 +1150,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';
@@ -1171,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');
-            var commentmap = {};
-            Y.each(filemap, function(code, filename) {
+            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;
+                    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;
@@ -1203,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);
@@ -1217,19 +1239,17 @@ YUI.add('docparser', function(Y) {
          * @method processblock
          * @param {Array} an array of the tag/text pairs
          */
-        processblock: function(block) {
+        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 === '') {
@@ -1237,7 +1257,6 @@ YUI.add('docparser', function(Y) {
                 }
 
                 if (tag && tag.tag) {
-
                     if (!(name in this.knowntags)) {
                         if (name in CORRECTIONS) {
                             this.warnings.push({
@@ -1254,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];
@@ -1266,7 +1286,6 @@ YUI.add('docparser', function(Y) {
                         target[name] = value;
                     }
                 }
-
             }, this);
 
             if (host) {
@@ -1275,10 +1294,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;
@@ -1294,34 +1315,35 @@ 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 = {},
                 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 +1355,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;
                 }
@@ -1360,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);
@@ -1369,11 +1391,11 @@ 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 : ''),
-                        line:  stringlog(v)
+                        line: stringlog(v)
                     });
                     Y.log('Missing item type: ' + stringlog(v), 'warn', 'DocParser');
                     if (v.description) {
@@ -1382,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 + '.', '');
                     });
@@ -1390,7 +1412,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);
                 }
@@ -1407,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', 'json-stringify'] });
-
+}, '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 a9a033b6..4bc93929 100644
--- a/lib/files.js
+++ b/lib/files.js
@@ -1,451 +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;
 
-
-
-
-var exists = function(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())) {
-            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 destStats   = statSync(dest),
-        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);
-    }
-}
-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 i = false;
-    link = (link === false) ? false : true;
-    try {
-        var stat = fs.lstatSync(path);
-        
-        if (stat) {
-            if (stat.isSymbolicLink() && link) {
-                stat = fs.statSync(path);
-            }
-            i = stat.isDirectory();
+            fs.rmdirSync(path);
         }
-    } catch (e) {
-        i = false;
     }
+    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;
 
-    return i;
-};
-
-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 i = false;
-    try {
-        var stat = fs.lstatSync(path);
-        
-        if (stat) {
-            if (stat.isSymbolicLink() && link) {
-                stat = fs.statSync(path);
+        link = (link === false) ? false : true;
+
+        try {
+            stat = fs.lstatSync(path);
+            if (stat) {
+                if (stat.isSymbolicLink() && link) {
+                    stat = fs.statSync(path);
+                }
+                result = stat.isDirectory();
             }
-            i = stat.isFile();
+        } catch (e) {
+            result = false;
         }
-    } catch (e) {
-        i = false;
-    }
 
-    return i;
-}
-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;
+        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();
+            }
+        } catch (e) {
+            result = false;
         }
 
-        throw ex;
+        return result;
     }
-}
-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.isFile = isFile;
+
 
-        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.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.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;
             }
 
-            copyPath(from[0], to, true, callback);
-        } else {
-            callback();
+            throw ex;
         }
-    } else {
-        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, function() {
-                if (isDirectory(from[1])) {
-                    copyPath(from[1], to, true, callback)
-                } else {
-                    callback();
+            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);
                 }
-            });
+
+                copyPath(from[0], to, true, callback);
+            } else {
+                callback();
+            }
         } else {
-            callback();
+            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();
+            }
+
         }
-        
     }
-}
+    Y.Files.copyAssets = copyAssets;
 
-Y.Files.copyAssets = copyAssets;
 
+    /**
+    Helper method for getting JSON data from a local file
 
-/**
-* 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'));
+    @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;
     };
-    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*
-*/
-
-var writeFileTimer = 100,
-    readFileTimer = 100;
-
-var writeFile = function(file, data, cb) {
-    var flags = {
-        flags: "w", encoding: Y.charset, mode: 0644
+
+    /**
+    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 {
+            out = fs.createWriteStream(file, flags);
+            out.write(data);
+            out.end();
+        }
     }
-    var args = arguments;
-    if (cb) {
-        fs.writeFile(file, data, flags, function(err) {
+    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('Writefile failed, too many open files (' + args[0] + '). Trying again.', 'warn', 'files');
-                writeFileTimer++;
-                Y.later(writeFileTimer, 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 {
-        var out = fs.createWriteStream(file, flags);
-        out.write(data);
-        out.end();
     }
-};
-
-Y.Files.writeFile = writeFile;
-
-
-var readFile = function(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);
-            return;
-        }
-        cb(err, data);
-    });
-};
+    readFile.timer = 100;
 
-Y.Files.readFile = readFile;
+    Y.Files.readFile = readFile;
 
 });
diff --git a/lib/help.js b/lib/help.js
index 3de4dd5a..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,23 +56,23 @@ 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 c6bd6a49..3fc62548 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');`  
@@ -24,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;
     }
@@ -36,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);
@@ -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..39a89ec7 100644
--- a/lib/options.js
+++ b/lib/options.js
@@ -1,21 +1,22 @@
-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 = {
             port: 3000,
             nocode: false
@@ -25,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 c66f31d7..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,8 +46,9 @@ YUI.add('project', function(Y) {
                 Y.showHelp();
                 process.exit(1);
             }
-            
+
             if (typeof options.tabtospace === 'number') {
+                /*jshint onevar:false */
                 options.tabspace = '';
                 for (var s = 0; s < options.tabtospace; s++) {
                     options.tabspace += ' ';
@@ -54,7 +56,7 @@ YUI.add('project', function(Y) {
             }
 
             return options;
-            
         }
     };
+
 });
diff --git a/lib/server.js b/lib/server.js
index 907ea6b6..90e765f5 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -1,228 +1,230 @@
-YUI.add('server', function(Y) {
-    
-    var path = require('path');
-    /**
-    * Provides the `--server` server option for YUIDoc
-    * @class Server
-    * @module yuidoc
-    */
-    var 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();
-            }
+YUI.add('server', function (Y) {
 
-            next();
-        },
+    var path = require('path'),
         /**
-        * 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);
+         * 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);
+                    });
                 });
-            });
 
 
-            app.get('/classes/:class.html', Server.parse, function(req, res, next) {
-                Server.clazz(req, res, next);
-            });
+                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('/modules/:module.html', Server.parse, function (req, res, next) {
+                    Server.module(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('/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);
-            });
+                //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);
+                });
 
-            app.get('*', function(req, res) {
-                var type = req.url.split('/')[1],
-                    oType = type;
-                var html = ['

Item Not Found in internal meta-data

']; + app.get('//files/:file.html', Server.parse, function (req, res, next) { + Server.files(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('*', function (req, res) { + var type = req.url.split('/')[1], + html = ['

Item Not Found in internal meta-data

']; + 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('
'); + } + } - 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; - var 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(); - } + res.send(html.join('\n'), 404); + }); - Y.log('Serving /files/' + data.name, 'info', 'server'); + }, + /** + * `/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(); + } - 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'); - - - Server.app = express(); - //console.log(Server.options); - var 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 = (new Y.YUIDoc(Server.options)).run(); - Server.options = Y.Project.mix(json, Server.options); - var 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 251538e8..9976dded 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -3,30 +3,30 @@ 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'); /** -* 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,410 +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 compiled = {}, - meta = {}, - 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) { - 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 ff7827c1..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; + files = files.sort(); + + 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); @@ -221,7 +235,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) { @@ -230,6 +244,7 @@ YUI.add('yuidoc', function(Y) { } } }, + /** * Writes the parser JSON data to disk. * @method writeJSON @@ -237,21 +252,23 @@ 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; + data, + file, + out; data = parser.data; 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 = {}; @@ -271,11 +288,13 @@ 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); } + if (!Y.Files.exists(self.options.outdir)) { Y.log('Making out dir: ' + self.options.outdir, 'info', 'yuidoc'); try { @@ -284,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(); @@ -294,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 @@ -322,15 +345,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/package.json b/package.json index fcdb2e4e..e0faf059 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.2.x" }, "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", diff --git a/tests/builder.js b/tests/builder.js index 628699dd..1de640e3 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')); @@ -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,11 +22,12 @@ var suite = new YUITest.TestSuite({ return ret; }, - setUp: function() { - var test = this; - var options = { + setUp: function () { + var options, json, builder; + + options = { quiet: true, - paths: [ 'input/' ], + paths: ['input/'], outdir: './out', helpers: [ path.join(__dirname, 'lib/davglass.js') @@ -35,13 +36,13 @@ 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.compile(function() { + + builder = new Y.DocBuilder(options, json); + builder.compile(function () { suite._setupComplete = true; }); this.builder = builder; @@ -53,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; /* @@ -62,69 +63,68 @@ 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() { - if (suite._setupComplete) { - clearInterval(timer); - test.resume(function() { - Assert.isTrue(suite._setupComplete); - }); - } - },10); + var test = this, + 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) + 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) + 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) + 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) + 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'); - var files = fs.readdirSync(dir); - files.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) { - var m = mods[name]; - var p = path.join(__dirname, 'out', 'modules', m.name + '.html'); + 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) { - var m = mods[name]; - var p = path.join(__dirname, 'out', 'classes', m.name + '.html'); + 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/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..4190cce2 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. @@ -12,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' ]); @@ -20,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' @@ -29,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' @@ -40,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. @@ -95,238 +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' ]); - 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() { + '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() { + '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() { + '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' ]); @@ -335,9 +313,9 @@ suite.add(new YUITest.TestCase({ Assert.isTrue(options.quiet); Assert.isFalse(options.writeJSON); }, - 'test --debug': function() { + 'test --debug': function () { Assert.isFalse(Y.config.debug); - var options = Y.Options([ + Y.Options([ '--debug' ]); Assert.isTrue(Y.config.debug); @@ -346,22 +324,22 @@ suite.add(new YUITest.TestCase({ debug: false }); }, - 'test: --charset': function() { - var options = Y.Options([ + 'test: --charset': function () { + Y.Options([ '--charset' ]); Assert.areEqual('utf8', Y.charset); - + }, - 'test: --charset foo': function() { - var options = Y.Options([ + 'test: --charset foo': function () { + Y.Options([ '--charset', 'foo' ]); 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' @@ -373,4 +351,3 @@ suite.add(new YUITest.TestCase({ YUITest.TestRunner.add(suite); - diff --git a/tests/parser.js b/tests/parser.js index 8b682e2e..e149bde4 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, @@ -12,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(); @@ -26,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; } @@ -42,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'); @@ -66,35 +67,36 @@ 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() { - var files = this.data.files; - + 'test: files parsing': function () { + 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'); }, - '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'); @@ -102,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 @@ -113,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.'; @@ -122,34 +124,36 @@ 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() { - var mods = this.data.modules; + 'test: submodule parsing': function () { + 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 m = mods.mymodule; + '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'); Assert.areSame(3, Object.keys(m.classes).length, 'Should have 3 class'); @@ -158,89 +162,106 @@ 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; + 'test: class parsing': function () { + 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'); - - var m = cl['P.storage.P.storage']; + 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'); Assert.isNotUndefined(cl['P.storage'], 'Should not have double namespaces'); 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 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'); 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'); - var item2 = this.findByName('testobjectparam', 'myclass'); - Assert.areSame('String', item2.return.type, 'Type should not be missing'); + 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'); + 'test: parameter parsing': function () { + 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,24 +280,25 @@ 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'); + 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'); 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() { - var item = this.findByName('testobjectparam', 'myclass'); + 'test: object parameters': function () { + 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'); @@ -294,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'); @@ -307,62 +329,60 @@ 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']; + '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'); - 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..08d96795 100644 --- a/tests/parser_coffee.js +++ b/tests/parser_coffee.js @@ -1,21 +1,18 @@ +/*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() { + setUp: function () { var json = (new Y.YUIDoc({ quiet: true, - paths: [ 'input/' ], + paths: ['input/'], outdir: './out', extension: '.coffee', syntaxtype: 'coffee' @@ -28,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; } @@ -44,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 4f146437..981bd88d 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,44 +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')); @@ -73,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); 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(); }