/*! markdown-it 3.1.0 https://github.com//markdown-it/markdown-it @license MIT */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownit = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o`\\x00-\\x20]+'; var single_quoted = "'[^']*'"; var double_quoted = '"[^"]*"'; var attr_value = '(?:' + unquoted + '|' + single_quoted + '|' + double_quoted + ')'; var attribute = '(?:\\s+' + attr_name + '(?:\\s*=\\s*' + attr_value + ')?)'; var open_tag = '<[A-Za-z][A-Za-z0-9\\-]*' + attribute + '*\\s*\\/?>'; var close_tag = '<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>'; var comment = '|'; var processing = '<[?].*?[?]>'; var declaration = ']*>'; var cdata = ''; var HTML_TAG_RE = new RegExp('^(?:' + open_tag + '|' + close_tag + '|' + comment + '|' + processing + '|' + declaration + '|' + cdata + ')'); module.exports.HTML_TAG_RE = HTML_TAG_RE; },{}],4:[function(require,module,exports){ // List of valid url schemas, accorting to commonmark spec // http://jgm.github.io/CommonMark/spec.html#autolinks 'use strict'; module.exports = [ 'coap', 'doi', 'javascript', 'aaa', 'aaas', 'about', 'acap', 'cap', 'cid', 'crid', 'data', 'dav', 'dict', 'dns', 'file', 'ftp', 'geo', 'go', 'gopher', 'h323', 'http', 'https', 'iax', 'icap', 'im', 'imap', 'info', 'ipp', 'iris', 'iris.beep', 'iris.xpc', 'iris.xpcs', 'iris.lwz', 'ldap', 'mailto', 'mid', 'msrp', 'msrps', 'mtqp', 'mupdate', 'news', 'nfs', 'ni', 'nih', 'nntp', 'opaquelocktoken', 'pop', 'pres', 'rtsp', 'service', 'session', 'shttp', 'sieve', 'sip', 'sips', 'sms', 'snmp', 'soap.beep', 'soap.beeps', 'tag', 'tel', 'telnet', 'tftp', 'thismessage', 'tn3270', 'tip', 'tv', 'urn', 'vemmi', 'ws', 'wss', 'xcon', 'xcon-userid', 'xmlrpc.beep', 'xmlrpc.beeps', 'xmpp', 'z39.50r', 'z39.50s', 'adiumxtra', 'afp', 'afs', 'aim', 'apt', 'attachment', 'aw', 'beshare', 'bitcoin', 'bolo', 'callto', 'chrome', 'chrome-extension', 'com-eventbrite-attendee', 'content', 'cvs', 'dlna-playsingle', 'dlna-playcontainer', 'dtn', 'dvb', 'ed2k', 'facetime', 'feed', 'finger', 'fish', 'gg', 'git', 'gizmoproject', 'gtalk', 'hcp', 'icon', 'ipn', 'irc', 'irc6', 'ircs', 'itms', 'jar', 'jms', 'keyparc', 'lastfm', 'ldaps', 'magnet', 'maps', 'market', 'message', 'mms', 'ms-help', 'msnim', 'mumble', 'mvn', 'notes', 'oid', 'palm', 'paparazzi', 'platform', 'proxy', 'psyc', 'query', 'res', 'resource', 'rmi', 'rsync', 'rtmp', 'secondlife', 'sftp', 'sgn', 'skype', 'smb', 'soldat', 'spotify', 'ssh', 'steam', 'svn', 'teamspeak', 'things', 'udp', 'unreal', 'ut2004', 'ventrilo', 'view-source', 'webcal', 'wtai', 'wyciwyg', 'xfire', 'xri', 'ymsgr' ]; },{}],5:[function(require,module,exports){ // Utilities // 'use strict'; function _class(obj) { return Object.prototype.toString.call(obj); } function isString(obj) { return _class(obj) === '[object String]'; } var _hasOwnProperty = Object.prototype.hasOwnProperty; function has(object, key) { return _hasOwnProperty.call(object, key); } // Merge objects // function assign(obj /*from1, from2, from3, ...*/) { var sources = Array.prototype.slice.call(arguments, 1); sources.forEach(function (source) { if (!source) { return; } if (typeof source !== 'object') { throw new TypeError(source + 'must be object'); } Object.keys(source).forEach(function (key) { obj[key] = source[key]; }); }); return obj; } // Remove element from array and put another array at those position. // Useful for some operations with tokens function arrayReplaceAt(src, pos, newElements) { return [].concat(src.slice(0, pos), newElements, src.slice(pos + 1)); } //////////////////////////////////////////////////////////////////////////////// function isValidEntityCode(c) { /*eslint no-bitwise:0*/ // broken sequence if (c >= 0xD800 && c <= 0xDFFF) { return false; } // never used if (c >= 0xFDD0 && c <= 0xFDEF) { return false; } if ((c & 0xFFFF) === 0xFFFF || (c & 0xFFFF) === 0xFFFE) { return false; } // control codes if (c >= 0x00 && c <= 0x08) { return false; } if (c === 0x0B) { return false; } if (c >= 0x0E && c <= 0x1F) { return false; } if (c >= 0x7F && c <= 0x9F) { return false; } // out of range if (c > 0x10FFFF) { return false; } return true; } function fromCodePoint(c) { /*eslint no-bitwise:0*/ if (c > 0xffff) { c -= 0x10000; var surrogate1 = 0xd800 + (c >> 10), surrogate2 = 0xdc00 + (c & 0x3ff); return String.fromCharCode(surrogate1, surrogate2); } return String.fromCharCode(c); } var UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g; var ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi; var UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + '|' + ENTITY_RE.source, 'gi'); var DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i; var entities = require('./entities'); function replaceEntityPattern(match, name) { var code = 0; if (has(entities, name)) { return entities[name]; } else if (name.charCodeAt(0) === 0x23/* # */ && DIGITAL_ENTITY_TEST_RE.test(name)) { code = name[1].toLowerCase() === 'x' ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10); if (isValidEntityCode(code)) { return fromCodePoint(code); } } return match; } function replaceEntities(str) { if (str.indexOf('&') < 0) { return str; } return str.replace(ENTITY_RE, replaceEntityPattern); } function unescapeMd(str) { if (str.indexOf('\\') < 0) { return str; } return str.replace(UNESCAPE_MD_RE, '$1'); } function unescapeAll(str) { if (str.indexOf('\\') < 0 && str.indexOf('&') < 0) { return str; } return str.replace(UNESCAPE_ALL_RE, function(match, escaped, entity) { if (escaped) { return escaped; } return replaceEntityPattern(match, entity); }); } //////////////////////////////////////////////////////////////////////////////// var HTML_ESCAPE_TEST_RE = /[&<>"]/; var HTML_ESCAPE_REPLACE_RE = /[&<>"]/g; var HTML_REPLACEMENTS = { '&': '&', '<': '<', '>': '>', '"': '"' }; function replaceUnsafeChar(ch) { return HTML_REPLACEMENTS[ch]; } function escapeHtml(str) { if (HTML_ESCAPE_TEST_RE.test(str)) { return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar); } return str; } //////////////////////////////////////////////////////////////////////////////// var encode = require('mdurl/encode'); // Incoming link can be partially encoded. Convert possible combinations to // unified form. // // TODO: Rewrite it. Should use: // // - encodeURIComponent for query // - encodeURI for path // - (?) punicode for domain mame (but encodeURI seems to work in real world) // function normalizeLink(url) { return encode(url); } //////////////////////////////////////////////////////////////////////////////// var REGEXP_ESCAPE_RE = /[.?*+^$[\]\\(){}|-]/g; function escapeRE (str) { return str.replace(REGEXP_ESCAPE_RE, '\\$&'); } //////////////////////////////////////////////////////////////////////////////// // Zs (unicode class) || [\t\f\v\r\n] function isWhiteSpace(code) { if (code >= 0x2000 && code <= 0x200A) { return true; } switch (code) { case 0x09: // \t case 0x0A: // \n case 0x0B: // \v case 0x0C: // \f case 0x0D: // \r case 0x20: case 0xA0: case 0x1680: case 0x202F: case 0x205F: case 0x3000: return true; } return false; } //////////////////////////////////////////////////////////////////////////////// /*eslint-disable max-len*/ var UNICODE_PUNCT_RE = require('uc.micro/categories/P/regex'); // Currently without astral characters support. function isPunctChar(char) { return UNICODE_PUNCT_RE.test(char); } // Markdown ASCII punctuation characters. // // !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~ // http://spec.commonmark.org/0.15/#ascii-punctuation-character // // Don't confuse with unicode punctuation !!! It lacks some chars in ascii range. // function isMdAsciiPunct(ch) { switch (ch) { case 0x21/* ! */: case 0x22/* " */: case 0x23/* # */: case 0x24/* $ */: case 0x25/* % */: case 0x26/* & */: case 0x27/* ' */: case 0x28/* ( */: case 0x29/* ) */: case 0x2A/* * */: case 0x2B/* + */: case 0x2C/* , */: case 0x2D/* - */: case 0x2E/* . */: case 0x2F/* / */: case 0x3A/* : */: case 0x3B/* ; */: case 0x3C/* < */: case 0x3D/* = */: case 0x3E/* > */: case 0x3F/* ? */: case 0x40/* @ */: case 0x5B/* [ */: case 0x5C/* \ */: case 0x5D/* ] */: case 0x5E/* ^ */: case 0x5F/* _ */: case 0x60/* ` */: case 0x7B/* { */: case 0x7C/* | */: case 0x7D/* } */: case 0x7E/* ~ */: return true; default: return false; } } // Hepler to unify [reference labels]. // function normalizeReference(str) { // use .toUpperCase() instead of .toLowerCase() // here to avoid a conflict with Object.prototype // members (most notably, `__proto__`) return str.trim().replace(/\s+/g, ' ').toUpperCase(); } //////////////////////////////////////////////////////////////////////////////// exports.assign = assign; exports.isString = isString; exports.has = has; exports.unescapeMd = unescapeMd; exports.unescapeAll = unescapeAll; exports.isValidEntityCode = isValidEntityCode; exports.fromCodePoint = fromCodePoint; exports.replaceEntities = replaceEntities; exports.escapeHtml = escapeHtml; exports.arrayReplaceAt = arrayReplaceAt; exports.normalizeLink = normalizeLink; exports.isWhiteSpace = isWhiteSpace; exports.isMdAsciiPunct = isMdAsciiPunct; exports.isPunctChar = isPunctChar; exports.escapeRE = escapeRE; exports.normalizeReference = normalizeReference; },{"./entities":1,"mdurl/encode":52,"uc.micro/categories/P/regex":55}],6:[function(require,module,exports){ // Just a shortcut for bulk export 'use strict'; exports.parseLinkLabel = require('./parse_link_label'); exports.parseLinkDestination = require('./parse_link_destination'); exports.parseLinkTitle = require('./parse_link_title'); },{"./parse_link_destination":7,"./parse_link_label":8,"./parse_link_title":9}],7:[function(require,module,exports){ // Parse link destination // 'use strict'; var normalizeLink = require('../common/utils').normalizeLink; var unescapeAll = require('../common/utils').unescapeAll; module.exports = function parseLinkDestination(str, pos, max) { var code, level, lines = 0, start = pos, result = { ok: false, pos: 0, lines: 0, str: '' }; if (str.charCodeAt(pos) === 0x3C /* < */) { pos++; while (pos < max) { code = str.charCodeAt(pos); if (code === 0x0A /* \n */) { return result; } if (code === 0x3E /* > */) { result.pos = pos + 1; result.str = normalizeLink(unescapeAll(str.slice(start + 1, pos))); result.ok = true; return result; } if (code === 0x5C /* \ */ && pos + 1 < max) { pos += 2; continue; } pos++; } // no closing '>' return result; } // this should be ... } else { ... branch level = 0; while (pos < max) { code = str.charCodeAt(pos); if (code === 0x20) { break; } // ascii control characters if (code < 0x20 || code === 0x7F) { break; } if (code === 0x5C /* \ */ && pos + 1 < max) { pos += 2; continue; } if (code === 0x28 /* ( */) { level++; if (level > 1) { break; } } if (code === 0x29 /* ) */) { level--; if (level < 0) { break; } } pos++; } if (start === pos) { return result; } result.str = normalizeLink(unescapeAll(str.slice(start, pos))); result.lines = lines; result.pos = pos; result.ok = true; return result; }; },{"../common/utils":5}],8:[function(require,module,exports){ // Parse link label // // this function assumes that first character ("[") already matches; // returns the end of the label // 'use strict'; module.exports = function parseLinkLabel(state, start, disableNested) { var level, found, marker, prevPos, labelEnd = -1, max = state.posMax, oldPos = state.pos; state.pos = start + 1; level = 1; while (state.pos < max) { marker = state.src.charCodeAt(state.pos); if (marker === 0x5D /* ] */) { level--; if (level === 0) { found = true; break; } } prevPos = state.pos; state.md.inline.skipToken(state); if (marker === 0x5B /* [ */) { if (prevPos === state.pos - 1) { // increase level if we find text `[`, which is not a part of any token level++; } else if (disableNested) { state.pos = oldPos; return -1; } } } if (found) { labelEnd = state.pos; } // restore old state state.pos = oldPos; return labelEnd; }; },{}],9:[function(require,module,exports){ // Parse link title // 'use strict'; var unescapeAll = require('../common/utils').unescapeAll; module.exports = function parseLinkTitle(str, pos, max) { var code, marker, lines = 0, start = pos, result = { ok: false, pos: 0, lines: 0, str: '' }; if (pos >= max) { return result; } marker = str.charCodeAt(pos); if (marker !== 0x22 /* " */ && marker !== 0x27 /* ' */ && marker !== 0x28 /* ( */) { return result; } pos++; // if opening marker is "(", switch it to closing marker ")" if (marker === 0x28) { marker = 0x29; } while (pos < max) { code = str.charCodeAt(pos); if (code === marker) { result.pos = pos + 1; result.lines = lines; result.str = unescapeAll(str.slice(start + 1, pos)); result.ok = true; return result; } else if (code === 0x0A) { lines++; } else if (code === 0x5C /* \ */ && pos + 1 < max) { pos++; if (str.charCodeAt(pos) === 0x0A) { lines++; } } pos++; } return result; }; },{"../common/utils":5}],10:[function(require,module,exports){ // Main perser class 'use strict'; var utils = require('./common/utils'); var helpers = require('./helpers'); var Renderer = require('./renderer'); var ParserCore = require('./parser_core'); var ParserBlock = require('./parser_block'); var ParserInline = require('./parser_inline'); var LinkifyIt = require('linkify-it'); var config = { 'default': require('./presets/default'), zero: require('./presets/zero'), commonmark: require('./presets/commonmark') }; /** * class MarkdownIt * * Main parser/renderer class. * * ##### Usage * * ```javascript * // node.js, "classic" way: * var MarkdownIt = require('markdown-it'), * md = new MarkdownIt(); * var result = md.render('# markdown-it rulezz!'); * * // node.js, the same, but with sugar: * var md = require('markdown-it')(); * var result = md.render('# markdown-it rulezz!'); * * // browser without AMD, added to "window" on script load * // Note, there are no dash. * var md = window.markdownit(); * var result = md.render('# markdown-it rulezz!'); * ``` * * Single line rendering, without paragraph wrap: * * ```javascript * var md = require('markdown-it')(); * var result = md.renderInline('__markdown-it__ rulezz!'); * ``` **/ /** * new MarkdownIt([presetName, options]) * - presetName (String): optional, `commonmark` / `zero` * - options (Object) * * Creates parser instanse with given config. Can be called without `new`. * * ##### presetName * * MarkdownIt provides named presets as a convenience to quickly * enable/disable active syntax rules and options for common use cases. * * - ["commonmark"](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/commonmark.js) - * configures parser to strict [CommonMark](http://commonmark.org/) mode. * - [default](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/default.js) - * similar to GFM, used when no preset name given. Enables all available rules, * but still without html, typographer & autolinker. * - ["zero"](https://github.com/markdown-it/markdown-it/blob/master/lib/presets/zero.js) - * all rules disabled. Useful to quickly setup your config via `.enable()`. * For example, when you need only `bold` and `italic` markup and nothing else. * * ##### options: * * - __html__ - `false`. Set `true` to enable HTML tags in source. Be careful! * That's not safe! You may need external sanitizer to protect output from XSS. * It's better to extend features via plugins, instead of enabling HTML. * - __xhtmlOut__ - `false`. Set `true` to add '/' when closing single tags * (`
`). This is needed only for full CommonMark compatibility. In real * world you will need HTML output. * - __breaks__ - `false`. Set `true` to convert `\n` in paragraphs into `
`. * - __langPrefix__ - `language-`. CSS language class prefix for fenced blocks. * Can be useful for external highlighters. * - __linkify__ - `false`. Set `true` to autoconvert URL-like text to links. * - __typographer__ - `false`. Set `true` to enable [some language-neutral * replacement](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.js) + * quotes beautification (smartquotes). * - __quotes__ - `“”‘’`, string. Double + single quotes replacement pairs, when * typographer enabled and smartquotes on. Set doubles to '«»' for Russian, * '„“' for German. * - __highlight__ - `null`. Highlighter function for fenced code blocks. * Highlighter `function (str, lang)` should return escaped HTML. It can also * return empty string if the source was not changed and should be escaped externaly. * * ##### Example * * ```javascript * // commonmark mode * var md = require('markdown-it')('commonmark'); * * // default mode * var md = require('markdown-it')(); * * // enable everything * var md = require('markdown-it')({ * html: true, * linkify: true, * typographer: true * }); * ``` * * ##### Syntax highlighting * * ```js * var hljs = require('highlight.js') // https://highlightjs.org/ * * var md = require('markdown-it')({ * highlight: function (str, lang) { * if (lang && hljs.getLanguage(lang)) { * try { * return hljs.highlight(lang, str).value; * } catch (__) {} * } * * try { * return hljs.highlightAuto(str).value; * } catch (__) {} * * return ''; // use external default escaping * } * }); * ``` **/ function MarkdownIt(presetName, options) { if (!(this instanceof MarkdownIt)) { return new MarkdownIt(presetName, options); } if (!options) { if (!utils.isString(presetName)) { options = presetName || {}; presetName = 'default'; } } /** * MarkdownIt#inline -> ParserInline * * Instance of [[ParserInline]]. You may need it to add new rules when * writing plugins. For simple rules control use [[MarkdownIt.disable]] and * [[MarkdownIt.enable]]. **/ this.inline = new ParserInline(); /** * MarkdownIt#block -> ParserBlock * * Instance of [[ParserBlock]]. You may need it to add new rules when * writing plugins. For simple rules control use [[MarkdownIt.disable]] and * [[MarkdownIt.enable]]. **/ this.block = new ParserBlock(); /** * MarkdownIt#core -> Core * * Instance of [[Core]] chain executor. You may need it to add new rules when * writing plugins. For simple rules control use [[MarkdownIt.disable]] and * [[MarkdownIt.enable]]. **/ this.core = new ParserCore(); /** * MarkdownIt#renderer -> Renderer * * Instance of [[Renderer]]. Use it to modify output look. Or to add rendering * rules for new token types, generated by plugins. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * function myToken(tokens, idx, options, env, self) { * //... * return result; * }; * * md.renderer.rules['my_token'] = myToken * ``` * * See [[Renderer]] docs and [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js). **/ this.renderer = new Renderer(); /** * MarkdownIt#linkify -> LinkifyIt * * [linkify-it](https://github.com/markdown-it/linkify-it) instance. * Used by [linkify](https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/linkify.js) * rule. **/ this.linkify = new LinkifyIt(); // Expose utils & helpers for easy acces from plugins /** * MarkdownIt#utils -> utils * * Assorted utility functions, useful to write plugins. See details * [here](https://github.com/markdown-it/markdown-it/blob/master/lib/common/utils.js). **/ this.utils = utils; /** * MarkdownIt#helpers -> helpers * * Link components parser functions, useful to write plugins. See details * [here](https://github.com/markdown-it/markdown-it/blob/master/lib/helpers). **/ this.helpers = helpers; this.options = {}; this.configure(presetName); if (options) { this.set(options); } } /** chainable * MarkdownIt.set(options) * * Set parser options (in the same format as in constructor). Probably, you * will never need it, but you can change options after constructor call. * * ##### Example * * ```javascript * var md = require('markdown-it')() * .set({ html: true, breaks: true }) * .set({ typographer, true }); * ``` * * __Note:__ To achieve the best possible performance, don't modify a * `markdown-it` instance options on the fly. If you need multiple configurations * it's best to create multiple instances and initialize each with separate * config. **/ MarkdownIt.prototype.set = function (options) { utils.assign(this.options, options); return this; }; /** chainable, internal * MarkdownIt.configure(presets) * * Batch load of all options and compenent settings. This is internal method, * and you probably will not need it. But if you with - see available presets * and data structure [here](https://github.com/markdown-it/markdown-it/tree/master/lib/presets) * * We strongly recommend to use presets instead of direct config loads. That * will give better compatibility with next versions. **/ MarkdownIt.prototype.configure = function (presets) { var self = this, presetName; if (utils.isString(presets)) { presetName = presets; presets = config[presetName]; if (!presets) { throw new Error('Wrong `markdown-it` preset "' + presetName + '", check name'); } } if (!presets) { throw new Error('Wrong `markdown-it` preset, can\'t be empty'); } if (presets.options) { self.set(presets.options); } if (presets.components) { Object.keys(presets.components).forEach(function (name) { if (presets.components[name].rules) { self[name].ruler.enableOnly(presets.components[name].rules); } }); } return this; }; /** chainable * MarkdownIt.enable(list, ignoreInvalid) * - list (String|Array): rule name or list of rule names to enable * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Enable list or rules. It will automatically find appropriate components, * containing rules with given names. If rule not found, and `ignoreInvalid` * not set - throws exception. * * ##### Example * * ```javascript * var md = require('markdown-it')() * .enable(['sub', 'sup']) * .disable('smartquotes'); * ``` **/ MarkdownIt.prototype.enable = function (list, ignoreInvalid) { var result = []; if (!Array.isArray(list)) { list = [ list ]; } [ 'core', 'block', 'inline' ].forEach(function (chain) { result = result.concat(this[chain].ruler.enable(list, true)); }, this); var missed = list.filter(function (name) { return result.indexOf(name) < 0; }); if (missed.length && !ignoreInvalid) { throw new Error('MarkdownIt. Failed to enable unknown rule(s): ' + missed); } return this; }; /** chainable * MarkdownIt.disable(list, ignoreInvalid) * - list (String|Array): rule name or list of rule names to disable. * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * The same as [[MarkdownIt.enable]], but turn specified rules off. **/ MarkdownIt.prototype.disable = function (list, ignoreInvalid) { var result = []; if (!Array.isArray(list)) { list = [ list ]; } [ 'core', 'block', 'inline' ].forEach(function (chain) { result = result.concat(this[chain].ruler.disable(list, true)); }, this); var missed = list.filter(function (name) { return result.indexOf(name) < 0; }); if (missed.length && !ignoreInvalid) { throw new Error('MarkdownIt. Failed to disable unknown rule(s): ' + missed); } return this; }; /** chainable * MarkdownIt.use(plugin, params) * * Load specified plugin with given params into current parser instance. * It's just a sugar to call `plugin(md, params)` with curring. * * ##### Example * * ```javascript * var iterator = require('markdown-it-for-inline'); * var md = require('markdown-it')() * .use(iterator, 'foo_replace', 'text', function (tokens, idx) { * tokens[idx].content = tokens[idx].content.replace(/foo/g, 'bar'); * }); * ``` **/ MarkdownIt.prototype.use = function (plugin /*, params, ... */) { var args = [ this ].concat(Array.prototype.slice.call(arguments, 1)); plugin.apply(plugin, args); return this; }; /** internal * MarkdownIt.parse(src, env) -> Array * - src (String): source string * - env (Object): environment sandbox * * Parse input string and returns list of block tokens (special token type * "inline" will contain list of inline tokens). You should not call this * method directly, until you write custom renderer (for example, to produce * AST). * * `env` is used to pass data between "distributed" rules and return additional * metadata like reference info, needed for for renderer. It also can be used to * inject data in specific cases. Usually, you will be ok to pass `{}`, * and then pass updated object to renderer. **/ MarkdownIt.prototype.parse = function (src, env) { var state = new this.core.State(src, this, env); this.core.process(state); return state.tokens; }; /** * MarkdownIt.render(src [, env]) -> String * - src (String): source string * - env (Object): environment sandbox * * Render markdown string into html. It does all magic for you :). * * `env` can be used to inject additional metadata (`{}` by default). * But you will not need it with high probability. See also comment * in [[MarkdownIt.parse]]. **/ MarkdownIt.prototype.render = function (src, env) { env = env || {}; return this.renderer.render(this.parse(src, env), this.options, env); }; /** internal * MarkdownIt.parseInline(src, env) -> Array * - src (String): source string * - env (Object): environment sandbox * * The same as [[MarkdownIt.parse]] but skip all block rules. It returns the * block tokens list with the single `inline` element, containing parsed inline * tokens in `children` property. Also updates `env` object. **/ MarkdownIt.prototype.parseInline = function (src, env) { var state = new this.core.State(src, this, env); state.inlineMode = true; this.core.process(state); return state.tokens; }; /** * MarkdownIt.renderInline(src [, env]) -> String * - src (String): source string * - env (Object): environment sandbox * * Similar to [[MarkdownIt.render]] but for single paragraph content. Result * will NOT be wrapped into `

