mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
feat(metadata): add support for embedded metadata
A simple metadata parser can be useful in markdown documents. This commit introduces the feature, with the following syntax: --- or ««« at tstart of the document, (optionally) followed by a alphanumeric format identifier followed by key value pairs separated by a colon and a space followed by --- or  Also, adds methods for retrieving the parsed metadata, namely: getMetadata() and getMetadataFormat Closes #260
This commit is contained in:
parent
a8427c9423
commit
63d949f731
182
dist/showdown.js
vendored
182
dist/showdown.js
vendored
@ -153,10 +153,15 @@ function getDefaultOpts (simple) {
|
|||||||
description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
|
description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
},
|
},
|
||||||
completeHTMLOutput: {
|
completeHTMLDocument: {
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
|
description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
defaultValue: false,
|
||||||
|
description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
|
||||||
|
type: 'boolean'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (simple === false) {
|
if (simple === false) {
|
||||||
@ -2172,7 +2177,17 @@ showdown.Converter = function (converterOptions) {
|
|||||||
/**
|
/**
|
||||||
* The flavor set in this converter
|
* The flavor set in this converter
|
||||||
*/
|
*/
|
||||||
setConvFlavor = setFlavor;
|
setConvFlavor = setFlavor,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata of the document
|
||||||
|
* @type {{parsed: {}, raw: string, format: string}}
|
||||||
|
*/
|
||||||
|
metadata = {
|
||||||
|
parsed: {},
|
||||||
|
raw: '',
|
||||||
|
format: ''
|
||||||
|
};
|
||||||
|
|
||||||
_constructor();
|
_constructor();
|
||||||
|
|
||||||
@ -2384,7 +2399,12 @@ showdown.Converter = function (converterOptions) {
|
|||||||
langExtensions: langExtensions,
|
langExtensions: langExtensions,
|
||||||
outputModifiers: outputModifiers,
|
outputModifiers: outputModifiers,
|
||||||
converter: this,
|
converter: this,
|
||||||
ghCodeBlocks: []
|
ghCodeBlocks: [],
|
||||||
|
metadata: {
|
||||||
|
parsed: {},
|
||||||
|
raw: '',
|
||||||
|
format: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lets us use ¨ trema as an escape char to avoid md5 hashes
|
// This lets us use ¨ trema as an escape char to avoid md5 hashes
|
||||||
@ -2428,6 +2448,7 @@ showdown.Converter = function (converterOptions) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// run the sub parsers
|
// run the sub parsers
|
||||||
|
text = showdown.subParser('metadata')(text, options, globals);
|
||||||
text = showdown.subParser('hashPreCodeTags')(text, options, globals);
|
text = showdown.subParser('hashPreCodeTags')(text, options, globals);
|
||||||
text = showdown.subParser('githubCodeBlocks')(text, options, globals);
|
text = showdown.subParser('githubCodeBlocks')(text, options, globals);
|
||||||
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
|
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
|
||||||
@ -2444,13 +2465,15 @@ showdown.Converter = function (converterOptions) {
|
|||||||
text = text.replace(/¨T/g, '¨');
|
text = text.replace(/¨T/g, '¨');
|
||||||
|
|
||||||
// render a complete html document instead of a partial if the option is enabled
|
// render a complete html document instead of a partial if the option is enabled
|
||||||
text = showdown.subParser('completeHTMLOutput')(text, options, globals);
|
text = showdown.subParser('completeHTMLDocument')(text, options, globals);
|
||||||
|
|
||||||
// Run output modifiers
|
// Run output modifiers
|
||||||
showdown.helper.forEach(outputModifiers, function (ext) {
|
showdown.helper.forEach(outputModifiers, function (ext) {
|
||||||
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update metadata
|
||||||
|
metadata = globals.metadata;
|
||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2558,6 +2581,52 @@ showdown.Converter = function (converterOptions) {
|
|||||||
output: outputModifiers
|
output: outputModifiers
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the metadata of the previously parsed document
|
||||||
|
* @param raw
|
||||||
|
* @returns {string|{}}
|
||||||
|
*/
|
||||||
|
this.getMetadata = function (raw) {
|
||||||
|
if (raw) {
|
||||||
|
return metadata.raw;
|
||||||
|
} else {
|
||||||
|
return metadata.parsed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the metadata format of the previously parsed document
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
this.getMetadataFormat = function () {
|
||||||
|
return metadata.format;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private: set a single key, value metadata pair
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
this._setMetadataPair = function (key, value) {
|
||||||
|
metadata.parsed[key] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private: set metadata format
|
||||||
|
* @param {string} format
|
||||||
|
*/
|
||||||
|
this._setMetadataFormat = function (format) {
|
||||||
|
metadata.format = format;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private: set metadata raw text
|
||||||
|
* @param {string} raw
|
||||||
|
*/
|
||||||
|
this._setMetadataRaw = function (raw) {
|
||||||
|
metadata.raw = raw;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2899,18 +2968,63 @@ showdown.subParser('codeSpans', function (text, options, globals) {
|
|||||||
/**
|
/**
|
||||||
* Turn Markdown link shortcuts into XHTML <a> tags.
|
* Turn Markdown link shortcuts into XHTML <a> tags.
|
||||||
*/
|
*/
|
||||||
showdown.subParser('completeHTMLOutput', function (text, options, globals) {
|
showdown.subParser('completeHTMLDocument', function (text, options, globals) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
if (!options.completeHTMLOutput) {
|
if (!options.completeHTMLDocument) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
text = globals.converter._dispatch('completeHTMLOutput.before', text, options, globals);
|
text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
|
||||||
|
|
||||||
text = '<html>\n<head>\n<meta charset="UTF-8">\n</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
|
var doctype = 'html',
|
||||||
|
doctypeParsed = '<!DOCTYPE HTML>\n',
|
||||||
|
title = '',
|
||||||
|
charset = '<meta charset="utf-8">\n',
|
||||||
|
lang = '',
|
||||||
|
metadata = '';
|
||||||
|
|
||||||
text = globals.converter._dispatch('completeHTMLOutput.after', text, options, globals);
|
if (typeof globals.metadata.parsed.doctype !== 'undefined') {
|
||||||
|
doctypeParsed = '<!DOCTYPE ' + globals.metadata.parsed.doctype + '>\n';
|
||||||
|
doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
|
||||||
|
if (doctype === 'html' || doctype === 'html5') {
|
||||||
|
charset = '<meta charset="utf-8">';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var meta in globals.metadata.parsed) {
|
||||||
|
if (globals.metadata.parsed.hasOwnProperty(meta)) {
|
||||||
|
switch (meta.toLowerCase()) {
|
||||||
|
case 'doctype':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'title':
|
||||||
|
title = '<title>' + globals.metadata.parsed.title + '</title>\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'charset':
|
||||||
|
if (doctype === 'html' || doctype === 'html5') {
|
||||||
|
charset = '<meta charset="' + globals.metadata.parsed.charset + '">\n';
|
||||||
|
} else {
|
||||||
|
charset = '<meta name="charset" content="' + globals.metadata.parsed.charset + '">\n';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'language':
|
||||||
|
case 'lang':
|
||||||
|
lang = ' lang="' + globals.metadata.parsed[meta] + '"';
|
||||||
|
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
|
||||||
|
|
||||||
|
text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
|
||||||
return text;
|
return text;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3886,6 +4000,56 @@ showdown.subParser('lists', function (text, options, globals) {
|
|||||||
return text;
|
return text;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse metadata at the top of the document
|
||||||
|
*/
|
||||||
|
showdown.subParser('metadata', function (text, options, globals) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (!options.metadata) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = globals.converter._dispatch('metadata.before', text, options, globals);
|
||||||
|
|
||||||
|
function parseMetadataContents (content) {
|
||||||
|
// raw is raw so it's not changed in any way
|
||||||
|
globals.metadata.raw = content;
|
||||||
|
|
||||||
|
// escape chars forbidden in html attributes
|
||||||
|
// double quotes
|
||||||
|
content = content
|
||||||
|
// ampersand first
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
// double quotes
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
|
||||||
|
content = content.replace(/\n {4}/g, ' ');
|
||||||
|
content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
|
||||||
|
globals.metadata.parsed[key] = value;
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
|
||||||
|
parseMetadataContents(content);
|
||||||
|
return '¨M';
|
||||||
|
});
|
||||||
|
|
||||||
|
text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
|
||||||
|
if (format) {
|
||||||
|
globals.metadata.format = format;
|
||||||
|
}
|
||||||
|
parseMetadataContents(content);
|
||||||
|
return '¨M';
|
||||||
|
});
|
||||||
|
|
||||||
|
text = text.replace(/¨M/g, '');
|
||||||
|
|
||||||
|
text = globals.converter._dispatch('metadata.after', text, options, globals);
|
||||||
|
return text;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove one level of line-leading tabs or spaces
|
* Remove one level of line-leading tabs or spaces
|
||||||
*/
|
*/
|
||||||
|
2
dist/showdown.js.map
vendored
2
dist/showdown.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/showdown.min.js
vendored
2
dist/showdown.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/showdown.min.js.map
vendored
2
dist/showdown.min.js.map
vendored
File diff suppressed because one or more lines are too long
@ -43,7 +43,17 @@ showdown.Converter = function (converterOptions) {
|
|||||||
/**
|
/**
|
||||||
* The flavor set in this converter
|
* The flavor set in this converter
|
||||||
*/
|
*/
|
||||||
setConvFlavor = setFlavor;
|
setConvFlavor = setFlavor,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata of the document
|
||||||
|
* @type {{parsed: {}, raw: string, format: string}}
|
||||||
|
*/
|
||||||
|
metadata = {
|
||||||
|
parsed: {},
|
||||||
|
raw: '',
|
||||||
|
format: ''
|
||||||
|
};
|
||||||
|
|
||||||
_constructor();
|
_constructor();
|
||||||
|
|
||||||
@ -255,7 +265,12 @@ showdown.Converter = function (converterOptions) {
|
|||||||
langExtensions: langExtensions,
|
langExtensions: langExtensions,
|
||||||
outputModifiers: outputModifiers,
|
outputModifiers: outputModifiers,
|
||||||
converter: this,
|
converter: this,
|
||||||
ghCodeBlocks: []
|
ghCodeBlocks: [],
|
||||||
|
metadata: {
|
||||||
|
parsed: {},
|
||||||
|
raw: '',
|
||||||
|
format: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lets us use ¨ trema as an escape char to avoid md5 hashes
|
// This lets us use ¨ trema as an escape char to avoid md5 hashes
|
||||||
@ -299,6 +314,7 @@ showdown.Converter = function (converterOptions) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// run the sub parsers
|
// run the sub parsers
|
||||||
|
text = showdown.subParser('metadata')(text, options, globals);
|
||||||
text = showdown.subParser('hashPreCodeTags')(text, options, globals);
|
text = showdown.subParser('hashPreCodeTags')(text, options, globals);
|
||||||
text = showdown.subParser('githubCodeBlocks')(text, options, globals);
|
text = showdown.subParser('githubCodeBlocks')(text, options, globals);
|
||||||
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
|
text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
|
||||||
@ -315,13 +331,15 @@ showdown.Converter = function (converterOptions) {
|
|||||||
text = text.replace(/¨T/g, '¨');
|
text = text.replace(/¨T/g, '¨');
|
||||||
|
|
||||||
// render a complete html document instead of a partial if the option is enabled
|
// render a complete html document instead of a partial if the option is enabled
|
||||||
text = showdown.subParser('completeHTMLOutput')(text, options, globals);
|
text = showdown.subParser('completeHTMLDocument')(text, options, globals);
|
||||||
|
|
||||||
// Run output modifiers
|
// Run output modifiers
|
||||||
showdown.helper.forEach(outputModifiers, function (ext) {
|
showdown.helper.forEach(outputModifiers, function (ext) {
|
||||||
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// update metadata
|
||||||
|
metadata = globals.metadata;
|
||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -429,4 +447,50 @@ showdown.Converter = function (converterOptions) {
|
|||||||
output: outputModifiers
|
output: outputModifiers
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the metadata of the previously parsed document
|
||||||
|
* @param raw
|
||||||
|
* @returns {string|{}}
|
||||||
|
*/
|
||||||
|
this.getMetadata = function (raw) {
|
||||||
|
if (raw) {
|
||||||
|
return metadata.raw;
|
||||||
|
} else {
|
||||||
|
return metadata.parsed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the metadata format of the previously parsed document
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
this.getMetadataFormat = function () {
|
||||||
|
return metadata.format;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private: set a single key, value metadata pair
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
this._setMetadataPair = function (key, value) {
|
||||||
|
metadata.parsed[key] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private: set metadata format
|
||||||
|
* @param {string} format
|
||||||
|
*/
|
||||||
|
this._setMetadataFormat = function (format) {
|
||||||
|
metadata.format = format;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private: set metadata raw text
|
||||||
|
* @param {string} raw
|
||||||
|
*/
|
||||||
|
this._setMetadataRaw = function (raw) {
|
||||||
|
metadata.raw = raw;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -151,10 +151,15 @@ function getDefaultOpts (simple) {
|
|||||||
description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
|
description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
},
|
},
|
||||||
completeHTMLOutput: {
|
completeHTMLDocument: {
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
|
description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
defaultValue: false,
|
||||||
|
description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
|
||||||
|
type: 'boolean'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (simple === false) {
|
if (simple === false) {
|
||||||
|
62
src/subParsers/completeHTMLDocument.js
Normal file
62
src/subParsers/completeHTMLDocument.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Turn Markdown link shortcuts into XHTML <a> tags.
|
||||||
|
*/
|
||||||
|
showdown.subParser('completeHTMLDocument', function (text, options, globals) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (!options.completeHTMLDocument) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = globals.converter._dispatch('completeHTMLDocument.before', text, options, globals);
|
||||||
|
|
||||||
|
var doctype = 'html',
|
||||||
|
doctypeParsed = '<!DOCTYPE HTML>\n',
|
||||||
|
title = '',
|
||||||
|
charset = '<meta charset="utf-8">\n',
|
||||||
|
lang = '',
|
||||||
|
metadata = '';
|
||||||
|
|
||||||
|
if (typeof globals.metadata.parsed.doctype !== 'undefined') {
|
||||||
|
doctypeParsed = '<!DOCTYPE ' + globals.metadata.parsed.doctype + '>\n';
|
||||||
|
doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
|
||||||
|
if (doctype === 'html' || doctype === 'html5') {
|
||||||
|
charset = '<meta charset="utf-8">';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var meta in globals.metadata.parsed) {
|
||||||
|
if (globals.metadata.parsed.hasOwnProperty(meta)) {
|
||||||
|
switch (meta.toLowerCase()) {
|
||||||
|
case 'doctype':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'title':
|
||||||
|
title = '<title>' + globals.metadata.parsed.title + '</title>\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'charset':
|
||||||
|
if (doctype === 'html' || doctype === 'html5') {
|
||||||
|
charset = '<meta charset="' + globals.metadata.parsed.charset + '">\n';
|
||||||
|
} else {
|
||||||
|
charset = '<meta name="charset" content="' + globals.metadata.parsed.charset + '">\n';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'language':
|
||||||
|
case 'lang':
|
||||||
|
lang = ' lang="' + globals.metadata.parsed[meta] + '"';
|
||||||
|
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
|
||||||
|
|
||||||
|
text = globals.converter._dispatch('completeHTMLDocument.after', text, options, globals);
|
||||||
|
return text;
|
||||||
|
});
|
@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Turn Markdown link shortcuts into XHTML <a> tags.
|
|
||||||
*/
|
|
||||||
showdown.subParser('completeHTMLOutput', function (text, options, globals) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
if (!options.completeHTMLOutput) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
text = globals.converter._dispatch('completeHTMLOutput.before', text, options, globals);
|
|
||||||
|
|
||||||
text = '<html>\n<head>\n<meta charset="UTF-8">\n</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
|
|
||||||
|
|
||||||
text = globals.converter._dispatch('completeHTMLOutput.after', text, options, globals);
|
|
||||||
return text;
|
|
||||||
});
|
|
49
src/subParsers/metadata.js
Normal file
49
src/subParsers/metadata.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Parse metadata at the top of the document
|
||||||
|
*/
|
||||||
|
showdown.subParser('metadata', function (text, options, globals) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (!options.metadata) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = globals.converter._dispatch('metadata.before', text, options, globals);
|
||||||
|
|
||||||
|
function parseMetadataContents (content) {
|
||||||
|
// raw is raw so it's not changed in any way
|
||||||
|
globals.metadata.raw = content;
|
||||||
|
|
||||||
|
// escape chars forbidden in html attributes
|
||||||
|
// double quotes
|
||||||
|
content = content
|
||||||
|
// ampersand first
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
// double quotes
|
||||||
|
.replace(/"/g, '"');
|
||||||
|
|
||||||
|
content = content.replace(/\n {4}/g, ' ');
|
||||||
|
content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
|
||||||
|
globals.metadata.parsed[key] = value;
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
|
||||||
|
parseMetadataContents(content);
|
||||||
|
return '¨M';
|
||||||
|
});
|
||||||
|
|
||||||
|
text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
|
||||||
|
if (format) {
|
||||||
|
globals.metadata.format = format;
|
||||||
|
}
|
||||||
|
parseMetadataContents(content);
|
||||||
|
return '¨M';
|
||||||
|
});
|
||||||
|
|
||||||
|
text = text.replace(/¨M/g, '');
|
||||||
|
|
||||||
|
text = globals.converter._dispatch('metadata.after', text, options, globals);
|
||||||
|
return text;
|
||||||
|
});
|
@ -1,14 +1,15 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>This is a <strong>markdown</strong> file</p>
|
<p>This is a <strong>markdown</strong> file</p>
|
||||||
<p>Converted into a full HTML document</p>
|
<p>Converted into a full HTML document</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>this</li>
|
<li>this</li>
|
||||||
<li>is</li>
|
<li>is</li>
|
||||||
<li>awesome</li>
|
<li>awesome</li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
8
test/features/metadata/dashes-conflict.html
Normal file
8
test/features/metadata/dashes-conflict.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<p><strong>some</strong> markdown text</p>
|
||||||
|
<ul>
|
||||||
|
<li>a list</li>
|
||||||
|
<li>another list ---</li>
|
||||||
|
<li>and stuff</li>
|
||||||
|
</ul>
|
||||||
|
<p>a paragraph --- with dashes</p>
|
||||||
|
<hr />
|
16
test/features/metadata/dashes-conflict.md
Normal file
16
test/features/metadata/dashes-conflict.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
|
||||||
|
---
|
||||||
|
**some** markdown text
|
||||||
|
|
||||||
|
- a list
|
||||||
|
- another list ---
|
||||||
|
- and stuff
|
||||||
|
|
||||||
|
a paragraph --- with dashes
|
||||||
|
|
||||||
|
---
|
16
test/features/metadata/embeded-in-output.html
Normal file
16
test/features/metadata/embeded-in-output.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>This is the document title</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="language" content="en">
|
||||||
|
<meta name="author" content="Tivie">
|
||||||
|
<meta name="contributors" content="John, Mary, Steve">
|
||||||
|
<meta name="description" content="This is a long text and so it spans on multiple lines. It must be indented, for showdown to parse it correctly. Markdown **such as bold** is not parsed and it will be rendered as plain text.">
|
||||||
|
<meta name="date" content="01-01-2010">
|
||||||
|
<meta name="keywords" content="foo, bar, baz">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p><strong>some</strong> markdown text</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
test/features/metadata/embeded-in-output.md
Normal file
16
test/features/metadata/embeded-in-output.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
«««
|
||||||
|
title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
contributors: John, Mary, Steve
|
||||||
|
description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown **such as bold** is not parsed
|
||||||
|
and it will be rendered as plain text.
|
||||||
|
date: 01-01-2010
|
||||||
|
keywords: foo, bar, baz
|
||||||
|
»»»
|
||||||
|
|
||||||
|
**some** markdown text
|
@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>This is the document title</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="language" content="en">
|
||||||
|
<meta name="author" content="Tivie">
|
||||||
|
<meta name="contributors" content="John, Mary, Steve">
|
||||||
|
<meta name="keywords" content="foo, bar, baz">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<hr />
|
||||||
|
<p>description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown <strong>such as bold</strong> is not parsed
|
||||||
|
and it will be rendered as plain text.</p>
|
||||||
|
<h2 id="date01012010">date: 01-01-2010</h2>
|
||||||
|
<p><strong>some</strong> markdown text</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,18 @@
|
|||||||
|
«««
|
||||||
|
title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
contributors: John, Mary, Steve
|
||||||
|
keywords: foo, bar, baz
|
||||||
|
»»»
|
||||||
|
---
|
||||||
|
description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown **such as bold** is not parsed
|
||||||
|
and it will be rendered as plain text.
|
||||||
|
date: 01-01-2010
|
||||||
|
---
|
||||||
|
|
||||||
|
**some** markdown text
|
@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>This is the document title</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="language" content="en">
|
||||||
|
<meta name="author" content="Tivie">
|
||||||
|
<meta name="contributors" content="John, Mary, Steve">
|
||||||
|
<meta name="keywords" content="foo, bar, baz">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<hr />
|
||||||
|
<p>description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown <strong>such as bold</strong> is not parsed
|
||||||
|
and it will be rendered as plain text.</p>
|
||||||
|
<h2 id="date01012010">date: 01-01-2010</h2>
|
||||||
|
<p><strong>some</strong> markdown text</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
contributors: John, Mary, Steve
|
||||||
|
keywords: foo, bar, baz
|
||||||
|
---
|
||||||
|
---
|
||||||
|
description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown **such as bold** is not parsed
|
||||||
|
and it will be rendered as plain text.
|
||||||
|
date: 01-01-2010
|
||||||
|
---
|
||||||
|
|
||||||
|
**some** markdown text
|
15
test/features/metadata/ignore-metadata.html
Normal file
15
test/features/metadata/ignore-metadata.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<hr />
|
||||||
|
<p>title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
contributors: John, Mary, Steve
|
||||||
|
description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown <strong>such as bold</strong> is not parsed
|
||||||
|
and it will be rendered as plain text.
|
||||||
|
date: 01-01-2010
|
||||||
|
keywords: foo, bar, baz</p>
|
||||||
|
<hr />
|
||||||
|
<p><strong>some</strong> markdown text</p>
|
18
test/features/metadata/ignore-metadata.md
Normal file
18
test/features/metadata/ignore-metadata.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
contributors: John, Mary, Steve
|
||||||
|
description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown **such as bold** is not parsed
|
||||||
|
and it will be rendered as plain text.
|
||||||
|
date: 01-01-2010
|
||||||
|
keywords: foo, bar, baz
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**some** markdown text
|
1
test/features/metadata/simple-angled-for-method.html
Normal file
1
test/features/metadata/simple-angled-for-method.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>some <strong>text</strong></p>
|
6
test/features/metadata/simple-angled-for-method.md
Normal file
6
test/features/metadata/simple-angled-for-method.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
«««
|
||||||
|
foo: bar
|
||||||
|
baz: bazinga
|
||||||
|
»»»
|
||||||
|
|
||||||
|
some **text**
|
1
test/features/metadata/simple-angled-quotes.html
Normal file
1
test/features/metadata/simple-angled-quotes.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p><strong>some</strong> markdown text</p>
|
16
test/features/metadata/simple-angled-quotes.md
Normal file
16
test/features/metadata/simple-angled-quotes.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
«««
|
||||||
|
title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
contributors: John, Mary, Steve
|
||||||
|
description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown **such as bold** is not parsed
|
||||||
|
and it will be rendered as plain text.
|
||||||
|
date: 01-01-2010
|
||||||
|
keywords: foo, bar, baz
|
||||||
|
»»»
|
||||||
|
|
||||||
|
**some** markdown text
|
1
test/features/metadata/simple-three-dashes.html
Normal file
1
test/features/metadata/simple-three-dashes.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p><strong>some</strong> markdown text</p>
|
16
test/features/metadata/simple-three-dashes.md
Normal file
16
test/features/metadata/simple-three-dashes.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: This is the document title
|
||||||
|
language: en
|
||||||
|
author: Tivie
|
||||||
|
contributors: John, Mary, Steve
|
||||||
|
description: This is a long text and so it
|
||||||
|
spans on multiple lines.
|
||||||
|
It must be indented,
|
||||||
|
for showdown to parse it correctly.
|
||||||
|
Markdown **such as bold** is not parsed
|
||||||
|
and it will be rendered as plain text.
|
||||||
|
date: 01-01-2010
|
||||||
|
keywords: foo, bar, baz
|
||||||
|
---
|
||||||
|
|
||||||
|
**some** markdown text
|
1
test/features/metadata/simple-with-format.html
Normal file
1
test/features/metadata/simple-with-format.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p><strong>some</strong> markdown text</p>
|
9
test/features/metadata/simple-with-format.md
Normal file
9
test/features/metadata/simple-with-format.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---YAML
|
||||||
|
foo: bar
|
||||||
|
baz:
|
||||||
|
- bazinga
|
||||||
|
- bling
|
||||||
|
- blang
|
||||||
|
---
|
||||||
|
|
||||||
|
**some** markdown text
|
@ -27,6 +27,20 @@ describe('showdown.Converter', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('metadata methods', function () {
|
||||||
|
var converter = new showdown.Converter();
|
||||||
|
|
||||||
|
it('_setMetadataPair() should set foo to bar', function () {
|
||||||
|
converter._setMetadataPair('foo', 'bar');
|
||||||
|
converter.getMetadata().should.eql({foo: 'bar'});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('_setMetadata should set metadata to {baz: bazinga}', function () {
|
||||||
|
converter._setMetadataRaw('{baz: bazinga}');
|
||||||
|
converter.getMetadata(true).should.eql('{baz: bazinga}');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('converter.setFlavor()', function () {
|
describe('converter.setFlavor()', function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,4 +59,41 @@ describe('showdown.Converter', function () {
|
|||||||
html.should.equal(expectedHtml);
|
html.should.equal(expectedHtml);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('makeHtml() with option metadata', function () {
|
||||||
|
var converter = new showdown.Converter(),
|
||||||
|
text1 =
|
||||||
|
'---SIMPLE\n' +
|
||||||
|
'foo: bar\n' +
|
||||||
|
'baz: bazinga\n' +
|
||||||
|
'---\n',
|
||||||
|
text2 =
|
||||||
|
'---TIVIE\n' +
|
||||||
|
'a: b\n' +
|
||||||
|
'c: 123\n' +
|
||||||
|
'---\n';
|
||||||
|
|
||||||
|
it('should correctly set metadata', function () {
|
||||||
|
converter.setOption('metadata', true);
|
||||||
|
|
||||||
|
var expectedHtml = '',
|
||||||
|
expectedObj = {foo: 'bar', baz: 'bazinga'},
|
||||||
|
expectedRaw = 'foo: bar\nbaz: bazinga',
|
||||||
|
expectedFormat = 'SIMPLE';
|
||||||
|
converter.makeHtml(text1).should.equal(expectedHtml);
|
||||||
|
converter.getMetadata().should.eql(expectedObj);
|
||||||
|
converter.getMetadata(true).should.equal(expectedRaw);
|
||||||
|
converter.getMetadataFormat().should.equal(expectedFormat);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('consecutive calls should reset metadata', function () {
|
||||||
|
converter.makeHtml(text2);
|
||||||
|
var expectedObj = {a: 'b', c: '123'},
|
||||||
|
expectedRaw = 'a: b\nc: 123',
|
||||||
|
expectedFormat = 'TIVIE';
|
||||||
|
converter.getMetadata().should.eql(expectedObj);
|
||||||
|
converter.getMetadata(true).should.equal(expectedRaw);
|
||||||
|
converter.getMetadataFormat().should.equal(expectedFormat);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,8 @@ var bootstrap = require('../bootstrap.js'),
|
|||||||
underlineSuite = bootstrap.getTestSuite('test/features/underline/'),
|
underlineSuite = bootstrap.getTestSuite('test/features/underline/'),
|
||||||
literalMidWordUnderscoresSuite = bootstrap.getTestSuite('test/features/literalMidWordUnderscores/'),
|
literalMidWordUnderscoresSuite = bootstrap.getTestSuite('test/features/literalMidWordUnderscores/'),
|
||||||
literalMidWordAsterisksSuite = bootstrap.getTestSuite('test/features/literalMidWordAsterisks/'),
|
literalMidWordAsterisksSuite = bootstrap.getTestSuite('test/features/literalMidWordAsterisks/'),
|
||||||
completeHTMLOutputSuite = bootstrap.getTestSuite('test/features/completeHTMLOutput/');
|
completeHTMLOutputSuite = bootstrap.getTestSuite('test/features/completeHTMLOutput/'),
|
||||||
|
metadataSuite = bootstrap.getTestSuite('test/features/metadata/');
|
||||||
|
|
||||||
describe('makeHtml() features testsuite', function () {
|
describe('makeHtml() features testsuite', function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
@ -233,12 +234,33 @@ describe('makeHtml() features testsuite', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/** test completeHTMLOutput option **/
|
|
||||||
describe('completeHTMLOutput option', function () {
|
/** test completeHTMLDocument option **/
|
||||||
|
describe('completeHTMLDocument option', function () {
|
||||||
var converter,
|
var converter,
|
||||||
suite = completeHTMLOutputSuite;
|
suite = completeHTMLOutputSuite;
|
||||||
for (var i = 0; i < suite.length; ++i) {
|
for (var i = 0; i < suite.length; ++i) {
|
||||||
converter = new showdown.Converter({completeHTMLOutput: true});
|
converter = new showdown.Converter({completeHTMLDocument: true});
|
||||||
|
|
||||||
|
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** test metadata option **/
|
||||||
|
describe('metadata option', function () {
|
||||||
|
var converter,
|
||||||
|
suite = metadataSuite;
|
||||||
|
|
||||||
|
for (var i = 0; i < suite.length; ++i) {
|
||||||
|
if (suite[i].name === 'embeded-in-output' ||
|
||||||
|
suite[i].name === 'embeded-two-consecutive-metadata-blocks' ||
|
||||||
|
suite[i].name === 'embeded-two-consecutive-metadata-blocks-different-symbols') {
|
||||||
|
converter = new showdown.Converter({metadata: true, completeHTMLDocument: true});
|
||||||
|
} else if (suite[i].name === 'ignore-metadata') {
|
||||||
|
converter = new showdown.Converter({metadata: false});
|
||||||
|
} else {
|
||||||
|
converter = new showdown.Converter({metadata: true});
|
||||||
|
}
|
||||||
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
|
it(suite[i].name.replace(/-/g, ' '), assertion(suite[i], converter));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user