From 3c2c0e3b7bab468e3a627bebc535b17743c4cb18 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Thu, 31 Oct 2013 09:03:06 -0700 Subject: [PATCH 1/9] whitespace --- lib/jsdoc/util/markdown.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/jsdoc/util/markdown.js b/lib/jsdoc/util/markdown.js index 3fdd217d..4a7cf72c 100644 --- a/lib/jsdoc/util/markdown.js +++ b/lib/jsdoc/util/markdown.js @@ -57,14 +57,14 @@ function getParseFunction(parser, conf) { if (parser === parsers.marked) { parser = require(parser); parser.setOptions({ - gfm: true, - tables: true, - breaks: false, - pedantic: false, - sanitize: true, - smartLists: true, - langPrefix: 'lang-' - }); + gfm: true, + tables: true, + breaks: false, + pedantic: false, + sanitize: true, + smartLists: true, + langPrefix: 'lang-' + }); parse = function(source) { source = escapeUnderscores(source); return parser(source) @@ -73,7 +73,8 @@ function getParseFunction(parser, conf) { }; parse._parser = parsers.marked; return parse; - } else if (parser === parsers.evilstreak) { + } + else if (parser === parsers.evilstreak) { parser = require(parser).markdown; parse = function(source) { @@ -84,7 +85,8 @@ function getParseFunction(parser, conf) { }; parse._parser = parsers.evilstreak; return parse; - } else { + } + else { throw new Error("unknown Markdown parser: '" + parser + "'"); } } @@ -102,10 +104,12 @@ exports.getParser = function() { var conf = env.conf.markdown; if (conf && conf.parser) { return getParseFunction(parsers[conf.parser], conf); - } else if (conf && conf.githubRepoOwner && conf.githubRepoName) { + } + else if (conf && conf.githubRepoOwner && conf.githubRepoName) { // use GitHub-friendly parser if GitHub-specific options are present return getParseFunction(parsers.gfm, conf); - } else { + } + else { // evilstreak is the default parser return getParseFunction(parsers.evilstreak, conf); } From e17b785a43fb9489be7df54030b1babf932e67c4 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Thu, 31 Oct 2013 09:25:24 -0700 Subject: [PATCH 2/9] whitespace --- plugins/markdown.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/markdown.js b/plugins/markdown.js index 0ec29ae5..6dcd9aa8 100644 --- a/plugins/markdown.js +++ b/plugins/markdown.js @@ -29,14 +29,16 @@ function process(doclet) { // treat '@see' specially, since we only want to process @see text that contains links (tag == 'see' && doclet[tag].indexOf('[') != -1))) { doclet[tag] = parse(doclet[tag]); - } else if (doclet[tag] instanceof Array) { - doclet[tag].forEach(function(value, index, original){ + } + else if (doclet[tag] instanceof Array) { + doclet[tag].forEach(function(value, index, original) { var inner = {}; inner[tag] = value; process(inner); original[index] = inner[tag]; }); - } else if (doclet[tag]) { + } + else if (doclet[tag]) { process(doclet[tag]); } }); From 7ac6bfee1cd76dbb78d6d0665f7ff8ff18bcc8fd Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Thu, 31 Oct 2013 15:32:08 -0700 Subject: [PATCH 3/9] cleanup --- plugins/markdown.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/markdown.js b/plugins/markdown.js index 6dcd9aa8..1271bb5d 100644 --- a/plugins/markdown.js +++ b/plugins/markdown.js @@ -12,6 +12,20 @@ var parse = require('jsdoc/util/markdown').getParser(); var tags = []; var excludeTags = []; +function shouldProcessString(tagName, text) { + var shouldProcess = false; + + if (tagName !== 'see') { + shouldProcess = true; + } + // we only want to process `@see` tags that contain Markdown links + else if (tagName === 'see' && text.indexOf('[') !== -1) { + shouldProcess = true; + } + + return shouldProcess; +} + /** * Process the markdown source in a doclet. The properties that should be * processed are configurable, but always include "classdesc", "description", @@ -24,13 +38,10 @@ function process(doclet) { return; } - if (typeof doclet[tag] === "string" && - (tag != 'see' || - // treat '@see' specially, since we only want to process @see text that contains links - (tag == 'see' && doclet[tag].indexOf('[') != -1))) { + if (typeof doclet[tag] === "string" && shouldProcessString(tag, doclet[tag]) ) { doclet[tag] = parse(doclet[tag]); } - else if (doclet[tag] instanceof Array) { + else if ( Array.isArray(doclet[tag]) ) { doclet[tag].forEach(function(value, index, original) { var inner = {}; inner[tag] = value; From 84188d6e62bb5b6321be82233a3861fc8c3e3172 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 2 Nov 2013 12:37:34 -0700 Subject: [PATCH 4/9] don't escape inline HTML in Markdown-tagged text; remove `markdown-js` parser, and default to `marked` (#510) --- Jake/templates/package.json.tmpl | 1 - LICENSE.md | 12 - lib/jsdoc/util/markdown.js | 64 +- node_modules/markdown/README.markdown | 115 -- node_modules/markdown/lib/index.js | 3 - node_modules/markdown/lib/markdown.js | 1616 ------------------------- node_modules/markdown/package.json | 68 -- package.json | 3 +- plugins/markdown.md | 86 -- test/specs/jsdoc/tutorial.js | 4 +- test/specs/jsdoc/util/markdown.js | 16 +- 11 files changed, 27 insertions(+), 1961 deletions(-) delete mode 100644 node_modules/markdown/README.markdown delete mode 100644 node_modules/markdown/lib/index.js delete mode 100644 node_modules/markdown/lib/markdown.js delete mode 100644 node_modules/markdown/package.json delete mode 100644 plugins/markdown.md diff --git a/Jake/templates/package.json.tmpl b/Jake/templates/package.json.tmpl index 3b5a5c77..0319ca2b 100644 --- a/Jake/templates/package.json.tmpl +++ b/Jake/templates/package.json.tmpl @@ -20,7 +20,6 @@ "crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505", "js2xmlparser": "0.1.0", "jshint": "0.9.1", - "markdown": "git+https://github.com/jsdoc3/markdown-js.git", "marked": "0.2.8", "taffydb": "git+https://github.com/hegemonic/taffydb.git", "underscore": "1.4.2", diff --git a/LICENSE.md b/LICENSE.md index e06fb138..de2d0492 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -199,18 +199,6 @@ The source code for JSHint is available at: https://github.com/jshint/jshint -## markdown-js ## - -markdown-js is distributed under the MIT license, which is reproduced above. - -Copyright (c) 2009-2010 Dominic Baggott. Copyright (c) 2009-2010 Ash Berlin. -Copyright (c) 2011 Christoph Dorn -(http://www.christophdorn.com). - -The source code for markdown-js is available at: -https://github.com/evilstreak/markdown-js - - ## Node.js ## Portions of the Node.js source code are incorporated into the following files: diff --git a/lib/jsdoc/util/markdown.js b/lib/jsdoc/util/markdown.js index 4a7cf72c..af14a582 100644 --- a/lib/jsdoc/util/markdown.js +++ b/lib/jsdoc/util/markdown.js @@ -11,9 +11,13 @@ * Enumeration of Markdown parsers that are available. * @enum {String} */ -var parsers = { - /** The "[markdown-js](https://github.com/evilstreak/markdown-js)" (aka "evilstreak") parser. */ - evilstreak: "markdown", +var parserNames = { + /** + * The "[markdown-js](https://github.com/evilstreak/markdown-js)" (aka "evilstreak") parser. + * + * @deprecated Replaced by "marked," as markdown-js does not support inline HTML. + */ + evilstreak: "marked", /** * The "GitHub-flavored Markdown" parser. * @deprecated Replaced by "marked." @@ -44,50 +48,30 @@ function escapeUnderscores(source) { * the specified parser to transform the Markdown source to HTML, then returns the HTML as a string. * * @private - * @param {String} parser The name of the selected parser. + * @param {String} parserName The name of the selected parser. * @param {Object} [conf] Configuration for the selected parser, if any. * @returns {Function} A function that accepts Markdown source, feeds it to the selected parser, and * returns the resulting HTML. * @throws {Error} If the name does not correspond to a known parser. */ -function getParseFunction(parser, conf) { - conf = conf || {}; - var parse; +function getParseFunction(parserName, conf) { + var marked = require('marked'); + var parserFunction; - if (parser === parsers.marked) { - parser = require(parser); - parser.setOptions({ - gfm: true, - tables: true, - breaks: false, - pedantic: false, - sanitize: true, - smartLists: true, - langPrefix: 'lang-' - }); - parse = function(source) { + conf = conf || {}; + + if (parserName === parserNames.marked) { + parserFunction = function(source) { source = escapeUnderscores(source); - return parser(source) + return marked(source) .replace(/\s+$/, '') .replace(/'/g, "'"); }; - parse._parser = parsers.marked; - return parse; - } - else if (parser === parsers.evilstreak) { - parser = require(parser).markdown; - - parse = function(source) { - source = escapeUnderscores(source); - // evilstreak parser expects line endings to be \n - source = source.replace(/\r\n|\r/g, '\n'); - return parser.toHTML(source, conf.dialect); - }; - parse._parser = parsers.evilstreak; - return parse; + parserFunction._parser = parserNames.marked; + return parserFunction; } else { - throw new Error("unknown Markdown parser: '" + parser + "'"); + throw new Error("unknown Markdown parser: '" + parserName + "'"); } } @@ -103,14 +87,10 @@ function getParseFunction(parser, conf) { exports.getParser = function() { var conf = env.conf.markdown; if (conf && conf.parser) { - return getParseFunction(parsers[conf.parser], conf); - } - else if (conf && conf.githubRepoOwner && conf.githubRepoName) { - // use GitHub-friendly parser if GitHub-specific options are present - return getParseFunction(parsers.gfm, conf); + return getParseFunction(parserNames[conf.parser], conf); } else { - // evilstreak is the default parser - return getParseFunction(parsers.evilstreak, conf); + // marked is the default parser + return getParseFunction(parserNames.marked, conf); } }; diff --git a/node_modules/markdown/README.markdown b/node_modules/markdown/README.markdown deleted file mode 100644 index 67f39696..00000000 --- a/node_modules/markdown/README.markdown +++ /dev/null @@ -1,115 +0,0 @@ -markdown-js -=========== - -Yet another markdown parser, this time for JavaScript. There's a few -options that precede this project but they all treat markdown to HTML -conversion as a single step process. You pass markdown in and get HTML -out, end of story. We had some pretty particular views on how the -process should actually look, which include: - - * producing well-formed HTML. This means that em and strong nesting is - important, as is the ability to output as both HTML and XHTML - - * having an intermediate representation to allow processing of parsed - data (we in fact have two, both [JsonML]: a markdown tree and an - HTML tree) - - * being easily extensible to add new dialects without having to - rewrite the entire parsing mechanics - - * having a good test suite. The only test suites we could find tested - massive blocks of input, and passing depended on outputting the HTML - with exactly the same whitespace as the original implementation - -[JsonML]: http://jsonml.org/ "JSON Markup Language" - -## Installation - -Just the `markdown` library: - - npm install markdown - -Also install `md2html` to `/usr/local/bin` (or wherever) - - npm install -g markdown - -## Usage - -The simple way to use it with CommonJS is: - - var input = "# Heading\n\nParagraph"; - var output = require( "markdown" ).markdown.toHTML( input ); - print( output ); - -If you want more control check out the documentation in -[lib/markdown.js] which details all the methods and parameters -available (including examples!). One day we'll get the docs generated -and hosted somewhere for nicer browsing. - -It also works in a browser; here is a complete example: - - - - - -
- - - - - -### md2html - - md2html /path/to/doc.md > /path/to/doc.html - -[lib/markdown.js]: http://github.com/evilstreak/markdown-js/blob/master/lib/markdown.js - -## Intermediate Representation - -Internally the process to convert a chunk of markdown into a chunk of -HTML has three steps: - - 1. Parse the markdown into a JsonML tree. Any references found in the - parsing are stored in the attribute hash of the root node under the - key `references`. - - 2. Convert the markdown tree into an HTML tree. Rename any nodes that - need it (`bulletlist` to `ul` for example) and lookup any references - used by links or images. Remove the references attribute once done. - - 3. Stringify the HTML tree being careful not to wreck whitespace where - whitespace is important (surrounding inline elements for example). - -Each step of this process can be called individually if you need to do -some processing or modification of the data at an intermediate stage. -For example, you may want to grab a list of all URLs linked to in the -document before rendering it to HTML which you could do by recursing -through the HTML tree looking for `a` nodes. - -## Running tests - -To run the tests under node you will need tap installed (it's listed as a -devDependencies so `npm install` from the checkout should be enough), then do - - $ ./node_modules/.bin/tap test/*.t.js - -## Contributing - -Do the usual github fork and pull request dance. Add yourself to the -contributors section of package.json too if you want to. - -## License - -Released under the MIT license. diff --git a/node_modules/markdown/lib/index.js b/node_modules/markdown/lib/index.js deleted file mode 100644 index 8bb08734..00000000 --- a/node_modules/markdown/lib/index.js +++ /dev/null @@ -1,3 +0,0 @@ -// super simple module for the most common nodejs use case. -exports.markdown = require("./markdown"); -exports.parse = exports.markdown.toHTML; diff --git a/node_modules/markdown/lib/markdown.js b/node_modules/markdown/lib/markdown.js deleted file mode 100644 index 817cfddc..00000000 --- a/node_modules/markdown/lib/markdown.js +++ /dev/null @@ -1,1616 +0,0 @@ -// Released under MIT license -// Copyright (c) 2009-2010 Dominic Baggott -// Copyright (c) 2009-2010 Ash Berlin -// Copyright (c) 2011 Christoph Dorn (http://www.christophdorn.com) - -(function( expose ) { - -/** - * class Markdown - * - * Markdown processing in Javascript done right. We have very particular views - * on what constitutes 'right' which include: - * - * - produces well-formed HTML (this means that em and strong nesting is - * important) - * - * - has an intermediate representation to allow processing of parsed data (We - * in fact have two, both as [JsonML]: a markdown tree and an HTML tree). - * - * - is easily extensible to add new dialects without having to rewrite the - * entire parsing mechanics - * - * - has a good test suite - * - * This implementation fulfills all of these (except that the test suite could - * do with expanding to automatically run all the fixtures from other Markdown - * implementations.) - * - * ##### Intermediate Representation - * - * *TODO* Talk about this :) Its JsonML, but document the node names we use. - * - * [JsonML]: http://jsonml.org/ "JSON Markup Language" - **/ -var Markdown = expose.Markdown = function Markdown(dialect) { - switch (typeof dialect) { - case "undefined": - this.dialect = Markdown.dialects.Gruber; - break; - case "object": - this.dialect = dialect; - break; - default: - if (dialect in Markdown.dialects) { - this.dialect = Markdown.dialects[dialect]; - } - else { - throw new Error("Unknown Markdown dialect '" + String(dialect) + "'"); - } - break; - } - this.em_state = []; - this.strong_state = []; - this.debug_indent = ""; -}; - -/** - * parse( markdown, [dialect] ) -> JsonML - * - markdown (String): markdown string to parse - * - dialect (String | Dialect): the dialect to use, defaults to gruber - * - * Parse `markdown` and return a markdown document as a Markdown.JsonML tree. - **/ -expose.parse = function( source, dialect ) { - // dialect will default if undefined - var md = new Markdown( dialect ); - return md.toTree( source ); -}; - -/** - * toHTML( markdown, [dialect] ) -> String - * toHTML( md_tree ) -> String - * - markdown (String): markdown string to parse - * - md_tree (Markdown.JsonML): parsed markdown tree - * - * Take markdown (either as a string or as a JsonML tree) and run it through - * [[toHTMLTree]] then turn it into a well-formated HTML fragment. - **/ -expose.toHTML = function toHTML( source , dialect , options ) { - var input = expose.toHTMLTree( source , dialect , options ); - - return expose.renderJsonML( input ); -}; - -/** - * toHTMLTree( markdown, [dialect] ) -> JsonML - * toHTMLTree( md_tree ) -> JsonML - * - markdown (String): markdown string to parse - * - dialect (String | Dialect): the dialect to use, defaults to gruber - * - md_tree (Markdown.JsonML): parsed markdown tree - * - * Turn markdown into HTML, represented as a JsonML tree. If a string is given - * to this function, it is first parsed into a markdown tree by calling - * [[parse]]. - **/ -expose.toHTMLTree = function toHTMLTree( input, dialect , options ) { - // convert string input to an MD tree - if ( typeof input ==="string" ) input = this.parse( input, dialect ); - - // Now convert the MD tree to an HTML tree - - // remove references from the tree - var attrs = extract_attr( input ), - refs = {}; - - if ( attrs && attrs.references ) { - refs = attrs.references; - } - - var html = convert_tree_to_html( input, refs , options ); - merge_text_nodes( html ); - return html; -}; - -// For Spidermonkey based engines -function mk_block_toSource() { - return "Markdown.mk_block( " + - uneval(this.toString()) + - ", " + - uneval(this.trailing) + - ", " + - uneval(this.lineNumber) + - " )"; -} - -// node -function mk_block_inspect() { - var util = require('util'); - return "Markdown.mk_block( " + - util.inspect(this.toString()) + - ", " + - util.inspect(this.trailing) + - ", " + - util.inspect(this.lineNumber) + - " )"; - -} - -var mk_block = Markdown.mk_block = function(block, trail, line) { - // Be helpful for default case in tests. - if ( arguments.length == 1 ) trail = "\n\n"; - - var s = new String(block); - s.trailing = trail; - // To make it clear its not just a string - s.inspect = mk_block_inspect; - s.toSource = mk_block_toSource; - - if (line != undefined) - s.lineNumber = line; - - return s; -}; - -function count_lines( str ) { - var n = 0, i = -1; - while ( ( i = str.indexOf('\n', i+1) ) !== -1) n++; - return n; -} - -// Internal - split source into rough blocks -Markdown.prototype.split_blocks = function splitBlocks( input, startLine ) { - // [\s\S] matches _anything_ (newline or space) - var re = /([\s\S]+?)($|\n(?:\s*\n|$)+)/g, - blocks = [], - m; - - var line_no = 1; - - if ( ( m = /^(\s*\n)/.exec(input) ) != null ) { - // skip (but count) leading blank lines - line_no += count_lines( m[0] ); - re.lastIndex = m[0].length; - } - - while ( ( m = re.exec(input) ) !== null ) { - blocks.push( mk_block( m[1], m[2], line_no ) ); - line_no += count_lines( m[0] ); - } - - return blocks; -}; - -/** - * Markdown#processBlock( block, next ) -> undefined | [ JsonML, ... ] - * - block (String): the block to process - * - next (Array): the following blocks - * - * Process `block` and return an array of JsonML nodes representing `block`. - * - * It does this by asking each block level function in the dialect to process - * the block until one can. Succesful handling is indicated by returning an - * array (with zero or more JsonML nodes), failure by a false value. - * - * Blocks handlers are responsible for calling [[Markdown#processInline]] - * themselves as appropriate. - * - * If the blocks were split incorrectly or adjacent blocks need collapsing you - * can adjust `next` in place using shift/splice etc. - * - * If any of this default behaviour is not right for the dialect, you can - * define a `__call__` method on the dialect that will get invoked to handle - * the block processing. - */ -Markdown.prototype.processBlock = function processBlock( block, next ) { - var cbs = this.dialect.block, - ord = cbs.__order__; - - if ( "__call__" in cbs ) { - return cbs.__call__.call(this, block, next); - } - - for ( var i = 0; i < ord.length; i++ ) { - //D:this.debug( "Testing", ord[i] ); - var res = cbs[ ord[i] ].call( this, block, next ); - if ( res ) { - //D:this.debug(" matched"); - if ( !isArray(res) || ( res.length > 0 && !( isArray(res[0]) ) ) ) - this.debug(ord[i], "didn't return a proper array"); - //D:this.debug( "" ); - return res; - } - } - - // Uhoh! no match! Should we throw an error? - return []; -}; - -Markdown.prototype.processInline = function processInline( block ) { - return this.dialect.inline.__call__.call( this, String( block ) ); -}; - -/** - * Markdown#toTree( source ) -> JsonML - * - source (String): markdown source to parse - * - * Parse `source` into a JsonML tree representing the markdown document. - **/ -// custom_tree means set this.tree to `custom_tree` and restore old value on return -Markdown.prototype.toTree = function toTree( source, custom_root ) { - var blocks = source instanceof Array ? source : this.split_blocks( source ); - - // Make tree a member variable so its easier to mess with in extensions - var old_tree = this.tree; - try { - this.tree = custom_root || this.tree || [ "markdown" ]; - - blocks: - while ( blocks.length ) { - var b = this.processBlock( blocks.shift(), blocks ); - - // Reference blocks and the like won't return any content - if ( !b.length ) continue blocks; - - this.tree.push.apply( this.tree, b ); - } - return this.tree; - } - finally { - if ( custom_root ) { - this.tree = old_tree; - } - } -}; - -// Noop by default -Markdown.prototype.debug = function () { - var args = Array.prototype.slice.call( arguments); - args.unshift(this.debug_indent); - if (typeof print !== "undefined") - print.apply( print, args ); - if (typeof console !== "undefined" && typeof console.log !== "undefined") - console.log.apply( null, args ); -} - -Markdown.prototype.loop_re_over_block = function( re, block, cb ) { - // Dont use /g regexps with this - var m, - b = block.valueOf(); - - while ( b.length && (m = re.exec(b) ) != null) { - b = b.substr( m[0].length ); - cb.call(this, m); - } - return b; -}; - -/** - * Markdown.dialects - * - * Namespace of built-in dialects. - **/ -Markdown.dialects = {}; - -/** - * Markdown.dialects.Gruber - * - * The default dialect that follows the rules set out by John Gruber's - * markdown.pl as closely as possible. Well actually we follow the behaviour of - * that script which in some places is not exactly what the syntax web page - * says. - **/ -Markdown.dialects.Gruber = { - block: { - atxHeader: function atxHeader( block, next ) { - var m = block.match( /^(#{1,6})\s*(.*?)\s*#*\s*(?:\n|$)/ ); - - if ( !m ) return undefined; - - var header = [ "header", { level: m[ 1 ].length } ]; - Array.prototype.push.apply(header, this.processInline(m[ 2 ])); - - if ( m[0].length < block.length ) - next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); - - return [ header ]; - }, - - setextHeader: function setextHeader( block, next ) { - var m = block.match( /^(.*)\n([-=])\2\2+(?:\n|$)/ ); - - if ( !m ) return undefined; - - var level = ( m[ 2 ] === "=" ) ? 1 : 2; - var header = [ "header", { level : level }, m[ 1 ] ]; - - if ( m[0].length < block.length ) - next.unshift( mk_block( block.substr( m[0].length ), block.trailing, block.lineNumber + 2 ) ); - - return [ header ]; - }, - - code: function code( block, next ) { - // | Foo - // |bar - // should be a code block followed by a paragraph. Fun - // - // There might also be adjacent code block to merge. - - var ret = [], - re = /^(?: {0,3}\t| {4})(.*)\n?/, - lines; - - // 4 spaces + content - if ( !block.match( re ) ) return undefined; - - block_search: - do { - // Now pull out the rest of the lines - var b = this.loop_re_over_block( - re, block.valueOf(), function( m ) { ret.push( m[1] ); } ); - - if (b.length) { - // Case alluded to in first comment. push it back on as a new block - next.unshift( mk_block(b, block.trailing) ); - break block_search; - } - else if (next.length) { - // Check the next block - it might be code too - if ( !next[0].match( re ) ) break block_search; - - // Pull how how many blanks lines follow - minus two to account for .join - ret.push ( block.trailing.replace(/[^\n]/g, '').substring(2) ); - - block = next.shift(); - } - else { - break block_search; - } - } while (true); - - return [ [ "code_block", ret.join("\n") ] ]; - }, - - horizRule: function horizRule( block, next ) { - // this needs to find any hr in the block to handle abutting blocks - var m = block.match( /^(?:([\s\S]*?)\n)?[ \t]*([-_*])(?:[ \t]*\2){2,}[ \t]*(?:\n([\s\S]*))?$/ ); - - if ( !m ) { - return undefined; - } - - var jsonml = [ [ "hr" ] ]; - - // if there's a leading abutting block, process it - if ( m[ 1 ] ) { - jsonml.unshift.apply( jsonml, this.processBlock( m[ 1 ], [] ) ); - } - - // if there's a trailing abutting block, stick it into next - if ( m[ 3 ] ) { - next.unshift( mk_block( m[ 3 ] ) ); - } - - return jsonml; - }, - - // There are two types of lists. Tight and loose. Tight lists have no whitespace - // between the items (and result in text just in the
  • ) and loose lists, - // which have an empty line between list items, resulting in (one or more) - // paragraphs inside the
  • . - // - // There are all sorts weird edge cases about the original markdown.pl's - // handling of lists: - // - // * Nested lists are supposed to be indented by four chars per level. But - // if they aren't, you can get a nested list by indenting by less than - // four so long as the indent doesn't match an indent of an existing list - // item in the 'nest stack'. - // - // * The type of the list (bullet or number) is controlled just by the - // first item at the indent. Subsequent changes are ignored unless they - // are for nested lists - // - lists: (function( ) { - // Use a closure to hide a few variables. - var any_list = "[*+-]|\\d+\\.", - bullet_list = /[*+-]/, - number_list = /\d+\./, - // Capture leading indent as it matters for determining nested lists. - is_list_re = new RegExp( "^( {0,3})(" + any_list + ")[ \t]+" ), - indent_re = "(?: {0,3}\\t| {4})"; - - // TODO: Cache this regexp for certain depths. - // Create a regexp suitable for matching an li for a given stack depth - function regex_for_depth( depth ) { - - return new RegExp( - // m[1] = indent, m[2] = list_type - "(?:^(" + indent_re + "{0," + depth + "} {0,3})(" + any_list + ")\\s+)|" + - // m[3] = cont - "(^" + indent_re + "{0," + (depth-1) + "}[ ]{0,4})" - ); - } - function expand_tab( input ) { - return input.replace( / {0,3}\t/g, " " ); - } - - // Add inline content `inline` to `li`. inline comes from processInline - // so is an array of content - function add(li, loose, inline, nl) { - if (loose) { - li.push( [ "para" ].concat(inline) ); - return; - } - // Hmmm, should this be any block level element or just paras? - var add_to = li[li.length -1] instanceof Array && li[li.length - 1][0] == "para" - ? li[li.length -1] - : li; - - // If there is already some content in this list, add the new line in - if (nl && li.length > 1) inline.unshift(nl); - - for (var i=0; i < inline.length; i++) { - var what = inline[i], - is_str = typeof what == "string"; - if (is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" ) { - add_to[ add_to.length-1 ] += what; - } - else { - add_to.push( what ); - } - } - } - - // contained means have an indent greater than the current one. On - // *every* line in the block - function get_contained_blocks( depth, blocks ) { - - var re = new RegExp( "^(" + indent_re + "{" + depth + "}.*?\\n?)*$" ), - replace = new RegExp("^" + indent_re + "{" + depth + "}", "gm"), - ret = []; - - while ( blocks.length > 0 ) { - if ( re.exec( blocks[0] ) ) { - var b = blocks.shift(), - // Now remove that indent - x = b.replace( replace, ""); - - ret.push( mk_block( x, b.trailing, b.lineNumber ) ); - } - break; - } - return ret; - } - - // passed to stack.forEach to turn list items up the stack into paras - function paragraphify(s, i, stack) { - var list = s.list; - var last_li = list[list.length-1]; - - if (last_li[1] instanceof Array && last_li[1][0] == "para") { - return; - } - if (i+1 == stack.length) { - // Last stack frame - // Keep the same array, but replace the contents - last_li.push( ["para"].concat( last_li.splice(1) ) ); - } - else { - var sublist = last_li.pop(); - last_li.push( ["para"].concat( last_li.splice(1) ), sublist ); - } - } - - // The matcher function - return function( block, next ) { - var m = block.match( is_list_re ); - if ( !m ) return undefined; - - function make_list( m ) { - var list = bullet_list.exec( m[2] ) - ? ["bulletlist"] - : ["numberlist"]; - - stack.push( { list: list, indent: m[1] } ); - return list; - } - - - var stack = [], // Stack of lists for nesting. - list = make_list( m ), - last_li, - loose = false, - ret = [ stack[0].list ], - i; - - // Loop to search over block looking for inner block elements and loose lists - loose_search: - while( true ) { - // Split into lines preserving new lines at end of line - var lines = block.split( /(?=\n)/ ); - - // We have to grab all lines for a li and call processInline on them - // once as there are some inline things that can span lines. - var li_accumulate = ""; - - // Loop over the lines in this block looking for tight lists. - tight_search: - for (var line_no=0; line_no < lines.length; line_no++) { - var nl = "", - l = lines[line_no].replace(/^\n/, function(n) { nl = n; return ""; }); - - // TODO: really should cache this - var line_re = regex_for_depth( stack.length ); - - m = l.match( line_re ); - //print( "line:", uneval(l), "\nline match:", uneval(m) ); - - // We have a list item - if ( m[1] !== undefined ) { - // Process the previous list item, if any - if ( li_accumulate.length ) { - add( last_li, loose, this.processInline( li_accumulate ), nl ); - // Loose mode will have been dealt with. Reset it - loose = false; - li_accumulate = ""; - } - - m[1] = expand_tab( m[1] ); - var wanted_depth = Math.floor(m[1].length/4)+1; - //print( "want:", wanted_depth, "stack:", stack.length); - if ( wanted_depth > stack.length ) { - // Deep enough for a nested list outright - //print ( "new nested list" ); - list = make_list( m ); - last_li.push( list ); - last_li = list[1] = [ "listitem" ]; - } - else { - // We aren't deep enough to be strictly a new level. This is - // where Md.pl goes nuts. If the indent matches a level in the - // stack, put it there, else put it one deeper then the - // wanted_depth deserves. - var found = false; - for (i = 0; i < stack.length; i++) { - if ( stack[ i ].indent != m[1] ) continue; - list = stack[ i ].list; - stack.splice( i+1 ); - found = true; - break; - } - - if (!found) { - //print("not found. l:", uneval(l)); - wanted_depth++; - if (wanted_depth <= stack.length) { - stack.splice(wanted_depth); - //print("Desired depth now", wanted_depth, "stack:", stack.length); - list = stack[wanted_depth-1].list; - //print("list:", uneval(list) ); - } - else { - //print ("made new stack for messy indent"); - list = make_list(m); - last_li.push(list); - } - } - - //print( uneval(list), "last", list === stack[stack.length-1].list ); - last_li = [ "listitem" ]; - list.push(last_li); - } // end depth of shenegains - nl = ""; - } - - // Add content - if (l.length > m[0].length) { - li_accumulate += nl + l.substr( m[0].length ); - } - } // tight_search - - if ( li_accumulate.length ) { - add( last_li, loose, this.processInline( li_accumulate ), nl ); - // Loose mode will have been dealt with. Reset it - loose = false; - li_accumulate = ""; - } - - // Look at the next block - we might have a loose list. Or an extra - // paragraph for the current li - var contained = get_contained_blocks( stack.length, next ); - - // Deal with code blocks or properly nested lists - if (contained.length > 0) { - // Make sure all listitems up the stack are paragraphs - forEach( stack, paragraphify, this); - - last_li.push.apply( last_li, this.toTree( contained, [] ) ); - } - - var next_block = next[0] && next[0].valueOf() || ""; - - if ( next_block.match(is_list_re) || next_block.match( /^ / ) ) { - block = next.shift(); - - // Check for an HR following a list: features/lists/hr_abutting - var hr = this.dialect.block.horizRule( block, next ); - - if (hr) { - ret.push.apply(ret, hr); - break; - } - - // Make sure all listitems up the stack are paragraphs - forEach( stack, paragraphify, this); - - loose = true; - continue loose_search; - } - break; - } // loose_search - - return ret; - }; - })(), - - blockquote: function blockquote( block, next ) { - if ( !block.match( /^>/m ) ) - return undefined; - - var jsonml = []; - - // separate out the leading abutting block, if any - if ( block[ 0 ] != ">" ) { - var lines = block.split( /\n/ ), - prev = []; - - // keep shifting lines until you find a crotchet - while ( lines.length && lines[ 0 ][ 0 ] != ">" ) { - prev.push( lines.shift() ); - } - - // reassemble! - block = lines.join( "\n" ); - jsonml.push.apply( jsonml, this.processBlock( prev.join( "\n" ), [] ) ); - } - - // if the next block is also a blockquote merge it in - while ( next.length && next[ 0 ][ 0 ] == ">" ) { - var b = next.shift(); - block = new String(block + block.trailing + b); - block.trailing = b.trailing; - } - - // Strip off the leading "> " and re-process as a block. - var input = block.replace( /^> ?/gm, '' ), - old_tree = this.tree; - jsonml.push( this.toTree( input, [ "blockquote" ] ) ); - - return jsonml; - }, - - referenceDefn: function referenceDefn( block, next) { - var re = /^\s*\[(.*?)\]:\s*(\S+)(?:\s+(?:(['"])(.*?)\3|\((.*?)\)))?\n?/; - // interesting matches are [ , ref_id, url, , title, title ] - - if ( !block.match(re) ) - return undefined; - - // make an attribute node if it doesn't exist - if ( !extract_attr( this.tree ) ) { - this.tree.splice( 1, 0, {} ); - } - - var attrs = extract_attr( this.tree ); - - // make a references hash if it doesn't exist - if ( attrs.references === undefined ) { - attrs.references = {}; - } - - var b = this.loop_re_over_block(re, block, function( m ) { - - if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' ) - m[2] = m[2].substring( 1, m[2].length - 1 ); - - var ref = attrs.references[ m[1].toLowerCase() ] = { - href: m[2] - }; - - if (m[4] !== undefined) - ref.title = m[4]; - else if (m[5] !== undefined) - ref.title = m[5]; - - } ); - - if (b.length) - next.unshift( mk_block( b, block.trailing ) ); - - return []; - }, - - para: function para( block, next ) { - // everything's a para! - return [ ["para"].concat( this.processInline( block ) ) ]; - } - } -}; - -Markdown.dialects.Gruber.inline = { - - __oneElement__: function oneElement( text, patterns_or_re, previous_nodes ) { - var m, - res, - lastIndex = 0; - - patterns_or_re = patterns_or_re || this.dialect.inline.__patterns__; - var re = new RegExp( "([\\s\\S]*?)(" + (patterns_or_re.source || patterns_or_re) + ")" ); - - m = re.exec( text ); - if (!m) { - // Just boring text - return [ text.length, text ]; - } - else if ( m[1] ) { - // Some un-interesting text matched. Return that first - return [ m[1].length, m[1] ]; - } - - var res; - if ( m[2] in this.dialect.inline ) { - res = this.dialect.inline[ m[2] ].call( - this, - text.substr( m.index ), m, previous_nodes || [] ); - } - // Default for now to make dev easier. just slurp special and output it. - res = res || [ m[2].length, m[2] ]; - return res; - }, - - __call__: function inline( text, patterns ) { - - var out = [], - res; - - function add(x) { - //D:self.debug(" adding output", uneval(x)); - if (typeof x == "string" && typeof out[out.length-1] == "string") - out[ out.length-1 ] += x; - else - out.push(x); - } - - while ( text.length > 0 ) { - res = this.dialect.inline.__oneElement__.call(this, text, patterns, out ); - text = text.substr( res.shift() ); - forEach(res, add ) - } - - return out; - }, - - // These characters are intersting elsewhere, so have rules for them so that - // chunks of plain text blocks don't include them - "]": function () {}, - "}": function () {}, - - "\\": function escaped( text ) { - // [ length of input processed, node/children to add... ] - // Only esacape: \ ` * _ { } [ ] ( ) # * + - . ! - if ( text.match( /^\\[\\`\*_{}\[\]()#\+.!\-]/ ) ) - return [ 2, text[1] ]; - else - // Not an esacpe - return [ 1, "\\" ]; - }, - - "![": function image( text ) { - - // Unlike images, alt text is plain text only. no other elements are - // allowed in there - - // ![Alt text](/path/to/img.jpg "Optional title") - // 1 2 3 4 <--- captures - var m = text.match( /^!\[(.*?)\][ \t]*\([ \t]*(\S*)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ ); - - if ( m ) { - if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' ) - m[2] = m[2].substring( 1, m[2].length - 1 ); - - m[2] = this.dialect.inline.__call__.call( this, m[2], /\\/ )[0]; - - var attrs = { alt: m[1], href: m[2] || "" }; - if ( m[4] !== undefined) - attrs.title = m[4]; - - return [ m[0].length, [ "img", attrs ] ]; - } - - // ![Alt text][id] - m = text.match( /^!\[(.*?)\][ \t]*\[(.*?)\]/ ); - - if ( m ) { - // We can't check if the reference is known here as it likely wont be - // found till after. Check it in md tree->hmtl tree conversion - return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), original: m[0] } ] ]; - } - - // Just consume the '![' - return [ 2, "![" ]; - }, - - "[": function link( text ) { - - var orig = String(text); - // Inline content is possible inside `link text` - var res = Markdown.DialectHelpers.inline_until_char.call( this, text.substr(1), ']' ); - - // No closing ']' found. Just consume the [ - if ( !res ) return [ 1, '[' ]; - - var consumed = 1 + res[ 0 ], - children = res[ 1 ], - link, - attrs; - - // At this point the first [...] has been parsed. See what follows to find - // out which kind of link we are (reference or direct url) - text = text.substr( consumed ); - - // [link text](/path/to/img.jpg "Optional title") - // 1 2 3 <--- captures - // This will capture up to the last paren in the block. We then pull - // back based on if there a matching ones in the url - // ([here](/url/(test)) - // The parens have to be balanced - var m = text.match( /^\s*\([ \t]*(\S+)(?:[ \t]+(["'])(.*?)\2)?[ \t]*\)/ ); - if ( m ) { - var url = m[1]; - consumed += m[0].length; - - if ( url && url[0] == '<' && url[url.length-1] == '>' ) - url = url.substring( 1, url.length - 1 ); - - // If there is a title we don't have to worry about parens in the url - if ( !m[3] ) { - var open_parens = 1; // One open that isn't in the capture - for (var len = 0; len < url.length; len++) { - switch ( url[len] ) { - case '(': - open_parens++; - break; - case ')': - if ( --open_parens == 0) { - consumed -= url.length - len; - url = url.substring(0, len); - } - break; - } - } - } - - // Process escapes only - url = this.dialect.inline.__call__.call( this, url, /\\/ )[0]; - - attrs = { href: url || "" }; - if ( m[3] !== undefined) - attrs.title = m[3]; - - link = [ "link", attrs ].concat( children ); - return [ consumed, link ]; - } - - // [Alt text][id] - // [Alt text] [id] - m = text.match( /^\s*\[(.*?)\]/ ); - - if ( m ) { - - consumed += m[ 0 ].length; - - // [links][] uses links as its reference - attrs = { ref: ( m[ 1 ] || String(children) ).toLowerCase(), original: orig.substr( 0, consumed ) }; - - link = [ "link_ref", attrs ].concat( children ); - - // We can't check if the reference is known here as it likely wont be - // found till after. Check it in md tree->hmtl tree conversion. - // Store the original so that conversion can revert if the ref isn't found. - return [ consumed, link ]; - } - - // [id] - // Only if id is plain (no formatting.) - if ( children.length == 1 && typeof children[0] == "string" ) { - - attrs = { ref: children[0].toLowerCase(), original: orig.substr( 0, consumed ) }; - link = [ "link_ref", attrs, children[0] ]; - return [ consumed, link ]; - } - - // Just consume the '[' - return [ 1, "[" ]; - }, - - - "<": function autoLink( text ) { - var m; - - if ( ( m = text.match( /^<(?:((https?|ftp|mailto):[^>]+)|(.*?@.*?\.[a-zA-Z]+))>/ ) ) != null ) { - if ( m[3] ) { - return [ m[0].length, [ "link", { href: "mailto:" + m[3] }, m[3] ] ]; - - } - else if ( m[2] == "mailto" ) { - return [ m[0].length, [ "link", { href: m[1] }, m[1].substr("mailto:".length ) ] ]; - } - else - return [ m[0].length, [ "link", { href: m[1] }, m[1] ] ]; - } - - return [ 1, "<" ]; - }, - - "`": function inlineCode( text ) { - // Inline code block. as many backticks as you like to start it - // Always skip over the opening ticks. - var m = text.match( /(`+)(([\s\S]*?)\1)/ ); - - if ( m && m[2] ) - return [ m[1].length + m[2].length, [ "inlinecode", m[3] ] ]; - else { - // TODO: No matching end code found - warn! - return [ 1, "`" ]; - } - }, - - " \n": function lineBreak( text ) { - return [ 3, [ "linebreak" ] ]; - } - -}; - -// Meta Helper/generator method for em and strong handling -function strong_em( tag, md ) { - - var state_slot = tag + "_state", - other_slot = tag == "strong" ? "em_state" : "strong_state"; - - function CloseTag(len) { - this.len_after = len; - this.name = "close_" + md; - } - - return function ( text, orig_match ) { - - if (this[state_slot][0] == md) { - // Most recent em is of this type - //D:this.debug("closing", md); - this[state_slot].shift(); - - // "Consume" everything to go back to the recrusion in the else-block below - return[ text.length, new CloseTag(text.length-md.length) ]; - } - else { - // Store a clone of the em/strong states - var other = this[other_slot].slice(), - state = this[state_slot].slice(); - - this[state_slot].unshift(md); - - //D:this.debug_indent += " "; - - // Recurse - var res = this.processInline( text.substr( md.length ) ); - //D:this.debug_indent = this.debug_indent.substr(2); - - var last = res[res.length - 1]; - - //D:this.debug("processInline from", tag + ": ", uneval( res ) ); - - var check = this[state_slot].shift(); - if (last instanceof CloseTag) { - res.pop(); - // We matched! Huzzah. - var consumed = text.length - last.len_after; - return [ consumed, [ tag ].concat(res) ]; - } - else { - // Restore the state of the other kind. We might have mistakenly closed it. - this[other_slot] = other; - this[state_slot] = state; - - // We can't reuse the processed result as it could have wrong parsing contexts in it. - return [ md.length, md ]; - } - } - }; // End returned function -} - -Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**"); -Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__"); -Markdown.dialects.Gruber.inline["*"] = strong_em("em", "*"); -Markdown.dialects.Gruber.inline["_"] = strong_em("em", "_"); - - -// Build default order from insertion order. -Markdown.buildBlockOrder = function(d) { - var ord = []; - for ( var i in d ) { - if ( i == "__order__" || i == "__call__" ) continue; - ord.push( i ); - } - d.__order__ = ord; -}; - -// Build patterns for inline matcher -Markdown.buildInlinePatterns = function(d) { - var patterns = []; - - for ( var i in d ) { - // __foo__ is reserved and not a pattern - if ( i.match( /^__.*__$/) ) continue; - var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" ) - .replace( /\n/, "\\n" ); - patterns.push( i.length == 1 ? l : "(?:" + l + ")" ); - } - - patterns = patterns.join("|"); - d.__patterns__ = patterns; - //print("patterns:", uneval( patterns ) ); - - var fn = d.__call__; - d.__call__ = function(text, pattern) { - if (pattern != undefined) { - return fn.call(this, text, pattern); - } - else - { - return fn.call(this, text, patterns); - } - }; -}; - -Markdown.DialectHelpers = {}; -Markdown.DialectHelpers.inline_until_char = function( text, want ) { - var consumed = 0, - nodes = []; - - while ( true ) { - if ( text[ consumed ] == want ) { - // Found the character we were looking for - consumed++; - return [ consumed, nodes ]; - } - - if ( consumed >= text.length ) { - // No closing char found. Abort. - return null; - } - - var res = this.dialect.inline.__oneElement__.call(this, text.substr( consumed ) ); - consumed += res[ 0 ]; - // Add any returned nodes. - nodes.push.apply( nodes, res.slice( 1 ) ); - } -} - -// Helper function to make sub-classing a dialect easier -Markdown.subclassDialect = function( d ) { - function Block() {} - Block.prototype = d.block; - function Inline() {} - Inline.prototype = d.inline; - - return { block: new Block(), inline: new Inline() }; -}; - -Markdown.buildBlockOrder ( Markdown.dialects.Gruber.block ); -Markdown.buildInlinePatterns( Markdown.dialects.Gruber.inline ); - -Markdown.dialects.Maruku = Markdown.subclassDialect( Markdown.dialects.Gruber ); - -Markdown.dialects.Maruku.processMetaHash = function processMetaHash( meta_string ) { - var meta = split_meta_hash( meta_string ), - attr = {}; - - for ( var i = 0; i < meta.length; ++i ) { - // id: #foo - if ( /^#/.test( meta[ i ] ) ) { - attr.id = meta[ i ].substring( 1 ); - } - // class: .foo - else if ( /^\./.test( meta[ i ] ) ) { - // if class already exists, append the new one - if ( attr['class'] ) { - attr['class'] = attr['class'] + meta[ i ].replace( /./, " " ); - } - else { - attr['class'] = meta[ i ].substring( 1 ); - } - } - // attribute: foo=bar - else if ( /\=/.test( meta[ i ] ) ) { - var s = meta[ i ].split( /\=/ ); - attr[ s[ 0 ] ] = s[ 1 ]; - } - } - - return attr; -} - -function split_meta_hash( meta_string ) { - var meta = meta_string.split( "" ), - parts = [ "" ], - in_quotes = false; - - while ( meta.length ) { - var letter = meta.shift(); - switch ( letter ) { - case " " : - // if we're in a quoted section, keep it - if ( in_quotes ) { - parts[ parts.length - 1 ] += letter; - } - // otherwise make a new part - else { - parts.push( "" ); - } - break; - case "'" : - case '"' : - // reverse the quotes and move straight on - in_quotes = !in_quotes; - break; - case "\\" : - // shift off the next letter to be used straight away. - // it was escaped so we'll keep it whatever it is - letter = meta.shift(); - default : - parts[ parts.length - 1 ] += letter; - break; - } - } - - return parts; -} - -Markdown.dialects.Maruku.block.document_meta = function document_meta( block, next ) { - // we're only interested in the first block - if ( block.lineNumber > 1 ) return undefined; - - // document_meta blocks consist of one or more lines of `Key: Value\n` - if ( ! block.match( /^(?:\w+:.*\n)*\w+:.*$/ ) ) return undefined; - - // make an attribute node if it doesn't exist - if ( !extract_attr( this.tree ) ) { - this.tree.splice( 1, 0, {} ); - } - - var pairs = block.split( /\n/ ); - for ( p in pairs ) { - var m = pairs[ p ].match( /(\w+):\s*(.*)$/ ), - key = m[ 1 ].toLowerCase(), - value = m[ 2 ]; - - this.tree[ 1 ][ key ] = value; - } - - // document_meta produces no content! - return []; -}; - -Markdown.dialects.Maruku.block.block_meta = function block_meta( block, next ) { - // check if the last line of the block is an meta hash - var m = block.match( /(^|\n) {0,3}\{:\s*((?:\\\}|[^\}])*)\s*\}$/ ); - if ( !m ) return undefined; - - // process the meta hash - var attr = this.dialect.processMetaHash( m[ 2 ] ); - - var hash; - - // if we matched ^ then we need to apply meta to the previous block - if ( m[ 1 ] === "" ) { - var node = this.tree[ this.tree.length - 1 ]; - hash = extract_attr( node ); - - // if the node is a string (rather than JsonML), bail - if ( typeof node === "string" ) return undefined; - - // create the attribute hash if it doesn't exist - if ( !hash ) { - hash = {}; - node.splice( 1, 0, hash ); - } - - // add the attributes in - for ( a in attr ) { - hash[ a ] = attr[ a ]; - } - - // return nothing so the meta hash is removed - return []; - } - - // pull the meta hash off the block and process what's left - var b = block.replace( /\n.*$/, "" ), - result = this.processBlock( b, [] ); - - // get or make the attributes hash - hash = extract_attr( result[ 0 ] ); - if ( !hash ) { - hash = {}; - result[ 0 ].splice( 1, 0, hash ); - } - - // attach the attributes to the block - for ( a in attr ) { - hash[ a ] = attr[ a ]; - } - - return result; -}; - -Markdown.dialects.Maruku.block.definition_list = function definition_list( block, next ) { - // one or more terms followed by one or more definitions, in a single block - var tight = /^((?:[^\s:].*\n)+):\s+([\s\S]+)$/, - list = [ "dl" ], - i; - - // see if we're dealing with a tight or loose block - if ( ( m = block.match( tight ) ) ) { - // pull subsequent tight DL blocks out of `next` - var blocks = [ block ]; - while ( next.length && tight.exec( next[ 0 ] ) ) { - blocks.push( next.shift() ); - } - - for ( var b = 0; b < blocks.length; ++b ) { - var m = blocks[ b ].match( tight ), - terms = m[ 1 ].replace( /\n$/, "" ).split( /\n/ ), - defns = m[ 2 ].split( /\n:\s+/ ); - - // print( uneval( m ) ); - - for ( i = 0; i < terms.length; ++i ) { - list.push( [ "dt", terms[ i ] ] ); - } - - for ( i = 0; i < defns.length; ++i ) { - // run inline processing over the definition - list.push( [ "dd" ].concat( this.processInline( defns[ i ].replace( /(\n)\s+/, "$1" ) ) ) ); - } - } - } - else { - return undefined; - } - - return [ list ]; -}; - -Markdown.dialects.Maruku.inline[ "{:" ] = function inline_meta( text, matches, out ) { - if ( !out.length ) { - return [ 2, "{:" ]; - } - - // get the preceeding element - var before = out[ out.length - 1 ]; - - if ( typeof before === "string" ) { - return [ 2, "{:" ]; - } - - // match a meta hash - var m = text.match( /^\{:\s*((?:\\\}|[^\}])*)\s*\}/ ); - - // no match, false alarm - if ( !m ) { - return [ 2, "{:" ]; - } - - // attach the attributes to the preceeding element - var meta = this.dialect.processMetaHash( m[ 1 ] ), - attr = extract_attr( before ); - - if ( !attr ) { - attr = {}; - before.splice( 1, 0, attr ); - } - - for ( var k in meta ) { - attr[ k ] = meta[ k ]; - } - - // cut out the string and replace it with nothing - return [ m[ 0 ].length, "" ]; -}; - -Markdown.buildBlockOrder ( Markdown.dialects.Maruku.block ); -Markdown.buildInlinePatterns( Markdown.dialects.Maruku.inline ); - -var isArray = Array.isArray || function(obj) { - return Object.prototype.toString.call(obj) == '[object Array]'; -}; - -var forEach; -// Don't mess with Array.prototype. Its not friendly -if ( Array.prototype.forEach ) { - forEach = function( arr, cb, thisp ) { - return arr.forEach( cb, thisp ); - }; -} -else { - forEach = function(arr, cb, thisp) { - for (var i = 0; i < arr.length; i++) { - cb.call(thisp || arr, arr[i], i, arr); - } - } -} - -function extract_attr( jsonml ) { - return isArray(jsonml) - && jsonml.length > 1 - && typeof jsonml[ 1 ] === "object" - && !( isArray(jsonml[ 1 ]) ) - ? jsonml[ 1 ] - : undefined; -} - - - -/** - * renderJsonML( jsonml[, options] ) -> String - * - jsonml (Array): JsonML array to render to XML - * - options (Object): options - * - * Converts the given JsonML into well-formed XML. - * - * The options currently understood are: - * - * - root (Boolean): wether or not the root node should be included in the - * output, or just its children. The default `false` is to not include the - * root itself. - */ -expose.renderJsonML = function( jsonml, options ) { - options = options || {}; - // include the root element in the rendered output? - options.root = options.root || false; - - var content = []; - - if ( options.root ) { - content.push( render_tree( jsonml ) ); - } - else { - jsonml.shift(); // get rid of the tag - if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { - jsonml.shift(); // get rid of the attributes - } - - while ( jsonml.length ) { - content.push( render_tree( jsonml.shift() ) ); - } - } - - return content.join( "\n\n" ); -}; - -function escapeHTML( text ) { - return text.replace( /&/g, "&" ) - .replace( //g, ">" ) - .replace( /"/g, """ ) - .replace( /'/g, "'" ); -} - -function render_tree( jsonml ) { - // basic case - if ( typeof jsonml === "string" ) { - return escapeHTML( jsonml ); - } - - var tag = jsonml.shift(), - attributes = {}, - content = []; - - if ( jsonml.length && typeof jsonml[ 0 ] === "object" && !( jsonml[ 0 ] instanceof Array ) ) { - attributes = jsonml.shift(); - } - - while ( jsonml.length ) { - content.push( arguments.callee( jsonml.shift() ) ); - } - - var tag_attrs = ""; - for ( var a in attributes ) { - tag_attrs += " " + a + '="' + escapeHTML( attributes[ a ] ) + '"'; - } - - // be careful about adding whitespace here for inline elements - if ( tag == "img" || tag == "br" || tag == "hr" ) { - return "<"+ tag + tag_attrs + "/>"; - } - else { - return "<"+ tag + tag_attrs + ">" + content.join( "" ) + ""; - } -} - -function convert_tree_to_html( tree, references, options ) { - var i; - options = options || {}; - - // shallow clone - var jsonml = tree.slice( 0 ); - - if (typeof options.preprocessTreeNode === "function") { - jsonml = options.preprocessTreeNode(jsonml, references); - } - - // Clone attributes if they exist - var attrs = extract_attr( jsonml ); - if ( attrs ) { - jsonml[ 1 ] = {}; - for ( i in attrs ) { - jsonml[ 1 ][ i ] = attrs[ i ]; - } - attrs = jsonml[ 1 ]; - } - - // basic case - if ( typeof jsonml === "string" ) { - return jsonml; - } - - // convert this node - switch ( jsonml[ 0 ] ) { - case "header": - jsonml[ 0 ] = "h" + jsonml[ 1 ].level; - delete jsonml[ 1 ].level; - break; - case "bulletlist": - jsonml[ 0 ] = "ul"; - break; - case "numberlist": - jsonml[ 0 ] = "ol"; - break; - case "listitem": - jsonml[ 0 ] = "li"; - break; - case "para": - jsonml[ 0 ] = "p"; - break; - case "markdown": - jsonml[ 0 ] = "html"; - if ( attrs ) delete attrs.references; - break; - case "code_block": - jsonml[ 0 ] = "pre"; - i = attrs ? 2 : 1; - var code = [ "code" ]; - code.push.apply( code, jsonml.splice( i ) ); - jsonml[ i ] = code; - break; - case "inlinecode": - jsonml[ 0 ] = "code"; - break; - case "img": - jsonml[ 1 ].src = jsonml[ 1 ].href; - delete jsonml[ 1 ].href; - break; - case "linebreak": - jsonml[ 0 ] = "br"; - break; - case "link": - jsonml[ 0 ] = "a"; - break; - case "link_ref": - jsonml[ 0 ] = "a"; - - // grab this ref and clean up the attribute node - var ref = Object.prototype.hasOwnProperty.call(references, attrs.ref) && references[ attrs.ref ]; - - // if the reference exists, make the link - if ( ref ) { - delete attrs.ref; - - // add in the href and title, if present - attrs.href = ref.href; - if ( ref.title ) { - attrs.title = ref.title; - } - - // get rid of the unneeded original text - delete attrs.original; - } - // the reference doesn't exist, so revert to plain text - else { - return attrs.original; - } - break; - case "img_ref": - jsonml[ 0 ] = "img"; - - // grab this ref and clean up the attribute node - var ref = Object.prototype.hasOwnProperty.call(references, attrs.ref) && references[ attrs.ref ]; - - // if the reference exists, make the link - if ( ref ) { - delete attrs.ref; - - // add in the href and title, if present - attrs.src = ref.href; - if ( ref.title ) { - attrs.title = ref.title; - } - - // get rid of the unneeded original text - delete attrs.original; - } - // the reference doesn't exist, so revert to plain text - else { - return attrs.original; - } - break; - } - - // convert all the children - i = 1; - - // deal with the attribute node, if it exists - if ( attrs ) { - // if there are keys, skip over it - for ( var key in jsonml[ 1 ] ) { - i = 2; - } - // if there aren't, remove it - if ( i === 1 ) { - jsonml.splice( i, 1 ); - } - } - - for ( ; i < jsonml.length; ++i ) { - jsonml[ i ] = arguments.callee( jsonml[ i ], references, options ); - } - - return jsonml; -} - - -// merges adjacent text nodes into a single node -function merge_text_nodes( jsonml ) { - // skip the tag name and attribute hash - var i = extract_attr( jsonml ) ? 2 : 1; - - while ( i < jsonml.length ) { - // if it's a string check the next item too - if ( typeof jsonml[ i ] === "string" ) { - if ( i + 1 < jsonml.length && typeof jsonml[ i + 1 ] === "string" ) { - // merge the second string into the first and remove it - jsonml[ i ] += jsonml.splice( i + 1, 1 )[ 0 ]; - } - else { - ++i; - } - } - // if it's not a string recurse - else { - arguments.callee( jsonml[ i ] ); - ++i; - } - } -} - -} )( (function() { - if ( typeof exports === "undefined" ) { - window.markdown = {}; - return window.markdown; - } - else { - return exports; - } -} )() ); diff --git a/node_modules/markdown/package.json b/node_modules/markdown/package.json deleted file mode 100644 index 0db4dd0a..00000000 --- a/node_modules/markdown/package.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "name": "markdown", - "version": "0.4.0", - "description": "A sensible Markdown parser for javascript", - "keywords": [ - "markdown", - "text processing", - "ast" - ], - "maintainers": [ - { - "name": "Dominic Baggott", - "email": "dominic.baggott@gmail.com", - "url": "http://evilstreak.co.uk" - }, - { - "name": "Ash Berlin", - "email": "ash_markdownjs@firemirror.com", - "url": "http://ashberlin.com" - } - ], - "contributors": [ - { - "name": "Dominic Baggott", - "email": "dominic.baggott@gmail.com", - "url": "http://evilstreak.co.uk" - }, - { - "name": "Ash Berlin", - "email": "ash_markdownjs@firemirror.com", - "url": "http://ashberlin.com" - } - ], - "bugs": { - "url": "http://github.com/evilstreak/markdown-js/issues" - }, - "licenses": [ - { - "type": "MIT", - "url": "http://www.opensource.org/licenses/mit-license.php" - } - ], - "repository": { - "type": "git", - "url": "git://github.com/evilstreak/markdown-js.git" - }, - "main": "./lib/index.js", - "bin": { - "md2html": "./bin/md2html.js" - }, - "dependencies": { - "nopt": "1" - }, - "devDependencies": { - "tap": "0" - }, - "scripts": { - "test": "tap test/*.t.js" - }, - "readme": "markdown-js\n===========\n\nYet another markdown parser, this time for JavaScript. There's a few\noptions that precede this project but they all treat markdown to HTML\nconversion as a single step process. You pass markdown in and get HTML\nout, end of story. We had some pretty particular views on how the\nprocess should actually look, which include:\n\n * producing well-formed HTML. This means that em and strong nesting is\n important, as is the ability to output as both HTML and XHTML\n\n * having an intermediate representation to allow processing of parsed\n data (we in fact have two, both [JsonML]: a markdown tree and an\n HTML tree)\n\n * being easily extensible to add new dialects without having to\n rewrite the entire parsing mechanics\n\n * having a good test suite. The only test suites we could find tested\n massive blocks of input, and passing depended on outputting the HTML\n with exactly the same whitespace as the original implementation\n\n[JsonML]: http://jsonml.org/ \"JSON Markup Language\"\n\n## Installation\n\nJust the `markdown` library:\n\n npm install markdown\n\nAlso install `md2html` to `/usr/local/bin` (or wherever)\n\n npm install -g markdown\n\n## Usage\n\nThe simple way to use it with CommonJS is:\n\n var input = \"# Heading\\n\\nParagraph\";\n var output = require( \"markdown\" ).markdown.toHTML( input );\n print( output );\n\nIf you want more control check out the documentation in\n[lib/markdown.js] which details all the methods and parameters\navailable (including examples!). One day we'll get the docs generated\nand hosted somewhere for nicer browsing.\n\nIt also works in a browser; here is a complete example:\n\n \n \n \n \n
    \n \n \n \n \n\n### md2html\n\n md2html /path/to/doc.md > /path/to/doc.html\n\n[lib/markdown.js]: http://github.com/evilstreak/markdown-js/blob/master/lib/markdown.js\n\n## Intermediate Representation\n\nInternally the process to convert a chunk of markdown into a chunk of\nHTML has three steps:\n\n 1. Parse the markdown into a JsonML tree. Any references found in the\n parsing are stored in the attribute hash of the root node under the\n key `references`.\n\n 2. Convert the markdown tree into an HTML tree. Rename any nodes that\n need it (`bulletlist` to `ul` for example) and lookup any references\n used by links or images. Remove the references attribute once done.\n\n 3. Stringify the HTML tree being careful not to wreck whitespace where\n whitespace is important (surrounding inline elements for example).\n\nEach step of this process can be called individually if you need to do\nsome processing or modification of the data at an intermediate stage.\nFor example, you may want to grab a list of all URLs linked to in the\ndocument before rendering it to HTML which you could do by recursing\nthrough the HTML tree looking for `a` nodes.\n\n## Running tests\n\nTo run the tests under node you will need tap installed (it's listed as a\ndevDependencies so `npm install` from the checkout should be enough), then do\n\n $ ./node_modules/.bin/tap test/*.t.js\n\n## Contributing\n\nDo the usual github fork and pull request dance. Add yourself to the\ncontributors section of package.json too if you want to.\n\n## License\n\nReleased under the MIT license.\n", - "readmeFilename": "README.markdown", - "_id": "markdown@0.4.0", - "dist": { - "shasum": "6a5d7cb751c1d1e6adc374d40e9d358474b59a8e" - }, - "_resolved": "git://github.com/jsdoc3/markdown-js.git#fd27f4c979f3f71e82e1fe76ffe6415980b31f00", - "_from": "git://github.com/jsdoc3/markdown-js.git" -} diff --git a/package.json b/package.json index 499204c5..60f8c11d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jsdoc", "version": "3.3.0-dev", - "revision": "1381768141793", + "revision": "1383420177733", "description": "An API documentation generator for JavaScript.", "keywords": [ "documentation", "javascript" ], "licenses": [ @@ -20,7 +20,6 @@ "crypto-browserify": "git+https://github.com/dominictarr/crypto-browserify.git#95c5d505", "js2xmlparser": "0.1.0", "jshint": "0.9.1", - "markdown": "git+https://github.com/jsdoc3/markdown-js.git", "marked": "0.2.8", "taffydb": "git+https://github.com/hegemonic/taffydb.git", "underscore": "1.4.2", diff --git a/plugins/markdown.md b/plugins/markdown.md deleted file mode 100644 index 578d751e..00000000 --- a/plugins/markdown.md +++ /dev/null @@ -1,86 +0,0 @@ -# How to use the Markdown plugin - -For most users, all you need to do is add the plugin to your JSDoc configuration (`conf.json`) as you would any other, by adding a reference to the plugin in the `"plugins"` entry: - - ... - "plugins": [ "plugins/markdown" ] - ... - -This will cause Markdown in `@description` tags (including implicit descriptions without tags), `@classdesc` tags, `@param` tags, `@property` tags, and `@returns` tags to be parsed. - -Also, be sure to use leading asterisks in your doc comments! If you omit the leading asterisks, JSDoc's code parser may remove other asterisks that are used for Markdown formatting. - -# Configuring the Markdown plugin - -The plugin also offers several configuration options for advanced users who want GitHub integration, extended tag support, etc. All configuration for the Markdown plugin should be added to a `"markdown"` property in your JSDoc configuration: - - ... - "plugins": [ "plugins/markdown" ], - - "markdown": { - "opt1": "value", - "opt2": [ "foo", "bar", "baz" ] - } - ... - -## Choosing a parser - -The plugin currently supports two Markdown parsers. You can select which parser to use by adding a `"parser"` property to your Markdown configuration: - - ... - "plugins": [ "plugins/markdown" ], - - "markdown": { - "parser": "gfm" - } - ... - -### Dominic "evilstreak" Baggott's markdown-js - -The default parser is Dominic Baggott's excellent [markdown-js](https://github.com/evilstreak/markdown-js). It can be explicitly selected by setting the `parser` to `evilstreak` and has one additional (and optional) configuration option, `dialect`, which can be used to select which of markdown-js' built-in dialects to use. If omitted, markdown-js' default dialect will be used. - - ... - "plugins": [ "plugins/markdown" ], - - "markdown": { - "parser": "evilstreak", - "dialect": "Maruku" - } - ... - -### GitHib Flavored Markdown - -The alternative parser is the modified Showdown parser supplied by GitHub for their [GitHub Flavored Markdown](http://github.github.com/github-flavored-markdown/). GFM provides several enhancements to standard Markdown syntax (see its documentation) intended to be useful to developers. It *also* has the ability to quickly link to GitHub repositories, files, and issues. It can be selected by setting the `parser` to `gfm` and supports three additional (and optional) configuration options. - -The `hardwrap` option controls the hard wrapping of line ends. Unlike standard Markdown, GFM considers a single newline to indicate a "hard break" in the paragraph, but this doesn't work well with the line length limitations commonly used with comment documentation, so is disabled by default. If you want to turn hard wrapping back on, set `hardwrap` to `true` (or any non-falsy value). - -The `githubRepoName` and `githubRepoOwner` indicate which GitHub repo should be used for GitHub links that do not fully specify a repo. These options have no effect unless used together, and if they are omitted, several of GFM's default link types will be unavailable. Conversely, if you supply both `github*` options but do not explicitly select `gfm` as your parser, it will be automatically selected for you. - - ... - "plugins": [ "plugins/markdown" ], - - "markdown": { - "parser": "gfm", - "hardwrap": true - } - ... - -### Why two parsers? - -The "evilstreak" parser is flexible, extensible, currently maintained, and was the only parser available in earlier versions of the Markdown plugin, but doesn't support the useful GFM extensions. The "gfm" parser is based on the no-longer-maintained Showdown parser, but it provides GFM extensions. - -In the future, if GFM support is made available for the "evilstreak" parser, this plugin will drop the "gfm" parser in favor of that support. - -## Extended tag support - -While the Markdown plugin already supports JSDoc's default tags, if you're using other plugins, you may well have extra tags available. You can tell the Markdown plugin to handle those extra tags as well using the `tags` property, which is an array of the tags* it should check in addition to the default set. - - ... - "plugins": [ "plugins/markdown" ], - - "markdown": { - "tags": [ "foo", "bars", "bazzes" ] - } - ... - -\* Because the Markdown plugin works with JSDoc's internal representation rather than with the source comments, the names you need to enter in the `tags` property aren't necessarily the same as the actual tag names. For example, in the default set of tags, `@param` is stored under `params`. If you are having trouble getting the Markdown plugin to work with your extra tags, either take a peek at the output of JSDoc's `--explain` command-line parameter (which displays the internal state that plugins work with) or ask the plugin author which "doclet properties" their plugin uses. The Markdown plugin supports strings, arrays, and objects/subdoclets. diff --git a/test/specs/jsdoc/tutorial.js b/test/specs/jsdoc/tutorial.js index caba78e6..7c3d62b6 100644 --- a/test/specs/jsdoc/tutorial.js +++ b/test/specs/jsdoc/tutorial.js @@ -1,4 +1,4 @@ -/*global describe: true, env: true, it: true */ +/*global describe: true, env: true, expect: true, it: true */ describe("jsdoc/tutorial", function() { var tutorial = require('jsdoc/tutorial'), name = "tuteID", @@ -209,8 +209,6 @@ describe("jsdoc/tutorial", function() { it("Tutorials with MARKDOWN type go through the markdown parser, respecting configuration options", function() { var old = env.conf.markdown; - env.conf.markdown = {parser: 'evilstreak'}; - expect(par.parse()).toBe("

    This is the parent tutorial's content & stuff AB XY

    "); env.conf.markdown = {parser: 'marked'}; expect(par.parse()).toBe("

    This is the parent tutorial's content & stuff A_B X_Y

    "); diff --git a/test/specs/jsdoc/util/markdown.js b/test/specs/jsdoc/util/markdown.js index fad1222b..ab959bab 100644 --- a/test/specs/jsdoc/util/markdown.js +++ b/test/specs/jsdoc/util/markdown.js @@ -40,7 +40,7 @@ describe('jsdoc/util/markdown', function() { } it('should retrieve a function when called with default settings', function() { - var storage = setMarkdownConf({parser: 'evilstreak'}); + var storage = setMarkdownConf({}); var parser = markdown.getParser(); expect(typeof parser).toEqual('function'); @@ -52,10 +52,10 @@ describe('jsdoc/util/markdown', function() { restoreMarkdownConf(storage); }); - it('should use the evilstreak parser when requested', function() { + it('should use the marked parser when evilstreak is requested', function() { var storage = setMarkdownConf({parser: 'evilstreak'}); var parser = markdown.getParser(); - expect(parser._parser).toEqual('markdown'); + expect(parser._parser).toEqual('marked'); restoreMarkdownConf(storage); }); @@ -73,16 +73,6 @@ describe('jsdoc/util/markdown', function() { restoreMarkdownConf(storage); }); - it('should not apply formatting to inline tags when the evilstreak parser is enabled', function() { - var storage = setMarkdownConf({parser: 'evilstreak'}); - var parser = markdown.getParser(); - - // get the evilstreak parser and do the test - expect(parser('{@link MyClass#_x} and {@link MyClass#_y}')).toEqual( - '

    {@link MyClass#_x} and {@link MyClass#_y}

    '); - restoreMarkdownConf(storage); - }); - it('should not apply formatting to inline tags when the marked parser is enabled', function() { var storage = setMarkdownConf({parser: 'marked'}); var parser = markdown.getParser(); From 4e93dcbd6c00b801b0eec705733bf31b08ac5b55 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 2 Nov 2013 12:38:45 -0700 Subject: [PATCH 5/9] whitespace --- test/specs/jsdoc/util/markdown.js | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/specs/jsdoc/util/markdown.js b/test/specs/jsdoc/util/markdown.js index ab959bab..c1114631 100644 --- a/test/specs/jsdoc/util/markdown.js +++ b/test/specs/jsdoc/util/markdown.js @@ -1,18 +1,18 @@ /*global describe: true, env: true, expect: true, it: true, xit: true */ describe('jsdoc/util/markdown', function() { - var markdown = require('jsdoc/util/markdown'); + var markdown = require('jsdoc/util/markdown'); - it('should exist', function() { - expect(markdown).toBeDefined(); - expect(typeof markdown).toEqual('object'); - }); + it('should exist', function() { + expect(markdown).toBeDefined(); + expect(typeof markdown).toEqual('object'); + }); - it('should export a "getParser" function', function() { - expect(markdown.getParser).toBeDefined(); - expect(typeof markdown.getParser).toEqual('function'); - }); + it('should export a "getParser" function', function() { + expect(markdown.getParser).toBeDefined(); + expect(typeof markdown.getParser).toEqual('function'); + }); - describe('getParser', function() { + describe('getParser', function() { // couple of convenience functions letting me set conf variables and restore // them back to the originals later. function setMarkdownConf(hash) { @@ -39,7 +39,7 @@ describe('jsdoc/util/markdown', function() { } } - it('should retrieve a function when called with default settings', function() { + it('should retrieve a function when called with default settings', function() { var storage = setMarkdownConf({}); var parser = markdown.getParser(); @@ -50,21 +50,21 @@ describe('jsdoc/util/markdown', function() { expect(typeof parser).toEqual('function'); restoreMarkdownConf(storage); - }); + }); - it('should use the marked parser when evilstreak is requested', function() { + it('should use the marked parser when evilstreak is requested', function() { var storage = setMarkdownConf({parser: 'evilstreak'}); var parser = markdown.getParser(); expect(parser._parser).toEqual('marked'); restoreMarkdownConf(storage); - }); + }); - it('should use the marked parser when requested', function() { + it('should use the marked parser when requested', function() { var storage = setMarkdownConf({parser: 'marked'}); var parser = markdown.getParser(); expect(parser._parser).toEqual('marked'); restoreMarkdownConf(storage); - }); + }); it('should use the marked parser when GFM is requested', function() { var storage = setMarkdownConf({parser: 'gfm'}); From b81de7863d6509b3f8c6e2a2ac4bd4304b454de6 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sat, 2 Nov 2013 21:37:17 -0700 Subject: [PATCH 6/9] use an empty excludePattern if the config file does not specify one (#508) --- lib/jsdoc/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jsdoc/config.js b/lib/jsdoc/config.js index 01edc2c2..8119cbbf 100644 --- a/lib/jsdoc/config.js +++ b/lib/jsdoc/config.js @@ -33,7 +33,7 @@ const defaults = { }, "source": { "includePattern": ".+\\.js(doc)?$", - "excludePattern": "(^|\\/|\\\\)_" + "excludePattern": "" }, "plugins": [] }; From a3d3384293d1318c3dfc64d04d85a05d2dd84677 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 4 Nov 2013 07:12:23 -0800 Subject: [PATCH 7/9] resolve paths relative to the user's working directory --- jsdoc.cmd | 3 +++ lib/jsdoc/src/filter.js | 8 ++++---- lib/jsdoc/src/scanner.js | 6 +++--- test/specs/jsdoc/src/filter.js | 2 +- test/specs/jsdoc/src/scanner.js | 4 ++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/jsdoc.cmd b/jsdoc.cmd index 9bd2a562..f81212d1 100644 --- a/jsdoc.cmd +++ b/jsdoc.cmd @@ -10,6 +10,9 @@ SET _BASEPATH=%_BASEPATH:~0,-1% REM for whatever reason, Rhino requires module paths to be valid URIs SET _URLPATH=file:/%_BASEPATH% +REM we need the ability to resolve paths relative to the user's pwd +SET PWD=%cd% + IF "%_URLPATH%"=="%_URLPATH: =%" GOTO NO_SPACES :ESCAPE_SPACE SET _TRAILING=%_URLPATH:* =% diff --git a/lib/jsdoc/src/filter.js b/lib/jsdoc/src/filter.js index 4bd18055..c9e18464 100644 --- a/lib/jsdoc/src/filter.js +++ b/lib/jsdoc/src/filter.js @@ -7,6 +7,8 @@ var path = require('jsdoc/path'); +var pwd = process.env.PWD; + /** @constructor @param {object} opts @@ -15,11 +17,9 @@ var path = require('jsdoc/path'); @param {string|RegExp} opts.excludePattern */ exports.Filter = function(opts) { - var cwd = process.cwd(); - this.exclude = opts.exclude && Array.isArray(opts.exclude) ? opts.exclude.map(function($) { - return path.resolve(cwd, $); + return path.resolve(pwd, $); }) : null; this.includePattern = opts.includePattern? @@ -35,7 +35,7 @@ exports.Filter = function(opts) { @returns {boolean} Should the given file be included? */ exports.Filter.prototype.isIncluded = function(filepath) { - filepath = path.resolve(process.cwd(), filepath); + filepath = path.resolve(pwd, filepath); if ( this.includePattern && !this.includePattern.test(filepath) ) { return false; diff --git a/lib/jsdoc/src/scanner.js b/lib/jsdoc/src/scanner.js index 914cf7fe..94d02af2 100644 --- a/lib/jsdoc/src/scanner.js +++ b/lib/jsdoc/src/scanner.js @@ -24,7 +24,7 @@ exports.Scanner.prototype = Object.create( require('events').EventEmitter.protot @fires sourceFileFound */ exports.Scanner.prototype.scan = function(searchPaths, depth, filter) { - var cwd = process.cwd(), + var pwd = process.env.PWD, filePaths = [], self = this; @@ -34,11 +34,11 @@ exports.Scanner.prototype.scan = function(searchPaths, depth, filter) { searchPaths.forEach(function($) { var filepath = decodeURIComponent($); if ( fs.statSync(filepath).isFile() ) { - filePaths.push( path.resolve(cwd, filepath) ); + filePaths.push( path.resolve(pwd, filepath) ); } else { filePaths = filePaths.concat( fs.ls(filepath, depth).map(function(item) { - return path.resolve(cwd, item); + return path.resolve(pwd, item); }) ); } }); diff --git a/test/specs/jsdoc/src/filter.js b/test/specs/jsdoc/src/filter.js index 98593378..6a983bbc 100644 --- a/test/specs/jsdoc/src/filter.js +++ b/test/specs/jsdoc/src/filter.js @@ -6,7 +6,7 @@ describe("jsdoc/src/filter", function() { exclude: ['.ignore', 'scratch/conf.js'] }); - var files = ['yes.js', '/yes.jsdoc', '/_nope.js', '.ignore', process.cwd() + '/scratch/conf.js']; + var files = ['yes.js', '/yes.jsdoc', '/_nope.js', '.ignore', process.env.PWD + '/scratch/conf.js']; files = files.filter(function($) { return filter.isIncluded($); diff --git a/test/specs/jsdoc/src/scanner.js b/test/specs/jsdoc/src/scanner.js index 3203c2aa..a36b9a8c 100644 --- a/test/specs/jsdoc/src/scanner.js +++ b/test/specs/jsdoc/src/scanner.js @@ -6,10 +6,10 @@ describe("jsdoc/src/scanner", function() { excludePattern: new RegExp("(^|\\/|\\\\)_") }), path = require('path'), - sourceFiles = scanner.scan([path.join(__dirname, 'test', 'fixtures', 'src')], 3, filter); + sourceFiles = scanner.scan([path.join(process.env.PWD, 'test', 'fixtures', 'src')], 3, filter); sourceFiles = sourceFiles.map(function($) { - return path.relative(__dirname, $); + return path.relative(process.env.PWD, $); }); it("should return the correct source files", function() { From 01882a231bb8e14b09b4f0577bf3374b31a6f088 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 4 Nov 2013 07:51:23 -0800 Subject: [PATCH 8/9] test for variable-scoping issue in JSDoc 3.2.1 (#513) --- test/fixtures/exportstag5.js | 13 +++++++++++++ test/specs/tags/exportstag.js | 12 +++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/exportstag5.js diff --git a/test/fixtures/exportstag5.js b/test/fixtures/exportstag5.js new file mode 100644 index 00000000..b2c5fc8e --- /dev/null +++ b/test/fixtures/exportstag5.js @@ -0,0 +1,13 @@ +define(function() { + 'use strict'; + var bar = function() {}; + /** @exports Foo */ + var Foo = Object.create( + /** @lends module:Foo.prototype */ + { + /** This should be in the Foo module doc. */ + bar : function() {} + } + ); + return Foo; +}); diff --git a/test/specs/tags/exportstag.js b/test/specs/tags/exportstag.js index 6d422754..c1e06a04 100644 --- a/test/specs/tags/exportstag.js +++ b/test/specs/tags/exportstag.js @@ -1,3 +1,4 @@ +/*global describe: true, expect: true, it: true, jasmine: true */ describe("@exports tag", function() { describe("object literals", function() { @@ -86,4 +87,13 @@ describe("@exports tag", function() { //expect(inhead.memberof, 'module:html/utils'); }); }); -}); \ No newline at end of file + + describe('variable shadowing', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/exportstag5.js'); + var method = docSet.getByLongname('module:Foo#bar')[0]; + + it('A variable defined in an inner scope should correctly shadow a variable in an outer scope.', function() { + expect(method.description).toBe('This should be in the Foo module doc.'); + }); + }); +}); From a8c910090d2b24eae5cc20450a5754208a3db21f Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Mon, 4 Nov 2013 08:11:59 -0800 Subject: [PATCH 9/9] 3.2.2 changelog --- changes.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/changes.md b/changes.md index 4fc58726..792fa130 100644 --- a/changes.md +++ b/changes.md @@ -3,6 +3,17 @@ This file describes notable changes in each version of JSDoc 3. To download a specific version of JSDoc 3, see [GitHub's tags page](https://github.com/jsdoc3/jsdoc/tags). +## 3.2.2 (November 2013) + +### Bug fixes ++ Addressed a regression in JSDoc 3.2.1 that could prevent a function declaration from shadowing a declaration with the same name in an outer scope. (#513) ++ If a child class overrides a method in a parent class without documenting the overridden method, the method's documentation is now copied from the parent class. (#503) ++ You can now use inline HTML tags in Markdown-formatted text. In addition, JSDoc now uses only the [marked Markdown parser](https://github.com/chjj/marked); the markdown-js parser has been removed. (#510) ++ Type expressions can now include a much broader range of repeatable types. In addition, you can now use Closure Compiler's nullable and non-nullable modifiers with repeatable types. For example, the type expression `...!string` (a repeatable, non-nullable string) is now parsed correctly. (#502) ++ If a function accepts a parameter named `prototype`, the parameter is no longer renamed during parsing. (#505) ++ If the list of input files includes relative paths, the paths are now resolved relative to the user's working directory. (a3d33842) + + ## 3.2.1 (October 2013) ### Enhancements