` tags. **/ MarkdownIt.prototype.renderInline = function (src, env) { env = env || {}; return this.renderer.render(this.parseInline(src, env), this.options, env); }; module.exports = MarkdownIt; },{"./common/utils":5,"./helpers":6,"./parser_block":11,"./parser_core":12,"./parser_inline":13,"./presets/commonmark":14,"./presets/default":15,"./presets/zero":16,"./renderer":17,"linkify-it":50}],11:[function(require,module,exports){ /** internal * class ParserBlock * * Block-level tokenizer. **/ 'use strict'; var Ruler = require('./ruler'); var _rules = [ // First 2 params - rule name & source. Secondary array - list of rules, // which can be terminated by this one. [ 'code', require('./rules_block/code') ], [ 'fence', require('./rules_block/fence'), [ 'paragraph', 'reference', 'blockquote', 'list' ] ], [ 'blockquote', require('./rules_block/blockquote'), [ 'paragraph', 'reference', 'list' ] ], [ 'hr', require('./rules_block/hr'), [ 'paragraph', 'reference', 'blockquote', 'list' ] ], [ 'list', require('./rules_block/list'), [ 'paragraph', 'reference', 'blockquote' ] ], [ 'reference', require('./rules_block/reference') ], [ 'heading', require('./rules_block/heading'), [ 'paragraph', 'reference', 'blockquote' ] ], [ 'lheading', require('./rules_block/lheading') ], [ 'html_block', require('./rules_block/html_block'), [ 'paragraph', 'reference', 'blockquote' ] ], [ 'table', require('./rules_block/table'), [ 'paragraph', 'reference' ] ], [ 'paragraph', require('./rules_block/paragraph') ] ]; /** * new ParserBlock() **/ function ParserBlock() { /** * ParserBlock#ruler -> Ruler * * [[Ruler]] instance. Keep configuration of block rules. **/ this.ruler = new Ruler(); for (var i = 0; i < _rules.length; i++) { this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() }); } } // Generate tokens for input range // ParserBlock.prototype.tokenize = function (state, startLine, endLine) { var ok, i, rules = this.ruler.getRules(''), len = rules.length, line = startLine, hasEmptyLines = false, maxNesting = state.md.options.maxNesting; while (line < endLine) { state.line = line = state.skipEmptyLines(line); if (line >= endLine) { break; } // Termination condition for nested calls. // Nested calls currently used for blockquotes & lists if (state.tShift[line] < state.blkIndent) { break; } // If nesting level exceeded - skip tail to the end. That's not ordinary // situation and we should not care about content. if (state.level >= maxNesting) { state.line = endLine; break; } // Try all possible rules. // On success, rule should: // // - update `state.line` // - update `state.tokens` // - return true for (i = 0; i < len; i++) { ok = rules[i](state, line, endLine, false); if (ok) { break; } } // set state.tight iff we had an empty line before current tag // i.e. latest empty line should not count state.tight = !hasEmptyLines; // paragraph might "eat" one newline after it in nested lists if (state.isEmpty(state.line - 1)) { hasEmptyLines = true; } line = state.line; if (line < endLine && state.isEmpty(line)) { hasEmptyLines = true; line++; // two empty lines should stop the parser in list mode if (line < endLine && state.parentType === 'list' && state.isEmpty(line)) { break; } state.line = line; } } }; /** * ParserBlock.parse(str, md, env, outTokens) * * Process input string and push block tokens into `outTokens` **/ ParserBlock.prototype.parse = function (src, md, env, outTokens) { var state; if (!src) { return []; } state = new this.State(src, md, env, outTokens); this.tokenize(state, state.line, state.lineMax); }; ParserBlock.prototype.State = require('./rules_block/state_block'); module.exports = ParserBlock; },{"./ruler":18,"./rules_block/blockquote":19,"./rules_block/code":20,"./rules_block/fence":21,"./rules_block/heading":22,"./rules_block/hr":23,"./rules_block/html_block":24,"./rules_block/lheading":25,"./rules_block/list":26,"./rules_block/paragraph":27,"./rules_block/reference":28,"./rules_block/state_block":29,"./rules_block/table":30}],12:[function(require,module,exports){ /** internal * class Core * * Top-level rules executor. Glues block/inline parsers and does intermediate * transformations. **/ 'use strict'; var Ruler = require('./ruler'); var _rules = [ [ 'normalize', require('./rules_core/normalize') ], [ 'block', require('./rules_core/block') ], [ 'inline', require('./rules_core/inline') ], [ 'replacements', require('./rules_core/replacements') ], [ 'smartquotes', require('./rules_core/smartquotes') ], [ 'linkify', require('./rules_core/linkify') ] ]; /** * new Core() **/ function Core() { /** * Core#ruler -> Ruler * * [[Ruler]] instance. Keep configuration of core rules. **/ this.ruler = new Ruler(); for (var i = 0; i < _rules.length; i++) { this.ruler.push(_rules[i][0], _rules[i][1]); } } /** * Core.process(state) * * Executes core chain rules. **/ Core.prototype.process = function (state) { var i, l, rules; rules = this.ruler.getRules(''); for (i = 0, l = rules.length; i < l; i++) { rules[i](state); } }; Core.prototype.State = require('./rules_core/state_core'); module.exports = Core; },{"./ruler":18,"./rules_core/block":31,"./rules_core/inline":32,"./rules_core/linkify":33,"./rules_core/normalize":34,"./rules_core/replacements":35,"./rules_core/smartquotes":36,"./rules_core/state_core":37}],13:[function(require,module,exports){ /** internal * class ParserInline * * Tokenizes paragraph content. **/ 'use strict'; var Ruler = require('./ruler'); var replaceEntities = require('./common/utils').replaceEntities; //////////////////////////////////////////////////////////////////////////////// // Parser rules var _rules = [ [ 'text', require('./rules_inline/text') ], [ 'newline', require('./rules_inline/newline') ], [ 'escape', require('./rules_inline/escape') ], [ 'backticks', require('./rules_inline/backticks') ], [ 'strikethrough', require('./rules_inline/strikethrough') ], [ 'emphasis', require('./rules_inline/emphasis') ], [ 'link', require('./rules_inline/link') ], [ 'image', require('./rules_inline/image') ], [ 'autolink', require('./rules_inline/autolink') ], [ 'html_inline', require('./rules_inline/html_inline') ], [ 'entity', require('./rules_inline/entity') ] ]; var BAD_PROTOCOLS = [ 'vbscript', 'javascript', 'file' ]; function validateLink(url) { // Care about digital entities "javascript:alert(1)" var str = replaceEntities(url); str = str.trim().toLowerCase(); if (str.indexOf(':') >= 0 && BAD_PROTOCOLS.indexOf(str.split(':')[0]) >= 0) { return false; } return true; } /** * new ParserInline() **/ function ParserInline() { /** * ParserInline#validateLink(url) -> Boolean * * Link validation function. CommonMark allows too much in links. By default * we disable `javascript:` and `vbscript:` schemas. You can change this * behaviour. * * ```javascript * var md = require('markdown-it')(); * // enable everything * md.inline.validateLink = function () { return true; } * ``` **/ this.validateLink = validateLink; /** * ParserInline#ruler -> Ruler * * [[Ruler]] instance. Keep configuration of inline rules. **/ this.ruler = new Ruler(); for (var i = 0; i < _rules.length; i++) { this.ruler.push(_rules[i][0], _rules[i][1]); } } // Skip single token by running all rules in validation mode; // returns `true` if any rule reported success // ParserInline.prototype.skipToken = function (state) { var i, cached_pos, pos = state.pos, rules = this.ruler.getRules(''), len = rules.length, maxNesting = state.md.options.maxNesting; if ((cached_pos = state.cacheGet(pos)) > 0) { state.pos = cached_pos; return; } /*istanbul ignore else*/ if (state.level < maxNesting) { for (i = 0; i < len; i++) { if (rules[i](state, true)) { state.cacheSet(pos, state.pos); return; } } } state.pos++; state.cacheSet(pos, state.pos); }; // Generate tokens for input range // ParserInline.prototype.tokenize = function (state) { var ok, i, rules = this.ruler.getRules(''), len = rules.length, end = state.posMax, maxNesting = state.md.options.maxNesting; while (state.pos < end) { // Try all possible rules. // On success, rule should: // // - update `state.pos` // - update `state.tokens` // - return true if (state.level < maxNesting) { for (i = 0; i < len; i++) { ok = rules[i](state, false); if (ok) { break; } } } if (ok) { if (state.pos >= end) { break; } continue; } state.pending += state.src[state.pos++]; } if (state.pending) { state.pushPending(); } }; /** * ParserInline.parse(str, md, env, outTokens) * * Process input string and push inline tokens into `outTokens` **/ ParserInline.prototype.parse = function (str, md, env, outTokens) { var state = new this.State(str, md, env, outTokens); this.tokenize(state); }; ParserInline.prototype.State = require('./rules_inline/state_inline'); module.exports = ParserInline; },{"./common/utils":5,"./ruler":18,"./rules_inline/autolink":38,"./rules_inline/backticks":39,"./rules_inline/emphasis":40,"./rules_inline/entity":41,"./rules_inline/escape":42,"./rules_inline/html_inline":43,"./rules_inline/image":44,"./rules_inline/link":45,"./rules_inline/newline":46,"./rules_inline/state_inline":47,"./rules_inline/strikethrough":48,"./rules_inline/text":49}],14:[function(require,module,exports){ // Commonmark default options 'use strict'; module.exports = { options: { html: true, // Enable HTML tags in source xhtmlOut: true, // Use '/' to close single tags (
) breaks: false, // Convert '\n' in paragraphs into
langPrefix: 'language-', // CSS language prefix for fenced blocks linkify: false, // autoconvert URL-like texts to links // Enable some language-neutral replacements + quotes beautification typographer: false, // Double + single quotes replacement pairs, when typographer enabled, // and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */, // Highlighter function. Should return escaped HTML, // or '' if input not changed // // function (/*str, lang*/) { return ''; } // highlight: null, maxNesting: 20 // Internal protection, recursion limit }, components: { core: { rules: [ 'normalize', 'block', 'inline' ] }, block: { rules: [ 'blockquote', 'code', 'fence', 'heading', 'hr', 'html_block', 'lheading', 'list', 'reference', 'paragraph' ] }, inline: { rules: [ 'autolink', 'backticks', 'emphasis', 'entity', 'escape', 'html_inline', 'image', 'link', 'newline', 'text' ] } } }; },{}],15:[function(require,module,exports){ // markdown-it default options 'use strict'; module.exports = { options: { html: false, // Enable HTML tags in source xhtmlOut: false, // Use '/' to close single tags (
) breaks: false, // Convert '\n' in paragraphs into
langPrefix: 'language-', // CSS language prefix for fenced blocks linkify: false, // autoconvert URL-like texts to links // Enable some language-neutral replacements + quotes beautification typographer: false, // Double + single quotes replacement pairs, when typographer enabled, // and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */, // Highlighter function. Should return escaped HTML, // or '' if input not changed // // function (/*str, lang*/) { return ''; } // highlight: null, maxNesting: 20 // Internal protection, recursion limit }, components: { core: {}, block: {}, inline: {} } }; },{}],16:[function(require,module,exports){ // "Zero" preset, with nothing enabled. Useful for manual configuring of simple // modes. For example, to parse bold/italic only. 'use strict'; module.exports = { options: { html: false, // Enable HTML tags in source xhtmlOut: false, // Use '/' to close single tags (
) breaks: false, // Convert '\n' in paragraphs into
langPrefix: 'language-', // CSS language prefix for fenced blocks linkify: false, // autoconvert URL-like texts to links // Enable some language-neutral replacements + quotes beautification typographer: false, // Double + single quotes replacement pairs, when typographer enabled, // and smartquotes on. Set doubles to '«»' for Russian, '„“' for German. quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */, // Highlighter function. Should return escaped HTML, // or '' if input not changed // // function (/*str, lang*/) { return ''; } // highlight: null, maxNesting: 20 // Internal protection, recursion limit }, components: { core: { rules: [ 'normalize', 'block', 'inline' ] }, block: { rules: [ 'paragraph' ] }, inline: { rules: [ 'text' ] } } }; },{}],17:[function(require,module,exports){ /** * class Renderer * * Generates HTML from parsed token stream. Each instance has independent * copy of rules. Those can be rewritten with ease. Also, you can add new * rules if you create plugin and adds new token types. **/ 'use strict'; var assign = require('./common/utils').assign; var unescapeAll = require('./common/utils').unescapeAll; var escapeHtml = require('./common/utils').escapeHtml; //////////////////////////////////////////////////////////////////////////////// var rules = {}; rules.blockquote_open = function () { return '

