Эх сурвалжийг харах

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
Estevao Soares dos Santos 7 жил өмнө
parent
commit
63d949f731
31 өөрчлөгдсөн 659 нэмэгдсэн , 38 устгасан
  1. 173 9
      dist/showdown.js
  2. 0 0
      dist/showdown.js.map
  3. 0 0
      dist/showdown.min.js
  4. 0 0
      dist/showdown.min.js.map
  5. 67 3
      src/converter.js
  6. 6 1
      src/options.js
  7. 62 0
      src/subParsers/completeHTMLDocument.js
  8. 0 17
      src/subParsers/completeHTMLOutput.js
  9. 49 0
      src/subParsers/metadata.js
  10. 5 4
      test/features/completeHTMLOutput/simple.html
  11. 8 0
      test/features/metadata/dashes-conflict.html
  12. 16 0
      test/features/metadata/dashes-conflict.md
  13. 16 0
      test/features/metadata/embeded-in-output.html
  14. 16 0
      test/features/metadata/embeded-in-output.md
  15. 22 0
      test/features/metadata/embeded-two-consecutive-metadata-blocks-different-symbols.html
  16. 18 0
      test/features/metadata/embeded-two-consecutive-metadata-blocks-different-symbols.md
  17. 22 0
      test/features/metadata/embeded-two-consecutive-metadata-blocks.html
  18. 18 0
      test/features/metadata/embeded-two-consecutive-metadata-blocks.md
  19. 15 0
      test/features/metadata/ignore-metadata.html
  20. 18 0
      test/features/metadata/ignore-metadata.md
  21. 1 0
      test/features/metadata/simple-angled-for-method.html
  22. 6 0
      test/features/metadata/simple-angled-for-method.md
  23. 1 0
      test/features/metadata/simple-angled-quotes.html
  24. 16 0
      test/features/metadata/simple-angled-quotes.md
  25. 1 0
      test/features/metadata/simple-three-dashes.html
  26. 16 0
      test/features/metadata/simple-three-dashes.md
  27. 1 0
      test/features/metadata/simple-with-format.html
  28. 9 0
      test/features/metadata/simple-with-format.md
  29. 14 0
      test/node/showdown.Converter.js
  30. 37 0
      test/node/showdown.Converter.makeHtml.js
  31. 26 4
      test/node/testsuite.features.js

+ 173 - 9
dist/showdown.js

@@ -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>`',
       type: 'boolean'
     },
-    completeHTMLOutput: {
+    completeHTMLDocument: {
       defaultValue: false,
       description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
       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) {
@@ -2172,7 +2177,17 @@ showdown.Converter = function (converterOptions) {
       /**
        * 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();
 
@@ -2384,7 +2399,12 @@ showdown.Converter = function (converterOptions) {
       langExtensions:  langExtensions,
       outputModifiers: outputModifiers,
       converter:       this,
-      ghCodeBlocks:    []
+      ghCodeBlocks:    [],
+      metadata: {
+        parsed: {},
+        raw: '',
+        format: ''
+      }
     };
 
     // 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
+    text = showdown.subParser('metadata')(text, options, globals);
     text = showdown.subParser('hashPreCodeTags')(text, options, globals);
     text = showdown.subParser('githubCodeBlocks')(text, options, globals);
     text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
@@ -2444,13 +2465,15 @@ showdown.Converter = function (converterOptions) {
     text = text.replace(/¨T/g, '¨');
 
     // 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
     showdown.helper.forEach(outputModifiers, function (ext) {
       text = showdown.subParser('runExtension')(ext, text, options, globals);
     });
 
+    // update metadata
+    metadata = globals.metadata;
     return text;
   };
 
@@ -2558,6 +2581,52 @@ showdown.Converter = function (converterOptions) {
       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.
  */
-showdown.subParser('completeHTMLOutput', function (text, options, globals) {
+showdown.subParser('completeHTMLDocument', function (text, options, globals) {
   'use strict';
 
-  if (!options.completeHTMLOutput) {
+  if (!options.completeHTMLDocument) {
     return text;
   }
 
-  text = globals.converter._dispatch('completeHTMLOutput.before', text, options, globals);
+  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 = '';
 
-  text = '<html>\n<head>\n<meta charset="UTF-8">\n</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
+  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;
 
-  text = globals.converter._dispatch('completeHTMLOutput.after', text, options, globals);
+        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;
 });
 
@@ -3886,6 +4000,56 @@ showdown.subParser('lists', function (text, options, globals) {
   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, '&amp;')
+      // double quotes
+      .replace(/"/g, '&quot;');
+
+    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
  */

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/showdown.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/showdown.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/showdown.min.js.map


+ 67 - 3
src/converter.js

@@ -43,7 +43,17 @@ showdown.Converter = function (converterOptions) {
       /**
        * 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();
 
@@ -255,7 +265,12 @@ showdown.Converter = function (converterOptions) {
       langExtensions:  langExtensions,
       outputModifiers: outputModifiers,
       converter:       this,
-      ghCodeBlocks:    []
+      ghCodeBlocks:    [],
+      metadata: {
+        parsed: {},
+        raw: '',
+        format: ''
+      }
     };
 
     // 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
+    text = showdown.subParser('metadata')(text, options, globals);
     text = showdown.subParser('hashPreCodeTags')(text, options, globals);
     text = showdown.subParser('githubCodeBlocks')(text, options, globals);
     text = showdown.subParser('hashHTMLBlocks')(text, options, globals);
@@ -315,13 +331,15 @@ showdown.Converter = function (converterOptions) {
     text = text.replace(/¨T/g, '¨');
 
     // 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
     showdown.helper.forEach(outputModifiers, function (ext) {
       text = showdown.subParser('runExtension')(ext, text, options, globals);
     });
 
+    // update metadata
+    metadata = globals.metadata;
     return text;
   };
 
@@ -429,4 +447,50 @@ showdown.Converter = function (converterOptions) {
       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;
+  };
 };

+ 6 - 1
src/options.js

@@ -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>`',
       type: 'boolean'
     },
