markdown.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. (function() {
  2. var async, fs, hljs, marked, parseMarkdownSync, path, resolveLink, url, yaml,
  3. extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  4. hasProp = {}.hasOwnProperty;
  5. async = require('async');
  6. fs = require('fs');
  7. hljs = require('highlight.js');
  8. marked = require('marked');
  9. path = require('path');
  10. url = require('url');
  11. yaml = require('js-yaml');
  12. if (marked.InlineLexer.prototype._outputLink == null) {
  13. marked.InlineLexer.prototype._outputLink = marked.InlineLexer.prototype.outputLink;
  14. marked.InlineLexer.prototype._resolveLink = function(href) {
  15. return href;
  16. };
  17. marked.InlineLexer.prototype.outputLink = function(cap, link) {
  18. link.href = this._resolveLink(link.href);
  19. return this._outputLink(cap, link);
  20. };
  21. }
  22. resolveLink = function(content, uri, baseUrl) {
  23. /* Resolve *uri* relative to *content*, resolves using
  24. *baseUrl* if no matching content is found.
  25. */
  26. var nav, part, ref, uriParts;
  27. uriParts = url.parse(uri);
  28. if (uriParts.protocol) {
  29. return uri;
  30. } else if (uriParts.hash === uri) {
  31. return uri;
  32. } else {
  33. nav = content.parent;
  34. path = ((ref = uriParts.pathname) != null ? ref.split('/') : void 0) || [];
  35. while (path.length && (nav != null)) {
  36. part = path.shift();
  37. if (part === '') {
  38. while (nav.parent) {
  39. nav = nav.parent;
  40. }
  41. } else if (part === '..') {
  42. nav = nav.parent;
  43. } else {
  44. nav = nav[part];
  45. }
  46. }
  47. if ((nav != null ? nav.getUrl : void 0) != null) {
  48. return nav.getUrl() + [uriParts.hash];
  49. }
  50. return url.resolve(baseUrl, uri);
  51. }
  52. };
  53. parseMarkdownSync = function(content, markdown, baseUrl, options) {
  54. /* Parse *markdown* found on *content* node of contents and
  55. resolve links by navigating in the content tree. use *baseUrl* as a last resort
  56. returns html.
  57. */
  58. marked.InlineLexer.prototype._resolveLink = function(uri) {
  59. return resolveLink(content, uri, baseUrl);
  60. };
  61. options.highlight = function(code, lang) {
  62. var error;
  63. try {
  64. if (lang === 'auto') {
  65. return hljs.highlightAuto(code).value;
  66. } else if (hljs.getLanguage(lang)) {
  67. return hljs.highlight(lang, code).value;
  68. }
  69. } catch (error1) {
  70. error = error1;
  71. return code;
  72. }
  73. };
  74. marked.setOptions(options);
  75. return marked(markdown);
  76. };
  77. module.exports = function(env, callback) {
  78. var JsonPage, MarkdownPage, hljsConfig, hljsConfigDefaults, key, value;
  79. hljsConfigDefaults = {
  80. classPrefix: ''
  81. };
  82. hljsConfig = env.config.highlightjs || {};
  83. for (key in hljsConfigDefaults) {
  84. value = hljsConfigDefaults[key];
  85. if (hljsConfig[key] == null) {
  86. hljsConfig[key] = hljsConfigDefaults[key];
  87. }
  88. }
  89. hljs.configure(hljsConfig);
  90. MarkdownPage = (function(superClass) {
  91. extend(MarkdownPage, superClass);
  92. function MarkdownPage(filepath1, metadata1, markdown1) {
  93. this.filepath = filepath1;
  94. this.metadata = metadata1;
  95. this.markdown = markdown1;
  96. }
  97. MarkdownPage.prototype.getLocation = function(base) {
  98. var uri;
  99. uri = this.getUrl(base);
  100. return uri.slice(0, +uri.lastIndexOf('/') + 1 || 9e9);
  101. };
  102. MarkdownPage.prototype.getHtml = function(base) {
  103. var options;
  104. if (base == null) {
  105. base = env.config.baseUrl;
  106. }
  107. /* parse @markdown and return html. also resolves any relative urls to absolute ones */
  108. options = env.config.markdown || {};
  109. return parseMarkdownSync(this, this.markdown, this.getLocation(base), options);
  110. };
  111. return MarkdownPage;
  112. })(env.plugins.Page);
  113. MarkdownPage.fromFile = function(filepath, callback) {
  114. return async.waterfall([
  115. function(callback) {
  116. return fs.readFile(filepath.full, callback);
  117. }, function(buffer, callback) {
  118. return MarkdownPage.extractMetadata(buffer.toString(), callback);
  119. }, (function(_this) {
  120. return function(result, callback) {
  121. var markdown, metadata, page;
  122. markdown = result.markdown, metadata = result.metadata;
  123. page = new _this(filepath, metadata, markdown);
  124. return callback(null, page);
  125. };
  126. })(this)
  127. ], callback);
  128. };
  129. MarkdownPage.extractMetadata = function(content, callback) {
  130. var end, markdown, metadata, parseMetadata, result;
  131. parseMetadata = function(source, callback) {
  132. var error, lines, markerPad;
  133. if (!(source.length > 0)) {
  134. return callback(null, {});
  135. }
  136. try {
  137. return callback(null, yaml.load(source) || {});
  138. } catch (error1) {
  139. error = error1;
  140. if ((error.problem != null) && (error.problemMark != null)) {
  141. lines = error.problemMark.buffer.split('\n');
  142. markerPad = ((function() {
  143. var i, ref, results;
  144. results = [];
  145. for (i = 0, ref = error.problemMark.column; 0 <= ref ? i < ref : i > ref; 0 <= ref ? i++ : i--) {
  146. results.push(' ');
  147. }
  148. return results;
  149. })()).join('');
  150. error.message = "YAML: " + error.problem + "\n\n" + lines[error.problemMark.line] + "\n" + markerPad + "^\n";
  151. } else {
  152. error.message = "YAML Parsing error " + error.message;
  153. }
  154. return callback(error);
  155. }
  156. };
  157. metadata = '';
  158. markdown = content;
  159. if (content.slice(0, 3) === '---') {
  160. result = content.match(/^-{3,}\s([\s\S]*?)-{3,}(\s[\s\S]*|\s?)$/);
  161. if ((result != null ? result.length : void 0) === 3) {
  162. metadata = result[1];
  163. markdown = result[2];
  164. }
  165. } else if (content.slice(0, 12) === '```metadata\n') {
  166. end = content.indexOf('\n```\n');
  167. if (end !== -1) {
  168. metadata = content.substring(12, end);
  169. markdown = content.substring(end + 5);
  170. }
  171. }
  172. return async.parallel({
  173. metadata: function(callback) {
  174. return parseMetadata(metadata, callback);
  175. },
  176. markdown: function(callback) {
  177. return callback(null, markdown);
  178. }
  179. }, callback);
  180. };
  181. MarkdownPage.resolveLink = resolveLink;
  182. JsonPage = (function(superClass) {
  183. /* Plugin that allows pages to be created with just metadata form a JSON file */
  184. extend(JsonPage, superClass);
  185. function JsonPage() {
  186. return JsonPage.__super__.constructor.apply(this, arguments);
  187. }
  188. return JsonPage;
  189. })(MarkdownPage);
  190. JsonPage.fromFile = function(filepath, callback) {
  191. return async.waterfall([
  192. async.apply(env.utils.readJSON, filepath.full), (function(_this) {
  193. return function(metadata, callback) {
  194. var markdown, page;
  195. markdown = metadata.content || '';
  196. page = new _this(filepath, metadata, markdown);
  197. return callback(null, page);
  198. };
  199. })(this)
  200. ], callback);
  201. };
  202. env.registerContentPlugin('pages', '**/*.*(markdown|mkd|md)', MarkdownPage);
  203. env.registerContentPlugin('pages', '**/*.json', JsonPage);
  204. return callback();
  205. };
  206. }).call(this);