\n'; }; rules.blockquote_close = function () { return '
\n'; }; rules.code_block = function (tokens, idx /*, options, env */) { return '
' + escapeHtml(tokens[idx].content) + '
\n'; }; rules.code_inline = function (tokens, idx /*, options, env */) { return '' + escapeHtml(tokens[idx].content) + ''; }; rules.fence = function (tokens, idx, options /*, env, self*/) { var token = tokens[idx]; var langClass = ''; var langPrefix = options.langPrefix; var langName = ''; var highlighted; if (token.params) { langName = escapeHtml(unescapeAll(token.params.split(/\s+/g)[0])); langClass = ' class="' + langPrefix + langName + '"'; } if (options.highlight) { highlighted = options.highlight(token.content, langName) || escapeHtml(token.content); } else { highlighted = escapeHtml(token.content); } return '
'
        + highlighted
        + '
\n'; }; rules.heading_open = function (tokens, idx /*, options, env */) { return ''; }; rules.heading_close = function (tokens, idx /*, options, env */) { return '\n'; }; rules.hr = function (tokens, idx, options /*, env */) { return (options.xhtmlOut ? '
\n' : '
\n'); }; rules.bullet_list_open = function () { return '\n'; }; rules.list_item_open = function (tokens, idx /*, options, env */) { var next = tokens[idx + 1]; if ((next.type === 'list_item_close') || (next.type === 'paragraph_open' && next.tight)) { return '
  • '; } return '
  • \n'; }; rules.list_item_close = function () { return '
  • \n'; }; rules.ordered_list_open = function (tokens, idx /*, options, env */) { if (tokens[idx].order > 1) { return '
      \n'; } return '
        \n'; }; rules.ordered_list_close = function () { return '
      \n'; }; rules.paragraph_open = function (tokens, idx /*, options, env */) { return tokens[idx].tight ? '' : '

      '; }; rules.paragraph_close = function (tokens, idx /*, options, env */) { if (tokens[idx].tight === true) { return tokens[idx + 1].type.slice(-5) === 'close' ? '' : '\n'; } return '

      \n'; }; rules.link_open = function (tokens, idx /*, options, env */) { var title = tokens[idx].title ? (' title="' + escapeHtml(tokens[idx].title) + '"') : ''; var target = tokens[idx].target ? (' target="' + escapeHtml(tokens[idx].target) + '"') : ''; return ''; }; rules.link_close = function (/* tokens, idx, options, env */) { return ''; }; rules.image = function (tokens, idx, options, env, self) { var src = ' src="' + escapeHtml(tokens[idx].src) + '"'; var title = tokens[idx].title ? (' title="' + escapeHtml(tokens[idx].title) + '"') : ''; var alt = ' alt="' + self.renderInlineAsText(tokens[idx].tokens, options, env) + '"'; var suffix = options.xhtmlOut ? ' /' : ''; return ''; }; rules.table_open = function () { return '\n'; }; rules.table_close = function () { return '
      \n'; }; rules.thead_open = function () { return '\n'; }; rules.thead_close = function () { return '\n'; }; rules.tbody_open = function () { return '\n'; }; rules.tbody_close = function () { return '\n'; }; rules.tr_open = function () { return ''; }; rules.tr_close = function () { return '\n'; }; rules.th_open = function (tokens, idx /*, options, env */) { if (tokens[idx].align) { return ''; } return ''; }; rules.th_close = function () { return ''; }; rules.td_open = function (tokens, idx /*, options, env */) { if (tokens[idx].align) { return ''; } return ''; }; rules.td_close = function () { return ''; }; rules.strong_open = function () { return ''; }; rules.strong_close = function () { return ''; }; rules.em_open = function () { return ''; }; rules.em_close = function () { return ''; }; rules.s_open = function () { return ''; }; rules.s_close = function () { return ''; }; rules.hardbreak = function (tokens, idx, options /*, env */) { return options.xhtmlOut ? '
      \n' : '
      \n'; }; rules.softbreak = function (tokens, idx, options /*, env */) { return options.breaks ? (options.xhtmlOut ? '
      \n' : '
      \n') : '\n'; }; rules.text = function (tokens, idx /*, options, env */) { return escapeHtml(tokens[idx].content); }; rules.html_block = function (tokens, idx /*, options, env */) { return tokens[idx].content; }; rules.html_inline = function (tokens, idx /*, options, env */) { return tokens[idx].content; }; /** * new Renderer() * * Creates new [[Renderer]] instance and fill [[Renderer#rules]] with defaults. **/ function Renderer() { /** * Renderer#rules -> Object * * Contains render rules for tokens. Can be updated and extended. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.renderer.rules.strong_open = function () { return ''; }; * md.renderer.rules.strong_close = function () { return ''; }; * * var result = md.renderInline(...); * ``` * * Each rule is called as independed static function with fixed signature: * * ```javascript * function my_token_render(tokens, idx, options, env, renderer) { * // ... * return renderedHTML; * } * ``` * * See [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js) * for more details and examples. **/ this.rules = assign({}, rules); } /** * Renderer.renderInline(tokens, options, env) -> String * - tokens (Array): list on block tokens to renter * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * The same as [[Renderer.render]], but for single token of `inline` type. **/ Renderer.prototype.renderInline = function (tokens, options, env) { var result = '', _rules = this.rules; for (var i = 0, len = tokens.length; i < len; i++) { result += _rules[tokens[i].type](tokens, i, options, env, this); } return result; }; /** internal * Renderer.renderInlineAsText(tokens, options, env) -> String * - tokens (Array): list on block tokens to renter * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * Special kludge for image `alt` attributes to conform CommonMark spec. * Don't try to use it! Spec requires to show `alt` content with stripped markup, * instead of simple escaping. **/ Renderer.prototype.renderInlineAsText = function (tokens, options, env) { var result = '', _rules = this.rules; for (var i = 0, len = tokens.length; i < len; i++) { if (tokens[i].type === 'text') { result += _rules.text(tokens, i, options, env, this); } else if (tokens[i].type === 'image') { result += this.renderInlineAsText(tokens[i].tokens, options, env); } } return result; }; /** * Renderer.render(tokens, options, env) -> String * - tokens (Array): list on block tokens to renter * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * Takes token stream and generates HTML. Probably, you will never need to call * this method directly. **/ Renderer.prototype.render = function (tokens, options, env) { var i, len, result = '', _rules = this.rules; for (i = 0, len = tokens.length; i < len; i++) { if (tokens[i].type === 'inline') { result += this.renderInline(tokens[i].children, options, env); } else { result += _rules[tokens[i].type](tokens, i, options, env, this); } } return result; }; module.exports = Renderer; },{"./common/utils":5}],18:[function(require,module,exports){ /** * class Ruler * * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and * [[MarkdownIt#inline]] to manage sequences of functions (rules): * * - keep rules in defined order * - assign the name to each rule * - enable/disable rules * - add/replace rules * - allow assign rules to additional named chains (in the same) * - cacheing lists of active rules * * You will not need use this class directly until write plugins. For simple * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and * [[MarkdownIt.use]]. **/ 'use strict'; /** * new Ruler() **/ function Ruler() { // List of added rules. Each element is: // // { // name: XXX, // enabled: Boolean, // fn: Function(), // alt: [ name2, name3 ] // } // this.__rules__ = []; // Cached rule chains. // // First level - chain name, '' for default. // Second level - diginal anchor for fast filtering by charcodes. // this.__cache__ = null; } //////////////////////////////////////////////////////////////////////////////// // Helper methods, should not be used directly // Find rule index by name // Ruler.prototype.__find__ = function (name) { for (var i = 0; i < this.__rules__.length; i++) { if (this.__rules__[i].name === name) { return i; } } return -1; }; // Build rules lookup cache // Ruler.prototype.__compile__ = function () { var self = this; var chains = [ '' ]; // collect unique names self.__rules__.forEach(function (rule) { if (!rule.enabled) { return; } rule.alt.forEach(function (altName) { if (chains.indexOf(altName) < 0) { chains.push(altName); } }); }); self.__cache__ = {}; chains.forEach(function (chain) { self.__cache__[chain] = []; self.__rules__.forEach(function (rule) { if (!rule.enabled) { return; } if (chain && rule.alt.indexOf(chain) < 0) { return; } self.__cache__[chain].push(rule.fn); }); }); }; /** * Ruler.at(name, fn [, options]) * - name (String): rule name to replace. * - fn (Function): new rule function. * - options (Object): new rule options (not mandatory). * * Replace rule by name with new function & options. Throws error if name not * found. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * Replace existing typorgapher replacement rule with new one: * * ```javascript * var md = require('markdown-it')(); * * md.core.ruler.at('replacements', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.at = function (name, fn, options) { var index = this.__find__(name); var opt = options || {}; if (index === -1) { throw new Error('Parser rule not found: ' + name); } this.__rules__[index].fn = fn; this.__rules__[index].alt = opt.alt || []; this.__cache__ = null; }; /** * Ruler.before(beforeName, ruleName, fn [, options]) * - beforeName (String): new rule will be added before this one. * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Add new rule to chain before one with given name. See also * [[Ruler.after]], [[Ruler.push]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.block.ruler.before('paragraph', 'my_rule', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.before = function (beforeName, ruleName, fn, options) { var index = this.__find__(beforeName); var opt = options || {}; if (index === -1) { throw new Error('Parser rule not found: ' + beforeName); } this.__rules__.splice(index, 0, { name: ruleName, enabled: true, fn: fn, alt: opt.alt || [] }); this.__cache__ = null; }; /** * Ruler.after(afterName, ruleName, fn [, options]) * - afterName (String): new rule will be added after this one. * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Add new rule to chain after one with given name. See also * [[Ruler.before]], [[Ruler.push]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.inline.ruler.after('text', 'my_rule', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.after = function (afterName, ruleName, fn, options) { var index = this.__find__(afterName); var opt = options || {}; if (index === -1) { throw new Error('Parser rule not found: ' + afterName); } this.__rules__.splice(index + 1, 0, { name: ruleName, enabled: true, fn: fn, alt: opt.alt || [] }); this.__cache__ = null; }; /** * Ruler.push(ruleName, fn [, options]) * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Push new rule to the end of chain. See also * [[Ruler.before]], [[Ruler.after]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.core.ruler.push('emphasis', 'my_rule', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.push = function (ruleName, fn, options) { var opt = options || {}; this.__rules__.push({ name: ruleName, enabled: true, fn: fn, alt: opt.alt || [] }); this.__cache__ = null; }; /** * Ruler.enable(list [, ignoreInvalid]) -> Array * - list (String|Array): list of rule names to enable. * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Enable rules with given names. If any rule name not found - throw Error. * Errors can be disabled by second param. * * Returns list of found rule names (if no exception happened). * * See also [[Ruler.disable]], [[Ruler.enableOnly]]. **/ Ruler.prototype.enable = function (list, ignoreInvalid) { if (!Array.isArray(list)) { list = [ list ]; } var result = []; // Search by name and enable list.forEach(function (name) { var idx = this.__find__(name); if (idx < 0) { if (ignoreInvalid) { return; } throw new Error('Rules manager: invalid rule name ' + name); } this.__rules__[idx].enabled = true; result.push(name); }, this); this.__cache__ = null; return result; }; /** * Ruler.enableOnly(list [, ignoreInvalid]) * - list (String|Array): list of rule names to enable (whitelist). * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Enable rules with given names, and disable everything else. If any rule name * not found - throw Error. Errors can be disabled by second param. * * See also [[Ruler.disable]], [[Ruler.enable]]. **/ Ruler.prototype.enableOnly = function (list, ignoreInvalid) { if (!Array.isArray(list)) { list = [ list ]; } this.__rules__.forEach(function (rule) { rule.enabled = false; }); this.enable(list, ignoreInvalid); }; /** * Ruler.disable(list [, ignoreInvalid]) -> Array * - list (String|Array): list of rule names to disable. * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Disable rules with given names. If any rule name not found - throw Error. * Errors can be disabled by second param. * * Returns list of found rule names (if no exception happened). * * See also [[Ruler.enable]], [[Ruler.enableOnly]]. **/ Ruler.prototype.disable = function (list, ignoreInvalid) { if (!Array.isArray(list)) { list = [ list ]; } var result = []; // Search by name and disable list.forEach(function (name) { var idx = this.__find__(name); if (idx < 0) { if (ignoreInvalid) { return; } throw new Error('Rules manager: invalid rule name ' + name); } this.__rules__[idx].enabled = false; result.push(name); }, this); this.__cache__ = null; return result; }; /** * Ruler.getRules(chainName) -> Array * * Return array of active functions (rules) for given chain name. It analyzes * rules configuration, compiles caches if not exists and returns result. * * Default chain name is `''` (empty string). It can't be skipped. That's * done intentionally, to keep signature monomorphic for high speed. **/ Ruler.prototype.getRules = function (chainName) { if (this.__cache__ === null) { this.__compile__(); } // Chain can be empty, if rules disabled. But we still have to return Array. return this.__cache__[chainName] || []; }; module.exports = Ruler; },{}],19:[function(require,module,exports){ // Block quotes 'use strict'; module.exports = function blockquote(state, startLine, endLine, silent) { var nextLine, lastLineEmpty, oldTShift, oldBMarks, oldIndent, oldParentType, lines, terminatorRules, i, l, terminate, pos = state.bMarks[startLine] + state.tShift[startLine], max = state.eMarks[startLine]; // check the block quote marker if (state.src.charCodeAt(pos++) !== 0x3E/* > */) { return false; } // we know that it's going to be a valid blockquote, // so no point trying to find the end of it in silent mode if (silent) { return true; } // skip one optional space after '>' if (state.src.charCodeAt(pos) === 0x20) { pos++; } oldIndent = state.blkIndent; state.blkIndent = 0; oldBMarks = [ state.bMarks[startLine] ]; state.bMarks[startLine] = pos; // check if we have an empty blockquote pos = pos < max ? state.skipSpaces(pos) : pos; lastLineEmpty = pos >= max; oldTShift = [ state.tShift[startLine] ]; state.tShift[startLine] = pos - state.bMarks[startLine]; terminatorRules = state.md.block.ruler.getRules('blockquote'); // Search the end of the block // // Block ends with either: // 1. an empty line outside: // ``` // > test // // ``` // 2. an empty line inside: // ``` // > // test // ``` // 3. another tag // ``` // > test // - - - // ``` for (nextLine = startLine + 1; nextLine < endLine; nextLine++) { pos = state.bMarks[nextLine] + state.tShift[nextLine]; max = state.eMarks[nextLine]; if (pos >= max) { // Case 1: line is not inside the blockquote, and this line is empty. break; } if (state.src.charCodeAt(pos++) === 0x3E/* > */) { // This line is inside the blockquote. // skip one optional space after '>' if (state.src.charCodeAt(pos) === 0x20) { pos++; } oldBMarks.push(state.bMarks[nextLine]); state.bMarks[nextLine] = pos; pos = pos < max ? state.skipSpaces(pos) : pos; lastLineEmpty = pos >= max; oldTShift.push(state.tShift[nextLine]); state.tShift[nextLine] = pos - state.bMarks[nextLine]; continue; } // Case 2: line is not inside the blockquote, and the last line was empty. if (lastLineEmpty) { break; } // Case 3: another tag found. terminate = false; for (i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } if (terminate) { break; } oldBMarks.push(state.bMarks[nextLine]); oldTShift.push(state.tShift[nextLine]); // A negative number means that this is a paragraph continuation; // // Any negative number will do the job here, but it's better for it // to be large enough to make any bugs obvious. state.tShift[nextLine] = -1337; } oldParentType = state.parentType; state.parentType = 'blockquote'; state.tokens.push({ type: 'blockquote_open', lines: lines = [ startLine, 0 ], level: state.level++ }); state.md.block.tokenize(state, startLine, nextLine); state.tokens.push({ type: 'blockquote_close', level: --state.level }); state.parentType = oldParentType; lines[1] = state.line; // Restore original tShift; this might not be necessary since the parser // has already been here, but just to make sure we can do that. for (i = 0; i < oldTShift.length; i++) { state.bMarks[i + startLine] = oldBMarks[i]; state.tShift[i + startLine] = oldTShift[i]; } state.blkIndent = oldIndent; return true; }; },{}],20:[function(require,module,exports){ // Code block (4 spaces padded) 'use strict'; module.exports = function code(state, startLine, endLine/*, silent*/) { var nextLine, last; if (state.tShift[startLine] - state.blkIndent < 4) { return false; } last = nextLine = startLine + 1; while (nextLine < endLine) { if (state.isEmpty(nextLine)) { nextLine++; continue; } if (state.tShift[nextLine] - state.blkIndent >= 4) { nextLine++; last = nextLine; continue; } break; } state.line = nextLine; state.tokens.push({ type: 'code_block', content: state.getLines(startLine, last, 4 + state.blkIndent, true), lines: [ startLine, state.line ], level: state.level }); return true; }; },{}],21:[function(require,module,exports){ // fences (``` lang, ~~~ lang) 'use strict'; module.exports = function fence(state, startLine, endLine, silent) { var marker, len, params, nextLine, mem, haveEndMarker = false, pos = state.bMarks[startLine] + state.tShift[startLine], max = state.eMarks[startLine]; if (pos + 3 > max) { return false; } marker = state.src.charCodeAt(pos); if (marker !== 0x7E/* ~ */ && marker !== 0x60 /* ` */) { return false; } // scan marker length mem = pos; pos = state.skipChars(pos, marker); len = pos - mem; if (len < 3) { return false; } params = state.src.slice(pos, max).trim(); if (params.indexOf('`') >= 0) { return false; } // Since start is found, we can report success here in validation mode if (silent) { return true; } // search end of block nextLine = startLine; for (;;) { nextLine++; if (nextLine >= endLine) { // unclosed block should be autoclosed by end of document. // also block seems to be autoclosed by end of parent break; } pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; max = state.eMarks[nextLine]; if (pos < max && state.tShift[nextLine] < state.blkIndent) { // non-empty line with negative indent should stop the list: // - ``` // test break; } if (state.src.charCodeAt(pos) !== marker) { continue; } if (state.tShift[nextLine] - state.blkIndent >= 4) { // closing fence should be indented less than 4 spaces continue; } pos = state.skipChars(pos, marker); // closing code fence must be at least as long as the opening one if (pos - mem < len) { continue; } // make sure tail has spaces only pos = state.skipSpaces(pos); if (pos < max) { continue; } haveEndMarker = true; // found! break; } // If a fence has heading spaces, they should be removed from its inner block len = state.tShift[startLine]; state.line = nextLine + (haveEndMarker ? 1 : 0); state.tokens.push({ type: 'fence', params: params, content: state.getLines(startLine + 1, nextLine, len, true), lines: [ startLine, state.line ], level: state.level }); return true; }; },{}],22:[function(require,module,exports){ // heading (#, ##, ...) 'use strict'; module.exports = function heading(state, startLine, endLine, silent) { var ch, level, tmp, pos = state.bMarks[startLine] + state.tShift[startLine], max = state.eMarks[startLine]; ch = state.src.charCodeAt(pos); if (ch !== 0x23/* # */ || pos >= max) { return false; } // count heading level level = 1; ch = state.src.charCodeAt(++pos); while (ch === 0x23/* # */ && pos < max && level <= 6) { level++; ch = state.src.charCodeAt(++pos); } if (level > 6 || (pos < max && ch !== 0x20/* space */)) { return false; } if (silent) { return true; } // Let's cut tails like ' ### ' from the end of string max = state.skipCharsBack(max, 0x20, pos); // space tmp = state.skipCharsBack(max, 0x23, pos); // # if (tmp > pos && state.src.charCodeAt(tmp - 1) === 0x20/* space */) { max = tmp; } state.line = startLine + 1; state.tokens.push({ type: 'heading_open', hLevel: level, lines: [ startLine, state.line ], level: state.level }); // only if header is not empty if (pos < max) { state.tokens.push({ type: 'inline', content: state.src.slice(pos, max).trim(), level: state.level + 1, lines: [ startLine, state.line ], children: [] }); } state.tokens.push({ type: 'heading_close', hLevel: level, level: state.level }); return true; }; },{}],23:[function(require,module,exports){ // Horizontal rule 'use strict'; module.exports = function hr(state, startLine, endLine, silent) { var marker, cnt, ch, pos = state.bMarks[startLine] + state.tShift[startLine], max = state.eMarks[startLine]; marker = state.src.charCodeAt(pos++); // Check hr marker if (marker !== 0x2A/* * */ && marker !== 0x2D/* - */ && marker !== 0x5F/* _ */) { return false; } // markers can be mixed with spaces, but there should be at least 3 one cnt = 1; while (pos < max) { ch = state.src.charCodeAt(pos++); if (ch !== marker && ch !== 0x20/* space */) { return false; } if (ch === marker) { cnt++; } } if (cnt < 3) { return false; } if (silent) { return true; } state.line = startLine + 1; state.tokens.push({ type: 'hr', lines: [ startLine, state.line ], level: state.level }); return true; }; },{}],24:[function(require,module,exports){ // HTML block 'use strict'; var block_names = require('../common/html_blocks'); var HTML_TAG_OPEN_RE = /^<([a-zA-Z]{1,15})[\s\/>]/; var HTML_TAG_CLOSE_RE = /^<\/([a-zA-Z]{1,15})[\s>]/; function isLetter(ch) { /*eslint no-bitwise:0*/ var lc = ch | 0x20; // to lower case return (lc >= 0x61/* a */) && (lc <= 0x7a/* z */); } module.exports = function html_block(state, startLine, endLine, silent) { var ch, match, nextLine, pos = state.bMarks[startLine], max = state.eMarks[startLine], shift = state.tShift[startLine]; pos += shift; if (!state.md.options.html) { return false; } if (shift > 3 || pos + 2 >= max) { return false; } if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; } ch = state.src.charCodeAt(pos + 1); if (ch === 0x21/* ! */ || ch === 0x3F/* ? */) { // Directive start / comment start / processing instruction start if (silent) { return true; } } else if (ch === 0x2F/* / */ || isLetter(ch)) { // Probably start or end of tag if (ch === 0x2F/* \ */) { // closing tag match = state.src.slice(pos, max).match(HTML_TAG_CLOSE_RE); if (!match) { return false; } } else { // opening tag match = state.src.slice(pos, max).match(HTML_TAG_OPEN_RE); if (!match) { return false; } } // Make sure tag name is valid if (block_names[match[1].toLowerCase()] !== true) { return false; } if (silent) { return true; } } else { return false; } // If we are here - we detected HTML block. // Let's roll down till empty line (block end). nextLine = startLine + 1; while (nextLine < state.lineMax && !state.isEmpty(nextLine)) { nextLine++; } state.line = nextLine; state.tokens.push({ type: 'html_block', level: state.level, lines: [ startLine, state.line ], content: state.getLines(startLine, nextLine, 0, true) }); return true; }; },{"../common/html_blocks":2}],25:[function(require,module,exports){ // lheading (---, ===) 'use strict'; module.exports = function lheading(state, startLine, endLine/*, silent*/) { var marker, pos, max, next = startLine + 1; if (next >= endLine) { return false; } if (state.tShift[next] < state.blkIndent) { return false; } // Scan next line if (state.tShift[next] - state.blkIndent > 3) { return false; } pos = state.bMarks[next] + state.tShift[next]; max = state.eMarks[next]; if (pos >= max) { return false; } marker = state.src.charCodeAt(pos); if (marker !== 0x2D/* - */ && marker !== 0x3D/* = */) { return false; } pos = state.skipChars(pos, marker); pos = state.skipSpaces(pos); if (pos < max) { return false; } pos = state.bMarks[startLine] + state.tShift[startLine]; state.line = next + 1; state.tokens.push({ type: 'heading_open', hLevel: marker === 0x3D/* = */ ? 1 : 2, lines: [ startLine, state.line ], level: state.level }); state.tokens.push({ type: 'inline', content: state.src.slice(pos, state.eMarks[startLine]).trim(), level: state.level + 1, lines: [ startLine, state.line - 1 ], children: [] }); state.tokens.push({ type: 'heading_close', hLevel: marker === 0x3D/* = */ ? 1 : 2, level: state.level }); return true; }; },{}],26:[function(require,module,exports){ // Lists 'use strict'; // Search `[-+*][\n ]`, returns next pos arter marker on success // or -1 on fail. function skipBulletListMarker(state, startLine) { var marker, pos, max; pos = state.bMarks[startLine] + state.tShift[startLine]; max = state.eMarks[startLine]; marker = state.src.charCodeAt(pos++); // Check bullet if (marker !== 0x2A/* * */ && marker !== 0x2D/* - */ && marker !== 0x2B/* + */) { return -1; } if (pos < max && state.src.charCodeAt(pos) !== 0x20) { // " 1.test " - is not a list item return -1; } return pos; } // Search `\d+[.)][\n ]`, returns next pos arter marker on success // or -1 on fail. function skipOrderedListMarker(state, startLine) { var ch, pos = state.bMarks[startLine] + state.tShift[startLine], max = state.eMarks[startLine]; // List marker should have at least 2 chars (digit + dot) if (pos + 1 >= max) { return -1; } ch = state.src.charCodeAt(pos++); if (ch < 0x30/* 0 */ || ch > 0x39/* 9 */) { return -1; } for (;;) { // EOL -> fail if (pos >= max) { return -1; } ch = state.src.charCodeAt(pos++); if (ch >= 0x30/* 0 */ && ch <= 0x39/* 9 */) { continue; } // found valid marker if (ch === 0x29/* ) */ || ch === 0x2e/* . */) { break; } return -1; } if (pos < max && state.src.charCodeAt(pos) !== 0x20/* space */) { // " 1.test " - is not a list item return -1; } return pos; } function markTightParagraphs(state, idx) { var i, l, level = state.level + 2; for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) { if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') { state.tokens[i + 2].tight = true; state.tokens[i].tight = true; i += 2; } } } module.exports = function list(state, startLine, endLine, silent) { var nextLine, indent, oldTShift, oldIndent, oldTight, oldParentType, start, posAfterMarker, max, indentAfterMarker, markerValue, markerCharCode, isOrdered, contentStart, listTokIdx, prevEmptyEnd, listLines, itemLines, tight = true, terminatorRules, i, l, terminate; // Detect list type and position after marker if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0) { isOrdered = true; } else if ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0) { isOrdered = false; } else { return false; } // We should terminate list on style change. Remember first one to compare. markerCharCode = state.src.charCodeAt(posAfterMarker - 1); // For validation mode we can terminate immediately if (silent) { return true; } // Start list listTokIdx = state.tokens.length; if (isOrdered) { start = state.bMarks[startLine] + state.tShift[startLine]; markerValue = Number(state.src.substr(start, posAfterMarker - start - 1)); state.tokens.push({ type: 'ordered_list_open', order: markerValue, lines: listLines = [ startLine, 0 ], level: state.level++ }); } else { state.tokens.push({ type: 'bullet_list_open', lines: listLines = [ startLine, 0 ], level: state.level++ }); } // // Iterate list items // nextLine = startLine; prevEmptyEnd = false; terminatorRules = state.md.block.ruler.getRules('list'); while (nextLine < endLine) { contentStart = state.skipSpaces(posAfterMarker); max = state.eMarks[nextLine]; if (contentStart >= max) { // trimming space in "- \n 3" case, indent is 1 here indentAfterMarker = 1; } else { indentAfterMarker = contentStart - posAfterMarker; } // If we have more than 4 spaces, the indent is 1 // (the rest is just indented code block) if (indentAfterMarker > 4) { indentAfterMarker = 1; } // " - test" // ^^^^^ - calculating total length of this thing indent = (posAfterMarker - state.bMarks[nextLine]) + indentAfterMarker; // Run subparser & write tokens state.tokens.push({ type: 'list_item_open', lines: itemLines = [ startLine, 0 ], level: state.level++ }); oldIndent = state.blkIndent; oldTight = state.tight; oldTShift = state.tShift[startLine]; oldParentType = state.parentType; state.tShift[startLine] = contentStart - state.bMarks[startLine]; state.blkIndent = indent; state.tight = true; state.parentType = 'list'; state.md.block.tokenize(state, startLine, endLine, true); // If any of list item is tight, mark list as tight if (!state.tight || prevEmptyEnd) { tight = false; } // Item become loose if finish with empty line, // but we should filter last element, because it means list finish prevEmptyEnd = (state.line - startLine) > 1 && state.isEmpty(state.line - 1); state.blkIndent = oldIndent; state.tShift[startLine] = oldTShift; state.tight = oldTight; state.parentType = oldParentType; state.tokens.push({ type: 'list_item_close', level: --state.level }); nextLine = startLine = state.line; itemLines[1] = nextLine; contentStart = state.bMarks[startLine]; if (nextLine >= endLine) { break; } if (state.isEmpty(nextLine)) { break; } // // Try to check if list is terminated or continued. // if (state.tShift[nextLine] < state.blkIndent) { break; } // fail if terminating block found terminate = false; for (i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } if (terminate) { break; } // fail if list has another type if (isOrdered) { posAfterMarker = skipOrderedListMarker(state, nextLine); if (posAfterMarker < 0) { break; } } else { posAfterMarker = skipBulletListMarker(state, nextLine); if (posAfterMarker < 0) { break; } } if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { break; } } // Finilize list state.tokens.push({ type: isOrdered ? 'ordered_list_close' : 'bullet_list_close', level: --state.level }); listLines[1] = nextLine; state.line = nextLine; // mark paragraphs tight if needed if (tight) { markTightParagraphs(state, listTokIdx); } return true; }; },{}],27:[function(require,module,exports){ // Paragraph 'use strict'; module.exports = function paragraph(state, startLine/*, endLine*/) { var endLine, content, terminate, i, l, nextLine = startLine + 1, terminatorRules; endLine = state.lineMax; // jump line-by-line until empty one or EOF if (nextLine < endLine && !state.isEmpty(nextLine)) { terminatorRules = state.md.block.ruler.getRules('paragraph'); for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { // this would be a code block normally, but after paragraph // it's considered a lazy continuation regardless of what's there if (state.tShift[nextLine] - state.blkIndent > 3) { continue; } // Some tags can terminate paragraph without empty line. terminate = false; for (i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } if (terminate) { break; } } } content = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); state.line = nextLine; state.tokens.push({ type: 'paragraph_open', tight: false, lines: [ startLine, state.line ], level: state.level }); state.tokens.push({ type: 'inline', content: content, level: state.level + 1, lines: [ startLine, state.line ], children: [] }); state.tokens.push({ type: 'paragraph_close', tight: false, level: state.level }); return true; }; },{}],28:[function(require,module,exports){ 'use strict'; var parseLinkDestination = require('../helpers/parse_link_destination'); var parseLinkTitle = require('../helpers/parse_link_title'); var normalizeReference = require('../common/utils').normalizeReference; module.exports = function reference(state, startLine, _endLine, silent) { var ch, destEndPos, destEndLineNo, endLine, href, i, l, label, labelEnd, res, start, str, terminate, terminatorRules, title, lines = 0, pos = state.bMarks[startLine] + state.tShift[startLine], max = state.eMarks[startLine], nextLine = startLine + 1; if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false; } // Simple check to quickly interrupt scan on [link](url) at the start of line. // Can be useful on practice: https://github.com/markdown-it/markdown-it/issues/54 while (++pos < max) { if (state.src.charCodeAt(pos) === 0x5D /* ] */ && state.src.charCodeAt(pos - 1) !== 0x5C/* \ */) { if (pos + 1 === max) { return false; } if (state.src.charCodeAt(pos + 1) !== 0x3A/* : */) { return false; } break; } } endLine = state.lineMax; // jump line-by-line until empty one or EOF if (nextLine < endLine && !state.isEmpty(nextLine)) { terminatorRules = state.md.block.ruler.getRules('reference'); for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { // this would be a code block normally, but after paragraph // it's considered a lazy continuation regardless of what's there if (state.tShift[nextLine] - state.blkIndent > 3) { continue; } // Some tags can terminate paragraph without empty line. terminate = false; for (i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } if (terminate) { break; } } } str = state.getLines(startLine, nextLine, state.blkIndent, false).trim(); max = str.length; for (pos = 1; pos < max; pos++) { ch = str.charCodeAt(pos); if (ch === 0x5B /* [ */) { return false; } else if (ch === 0x5D /* ] */) { labelEnd = pos; break; } else if (ch === 0x0A /* \n */) { lines++; } else if (ch === 0x5C /* \ */) { pos++; if (pos < max && str.charCodeAt(pos) === 0x0A) { lines++; } } } if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return false; } // [label]: destination 'title' // ^^^ skip optional whitespace here for (pos = labelEnd + 2; pos < max; pos++) { ch = str.charCodeAt(pos); if (ch === 0x0A) { lines++; } else if (ch === 0x20) { /*eslint no-empty:0*/ } else { break; } } // [label]: destination 'title' // ^^^^^^^^^^^ parse this res = parseLinkDestination(str, pos, max); if (!res.ok) { return false; } if (!state.md.inline.validateLink(res.str)) { return false; } href = res.str; pos = res.pos; lines += res.lines; // save cursor state, we could require to rollback later destEndPos = pos; destEndLineNo = lines; // [label]: destination 'title' // ^^^ skipping those spaces start = pos; for (; pos < max; pos++) { ch = str.charCodeAt(pos); if (ch === 0x0A) { lines++; } else if (ch === 0x20) { /*eslint no-empty:0*/ } else { break; } } // [label]: destination 'title' // ^^^^^^^ parse this res = parseLinkTitle(str, pos, max); if (pos < max && start !== pos && res.ok) { title = res.str; pos = res.pos; lines += res.lines; } else { title = ''; pos = destEndPos; lines = destEndLineNo; } // skip trailing spaces until the rest of the line while (pos < max && str.charCodeAt(pos) === 0x20/* space */) { pos++; } if (pos < max && str.charCodeAt(pos) !== 0x0A) { // garbage at the end of the line return false; } // Reference can not terminate anything. This check is for safety only. /*istanbul ignore if*/ if (silent) { return true; } label = normalizeReference(str.slice(1, labelEnd)); if (typeof state.env.references === 'undefined') { state.env.references = {}; } if (typeof state.env.references[label] === 'undefined') { state.env.references[label] = { title: title, href: href }; } state.line = startLine + lines + 1; return true; }; },{"../common/utils":5,"../helpers/parse_link_destination":7,"../helpers/parse_link_title":9}],29:[function(require,module,exports){ // Parser state class 'use strict'; function StateBlock(src, md, env, tokens) { var ch, s, start, pos, len, indent, indent_found; this.src = src; // link to parser instance this.md = md; this.env = env; // // Internal state vartiables // this.tokens = tokens; this.bMarks = []; // line begin offsets for fast jumps this.eMarks = []; // line end offsets for fast jumps this.tShift = []; // indent for each line // block parser variables this.blkIndent = 0; // required block content indent // (for example, if we are in list) this.line = 0; // line index in src this.lineMax = 0; // lines count this.tight = false; // loose/tight mode for lists this.parentType = 'root'; // if `list`, block parser stops on two newlines this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any) this.level = 0; // renderer this.result = ''; // Create caches // Generate markers. s = this.src; indent = 0; indent_found = false; for (start = pos = indent = 0, len = s.length; pos < len; pos++) { ch = s.charCodeAt(pos); if (!indent_found) { if (ch === 0x20/* space */) { indent++; continue; } else { indent_found = true; } } if (ch === 0x0A || pos === len - 1) { if (ch !== 0x0A) { pos++; } this.bMarks.push(start); this.eMarks.push(pos); this.tShift.push(indent); indent_found = false; indent = 0; start = pos + 1; } } // Push fake entry to simplify cache bounds checks this.bMarks.push(s.length); this.eMarks.push(s.length); this.tShift.push(0); this.lineMax = this.bMarks.length - 1; // don't count last fake line } StateBlock.prototype.isEmpty = function isEmpty(line) { return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]; }; StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) { for (var max = this.lineMax; from < max; from++) { if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) { break; } } return from; }; // Skip spaces from given position. StateBlock.prototype.skipSpaces = function skipSpaces(pos) { for (var max = this.src.length; pos < max; pos++) { if (this.src.charCodeAt(pos) !== 0x20/* space */) { break; } } return pos; }; // Skip char codes from given position StateBlock.prototype.skipChars = function skipChars(pos, code) { for (var max = this.src.length; pos < max; pos++) { if (this.src.charCodeAt(pos) !== code) { break; } } return pos; }; // Skip char codes reverse from given position - 1 StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) { if (pos <= min) { return pos; } while (pos > min) { if (code !== this.src.charCodeAt(--pos)) { return pos + 1; } } return pos; }; // cut lines range from source. StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) { var i, first, last, queue, shift, line = begin; if (begin >= end) { return ''; } // Opt: don't use push queue for single line; if (line + 1 === end) { first = this.bMarks[line] + Math.min(this.tShift[line], indent); last = keepLastLF ? this.bMarks[end] : this.eMarks[end - 1]; return this.src.slice(first, last); } queue = new Array(end - begin); for (i = 0; line < end; line++, i++) { shift = this.tShift[line]; if (shift > indent) { shift = indent; } if (shift < 0) { shift = 0; } first = this.bMarks[line] + shift; if (line + 1 < end || keepLastLF) { // No need for bounds check because we have fake entry on tail. last = this.eMarks[line] + 1; } else { last = this.eMarks[line]; } queue[i] = this.src.slice(first, last); } return queue.join(''); }; module.exports = StateBlock; },{}],30:[function(require,module,exports){ // GFM table, non-standard 'use strict'; function getLine(state, line) { var pos = state.bMarks[line] + state.blkIndent, max = state.eMarks[line]; return state.src.substr(pos, max - pos); } function escapedSplit(str) { var result = [], pos = 0, max = str.length, ch, escapes = 0, lastPos = 0; ch = str.charCodeAt(pos); while (pos < max) { if (ch === 0x7c/* | */ && (escapes % 2 === 0)) { result.push(str.substring(lastPos, pos)); lastPos = pos + 1; } else if (ch === 0x5c/* \ */) { escapes++; } else { escapes = 0; } ch = str.charCodeAt(++pos); } result.push(str.substring(lastPos)); return result; } module.exports = function table(state, startLine, endLine, silent) { var ch, lineText, pos, i, nextLine, rows, aligns, t, tableLines, tbodyLines; // should have at least three lines if (startLine + 2 > endLine) { return false; } nextLine = startLine + 1; if (state.tShift[nextLine] < state.blkIndent) { return false; } // first character of the second line should be '|' or '-' pos = state.bMarks[nextLine] + state.tShift[nextLine]; if (pos >= state.eMarks[nextLine]) { return false; } ch = state.src.charCodeAt(pos); if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */) { return false; } lineText = getLine(state, startLine + 1); if (!/^[-:| ]+$/.test(lineText)) { return false; } rows = lineText.split('|'); if (rows.length < 2) { return false; } aligns = []; for (i = 0; i < rows.length; i++) { t = rows[i].trim(); if (!t) { // allow empty columns before and after table, but not in between columns; // e.g. allow ` |---| `, disallow ` ---||--- ` if (i === 0 || i === rows.length - 1) { continue; } else { return false; } } if (!/^:?-+:?$/.test(t)) { return false; } if (t.charCodeAt(t.length - 1) === 0x3A/* : */) { aligns.push(t.charCodeAt(0) === 0x3A/* : */ ? 'center' : 'right'); } else if (t.charCodeAt(0) === 0x3A/* : */) { aligns.push('left'); } else { aligns.push(''); } } lineText = getLine(state, startLine).trim(); if (lineText.indexOf('|') === -1) { return false; } rows = escapedSplit(lineText.replace(/^\||\|$/g, '')); if (aligns.length !== rows.length) { return false; } if (silent) { return true; } state.tokens.push({ type: 'table_open', lines: tableLines = [ startLine, 0 ], level: state.level++ }); state.tokens.push({ type: 'thead_open', lines: [ startLine, startLine + 1 ], level: state.level++ }); state.tokens.push({ type: 'tr_open', lines: [ startLine, startLine + 1 ], level: state.level++ }); for (i = 0; i < rows.length; i++) { state.tokens.push({ type: 'th_open', align: aligns[i], lines: [ startLine, startLine + 1 ], level: state.level++ }); state.tokens.push({ type: 'inline', content: rows[i].trim(), lines: [ startLine, startLine + 1 ], level: state.level, children: [] }); state.tokens.push({ type: 'th_close', level: --state.level }); } state.tokens.push({ type: 'tr_close', level: --state.level }); state.tokens.push({ type: 'thead_close', level: --state.level }); state.tokens.push({ type: 'tbody_open', lines: tbodyLines = [ startLine + 2, 0 ], level: state.level++ }); for (nextLine = startLine + 2; nextLine < endLine; nextLine++) { if (state.tShift[nextLine] < state.blkIndent) { break; } lineText = getLine(state, nextLine).trim(); if (lineText.indexOf('|') === -1) { break; } rows = escapedSplit(lineText.replace(/^\||\|$/g, '')); // set number of columns to number of columns in header row rows.length = aligns.length; state.tokens.push({ type: 'tr_open', level: state.level++ }); for (i = 0; i < rows.length; i++) { state.tokens.push({ type: 'td_open', align: aligns[i], level: state.level++ }); state.tokens.push({ type: 'inline', content: rows[i] ? rows[i].trim() : '', level: state.level, children: [] }); state.tokens.push({ type: 'td_close', level: --state.level }); } state.tokens.push({ type: 'tr_close', level: --state.level }); } state.tokens.push({ type: 'tbody_close', level: --state.level }); state.tokens.push({ type: 'table_close', level: --state.level }); tableLines[1] = tbodyLines[1] = nextLine; state.line = nextLine; return true; }; },{}],31:[function(require,module,exports){ 'use strict'; module.exports = function block(state) { if (state.inlineMode) { state.tokens.push({ type: 'inline', content: state.src, level: 0, lines: [ 0, 1 ], children: [] }); } else { state.md.block.parse(state.src, state.md, state.env, state.tokens); } }; },{}],32:[function(require,module,exports){ 'use strict'; module.exports = function inline(state) { var tokens = state.tokens, tok, i, l; // Parse inlines for (i = 0, l = tokens.length; i < l; i++) { tok = tokens[i]; if (tok.type === 'inline') { state.md.inline.parse(tok.content, state.md, state.env, tok.children); } } }; },{}],33:[function(require,module,exports){ // Replace link-like texts with link nodes. // // Currently restricted by `inline.validateLink()` to http/https/ftp // 'use strict'; var arrayReplaceAt = require('../common/utils').arrayReplaceAt; var normalizeLink = require('../common/utils').normalizeLink; function isLinkOpen(str) { return /^\s]/i.test(str); } function isLinkClose(str) { return /^<\/a\s*>/i.test(str); } module.exports = function linkify(state) { var i, j, l, tokens, token, nodes, ln, text, pos, lastPos, level, htmlLinkLevel, blockTokens = state.tokens, links; if (!state.md.options.linkify) { return; } for (j = 0, l = blockTokens.length; j < l; j++) { if (blockTokens[j].type !== 'inline') { continue; } tokens = blockTokens[j].children; htmlLinkLevel = 0; // We scan from the end, to keep position when new tags added. // Use reversed logic in links start/end match for (i = tokens.length - 1; i >= 0; i--) { token = tokens[i]; // Skip content of markdown links if (token.type === 'link_close') { i--; while (tokens[i].level !== token.level && tokens[i].type !== 'link_open') { i--; } continue; } // Skip content of html tag links if (token.type === 'html_inline') { if (isLinkOpen(token.content) && htmlLinkLevel > 0) { htmlLinkLevel--; } if (isLinkClose(token.content)) { htmlLinkLevel++; } } if (htmlLinkLevel > 0) { continue; } if (token.type === 'text' && state.md.linkify.test(token.content)) { text = token.content; links = state.md.linkify.match(text); // Now split string to nodes nodes = []; level = token.level; lastPos = 0; for (ln = 0; ln < links.length; ln++) { if (!state.md.inline.validateLink(links[ln].url)) { continue; } pos = links[ln].index; if (pos > lastPos) { level = level; nodes.push({ type: 'text', content: text.slice(lastPos, pos), level: level }); } nodes.push({ type: 'link_open', href: normalizeLink(links[ln].url), target: '', title: '', level: level++ }); nodes.push({ type: 'text', content: links[ln].text, level: level }); nodes.push({ type: 'link_close', level: --level }); lastPos = links[ln].lastIndex; } if (lastPos < text.length) { nodes.push({ type: 'text', content: text.slice(lastPos), level: level }); } // replace current node blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes); } } } }; },{"../common/utils":5}],34:[function(require,module,exports){ // Normalize input string 'use strict'; var TABS_SCAN_RE = /[\n\t]/g; var NEWLINES_RE = /\r[\n\u0085]|[\u2424\u2028\u0085]/g; var NULL_RE = /\u0000/g; module.exports = function inline(state) { var str, lineStart, lastTabPos; // Normalize newlines str = state.src.replace(NEWLINES_RE, '\n'); // Replace NULL characters str = str.replace(NULL_RE, '\uFFFD'); // Replace tabs with proper number of spaces (1..4) if (str.indexOf('\t') >= 0) { lineStart = 0; lastTabPos = 0; str = str.replace(TABS_SCAN_RE, function (match, offset) { var result; if (str.charCodeAt(offset) === 0x0A) { lineStart = offset + 1; lastTabPos = 0; return match; } result = ' '.slice((offset - lineStart - lastTabPos) % 4); lastTabPos = offset - lineStart + 1; return result; }); } state.src = str; }; },{}],35:[function(require,module,exports){ // Simple typographyc replacements // // '' → ‘’ // "" → “”. Set '«»' for Russian, '„“' for German, empty to disable // (c) (C) → © // (tm) (TM) → ™ // (r) (R) → ® // +- → ± // (p) (P) -> § // ... → … (also ?.... → ?.., !.... → !..) // ???????? → ???, !!!!! → !!!, `,,` → `,` // -- → –, --- → — // 'use strict'; // TODO: // - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾ // - miltiplication 2 x 4 -> 2 × 4 var RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/; var SCOPED_ABBR_RE = /\((c|tm|r|p)\)/ig; var SCOPED_ABBR = { 'c': '©', 'r': '®', 'p': '§', 'tm': '™' }; function replaceScopedAbbr(str) { if (str.indexOf('(') < 0) { return str; } return str.replace(SCOPED_ABBR_RE, function(match, name) { return SCOPED_ABBR[name.toLowerCase()]; }); } module.exports = function replace(state) { var i, token, text, inlineTokens, blkIdx; if (!state.md.options.typographer) { return; } for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { if (state.tokens[blkIdx].type !== 'inline') { continue; } inlineTokens = state.tokens[blkIdx].children; for (i = inlineTokens.length - 1; i >= 0; i--) { token = inlineTokens[i]; if (token.type === 'text') { text = token.content; text = replaceScopedAbbr(text); if (RARE_RE.test(text)) { text = text.replace(/\+-/g, '±') // .., ..., ....... -> … // but ?..... & !..... -> ?.. & !.. .replace(/\.{2,}/g, '…').replace(/([?!])…/g, '$1..') .replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ',') // em-dash .replace(/(^|[^-])---([^-]|$)/mg, '$1\u2014$2') // en-dash .replace(/(^|\s)--(\s|$)/mg, '$1\u2013$2') .replace(/(^|[^-\s])--([^-\s]|$)/mg, '$1\u2013$2'); } token.content = text; } } } }; },{}],36:[function(require,module,exports){ // Convert straight quotation marks to typographic ones // 'use strict'; var isWhiteSpace = require('../common/utils').isWhiteSpace; var isPunctChar = require('../common/utils').isPunctChar; var isMdAsciiPunct = require('../common/utils').isMdAsciiPunct; var QUOTE_TEST_RE = /['"]/; var QUOTE_RE = /['"]/g; var APOSTROPHE = '\u2019'; /* ’ */ function replaceAt(str, index, ch) { return str.substr(0, index) + ch + str.substr(index + 1); } module.exports = function smartquotes(state) { /*eslint max-depth:0*/ var i, token, text, t, pos, max, thisLevel, item, lastChar, nextChar, isLastPunctChar, isNextPunctChar, isLastWhiteSpace, isNextWhiteSpace, canOpen, canClose, j, isSingle, blkIdx, tokens, stack; if (!state.md.options.typographer) { return; } stack = []; for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { if (state.tokens[blkIdx].type !== 'inline') { continue; } tokens = state.tokens[blkIdx].children; stack.length = 0; for (i = 0; i < tokens.length; i++) { token = tokens[i]; if (token.type !== 'text' || QUOTE_TEST_RE.test(token.text)) { continue; } thisLevel = tokens[i].level; for (j = stack.length - 1; j >= 0; j--) { if (stack[j].level <= thisLevel) { break; } } stack.length = j + 1; text = token.content; pos = 0; max = text.length; /*eslint no-labels:0,block-scoped-var:0*/ OUTER: while (pos < max) { QUOTE_RE.lastIndex = pos; t = QUOTE_RE.exec(text); if (!t) { break; } canOpen = canClose = true; pos = t.index + 1; isSingle = (t[0] === "'"); lastChar = t.index - 1 >= 0 ? text.charCodeAt(t.index - 1) : -1; nextChar = pos < max ? text.charCodeAt(pos) : -1; isLastPunctChar = lastChar >= 0 && (isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar))); isNextPunctChar = nextChar >= 0 && (isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar))); // begin/end of the line counts as a whitespace too isLastWhiteSpace = lastChar < 0 || isWhiteSpace(lastChar); isNextWhiteSpace = nextChar < 0 || isWhiteSpace(nextChar); if (isNextWhiteSpace) { canOpen = false; } else if (isNextPunctChar) { if (!(isLastWhiteSpace || isLastPunctChar)) { canOpen = false; } } if (isLastWhiteSpace) { canClose = false; } else if (isLastPunctChar) { if (!(isNextWhiteSpace || isNextPunctChar)) { canClose = false; } } if (nextChar === 0x22 /* " */ && t[0] === '"') { if (lastChar >= 0x30 /* 0 */ && lastChar <= 0x39 /* 9 */) { // special case: 1"" - count first quote as an inch canClose = canOpen = false; } } if (canOpen && canClose) { // treat this as the middle of the word canOpen = canClose = false; } if (!canOpen && !canClose) { // middle of word if (isSingle) { token.content = replaceAt(token.content, t.index, APOSTROPHE); } continue; } if (canClose) { // this could be a closing quote, rewind the stack to get a match for (j = stack.length - 1; j >= 0; j--) { item = stack[j]; if (stack[j].level < thisLevel) { break; } if (item.single === isSingle && stack[j].level === thisLevel) { item = stack[j]; if (isSingle) { tokens[item.token].content = replaceAt( tokens[item.token].content, item.pos, state.md.options.quotes[2]); token.content = replaceAt( token.content, t.index, state.md.options.quotes[3]); } else { tokens[item.token].content = replaceAt( tokens[item.token].content, item.pos, state.md.options.quotes[0]); token.content = replaceAt(token.content, t.index, state.md.options.quotes[1]); } stack.length = j; continue OUTER; } } } if (canOpen) { stack.push({ token: i, pos: t.index, single: isSingle, level: thisLevel }); } else if (canClose && isSingle) { token.content = replaceAt(token.content, t.index, APOSTROPHE); } } } } }; },{"../common/utils":5}],37:[function(require,module,exports){ // Core state object // 'use strict'; module.exports = function StateCore(src, md, env) { this.src = src; this.env = env; this.tokens = []; this.inlineMode = false; this.md = md; // link to parser instance }; },{}],38:[function(require,module,exports){ // Process autolinks '' 'use strict'; var url_schemas = require('../common/url_schemas'); var normalizeLink = require('../common/utils').normalizeLink; /*eslint max-len:0*/ var EMAIL_RE = /^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/; var AUTOLINK_RE = /^<([a-zA-Z.\-]{1,25}):([^<>\x00-\x20]*)>/; module.exports = function autolink(state, silent) { var tail, linkMatch, emailMatch, url, fullUrl, pos = state.pos; if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false; } tail = state.src.slice(pos); if (tail.indexOf('>') < 0) { return false; } linkMatch = tail.match(AUTOLINK_RE); if (linkMatch) { if (url_schemas.indexOf(linkMatch[1].toLowerCase()) < 0) { return false; } url = linkMatch[0].slice(1, -1); fullUrl = normalizeLink(url); if (!state.md.inline.validateLink(url)) { return false; } if (!silent) { state.push({ type: 'link_open', href: fullUrl, target: '', level: state.level }); state.push({ type: 'text', content: url, level: state.level + 1 }); state.push({ type: 'link_close', level: state.level }); } state.pos += linkMatch[0].length; return true; } emailMatch = tail.match(EMAIL_RE); if (emailMatch) { url = emailMatch[0].slice(1, -1); fullUrl = normalizeLink('mailto:' + url); if (!state.md.inline.validateLink(fullUrl)) { return false; } if (!silent) { state.push({ type: 'link_open', href: fullUrl, target: '', level: state.level }); state.push({ type: 'text', content: url, level: state.level + 1 }); state.push({ type: 'link_close', level: state.level }); } state.pos += emailMatch[0].length; return true; } return false; }; },{"../common/url_schemas":4,"../common/utils":5}],39:[function(require,module,exports){ // Parse backticks 'use strict'; module.exports = function backtick(state, silent) { var start, max, marker, matchStart, matchEnd, pos = state.pos, ch = state.src.charCodeAt(pos); if (ch !== 0x60/* ` */) { return false; } start = pos; pos++; max = state.posMax; while (pos < max && state.src.charCodeAt(pos) === 0x60/* ` */) { pos++; } marker = state.src.slice(start, pos); matchStart = matchEnd = pos; while ((matchStart = state.src.indexOf('`', matchEnd)) !== -1) { matchEnd = matchStart + 1; while (matchEnd < max && state.src.charCodeAt(matchEnd) === 0x60/* ` */) { matchEnd++; } if (matchEnd - matchStart === marker.length) { if (!silent) { state.push({ type: 'code_inline', content: state.src.slice(pos, matchStart) .replace(/[ \n]+/g, ' ') .trim(), level: state.level }); } state.pos = matchEnd; return true; } } if (!silent) { state.pending += marker; } state.pos += marker.length; return true; }; },{}],40:[function(require,module,exports){ // Process *this* and _that_ // 'use strict'; var isWhiteSpace = require('../common/utils').isWhiteSpace; var isPunctChar = require('../common/utils').isPunctChar; var isMdAsciiPunct = require('../common/utils').isMdAsciiPunct; // parse sequence of emphasis markers, // "start" should point at a valid marker function scanDelims(state, start) { var pos = start, lastChar, nextChar, count, isLastWhiteSpace, isLastPunctChar, isNextWhiteSpace, isNextPunctChar, can_open = true, can_close = true, max = state.posMax, marker = state.src.charCodeAt(start); lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; } if (pos >= max) { can_open = false; } count = pos - start; nextChar = pos < max ? state.src.charCodeAt(pos) : -1; isLastPunctChar = lastChar >= 0 && (isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar))); isNextPunctChar = nextChar >= 0 && (isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar))); // begin/end of the line counts as a whitespace too isLastWhiteSpace = lastChar < 0 || isWhiteSpace(lastChar); isNextWhiteSpace = nextChar < 0 || isWhiteSpace(nextChar); if (isNextWhiteSpace) { can_open = false; } else if (isNextPunctChar) { if (!(isLastWhiteSpace || isLastPunctChar)) { can_open = false; } } if (isLastWhiteSpace) { can_close = false; } else if (isLastPunctChar) { if (!(isNextWhiteSpace || isNextPunctChar)) { can_close = false; } } if (marker === 0x5F /* _ */) { if (can_open && can_close) { // "_" inside a word can neither open nor close an emphasis can_open = can_close = false; } } return { can_open: can_open, can_close: can_close, delims: count }; } module.exports = function emphasis(state, silent) { var startCount, count, found, oldCount, newCount, stack, res, max = state.posMax, start = state.pos, marker = state.src.charCodeAt(start); if (marker !== 0x5F/* _ */ && marker !== 0x2A /* * */) { return false; } if (silent) { return false; } // don't run any pairs in validation mode res = scanDelims(state, start); startCount = res.delims; if (!res.can_open) { state.pos += startCount; // Earlier we checked !silent, but this implementation does not need it state.pending += state.src.slice(start, state.pos); return true; } state.pos = start + startCount; stack = [ startCount ]; while (state.pos < max) { if (state.src.charCodeAt(state.pos) === marker) { res = scanDelims(state, state.pos); count = res.delims; if (res.can_close) { oldCount = stack.pop(); newCount = count; while (oldCount !== newCount) { if (newCount < oldCount) { stack.push(oldCount - newCount); break; } // assert(newCount > oldCount) newCount -= oldCount; if (stack.length === 0) { break; } state.pos += oldCount; oldCount = stack.pop(); } if (stack.length === 0) { startCount = oldCount; found = true; break; } state.pos += count; continue; } if (res.can_open) { stack.push(count); } state.pos += count; continue; } state.md.inline.skipToken(state); } if (!found) { // parser failed to find ending tag, so it's not valid emphasis state.pos = start; return false; } // found! state.posMax = state.pos; state.pos = start + startCount; // Earlier we checked !silent, but this implementation does not need it // we have `startCount` starting and ending markers, // now trying to serialize them into tokens for (count = startCount; count > 1; count -= 2) { state.push({ type: 'strong_open', level: state.level++ }); } if (count % 2) { state.push({ type: 'em_open', level: state.level++ }); } state.md.inline.tokenize(state); if (count % 2) { state.push({ type: 'em_close', level: --state.level }); } for (count = startCount; count > 1; count -= 2) { state.push({ type: 'strong_close', level: --state.level }); } state.pos = state.posMax + startCount; state.posMax = max; return true; }; },{"../common/utils":5}],41:[function(require,module,exports){ // Process html entity - {, ¯, ", ... 'use strict'; var entities = require('../common/entities'); var has = require('../common/utils').has; var isValidEntityCode = require('../common/utils').isValidEntityCode; var fromCodePoint = require('../common/utils').fromCodePoint; var DIGITAL_RE = /^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i; var NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i; module.exports = function entity(state, silent) { var ch, code, match, pos = state.pos, max = state.posMax; if (state.src.charCodeAt(pos) !== 0x26/* & */) { return false; } if (pos + 1 < max) { ch = state.src.charCodeAt(pos + 1); if (ch === 0x23 /* # */) { match = state.src.slice(pos).match(DIGITAL_RE); if (match) { if (!silent) { code = match[1][0].toLowerCase() === 'x' ? parseInt(match[1].slice(1), 16) : parseInt(match[1], 10); state.pending += isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD); } state.pos += match[0].length; return true; } } else { match = state.src.slice(pos).match(NAMED_RE); if (match) { if (has(entities, match[1])) { if (!silent) { state.pending += entities[match[1]]; } state.pos += match[0].length; return true; } } } } if (!silent) { state.pending += '&'; } state.pos++; return true; }; },{"../common/entities":1,"../common/utils":5}],42:[function(require,module,exports){ // Proceess escaped chars and hardbreaks 'use strict'; var ESCAPED = []; for (var i = 0; i < 256; i++) { ESCAPED.push(0); } '\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-' .split('').forEach(function(ch) { ESCAPED[ch.charCodeAt(0)] = 1; }); module.exports = function escape(state, silent) { var ch, pos = state.pos, max = state.posMax; if (state.src.charCodeAt(pos) !== 0x5C/* \ */) { return false; } pos++; if (pos < max) { ch = state.src.charCodeAt(pos); if (ch < 256 && ESCAPED[ch] !== 0) { if (!silent) { state.pending += state.src[pos]; } state.pos += 2; return true; } if (ch === 0x0A) { if (!silent) { state.push({ type: 'hardbreak', level: state.level }); } pos++; // skip leading whitespaces from next line while (pos < max && state.src.charCodeAt(pos) === 0x20) { pos++; } state.pos = pos; return true; } } if (!silent) { state.pending += '\\'; } state.pos++; return true; }; },{}],43:[function(require,module,exports){ // Process html tags 'use strict'; var HTML_TAG_RE = require('../common/html_re').HTML_TAG_RE; function isLetter(ch) { /*eslint no-bitwise:0*/ var lc = ch | 0x20; // to lower case return (lc >= 0x61/* a */) && (lc <= 0x7a/* z */); } module.exports = function html_inline(state, silent) { var ch, match, max, pos = state.pos; if (!state.md.options.html) { return false; } // Check start max = state.posMax; if (state.src.charCodeAt(pos) !== 0x3C/* < */ || pos + 2 >= max) { return false; } // Quick fail on second char ch = state.src.charCodeAt(pos + 1); if (ch !== 0x21/* ! */ && ch !== 0x3F/* ? */ && ch !== 0x2F/* / */ && !isLetter(ch)) { return false; } match = state.src.slice(pos).match(HTML_TAG_RE); if (!match) { return false; } if (!silent) { state.push({ type: 'html_inline', content: state.src.slice(pos, pos + match[0].length), level: state.level }); } state.pos += match[0].length; return true; }; },{"../common/html_re":3}],44:[function(require,module,exports){ // Process ![image]( "title") 'use strict'; var parseLinkLabel = require('../helpers/parse_link_label'); var parseLinkDestination = require('../helpers/parse_link_destination'); var parseLinkTitle = require('../helpers/parse_link_title'); var normalizeReference = require('../common/utils').normalizeReference; module.exports = function image(state, silent) { var code, href, label, labelEnd, labelStart, pos, ref, res, title, tokens, start, oldPos = state.pos, max = state.posMax; if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false; } if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false; } labelStart = state.pos + 2; labelEnd = parseLinkLabel(state, state.pos + 1, false); // parser failed to find ']', so it's not a valid link if (labelEnd < 0) { return false; } pos = labelEnd + 1; if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) { // // Inline link // // [link]( "title" ) // ^^ skipping these spaces pos++; for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } if (pos >= max) { return false; } // [link]( "title" ) // ^^^^^^ parsing link destination start = pos; res = parseLinkDestination(state.src, pos, state.posMax); if (res.ok && state.md.inline.validateLink(res.str)) { href = res.str; pos = res.pos; } else { href = ''; } // [link]( "title" ) // ^^ skipping these spaces start = pos; for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } // [link]( "title" ) // ^^^^^^^ parsing link title res = parseLinkTitle(state.src, pos, state.posMax); if (pos < max && start !== pos && res.ok) { title = res.str; pos = res.pos; // [link]( "title" ) // ^^ skipping these spaces for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } } else { title = ''; } if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) { state.pos = oldPos; return false; } pos++; } else { // // Link reference // if (typeof state.env.references === 'undefined') { return false; } // [foo] [bar] // ^^ optional whitespace (can include newlines) for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) { start = pos + 1; pos = parseLinkLabel(state, pos); if (pos >= 0) { label = state.src.slice(start, pos++); } else { pos = labelEnd + 1; } } else { pos = labelEnd + 1; } // covers label === '' and label === undefined // (collapsed reference link and shortcut reference link respectively) if (!label) { label = state.src.slice(labelStart, labelEnd); } ref = state.env.references[normalizeReference(label)]; if (!ref) { state.pos = oldPos; return false; } href = ref.href; title = ref.title; } // // We found the end of the link, and know for a fact it's a valid link; // so all that's left to do is to call tokenizer. // if (!silent) { state.pos = labelStart; state.posMax = labelEnd; var newState = new state.md.inline.State( state.src.slice(labelStart, labelEnd), state.md, state.env, tokens = [] ); newState.md.inline.tokenize(newState); state.push({ type: 'image', src: href, title: title, tokens: tokens, level: state.level }); } state.pos = pos; state.posMax = max; return true; }; },{"../common/utils":5,"../helpers/parse_link_destination":7,"../helpers/parse_link_label":8,"../helpers/parse_link_title":9}],45:[function(require,module,exports){ // Process [link]( "stuff") 'use strict'; var parseLinkLabel = require('../helpers/parse_link_label'); var parseLinkDestination = require('../helpers/parse_link_destination'); var parseLinkTitle = require('../helpers/parse_link_title'); var normalizeReference = require('../common/utils').normalizeReference; module.exports = function link(state, silent) { var code, href, label, labelEnd, labelStart, pos, res, ref, title, oldPos = state.pos, max = state.posMax, start = state.pos; if (state.src.charCodeAt(state.pos) !== 0x5B/* [ */) { return false; } labelStart = state.pos + 1; labelEnd = parseLinkLabel(state, state.pos, true); // parser failed to find ']', so it's not a valid link if (labelEnd < 0) { return false; } pos = labelEnd + 1; if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) { // // Inline link // // [link]( "title" ) // ^^ skipping these spaces pos++; for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } if (pos >= max) { return false; } // [link]( "title" ) // ^^^^^^ parsing link destination start = pos; res = parseLinkDestination(state.src, pos, state.posMax); if (res.ok && state.md.inline.validateLink(res.str)) { href = res.str; pos = res.pos; } else { href = ''; } // [link]( "title" ) // ^^ skipping these spaces start = pos; for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } // [link]( "title" ) // ^^^^^^^ parsing link title res = parseLinkTitle(state.src, pos, state.posMax); if (pos < max && start !== pos && res.ok) { title = res.str; pos = res.pos; // [link]( "title" ) // ^^ skipping these spaces for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } } else { title = ''; } if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) { state.pos = oldPos; return false; } pos++; } else { // // Link reference // if (typeof state.env.references === 'undefined') { return false; } // [foo] [bar] // ^^ optional whitespace (can include newlines) for (; pos < max; pos++) { code = state.src.charCodeAt(pos); if (code !== 0x20 && code !== 0x0A) { break; } } if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) { start = pos + 1; pos = parseLinkLabel(state, pos); if (pos >= 0) { label = state.src.slice(start, pos++); } else { pos = labelEnd + 1; } } else { pos = labelEnd + 1; } // covers label === '' and label === undefined // (collapsed reference link and shortcut reference link respectively) if (!label) { label = state.src.slice(labelStart, labelEnd); } ref = state.env.references[normalizeReference(label)]; if (!ref) { state.pos = oldPos; return false; } href = ref.href; title = ref.title; } // // We found the end of the link, and know for a fact it's a valid link; // so all that's left to do is to call tokenizer. // if (!silent) { state.pos = labelStart; state.posMax = labelEnd; state.push({ type: 'link_open', href: href, target: '', title: title, level: state.level++ }); state.md.inline.tokenize(state); state.push({ type: 'link_close', level: --state.level }); } state.pos = pos; state.posMax = max; return true; }; },{"../common/utils":5,"../helpers/parse_link_destination":7,"../helpers/parse_link_label":8,"../helpers/parse_link_title":9}],46:[function(require,module,exports){ // Proceess '\n' 'use strict'; module.exports = function newline(state, silent) { var pmax, max, pos = state.pos; if (state.src.charCodeAt(pos) !== 0x0A/* \n */) { return false; } pmax = state.pending.length - 1; max = state.posMax; // ' \n' -> hardbreak // Lookup in pending chars is bad practice! Don't copy to other rules! // Pending string is stored in concat mode, indexed lookups will cause // convertion to flat mode. if (!silent) { if (pmax >= 0 && state.pending.charCodeAt(pmax) === 0x20) { if (pmax >= 1 && state.pending.charCodeAt(pmax - 1) === 0x20) { state.pending = state.pending.replace(/ +$/, ''); state.push({ type: 'hardbreak', level: state.level }); } else { state.pending = state.pending.slice(0, -1); state.push({ type: 'softbreak', level: state.level }); } } else { state.push({ type: 'softbreak', level: state.level }); } } pos++; // skip heading spaces for next line while (pos < max && state.src.charCodeAt(pos) === 0x20) { pos++; } state.pos = pos; return true; }; },{}],47:[function(require,module,exports){ // Inline parser state 'use strict'; function StateInline(src, md, env, outTokens) { this.src = src; this.env = env; this.md = md; this.tokens = outTokens; this.pos = 0; this.posMax = this.src.length; this.level = 0; this.pending = ''; this.pendingLevel = 0; this.cache = []; // Stores { start: end } pairs. Useful for backtrack // optimization of pairs parse (emphasis, strikes). } // Flush pending text // StateInline.prototype.pushPending = function () { this.tokens.push({ type: 'text', content: this.pending, level: this.pendingLevel }); this.pending = ''; }; // Push new token to "stream". // If pending text exists - flush it as text token // StateInline.prototype.push = function (token) { if (this.pending) { this.pushPending(); } this.tokens.push(token); this.pendingLevel = this.level; }; // Store value to cache. // !!! Implementation has parser-specific optimizations // !!! keys MUST be integer, >= 0; values MUST be integer, > 0 // StateInline.prototype.cacheSet = function (key, val) { for (var i = this.cache.length; i <= key; i++) { this.cache.push(0); } this.cache[key] = val; }; // Get cache value // StateInline.prototype.cacheGet = function (key) { return key < this.cache.length ? this.cache[key] : 0; }; module.exports = StateInline; },{}],48:[function(require,module,exports){ // ~~strike through~~ // 'use strict'; var isWhiteSpace = require('../common/utils').isWhiteSpace; var isPunctChar = require('../common/utils').isPunctChar; var isMdAsciiPunct = require('../common/utils').isMdAsciiPunct; // parse sequence of markers, // "start" should point at a valid marker function scanDelims(state, start) { var pos = start, lastChar, nextChar, count, isLastWhiteSpace, isLastPunctChar, isNextWhiteSpace, isNextPunctChar, can_open = true, can_close = true, max = state.posMax, marker = state.src.charCodeAt(start); lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; while (pos < max && state.src.charCodeAt(pos) === marker) { pos++; } if (pos >= max) { can_open = false; } count = pos - start; nextChar = pos < max ? state.src.charCodeAt(pos) : -1; isLastPunctChar = lastChar >= 0 && (isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar))); isNextPunctChar = nextChar >= 0 && (isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar))); isLastWhiteSpace = lastChar >= 0 && isWhiteSpace(lastChar); isNextWhiteSpace = nextChar >= 0 && isWhiteSpace(nextChar); if (isNextWhiteSpace) { can_open = false; } else if (isNextPunctChar) { if (!(isLastWhiteSpace || isLastPunctChar || lastChar === -1)) { can_open = false; } } if (isLastWhiteSpace) { can_close = false; } else if (isLastPunctChar) { if (!(isNextWhiteSpace || isNextPunctChar || nextChar === -1)) { can_close = false; } } return { can_open: can_open, can_close: can_close, delims: count }; } module.exports = function strikethrough(state, silent) { var startCount, count, tagCount, found, stack, res, max = state.posMax, start = state.pos, marker = state.src.charCodeAt(start); if (marker !== 0x7E/* ~ */) { return false; } if (silent) { return false; } // don't run any pairs in validation mode res = scanDelims(state, start); startCount = res.delims; if (!res.can_open) { state.pos += startCount; // Earlier we checked !silent, but this implementation does not need it state.pending += state.src.slice(start, state.pos); return true; } stack = Math.floor(startCount / 2); if (stack <= 0) { return false; } state.pos = start + startCount; while (state.pos < max) { if (state.src.charCodeAt(state.pos) === marker) { res = scanDelims(state, state.pos); count = res.delims; tagCount = Math.floor(count / 2); if (res.can_close) { if (tagCount >= stack) { state.pos += count - 2; found = true; break; } stack -= tagCount; state.pos += count; continue; } if (res.can_open) { stack += tagCount; } state.pos += count; continue; } state.md.inline.skipToken(state); } if (!found) { // parser failed to find ending tag, so it's not valid emphasis state.pos = start; return false; } // found! state.posMax = state.pos; state.pos = start + 2; // Earlier we checked !silent, but this implementation does not need it state.push({ type: 's_open', level: state.level++ }); state.md.inline.tokenize(state); state.push({ type: 's_close', level: --state.level }); state.pos = state.posMax + 2; state.posMax = max; return true; }; },{"../common/utils":5}],49:[function(require,module,exports){ // Skip text characters for text token, place those to pending buffer // and increment current pos 'use strict'; // Rule to skip pure text // '{}$%@~+=:' reserved for extentions // !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~ // !!!! Don't confuse with "Markdown ASCII Punctuation" chars // http://spec.commonmark.org/0.15/#ascii-punctuation-character function isTerminatorChar(ch) { switch (ch) { case 0x0A/* \n */: case 0x21/* ! */: case 0x23/* # */: case 0x24/* $ */: case 0x25/* % */: case 0x26/* & */: case 0x2A/* * */: case 0x2B/* + */: case 0x2D/* - */: case 0x3A/* : */: case 0x3C/* < */: case 0x3D/* = */: case 0x3E/* > */: case 0x40/* @ */: case 0x5B/* [ */: case 0x5C/* \ */: case 0x5D/* ] */: case 0x5E/* ^ */: case 0x5F/* _ */: case 0x60/* ` */: case 0x7B/* { */: case 0x7D/* } */: case 0x7E/* ~ */: return true; default: return false; } } module.exports = function text(state, silent) { var pos = state.pos; while (pos < state.posMax && !isTerminatorChar(state.src.charCodeAt(pos))) { pos++; } if (pos === state.pos) { return false; } if (!silent) { state.pending += state.src.slice(state.pos, pos); } state.pos = pos; return true; }; },{}],50:[function(require,module,exports){ 'use strict'; //////////////////////////////////////////////////////////////////////////////// // Helpers // Merge objects // function assign(obj /*from1, from2, from3, ...*/) { var sources = Array.prototype.slice.call(arguments, 1); sources.forEach(function (source) { if (!source) { return; } Object.keys(source).forEach(function (key) { obj[key] = source[key]; }); }); return obj; } function _class(obj) { return Object.prototype.toString.call(obj); } function isString(obj) { return _class(obj) === '[object String]'; } function isObject(obj) { return _class(obj) === '[object Object]'; } function isRegExp(obj) { return _class(obj) === '[object RegExp]'; } function isFunction(obj) { return _class(obj) === '[object Function]'; } function escapeRE (str) { return str.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&'); } //////////////////////////////////////////////////////////////////////////////// var defaultSchemas = { 'http:': { validate: function (text, pos, self) { var tail = text.slice(pos); if (!self.re.http) { // compile lazily, because "host"-containing variables can change on tlds update. self.re.http = new RegExp( '^\\/\\/' + self.re.src_auth + self.re.src_host_port_strict + self.re.src_path, 'i' ); } if (self.re.http.test(tail)) { return tail.match(self.re.http)[0].length; } return 0; } }, 'https:': 'http:', 'ftp:': 'http:', '//': { validate: function (text, pos, self) { var tail = text.slice(pos); if (!self.re.no_http) { // compile lazily, becayse "host"-containing variables can change on tlds update. self.re.no_http = new RegExp( '^' + self.re.src_auth + self.re.src_host_port_strict + self.re.src_path, 'i' ); } if (self.re.no_http.test(tail)) { // should not be `://`, that protects from errors in protocol name if (pos >= 3 && text[pos - 3] === ':') { return 0; } return tail.match(self.re.no_http)[0].length; } return 0; } }, 'mailto:': { validate: function (text, pos, self) { var tail = text.slice(pos); if (!self.re.mailto) { self.re.mailto = new RegExp( '^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i' ); } if (self.re.mailto.test(tail)) { return tail.match(self.re.mailto)[0].length; } return 0; } } }; // DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead var tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'.split('|'); //////////////////////////////////////////////////////////////////////////////// function resetScanCache(self) { self.__index__ = -1; self.__text_cache__ = ''; } function createValidator(re) { return function (text, pos) { var tail = text.slice(pos); if (re.test(tail)) { return tail.match(re)[0].length; } return 0; }; } function createNormalizer() { return function (match, self) { self.normalize(match); }; } // Schemas compiler. Build regexps. // function compile(self) { // Load & clone RE patterns. var re = self.re = assign({}, require('./lib/re')); // Define dynamic patterns var tlds = self.__tlds__.slice(); if (!self.__tlds_replaced__) { tlds.push('[a-z]{2}'); } tlds.push(re.src_xn); re.src_tlds = tlds.join('|'); function untpl(tpl) { return tpl.replace('%TLDS%', re.src_tlds); } re.email_fuzzy = RegExp(untpl(re.tpl_email_fuzzy), 'i'); re.link_fuzzy = RegExp(untpl(re.tpl_link_fuzzy), 'i'); re.host_fuzzy_test = RegExp(untpl(re.tpl_host_fuzzy_test), 'i'); // // Compile each schema // var aliases = []; self.__compiled__ = {}; // Reset compiled data function schemaError(name, val) { throw new Error('(LinkifyIt) Invalid schema "' + name + '": ' + val); } Object.keys(self.__schemas__).forEach(function (name) { var val = self.__schemas__[name]; // skip disabled methods if (val === null) { return; } var compiled = { validate: null, link: null }; self.__compiled__[name] = compiled; if (isObject(val)) { if (isRegExp(val.validate)) { compiled.validate = createValidator(val.validate); } else if (isFunction(val.validate)) { compiled.validate = val.validate; } else { schemaError(name, val); } if (isFunction(val.normalize)) { compiled.normalize = val.normalize; } else if (!val.normalize) { compiled.normalize = createNormalizer(); } else { schemaError(name, val); } return; } if (isString(val)) { aliases.push(name); return; } schemaError(name, val); }); // // Compile postponed aliases // aliases.forEach(function (alias) { if (!self.__compiled__[self.__schemas__[alias]]) { // Silently fail on missed schemas to avoid errons on disable. // schemaError(alias, self.__schemas__[alias]); return; } self.__compiled__[alias].validate = self.__compiled__[self.__schemas__[alias]].validate; self.__compiled__[alias].normalize = self.__compiled__[self.__schemas__[alias]].normalize; }); // // Fake record for guessed links // self.__compiled__[''] = { validate: null, normalize: createNormalizer() }; // // Build schema condition // var slist = Object.keys(self.__compiled__) .filter(function(name) { // Filter disabled & fake schemas return name.length > 0 && self.__compiled__[name]; }) .map(escapeRE) .join('|'); // (?!_) cause 1.5x slowdown self.re.schema_test = RegExp('(^|(?!_)(?:>|' + re.src_ZPCcCf + '))(' + slist + ')', 'i'); self.re.schema_search = RegExp('(^|(?!_)(?:>|' + re.src_ZPCcCf + '))(' + slist + ')', 'ig'); // // Cleanup // resetScanCache(self); } /** * class Match * * Match result. Single element of array, returned by [[LinkifyIt#match]] **/ function Match(self, shift) { var start = self.__index__, end = self.__last_index__, text = self.__text_cache__.slice(start, end); /** * Match#schema -> String * * Prefix (protocol) for matched string. **/ this.schema = self.__schema__.toLowerCase(); /** * Match#index -> Number * * First position of matched string. **/ this.index = start + shift; /** * Match#lastIndex -> Number * * Next position after matched string. **/ this.lastIndex = end + shift; /** * Match#raw -> String * * Matched string. **/ this.raw = text; /** * Match#text -> String * * Notmalized text of matched string. **/ this.text = text; /** * Match#url -> String * * Normalized url of matched string. **/ this.url = text; } function createMatch(self, shift) { var match = new Match(self, shift); self.__compiled__[match.schema].normalize(match, self); return match; } /** * class LinkifyIt **/ /** * new LinkifyIt(schemas) * - schemas (Object): Optional. Additional schemas to validate (prefix/validator) * * Creates new linkifier instance with optional additional schemas. * Can be called without `new` keyword for convenience. * * By default understands: * * - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links * - "fuzzy" links and emails (example.com, foo@bar.com). * * `schemas` is an object, where each key/value describes protocol/rule: * * - __key__ - link prefix (usually, protocol name with `:` at the end, `skype:` * for example). `linkify-it` makes shure that prefix is not preceeded with * alphanumeric char and symbols. Only whitespaces and punctuation allowed. * - __value__ - rule to check tail after link prefix * - _String_ - just alias to existing rule * - _Object_ * - _validate_ - validator function (should return matched length on success), * or `RegExp`. * - _normalize_ - optional function to normalize text & url of matched result * (for example, for @twitter mentions). **/ function LinkifyIt(schemas) { if (!(this instanceof LinkifyIt)) { return new LinkifyIt(schemas); } // Cache last tested result. Used to skip repeating steps on next `match` call. this.__index__ = -1; this.__last_index__ = -1; // Next scan position this.__schema__ = ''; this.__text_cache__ = ''; this.__schemas__ = assign({}, defaultSchemas, schemas); this.__compiled__ = {}; this.__tlds__ = tlds_default; this.__tlds_replaced__ = false; this.re = {}; compile(this); } /** chainable * LinkifyIt#add(schema, definition) * - schema (String): rule name (fixed pattern prefix) * - definition (String|RegExp|Object): schema definition * * Add new rule definition. See constructor description for details. **/ LinkifyIt.prototype.add = function add(schema, definition) { this.__schemas__[schema] = definition; compile(this); return this; }; /** * LinkifyIt#test(text) -> Boolean * * Searches linkifiable pattern and returns `true` on success or `false` on fail. **/ LinkifyIt.prototype.test = function test(text) { // Reset scan cache this.__text_cache__ = text; this.__index__ = -1; if (!text.length) { return false; } var m, ml, me, len, shift, next, re, tld_pos, at_pos; // try to scan for link with schema - that's the most simple rule if (this.re.schema_test.test(text)) { re = this.re.schema_search; re.lastIndex = 0; while ((m = re.exec(text)) !== null) { len = this.testSchemaAt(text, m[2], re.lastIndex); if (len) { this.__schema__ = m[2]; this.__index__ = m.index + m[1].length; this.__last_index__ = m.index + m[0].length + len; break; } } } if (this.__compiled__['http:']) { // guess schemaless links tld_pos = text.search(this.re.host_fuzzy_test); if (tld_pos >= 0) { // if tld is located after found link - no need to check fuzzy pattern if (this.__index__ < 0 || tld_pos < this.__index__) { if ((ml = text.match(this.re.link_fuzzy)) !== null) { shift = ml.index + ml[1].length; if (this.__index__ < 0 || shift < this.__index__) { this.__schema__ = ''; this.__index__ = shift; this.__last_index__ = ml.index + ml[0].length; } } } } } if (this.__compiled__['mailto:']) { // guess schemaless emails at_pos = text.indexOf('@'); if (at_pos >= 0) { // We can't skip this check, because this cases are possible: // 192.168.1.1@gmail.com, my.in@example.com if ((me = text.match(this.re.email_fuzzy)) !== null) { shift = me.index + me[1].length; next = me.index + me[0].length; if (this.__index__ < 0 || shift < this.__index__ || (shift === this.__index__ && next > this.__last_index__)) { this.__schema__ = 'mailto:'; this.__index__ = shift; this.__last_index__ = next; } } } } return this.__index__ >= 0; }; /** * LinkifyIt#testSchemaAt(text, name, position) -> Number * - text (String): text to scan * - name (String): rule (schema) name * - position (Number): text offset to check from * * Similar to [[LinkifyIt#test]] but checks only specific protocol tail exactly * at given position. Returns length of found pattern (0 on fail). **/ LinkifyIt.prototype.testSchemaAt = function testSchemaAt(text, schema, pos) { // If not supported schema check requested - terminate if (!this.__compiled__[schema.toLowerCase()]) { return 0; } return this.__compiled__[schema.toLowerCase()].validate(text, pos, this); }; /** * LinkifyIt#match(text) -> Array|null * * Returns array of found link descriptions or `null` on fail. We strongly * to use [[LinkifyIt#test]] first, for best speed. * * ##### Result match description * * - __schema__ - link schema, can be empty for fuzzy links, or `//` for * protocol-neutral links. * - __index__ - offset of matched text * - __lastIndex__ - index of next char after mathch end * - __raw__ - matched text * - __text__ - normalized text * - __url__ - link, generated from matched text **/ LinkifyIt.prototype.match = function match(text) { var shift = 0, result = []; // Try to take previous element from cache, if .test() called before if (this.__index__ >= 0 && this.__text_cache__ === text) { result.push(createMatch(this, shift)); shift = this.__last_index__; } // Cut head if cache was used var tail = shift ? text.slice(shift) : text; // Scan string until end reached while (this.test(tail)) { result.push(createMatch(this, shift)); tail = tail.slice(this.__last_index__); shift += this.__last_index__; } if (result.length) { return result; } return null; }; /** chainable * LinkifyIt#tlds(list [, keepOld]) -> this * - list (Array): list of tlds * - keepOld (Boolean): merge with current list if `true` (`false` by default) * * Load (or merge) new tlds list. Those are user for fuzzy links (without prefix) * to avoid false positives. By default this algorythm used: * * - hostname with any 2-letter root zones are ok. * - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф * are ok. * - encoded (`xn--...`) root zones are ok. * * If list is replaced, then exact match for 2-chars root zones will be checked. **/ LinkifyIt.prototype.tlds = function tlds(list, keepOld) { list = Array.isArray(list) ? list : [ list ]; if (!keepOld) { this.__tlds__ = list.slice(); this.__tlds_replaced__ = true; compile(this); return this; } this.__tlds__ = this.__tlds__.concat(list) .sort() .filter(function(el, idx, arr) { return el !== arr[idx - 1]; }) .reverse(); compile(this); return this; }; /** * LinkifyIt#normalize(match) * * Default normalizer (if schema does not define it's own). **/ LinkifyIt.prototype.normalize = function normalize(match) { // Do minimal possible changes by default. Need to collect feedback prior // to move forward https://github.com/markdown-it/linkify-it/issues/1 if (!match.schema) { match.url = 'http://' + match.url; } if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) { match.url = 'mailto:' + match.url; } }; module.exports = LinkifyIt; },{"./lib/re":51}],51:[function(require,module,exports){ 'use strict'; // Use direct extract instead of `regenerate` to reduse browserified size var src_Any = exports.src_Any = require('uc.micro/properties/Any/regex').source; var src_Cc = exports.src_Cc = require('uc.micro/categories/Cc/regex').source; var src_Cf = exports.src_Cf = require('uc.micro/categories/Cf/regex').source; var src_Z = exports.src_Z = require('uc.micro/categories/Z/regex').source; var src_P = exports.src_P = require('uc.micro/categories/P/regex').source; // \p{\Z\P\Cc\CF} (white spaces + control + format + punctuation) var src_ZPCcCf = exports.src_ZPCcCf = [ src_Z, src_P, src_Cc, src_Cf ].join('|'); // All possible word characters (everything without punctuation, spaces & controls) // Defined via punctuation & spaces to save space // Should be something like \p{\L\N\S\M} (\w but without `_`) var src_pseudo_letter = '(?:(?!' + src_ZPCcCf + ')' + src_Any + ')'; // The same as abothe but without [0-9] var src_pseudo_letter_non_d = '(?:(?![0-9]|' + src_ZPCcCf + ')' + src_Any + ')'; //////////////////////////////////////////////////////////////////////////////// var src_ip4 = exports.src_ip4 = '(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'; exports.src_auth = '(?:(?:(?!' + src_Z + ').)+@)?'; var src_port = exports.src_port = '(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?'; var src_host_terminator = exports.src_host_terminator = '(?=$|' + src_ZPCcCf + ')(?!-|_|:\\d|\\.-|\\.(?!$|' + src_ZPCcCf + '))'; var src_path = exports.src_path = '(?:' + '[/?#]' + '(?:' + '(?!' + src_Z + '|[()[\\]{}.,"\'?!\\-]).|' + '\\[(?:(?!' + src_Z + '|\\]).)*\\]|' + '\\((?:(?!' + src_Z + '|[)]).)*\\)|' + '\\{(?:(?!' + src_Z + '|[}]).)*\\}|' + '\\"(?:(?!' + src_Z + '|["]).)+\\"|' + "\\'(?:(?!" + src_Z + "|[']).)+\\'|" + "\\'(?=" + src_pseudo_letter + ').|' + // allow `I'm_king` if no pair found '\\.(?!' + src_Z + '|[.]).|' + '\\-(?!' + src_Z + '|--(?:[^-]|$))(?:[-]+|.)|' + // `---` => long dash, terminate '\\,(?!' + src_Z + ').|' + // allow `,,,` in paths '\\!(?!' + src_Z + '|[!]).|' + '\\?(?!' + src_Z + '|[?]).' + ')+' + '|\\/' + ')?'; var src_email_name = exports.src_email_name = '[\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]+'; var src_xn = exports.src_xn = 'xn--[a-z0-9\\-]{1,59}'; // More to read about domain names // http://serverfault.com/questions/638260/ var src_domain_root = exports.src_domain_root = // Can't have digits and dashes '(?:' + src_xn + '|' + src_pseudo_letter_non_d + '{1,63}' + ')'; var src_domain = exports.src_domain = '(?:' + src_xn + '|' + '(?:' + src_pseudo_letter + ')' + '|' + // don't allow `--` in domain names, because: // - that can conflict with markdown — / – // - nobody use those anyway '(?:' + src_pseudo_letter + '(?:-(?!-)|' + src_pseudo_letter + '){0,61}' + src_pseudo_letter + ')' + ')'; var src_host = exports.src_host = '(?:' + src_ip4 + '|' + '(?:(?:(?:' + src_domain + ')\\.)*' + src_domain_root + ')' + ')'; var tpl_host_fuzzy = exports.tpl_host_fuzzy = '(?:' + src_ip4 + '|' + '(?:(?:(?:' + src_domain + ')\\.)+(?:%TLDS%))' + ')'; exports.src_host_strict = src_host + src_host_terminator; var tpl_host_fuzzy_strict = exports.tpl_host_fuzzy_strict = tpl_host_fuzzy + src_host_terminator; exports.src_host_port_strict = src_host + src_port + src_host_terminator; var tpl_host_port_fuzzy_strict = exports.tpl_host_port_fuzzy_strict = tpl_host_fuzzy + src_port + src_host_terminator; //////////////////////////////////////////////////////////////////////////////// // Main rules // Rude test fuzzy links by host, for quick deny exports.tpl_host_fuzzy_test = 'localhost|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:' + src_ZPCcCf + '|$))'; exports.tpl_email_fuzzy = '(^|>|' + src_Z + ')(' + src_email_name + '@' + tpl_host_fuzzy_strict + ')'; exports.tpl_link_fuzzy = // Fuzzy link can't be prepended with .:/\- and non punctuation. // but can start with > (markdown blockquote) '(^|(?![.:/\\-_@])(?:[$+<=>^`|]|' + src_ZPCcCf + '))' + '((?![$+<=>^`|])' + tpl_host_port_fuzzy_strict + src_path + ')'; },{"uc.micro/categories/Cc/regex":53,"uc.micro/categories/Cf/regex":54,"uc.micro/categories/P/regex":55,"uc.micro/categories/Z/regex":56,"uc.micro/properties/Any/regex":57}],52:[function(require,module,exports){ 'use strict'; var encodeCache = {}; // Create a lookup array where anything but characters in `chars` string // and alphanumeric chars is percent-encoded. // function getEncodeCache(exclude) { var i, ch, cache = encodeCache[exclude]; if (cache) { return cache; } cache = encodeCache[exclude] = []; for (i = 0; i < 128; i++) { ch = String.fromCharCode(i); if (/^[0-9a-z]$/i.test(ch)) { // always allow unencoded alphanumeric characters cache.push(ch); } else { cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2)); } } for (i = 0; i < exclude.length; i++) { cache[exclude.charCodeAt(i)] = exclude[i]; } return cache; } // Encode unsafe characters with percent-encoding, skipping already // encoded sequences. // // - string - string to encode // - exclude - list of characters to ignore (in addition to a-zA-Z0-9) // - keepEscaped - don't encode '%' in a correct escape sequence (default: true) // function encode(string, exclude, keepEscaped) { var i, l, code, nextCode, cache, result = ''; if (typeof exclude !== 'string') { // encode(string, keepEscaped) keepEscaped = exclude; exclude = encode.defaultChars; } if (typeof keepEscaped === 'undefined') { keepEscaped = true; } cache = getEncodeCache(exclude); for (i = 0, l = string.length; i < l; i++) { code = string.charCodeAt(i); if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) { if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) { result += string.slice(i, i + 3); i += 2; continue; } } if (code < 128) { result += cache[code]; continue; } if (code >= 0xD800 && code <= 0xDFFF) { if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) { nextCode = string.charCodeAt(i + 1); if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) { result += encodeURIComponent(string[i] + string[i + 1]); i++; continue; } } result += '%EF%BF%BD'; continue; } result += encodeURIComponent(string[i]); } return result; } encode.defaultChars = ";/?:@&=+$,-_.!~*'()#"; encode.componentChars = "-_.!~*'()"; module.exports = encode; },{}],53:[function(require,module,exports){ module.exports=/[\0-\x1F\x7F-\x9F]/ },{}],54:[function(require,module,exports){ module.exports=/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804\uDCBD|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/ },{}],55:[function(require,module,exports){ module.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDE38-\uDE3D]|\uD805[\uDCC6\uDDC1-\uDDC9\uDE41-\uDE43]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F/ },{}],56:[function(require,module,exports){ module.exports=/[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/ },{}],57:[function(require,module,exports){ module.exports=/[\0-\uD7FF\uDC00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF]/ },{}],58:[function(require,module,exports){ 'use strict'; module.exports = require('./lib/'); },{"./lib/":10}]},{},[58])(58) });