-    completeHTMLOutput: {
+    completeHTMLDocument: {
       defaultValue: false,
       description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
       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) {

+ 62 - 0
src/subParsers/completeHTMLDocument.js

@@ -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;
+});

+ 0 - 17
src/subParsers/completeHTMLOutput.js

@@ -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 - 0
src/subParsers/metadata.js

@@ -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, '&amp;')
+      // double quotes
+      .replace(/"/g, '&quot;');
+
+    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;
+});

+ 5 - 4
test/features/completeHTMLOutput/simple.html

@@ -1,14 +1,15 @@
+<!DOCTYPE HTML>
 <html>
 <head>
- <meta charset="UTF-8">
+<meta charset="utf-8">
 </head>
 <body>
 <p>This is a <strong>markdown</strong> file</p>
 <p>Converted into a full HTML document</p>
 <ul>
- <li>this</li>
- <li>is</li>
- <li>awesome</li>
+<li>this</li>
+<li>is</li>
+<li>awesome</li>
 </ul>
 </body>
 </html>

+ 8 - 0
test/features/metadata/dashes-conflict.html

@@ -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 - 0
test/features/metadata/dashes-conflict.md

@@ -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 - 0
test/features/metadata/embeded-in-output.html

@@ -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 - 0
test/features/metadata/embeded-in-output.md

@@ -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

+ 22 - 0
test/features/metadata/embeded-two-consecutive-metadata-blocks-different-symbols.html

@@ -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>

+ 18 - 0
test/features/metadata/embeded-two-consecutive-metadata-blocks-different-symbols.md

@@ -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

+ 22 - 0
test/features/metadata/embeded-two-consecutive-metadata-blocks.html

@@ -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>

+ 18 - 0
test/features/metadata/embeded-two-consecutive-metadata-blocks.md

@@ -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 - 0
test/features/metadata/ignore-metadata.html

@@ -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 - 0
test/features/metadata/ignore-metadata.md

@@ -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 - 0
test/features/metadata/simple-angled-for-method.html

@@ -0,0 +1 @@
+<p>some <strong>text</strong></p>

+ 6 - 0
test/features/metadata/simple-angled-for-method.md

@@ -0,0 +1,6 @@
+«««
+foo: bar
+baz: bazinga
+»»»
+
+some **text**

+ 1 - 0
test/features/metadata/simple-angled-quotes.html

@@ -0,0 +1 @@
+<p><strong>some</strong> markdown text</p>

+ 16 - 0
test/features/metadata/simple-angled-quotes.md

@@ -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 - 0
test/features/metadata/simple-three-dashes.html

@@ -0,0 +1 @@
+<p><strong>some</strong> markdown text</p>

+ 16 - 0
test/features/metadata/simple-three-dashes.md

@@ -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 - 0
test/features/metadata/simple-with-format.html

@@ -0,0 +1 @@
+<p><strong>some</strong> markdown text</p>

+ 9 - 0
test/features/metadata/simple-with-format.md

@@ -0,0 +1,9 @@
+---YAML
+foo: bar
+baz:
+ - bazinga
+ - bling
+ - blang
+---
+
+**some** markdown text

+ 14 - 0
test/node/showdown.Converter.js

@@ -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 () {
 
     /**

+ 37 - 0
test/node/showdown.Converter.makeHtml.js

@@ -59,4 +59,41 @@ describe('showdown.Converter', function () {
       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);
+    });
+  });
 });

+ 26 - 4
test/node/testsuite.features.js

@@ -15,7 +15,8 @@ var bootstrap = require('../bootstrap.js'),
     underlineSuite = bootstrap.getTestSuite('test/features/underline/'),
     literalMidWordUnderscoresSuite = bootstrap.getTestSuite('test/features/literalMidWordUnderscores/'),
     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 () {
   '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,
         suite = completeHTMLOutputSuite;
     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));
     }
   });

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно