/** 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') ], [ 'backtick', require('./rules_inline/backtick') ], [ 'del', require('./rules_inline/del') ], [ '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) { var str = url.trim().toLowerCase(); // Care about digital entities "javascript:alert(1)" str = replaceEntities(str); 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; } if (state.level < maxNesting) { for (i = 0; i < len; i++) { if (rules[i](state, true)) { state.cacheSet(pos, state.pos); return; } } state.pos++; } else { // If nesting level exceeded - skip tail to the end. That's not ordinary // situation and we should not care about content. state.pos = state.max; } 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) { // 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.pos = end; break; } // Try all possible rules. // On success, rule should: // // - update `state.pos` // - update `state.tokens` // - return true 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;